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