1 package gnu.kawa.lispexpr; 2 import gnu.bytecode.*; 3 import gnu.math.*; 4 import gnu.expr.*; 5 import gnu.kawa.reflect.LazyType; 6 import gnu.lists.Sequence; 7 import gnu.text.Char; 8 import gnu.kawa.reflect.InstanceOf; 9 import gnu.mapping.Procedure; 10 import gnu.mapping.Values; 11 12 /** Use to implement some special types that convert differently. 13 * May not be needed/appropriate any more now that we support 14 * arithmetic on a mix of Java and Kawa types. 15 */ 16 17 public class LangPrimType extends PrimType 18 implements TypeValue, HasOwningField 19 { 20 Language language; 21 PrimType implementationType; 22 boolean isUnsigned; 23 24 public static final PrimType byteType = Type.byteType; 25 public static final PrimType shortType = Type.shortType; 26 public static final PrimType intType = Type.intType; 27 public static final PrimType longType = Type.longType; 28 public static final PrimType floatType = Type.floatType; 29 public static final PrimType doubleType = Type.doubleType; 30 public static final LangPrimType charType 31 = new LangPrimType(Type.charType); 32 public static final PrimType voidType = Type.voidType; 33 34 static final ClassType scmCharType = ClassType.make("gnu.text.Char"); 35 static final ClassType boxedStringCursorType = 36 ClassType.make("gnu.text.StringCursor"); 37 38 public static final LangPrimType characterType 39 = new LangPrimType(Type.intType); 40 static { characterType.setName("character"); } 41 public static final LangPrimType characterOrEofType 42 = new LangPrimType(Type.intType); 43 static { characterOrEofType.setName("character-or-eof"); } 44 45 public static final LangPrimType unsignedLongType 46 = new LangPrimType(Type.longType); 47 static { unsignedLongType.setName("ulong"); } 48 static { unsignedLongType.isUnsigned = true; } 49 static final ClassType boxedULongType = ClassType.make("gnu.math.ULong"); 50 51 public static final LangPrimType unsignedIntType 52 = new LangPrimType(Type.intType); 53 static { unsignedIntType.setName("uint"); } 54 static { unsignedIntType.isUnsigned = true; } 55 static final ClassType boxedUIntType = ClassType.make("gnu.math.UInt"); 56 57 public static final LangPrimType unsignedShortType 58 = new LangPrimType(Type.shortType); 59 static { unsignedShortType.setName("ushort"); } 60 static { unsignedShortType.isUnsigned = true; } 61 static final ClassType boxedUShortType = ClassType.make("gnu.math.UShort"); 62 63 public static final LangPrimType unsignedByteType 64 = new LangPrimType(Type.byteType); 65 static { unsignedByteType.setName("ubyte"); } 66 static { unsignedByteType.isUnsigned = true; } 67 static final ClassType boxedUByteType = ClassType.make("gnu.math.UByte"); 68 69 public static final LangPrimType stringCursorType 70 = new LangPrimType(Type.intType); 71 static { stringCursorType.setName("string-cursor"); } 72 73 /** Special type used for boolean-valued guard expressions in patterns. */ 74 public static final LangPrimType isTrueType 75 = new LangPrimType(Type.booleanType); 76 static { isTrueType.setName("true-values"); } 77 LangPrimType(PrimType type)78 public LangPrimType(PrimType type) { 79 super(type); 80 implementationType = type; 81 } 82 LangPrimType(PrimType type, Language language)83 public LangPrimType(PrimType type, Language language) { 84 super(type); 85 this.language = language; 86 implementationType = type; 87 } 88 LangPrimType(String nam, String sig, int siz, Class reflectClass)89 public LangPrimType(String nam, String sig, int siz, Class reflectClass) { 90 super (nam, sig, siz, reflectClass); 91 } 92 LangPrimType(String nam, String sig, int siz, Class reflectClass, Language language)93 public LangPrimType(String nam, String sig, int siz, Class reflectClass, 94 Language language) { 95 this(nam, sig, siz, reflectClass); 96 implementationType = Type.signatureToPrimitive(sig.charAt(0)); 97 this.language = language; 98 } 99 getImplementationType()100 public Type getImplementationType() { 101 return implementationType; 102 } 103 getOwningField()104 public Field getOwningField() { 105 if (language != null) { 106 return ((ClassType) Type.make(language.getClass())) 107 .getDeclaredField(getName()+ "Type"); 108 } 109 return null; 110 } 111 isUnsigned()112 public boolean isUnsigned() { return isUnsigned; } 113 114 @Override boxedType()115 public ClassType boxedType() { 116 if (this == characterType) 117 return scmCharType; 118 if (this == stringCursorType) 119 return boxedStringCursorType; 120 if (this == characterOrEofType) 121 return Type.objectType; 122 if (this == unsignedLongType) 123 return boxedULongType; 124 if (this == unsignedIntType) 125 return boxedUIntType; 126 if (this == unsignedShortType) 127 return boxedUShortType; 128 if (this == unsignedByteType) 129 return boxedUByteType; 130 return super.boxedType(); 131 } 132 133 @Override coerceFromObject(Object obj)134 public Object coerceFromObject(Object obj) { 135 if (obj.getClass() == reflectClass) 136 return obj; 137 char sig1 = getSignature().charAt(0); 138 switch (sig1) { 139 case 'J': 140 if (isUnsigned()) 141 return ULong.valueOf(((Number) obj).longValue()); 142 break; 143 case 'I': 144 if (this == characterType || this == characterOrEofType) { 145 int ival; 146 if (obj instanceof Integer) 147 return obj; 148 if (obj instanceof Char) 149 ival = ((Char) obj).intValue(); 150 else if (obj == Sequence.eofValue && this == characterOrEofType) 151 ival = -1; 152 else 153 ival = ((Character) obj).charValue(); 154 return Integer.valueOf(ival); 155 } 156 if (isUnsigned()) 157 return UInt.valueOf(((Number) obj).intValue()); 158 break; 159 case 'S': 160 if (isUnsigned()) 161 return UShort.valueOf(((Number) obj).shortValue()); 162 break; 163 case 'B': 164 if (isUnsigned()) 165 return UByte.valueOf(((Number) obj).byteValue()); 166 break; 167 case 'Z': 168 return language.isTrue(obj) ? Boolean.TRUE : Boolean.FALSE; 169 case 'C': 170 return new Character(((Char) obj).charValue()); 171 case 'V': 172 return Values.empty; 173 } 174 return super.coerceFromObject(obj); 175 } 176 charValue(Object value)177 public char charValue(Object value) { 178 if (value instanceof Character) 179 return ((Character) value).charValue(); 180 return ((Char) value).charValue(); 181 } 182 emitIsInstance(CodeAttr code)183 public void emitIsInstance(CodeAttr code) { 184 char sig1 = getSignature().charAt(0); 185 switch (sig1) { 186 case 'I': 187 String mname = this == characterType ? "isChar" 188 : this == characterOrEofType ? "isCharOrEof" : null; 189 if (mname != null) { 190 code.emitInvokeStatic(scmCharType.getDeclaredMethod(mname,1)); 191 return; 192 } 193 break; 194 case 'C': 195 ClassType scmCharType = ClassType.make("gnu.text.Char"); 196 code.emitInvokeStatic(scmCharType.getDeclaredMethod("isChar",1)); 197 return; 198 } 199 super.emitIsInstance(code); 200 } 201 emitCoerceFromObject(CodeAttr code)202 public void emitCoerceFromObject(CodeAttr code) { 203 char sig1 = getSignature().charAt(0); 204 switch (sig1) { 205 case 'I': 206 if (this == characterType || this == characterOrEofType) { 207 Type top = code.topType(); 208 if (top == javalangCharacterType) { 209 code.emitInvokeVirtual(javalangCharacterType.getDeclaredMethod("charValue", 0)); 210 return; 211 } 212 if (top == scmCharType) { 213 code.emitInvokeVirtual(scmCharType.getDeclaredMethod("intValue", 0)); 214 return; 215 } 216 } 217 if (this == stringCursorType) { 218 boxedStringCursorType.emitCoerceFromObject(code); 219 code.emitInvoke(boxedStringCursorType 220 .getDeclaredMethod("getValue", 0)); 221 return; 222 } 223 String mname = this == characterType ? "castToCharacter" 224 : this == characterOrEofType ? "castToCharacterOrEof" : null; 225 if (mname != null) { 226 code.emitInvokeStatic(scmCharType.getDeclaredMethod(mname,1)); 227 return; 228 } 229 break; 230 case 'Z': 231 Compilation.getCurrent().emitCoerceToBoolean(); 232 return; 233 case 'C': 234 if (code.topType() == javalangCharacterType) { 235 code.emitInvokeVirtual(javalangCharacterType.getDeclaredMethod("charValue", 0)); 236 return; 237 } 238 Method charValueMethod = scmCharType.getDeclaredMethod("castToChar",1); 239 code.emitInvokeStatic(charValueMethod); 240 return; 241 } 242 super.emitCoerceFromObject(code); 243 } 244 coerceToObject(Object obj)245 public Object coerceToObject(Object obj) { 246 char sig1 = getSignature().charAt(0); 247 switch (sig1) { 248 case 'I': 249 if (this == characterType || this == characterOrEofType) { 250 if (obj instanceof Integer) { 251 int ival = ((Integer) obj).intValue(); 252 if (ival >= 0) 253 return Char.make(ival); 254 if (ival == -1 && this == characterOrEofType) 255 return Sequence.eofValue; 256 } 257 if (obj instanceof Char 258 && (obj == Sequence.eofValue && this == characterOrEofType)) 259 return obj; 260 } 261 break; 262 case 'Z': 263 return language.booleanObject(((Boolean) obj).booleanValue()); 264 case 'C': 265 if (obj instanceof Char) 266 return obj; 267 return Char.make(((Character) obj).charValue()); 268 case 'V': 269 // Perhaps we should return Language.noValue() instead? 270 return gnu.mapping.Values.empty; 271 } 272 return super.coerceToObject(obj); 273 } 274 275 @Override convertToRaw(Object obj)276 public Object convertToRaw(Object obj) { 277 if (this == characterType || this == characterOrEofType) { 278 if (obj instanceof Char) 279 return Integer.valueOf(((Char) obj).intValue()); 280 if (obj == Sequence.eofValue && this == characterOrEofType) 281 return Integer.valueOf(-1); 282 } 283 return obj; 284 } 285 emitCoerceToObject(CodeAttr code)286 public void emitCoerceToObject(CodeAttr code) { 287 char sig1 = getSignature().charAt(0); 288 Type argType = null; 289 String cname = null; 290 switch (sig1) { 291 case 'I': 292 String mname = this == characterType ? "make" 293 : this == characterOrEofType ? "makeOrEof" : null; 294 if (mname != null) { 295 Method makeCharMethod = scmCharType.getDeclaredMethod(mname, 1); 296 code.emitInvokeStatic(makeCharMethod); 297 } else 298 super.emitCoerceToObject(code); 299 break; 300 case 'Z': 301 Compilation comp = Compilation.getCurrent(); 302 code.emitIfIntNotZero(); 303 comp.emitPushBoolean(true); 304 code.emitElse(); 305 comp.emitPushBoolean(false); 306 code.emitFi(); 307 break; 308 case 'C': 309 ClassType scmCharType = ClassType.make("gnu.text.Char"); 310 Method makeCharMethod = scmCharType.getDeclaredMethod("make", 1); 311 code.emitInvokeStatic(makeCharMethod); 312 break; 313 default: 314 super.emitCoerceToObject(code); 315 } 316 if (cname != null) { 317 ClassType clas = ClassType.make(cname); 318 Type[] args = { argType }; 319 code.emitInvokeStatic(clas.getDeclaredMethod("make", args)); 320 } 321 } 322 323 @Override compare(Type other)324 public int compare(Type other) { 325 if (other instanceof LazyType) 326 other = ((LazyType) other).getValueType(); 327 char sig1 = getSignature().charAt(0); 328 char sig2 = other.getSignature().charAt(0); 329 if (sig1 == 'Z') 330 return implementationType.compare(other); 331 if (this == other) 332 return 0; 333 if (this == stringCursorType) 334 return other == Type.objectType ? -1 : -3; 335 if (this == charType) { 336 if (other == characterType || other == characterOrEofType 337 || other == scmCharType) 338 return -1; 339 return getImplementationType().compare(other); 340 } 341 if (this == characterType) { 342 if (other == characterOrEofType) 343 return -1; 344 if (other == charType || sig2 == 'C') 345 return 1; 346 return scmCharType.compare(other); 347 } 348 if (this == characterOrEofType) { 349 if (other == characterType 350 || other == ClassType.make("gnu.lists.EofClass") // FIXME pre-alloate 351 || other == charType || other == scmCharType || sig2 == 'C') 352 return 1; 353 return other == Type.objectType ? -1 : -3; 354 } 355 if (other instanceof PrimType) { 356 return getImplementationType().compare(other); 357 } 358 if (sig1 == 'V') 359 return 1; 360 if (other instanceof LangObjType) 361 return swappedCompareResult(other.compare(this)); 362 return super.compare(other); 363 } 364 365 @Override isCompatibleWithValue(Type valueType)366 public int isCompatibleWithValue(Type valueType) { 367 if (this == charType && valueType == Type.charType) 368 return 2; 369 int r = super.isCompatibleWithValue(valueType); 370 if (r < 0 && getSignature().charAt(0) == 'Z') 371 r = 0; 372 return r; 373 } 374 emitTestIfNumber(Variable incoming, Declaration decl, Type type, Compilation comp)375 public static void emitTestIfNumber(Variable incoming, Declaration decl, 376 Type type, Compilation comp) { 377 CodeAttr code = comp.getCode(); 378 Type.javalangNumberType.emitIsInstance(code); 379 code.emitIfIntNotZero(); 380 if (decl != null) { 381 code.emitLoad(incoming); 382 type.emitCoerceFromObject(code); 383 decl.compileStore(comp); 384 } 385 } 386 emitTestIf(Variable incoming, Declaration decl, Compilation comp)387 public void emitTestIf(Variable incoming, Declaration decl, 388 Compilation comp) { 389 CodeAttr code = comp.getCode(); 390 char sig1 = getSignature().charAt(0); 391 if (incoming != null) 392 code.emitLoad(incoming); 393 if (this == isTrueType) { 394 code.emitIfIntNotZero(); 395 return; 396 } 397 switch (sig1) { 398 case 'Z': 399 Type.javalangBooleanType.emitIsInstance(code); 400 code.emitIfIntNotZero(); 401 if (decl != null) { 402 code.emitLoad(incoming); 403 super.emitCoerceFromObject(code); 404 decl.compileStore(comp); 405 } 406 return; 407 } 408 if (this == characterType || this == characterOrEofType 409 || this == charType) { 410 code.emitInvokeStatic(scmCharType 411 .getDeclaredMethod("checkCharOrEof", 1)); 412 } else if (this == stringCursorType) { 413 code.emitInvokeStatic(boxedStringCursorType 414 .getDeclaredMethod("checkStringCursor", 1)); 415 } else { 416 emitTestIfNumber(incoming, decl, getImplementationType(), comp); 417 return; 418 } 419 if (decl != null) { 420 code.emitDup(); 421 decl.compileStore(comp); 422 } 423 if (this == characterType || this == stringCursorType) { 424 code.emitIfIntGEqZero(); 425 } else if (this == charType) { 426 code.emitPushInt(16); 427 code.emitUshr(); 428 code.emitIfIntEqZero(); 429 } else if (this == characterOrEofType) { 430 code.emitPushInt(-1); 431 code.emitIfIntGEq(); 432 } else { 433 emitIsInstance(code); 434 code.emitIfIntNotZero(); 435 } 436 } 437 convertValue(Expression value)438 public Expression convertValue(Expression value) { 439 return null; 440 } 441 emitIsInstance(Variable incoming, Compilation comp, Target target)442 public void emitIsInstance(Variable incoming, 443 Compilation comp, Target target) { 444 InstanceOf.emitIsInstance(this, incoming, comp, target); 445 } 446 getConstructor()447 public Procedure getConstructor() { 448 return null; 449 } 450 convertIntegerLiteral(IntNum ivalue, PrimType type, boolean nativeValue)451 public static Object convertIntegerLiteral(IntNum ivalue, PrimType type, boolean nativeValue) { 452 boolean unsigned; 453 switch (type.getSignature().charAt(0)) { 454 case 'B': 455 unsigned = type == LangPrimType.unsignedByteType; 456 if (unsigned ? ivalue.inRange(0, 255) 457 : ivalue.inRange(Byte.MIN_VALUE, Byte.MAX_VALUE)) { 458 byte i = ivalue.byteValue(); 459 return unsigned && ! nativeValue ? UByte.valueOf(i) 460 : Byte.valueOf(i); 461 } 462 break; 463 case 'S': 464 unsigned = type == LangPrimType.unsignedShortType; 465 if (unsigned ? ivalue.inRange(0, 0xFFFF) 466 : ivalue.inRange(Short.MIN_VALUE, Short.MAX_VALUE)) { 467 short i = ivalue.shortValue(); 468 return unsigned && ! nativeValue ? UShort.valueOf(i) 469 : Short.valueOf(i); 470 } 471 break; 472 case 'I': 473 unsigned = type == LangPrimType.unsignedIntType; 474 if (unsigned ? ivalue.inRange(0, 0xFFFFFFFFl) 475 : ivalue.inRange(Integer.MIN_VALUE, Integer.MAX_VALUE)) { 476 int i = ivalue.intValue(); 477 return unsigned && ! nativeValue ? UInt.valueOf(i) 478 : Integer.valueOf(i); 479 } 480 break; 481 case 'J': 482 unsigned = type == LangPrimType.unsignedLongType; 483 if (unsigned ? (IntNum.compare(ivalue, 0) >= 0 484 && IntNum.compare(ivalue, 485 IntNum.valueOfUnsigned(-1)) <= 0) 486 : ivalue.inRange(Long.MIN_VALUE, Long.MAX_VALUE)) { 487 long i = ivalue.longValue(); 488 return unsigned && ! nativeValue ? ULong.valueOf(i) 489 : Long.valueOf(i); 490 } 491 break; 492 } 493 return null; 494 } 495 encodeType(Language language)496 public String encodeType(Language language) { 497 if (this == characterType) return "character"; 498 if (this == characterOrEofType) return "character-or-eof"; 499 if (this == stringCursorType) return "string-cursor"; 500 if (this == unsignedLongType) return "ulong"; 501 if (this == unsignedIntType) return "uint"; 502 if (this == unsignedShortType) return "ushort"; 503 if (this == unsignedByteType) return "ubyte"; 504 return null; 505 } 506 } 507