001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2006-2007 IBM Corp.
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * version 2 as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
017: * USA.
018: */
019: package xtc.lang;
020:
021: import java.io.File;
022: import java.util.Iterator;
023: import java.util.List;
024:
025: import xtc.lang.JavaEntities.SuperTypesIter;
026: import xtc.type.IntegerT;
027: import xtc.type.InterfaceT;
028: import xtc.type.MethodT;
029: import xtc.type.NumberT;
030: import xtc.type.Type;
031: import xtc.util.SymbolTable;
032:
033: /**
034: * Java type conversions and promotions.
035: *
036: * @author Martin Hirzel
037: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#27529">JLS 2 §5</a>
038: */
039: public final class JavaTypeConverter {
040: /**
041: * Perform assignment conversion. Assumes that src and tgt are
042: * either not aliases, or if aliases, are already resolved. May
043: * resolve other aliases, such as supertypes, method parameter and
044: * return types, etc.
045: *
046: * @return The converted type, or null if this kind of conversion does not apply.
047: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#184206">JLS 2 §5.2</a>
048: */
049: public static Type convertAssigning(final SymbolTable tab,
050: final List<File> paths, final Type tgt, final Type src) {
051: assert JavaEntities.isGeneralRValueT(tgt)
052: && JavaEntities.isGeneralRValueT(src);
053: final Type result = convertAssigningInternal(tab, paths, tgt,
054: src);
055: assert null == result || JavaEntities.isWrappedRValueT(result);
056: return result;
057: }
058:
059: private static Type convertAssigningInternal(final SymbolTable tab,
060: final List<File> paths, final Type tgt, final Type src) {
061: final Type resIdentity = convertIdentity(tgt, src);
062: if (null != resIdentity)
063: return resIdentity;
064: final Type resWidenPrimitive = widenPrimitive(tgt, src);
065: if (null != resWidenPrimitive)
066: return resWidenPrimitive;
067: final Type resWidenReference = widenReference(tab, paths, tgt,
068: src);
069: if (null != resWidenReference)
070: return resWidenReference;
071: if (src.hasConstant()) {
072: final Type srcRaw = JavaEntities.resolveToRawRValue(src);
073: final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt);
074: if (srcRaw.isInteger() && tgtRaw.isInteger()) {
075: final NumberT srcInt = (IntegerT) srcRaw, tgtInt = (IntegerT) tgtRaw;
076: switch (srcInt.getKind()) {
077: case BYTE:
078: case SHORT:
079: case CHAR:
080: case INT:
081: switch (tgtInt.getKind()) {
082: case BYTE:
083: case SHORT:
084: case CHAR:
085: final Type resNarrowPrimitive = narrowPrimitive(
086: tgt, src);
087: final Object srcObj = src.getConstant()
088: .getValue();
089: final Object resObj = resNarrowPrimitive
090: .getConstant().getValue();
091: final long srcVal = srcObj instanceof Number ? ((Number) srcObj)
092: .longValue()
093: : ((Character) srcObj).charValue();
094: final long resVal = resObj instanceof Number ? ((Number) resObj)
095: .longValue()
096: : ((Character) resObj).charValue();
097: if (srcVal == resVal)
098: return resNarrowPrimitive;
099: }
100: }
101: }
102: }
103: return null;
104: }
105:
106: /**
107: * Perform casting conversion. Assumes that src and tgt are either
108: * not aliases, or if aliases, are already resolved. May resolve
109: * other aliases, such as supertypes, method parameter and return
110: * types, etc.
111: *
112: * @return The converted type, or null if this kind of conversion does not
113: * apply.
114: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#20232">JLS 2 §5.5</a>
115: */
116: public static Type convertCasting(final SymbolTable tab,
117: final List<File> paths, final Type tgt, final Type src) {
118: assert JavaEntities.isGeneralRValueT(tgt)
119: && JavaEntities.isGeneralRValueT(src);
120: final Type result = convertCastingInternal(tab, paths, tgt, src);
121: assert null == result || JavaEntities.isGeneralRValueT(result);
122: return result;
123: }
124:
125: private static Type convertCastingInternal(final SymbolTable tab,
126: final List<File> paths, final Type tgt, final Type src) {
127: final Type resIdentity = convertIdentity(tgt, src);
128: if (null != resIdentity)
129: return resIdentity;
130: final Type resWidenPrimitive = widenPrimitive(tgt, src);
131: if (null != resWidenPrimitive)
132: return resWidenPrimitive;
133: final Type resNarrowPrimitive = narrowPrimitive(tgt, src);
134: if (null != resNarrowPrimitive)
135: return resNarrowPrimitive;
136: final Type resWidenReference = widenReference(tab, paths, tgt,
137: src);
138: if (null != resWidenReference)
139: return resWidenReference;
140: final Type resNarrowReference = narrowReference(tab, paths,
141: tgt, src);
142: if (null != resNarrowReference)
143: return resNarrowReference;
144: return null;
145: }
146:
147: /**
148: * Perform identity conversion. Assumes that src and tgt are either
149: * not aliases, or if aliases, are already resolved.
150: *
151: * @return The converted type, or null if this kind of conversion does not apply.
152: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25209">JLS 2 §5.1.1</a>
153: */
154: public static Type convertIdentity(final Type tgt, final Type src) {
155: assert JavaEntities.isGeneralRValueT(tgt)
156: && JavaEntities.isGeneralRValueT(src);
157: final Type result = isIdentical(tgt, src) ? src : null;
158: assert null == result || JavaEntities.isGeneralRValueT(result);
159: return result;
160: }
161:
162: /**
163: * Perform method invocation conversion. Assumes that src and tgt
164: * are either not aliases, or if aliases, are already resolved. May
165: * resolve other aliases, such as supertypes, method parameter and
166: * return types, etc.
167: *
168: * @return The converted type, or null if this kind of conversion does not apply.
169: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#12687">JLS 2 §5.3</a>
170: */
171: public static Type convertParameterPassing(final SymbolTable tab,
172: final List<File> paths, final Type tgt, final Type src) {
173: assert JavaEntities.isGeneralRValueT(tgt)
174: && JavaEntities.isGeneralRValueT(src) : "tgt == " + tgt
175: + ", src == " + src;
176: final Type result = convertParameterPassingInternal(tab, paths,
177: tgt, src);
178: assert null == result || JavaEntities.isGeneralRValueT(result);
179: return result;
180: }
181:
182: private static Type convertParameterPassingInternal(
183: final SymbolTable tab, final List<File> paths,
184: final Type tgt, final Type src) {
185: if (isIdentical(tgt, src) || isWiderPrimitive(tgt, src)
186: || isWiderReference(tab, paths, tgt, src))
187: return tgt;
188: return null;
189: }
190:
191: /**
192: * Perform string conversion. May resolve JavaEntities.tString(tab)
193: * if that is an alias.
194: *
195: * @return The converted type, or null if this kind of conversion does not apply.
196: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#176886">JLS 2 §5.1.6</a>
197: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#186035">JLS 2 §5.4</a>
198: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#39990">JLS 2 §15.18.1</a>
199: */
200: public static Type convertString(final SymbolTable tab,
201: final Type src) {
202: assert JavaEntities.isGeneralRValueT(src);
203: final Type result = convertStringInternal(tab, src);
204: assert null == result || JavaEntities.isWrappedRValueT(result);
205: return result;
206: }
207:
208: private static Type convertStringInternal(final SymbolTable tab,
209: final Type src) {
210: final Type tgt = JavaEntities.tString(tab);
211: if (src.hasConstant()) {
212: if (JavaEntities.isNullT(src))
213: return tgt.annotate().constant("null");
214: return tgt.annotate().constant(
215: src.getConstant().getValue().toString());
216: }
217: return tgt;
218: }
219:
220: public static boolean isAssignable(final SymbolTable tab,
221: final List<File> paths, final Type tgt, final Type src) {
222: return null != convertAssigning(tab, paths, tgt, src);
223: }
224:
225: public static boolean isCastable(final SymbolTable tab,
226: final List<File> paths, final Type tgt, final Type src) {
227: return null != convertCasting(tab, paths, tgt, src);
228: }
229:
230: public static boolean isIdentical(final Type x, final Type y) {
231: if (x.isVoid() || y.isVoid())
232: return x.isVoid() && y.isVoid();
233: assert JavaEntities.isGeneralRValueT(x)
234: && JavaEntities.isGeneralRValueT(y);
235: final Type rawX = JavaEntities.resolveToRawRValue(x);
236: final Type rawY = JavaEntities.resolveToRawRValue(y);
237: if (rawX.isArray()) {
238: if (!rawY.isArray())
239: return false;
240: final Type elemX = JavaEntities.arrayElementType(rawX
241: .toArray());
242: final Type elemY = JavaEntities.arrayElementType(rawY
243: .toArray());
244: return isIdentical(elemX, elemY);
245: }
246: return rawX == rawY;
247: }
248:
249: public static boolean isNarrowerPrimitive(final Type tgt,
250: final Type src) {
251: return null != narrowPrimitive(tgt, src);
252: }
253:
254: public static boolean isNarrowerReference(final SymbolTable tab,
255: final List<File> paths, final Type tgt, final Type src) {
256: return null != narrowReference(tab, paths, tgt, src);
257: }
258:
259: public static boolean isParameterPassable(final SymbolTable tab,
260: final List<File> paths, final Type tgt, final Type src) {
261: return null != convertParameterPassing(tab, paths, tgt, src);
262: }
263:
264: public static boolean isPromotableBinaryNumeric(final Type other,
265: final Type src) {
266: final Type t1 = promoteBinaryNumeric(other, src);
267: final Type t2 = promoteBinaryNumeric(src, other);
268: return null != t1 && null != t2;
269: }
270:
271: /**
272: * Is src return-type substitutable for tgt? This method implements
273: * Java 3 Language Specification §8.4.5, unlike the rest of
274: * this type checker, which deals with Java 2 only.
275: */
276: public static boolean isReturnTypeSubstitutable(
277: final SymbolTable tab, final List<File> paths,
278: final Type tgt, final Type src) {
279: if (null == tgt || null == src)
280: return null == tgt && null == src;
281: if (JavaEntities.isPrimitiveT(src))
282: return isIdentical(src, tgt);
283: if (JavaEntities.isReferenceT(src))
284: return isIdentical(src, tgt)
285: || isWiderReference(tab, paths, src, tgt); //contravariant
286: if (src.isVoid())
287: return tgt.isVoid();
288: assert false;
289: return false;
290: }
291:
292: public static boolean isWiderPrimitive(final Type tgt,
293: final Type src) {
294: return null != widenPrimitive(tgt, src);
295: }
296:
297: public static boolean isWiderReference(final SymbolTable tab,
298: final List<File> paths, final Type tgt, final Type src) {
299: return null != widenReference(tab, paths, tgt, src);
300: }
301:
302: /**
303: * Perform narrowing primitive conversion.
304: *
305: * @return The converted type, or null if this kind of conversion does not apply.
306: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25363">JLS 2 §5.1.3</a>
307: */
308: public static strictfp Type narrowPrimitive(final Type tgt,
309: final Type src) {
310: assert JavaEntities.isGeneralRValueT(tgt)
311: && JavaEntities.isGeneralRValueT(src);
312: final Type result = narrowPrimitiveInternal(tgt, src);
313: assert null == result || JavaEntities.isGeneralRValueT(result)
314: && JavaEntities.resolveToRawRValue(result).isNumber();
315: return result;
316: }
317:
318: private static strictfp Type narrowPrimitiveInternal(
319: final Type tgt, final Type src) {
320: final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt);
321: final Type srcRaw = JavaEntities.resolveToRawRValue(src);
322: if (!srcRaw.isNumber() || !tgtRaw.isNumber())
323: return null;
324: final NumberT srcNum = (NumberT) srcRaw, tgtNum = (NumberT) tgtRaw;
325: final NumberT.Kind srcKind = srcNum.getKind(), tgtKind = tgtNum
326: .getKind();
327: switch (srcKind) {
328: case BYTE:
329: if (NumberT.Kind.CHAR != tgtKind)
330: return null;
331: break;
332: case SHORT:
333: if (NumberT.Kind.BYTE != tgtKind
334: && NumberT.Kind.CHAR != tgtKind)
335: return null;
336: break;
337: case CHAR:
338: if (NumberT.Kind.BYTE != tgtKind
339: && NumberT.Kind.SHORT != tgtKind)
340: return null;
341: break;
342: case INT:
343: if (NumberT.Kind.BYTE != tgtKind
344: && NumberT.Kind.SHORT != tgtKind
345: && NumberT.Kind.CHAR != tgtKind)
346: return null;
347: break;
348: case LONG:
349: if (NumberT.Kind.BYTE != tgtKind
350: && NumberT.Kind.SHORT != tgtKind
351: && NumberT.Kind.CHAR != tgtKind
352: && NumberT.Kind.INT != tgtKind)
353: return null;
354: break;
355: case FLOAT:
356: if (NumberT.Kind.BYTE != tgtKind
357: && NumberT.Kind.SHORT != tgtKind
358: && NumberT.Kind.CHAR != tgtKind
359: && NumberT.Kind.INT != tgtKind
360: && NumberT.Kind.LONG != tgtKind)
361: return null;
362: break;
363: case DOUBLE:
364: if (NumberT.Kind.BYTE != tgtKind
365: && NumberT.Kind.SHORT != tgtKind
366: && NumberT.Kind.CHAR != tgtKind
367: && NumberT.Kind.INT != tgtKind
368: && NumberT.Kind.LONG != tgtKind
369: && NumberT.Kind.FLOAT != tgtKind)
370: return null;
371: }
372: if (src.hasConstant()) {
373: final Object obj;
374: switch (srcKind) {
375: case BYTE: {
376: final byte v = ((Byte) src.getConstant().getValue())
377: .byteValue();
378: switch (tgtKind) {
379: case CHAR:
380: obj = new Character((char) v);
381: break;
382: default:
383: obj = null;
384: }
385: break;
386: }
387: case SHORT: {
388: final short v = ((Short) src.getConstant().getValue())
389: .shortValue();
390: switch (tgtKind) {
391: case BYTE:
392: obj = new Byte((byte) v);
393: break;
394: case CHAR:
395: obj = new Character((char) v);
396: break;
397: default:
398: obj = null;
399: }
400: break;
401: }
402: case CHAR: {
403: final char v = ((Character) src.getConstant()
404: .getValue()).charValue();
405: switch (tgtKind) {
406: case BYTE:
407: obj = new Byte((byte) v);
408: break;
409: case SHORT:
410: obj = new Short((short) v);
411: break;
412: default:
413: obj = null;
414: }
415: break;
416: }
417: case INT: {
418: final int v = ((Integer) src.getConstant().getValue())
419: .intValue();
420: switch (tgtKind) {
421: case BYTE:
422: obj = new Byte((byte) v);
423: break;
424: case SHORT:
425: obj = new Short((short) v);
426: break;
427: case CHAR:
428: obj = new Character((char) v);
429: break;
430: default:
431: obj = null;
432: }
433: break;
434: }
435: case LONG: {
436: final long v = ((Long) src.getConstant().getValue())
437: .longValue();
438: switch (tgtKind) {
439: case BYTE:
440: obj = new Byte((byte) v);
441: break;
442: case SHORT:
443: obj = new Short((short) v);
444: break;
445: case CHAR:
446: obj = new Character((char) v);
447: break;
448: case INT:
449: obj = new Integer((int) v);
450: break;
451: default:
452: obj = null;
453: }
454: break;
455: }
456: case FLOAT: {
457: final float v = ((Float) src.getConstant().getValue())
458: .floatValue();
459: switch (tgtKind) {
460: case BYTE:
461: obj = new Byte((byte) v);
462: break;
463: case SHORT:
464: obj = new Short((short) v);
465: break;
466: case CHAR:
467: obj = new Character((char) v);
468: break;
469: case INT:
470: obj = new Integer((int) v);
471: break;
472: case LONG:
473: obj = new Long((long) v);
474: break;
475: default:
476: obj = null;
477: }
478: break;
479: }
480: case DOUBLE: {
481: final double v = ((Double) src.getConstant().getValue())
482: .doubleValue();
483: switch (tgtKind) {
484: case BYTE:
485: obj = new Byte((byte) v);
486: break;
487: case SHORT:
488: obj = new Short((short) v);
489: break;
490: case CHAR:
491: obj = new Character((char) v);
492: break;
493: case INT:
494: obj = new Integer((int) v);
495: break;
496: case LONG:
497: obj = new Long((long) v);
498: break;
499: case FLOAT:
500: obj = new Float((float) v);
501: break;
502: default:
503: obj = null;
504: }
505: break;
506: }
507: default: {
508: obj = null;
509: break;
510: }
511: }
512: if (null == obj)
513: throw new Error();
514: return tgtRaw.annotate().constant(obj);
515: }
516: return tgt;
517: }
518:
519: /**
520: * Perform narrowing reference conversion. Assumes that src and tgt
521: * are either not aliases, or if aliases, are already resolved. May
522: * resolve other aliases, such as supertypes, method parameter and
523: * return types, etc.
524: *
525: * @return The converted type, or null if this kind of conversion does not apply.
526: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25379">JLS 2 §5.1.5</a>
527: */
528: public static Type narrowReference(final SymbolTable tab,
529: final List<File> paths, final Type tgt, final Type src) {
530: assert JavaEntities.isGeneralRValueT(tgt)
531: && JavaEntities.isGeneralRValueT(src);
532: final Type result = narrowReferenceInternal(tab, paths, tgt,
533: src);
534: assert null == result
535: || JavaEntities.isGeneralRValueT(result)
536: && !JavaEntities.isPrimitiveT(JavaEntities
537: .resolveToRawRValue(result));
538: return result;
539: }
540:
541: private static Type narrowReferenceInternal(final SymbolTable tab,
542: final List<File> paths, final Type tgt, final Type src) {
543: if (isIdentical(tgt, src))
544: return null; // an identity conversion is not a narrowing conversion
545: final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt);
546: if (isIdentical(JavaEntities.tObject(tab), src))
547: if (JavaEntities.isWrappedClassT(tgt)
548: || JavaEntities.isWrappedInterfaceT(tgt)
549: || tgtRaw.isArray())
550: return tgt;
551: if (JavaEntities.isWrappedClassT(src)
552: || JavaEntities.isWrappedInterfaceT(src))
553: for (final Iterator<Type> i = new SuperTypesIter(tab,
554: paths, src); i.hasNext();)
555: if (isIdentical(tgt, i.next()))
556: return null;
557: boolean tgtInheritsFromSrc = false;
558: if (JavaEntities.isWrappedClassT(tgt)
559: || JavaEntities.isWrappedInterfaceT(tgt))
560: for (final Iterator<Type> i = new SuperTypesIter(tab,
561: paths, tgt); i.hasNext();)
562: if (isIdentical(i.next(), src)) {
563: tgtInheritsFromSrc = true;
564: break;
565: }
566: if (JavaEntities.isWrappedClassT(src)) {
567: if (JavaEntities.isWrappedClassT(tgt) && tgtInheritsFromSrc)
568: return tgt;
569: if (JavaEntities.isWrappedInterfaceT(tgt)
570: && !src.hasAttribute(JavaEntities
571: .nameToModifier("final")))
572: return tgt;
573: }
574: final Type srcRaw = JavaEntities.resolveToRawRValue(src);
575: if (JavaEntities.isWrappedInterfaceT(src)) {
576: if (JavaEntities.isWrappedClassT(tgt))
577: if (tgtInheritsFromSrc
578: || !tgt.hasAttribute(JavaEntities
579: .nameToModifier("final")))
580: return tgt;
581: if (JavaEntities.isWrappedInterfaceT(tgt)) {
582: final List<Type> srcMethods = ((InterfaceT) srcRaw)
583: .getMethods();
584: final List<Type> tgtMethods = ((InterfaceT) tgtRaw)
585: .getMethods();
586: if (100 < srcMethods.size() * tgtMethods.size())
587: throw new Error(
588: "implement sub-quadratic algorithm here");
589: for (final Type wrappedMethA : srcMethods)
590: for (final Type wrappedMethB : tgtMethods) {
591: final MethodT methA = wrappedMethA.toMethod();
592: final MethodT methB = wrappedMethB.toMethod();
593: if (methA.getName().equals(methB.getName())
594: && !isIdentical(methA.getResult(),
595: methB.getResult())) {
596: final List<Type> lParamA = methA
597: .getParameters();
598: final List<Type> lParamB = methB
599: .getParameters();
600: if (lParamA.size() == lParamB.size()) {
601: final Iterator<Type> iParamA = lParamA
602: .iterator();
603: final Iterator<Type> iParamB = lParamB
604: .iterator();
605: boolean sameSignature = true;
606: while (sameSignature
607: && iParamA.hasNext()) {
608: final Type a = JavaEntities
609: .dereference(iParamA.next());
610: final Type b = JavaEntities
611: .dereference(iParamB.next());
612: sameSignature = isIdentical(a, b);
613: }
614: if (sameSignature)
615: return null;
616: }
617: }
618: }
619: return tgt;
620: }
621: }
622: if (tgtRaw.isArray() && srcRaw.isArray()) {
623: final Type tgtElem = JavaEntities.arrayElementType(tgtRaw
624: .toArray());
625: final Type srcElem = JavaEntities.arrayElementType(srcRaw
626: .toArray());
627: if (isNarrowerReference(tab, paths, tgtElem, srcElem))
628: return tgt;
629: }
630: return null;
631: }
632:
633: /**
634: * Perform binary numeric promotion. Given a binary expression "a op
635: * b", this method should be called twice: once with src=a and
636: * other=b, and once with src=b and other=a.
637: *
638: * @param src The type of the operand that is being promoted.
639: * @param other The type of the other operand.
640: * @return The converted type of the src operand, or null if this kind of
641: * conversion does not apply.
642: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#170983">JLS 2 §5.6.2</a>
643: */
644: public static Type promoteBinaryNumeric(final Type other,
645: final Type src) {
646: assert JavaEntities.isGeneralRValueT(other)
647: && JavaEntities.isGeneralRValueT(src);
648: final Type result = promoteBinaryNumericInternal(other, src);
649: assert null == result || JavaEntities.isGeneralRValueT(result)
650: && JavaEntities.resolveToRawRValue(result).isNumber();
651: return result;
652: }
653:
654: private static Type promoteBinaryNumericInternal(final Type other,
655: final Type src) {
656: final Type srcRaw = JavaEntities.resolveToRawRValue(src);
657: final Type otherRaw = JavaEntities.resolveToRawRValue(other);
658: if (!srcRaw.isNumber() || !otherRaw.isNumber())
659: return null;
660: final NumberT srcNum = (NumberT) srcRaw, otherNum = (NumberT) otherRaw;
661: final NumberT.Kind srcKind = srcNum.getKind(), otherKind = otherNum
662: .getKind();
663: switch (otherKind) {
664: case DOUBLE:
665: switch (srcKind) {
666: case DOUBLE:
667: return src;
668: }
669: return widenPrimitive(other, src);
670: case FLOAT:
671: switch (srcKind) {
672: case DOUBLE:
673: case FLOAT:
674: return src;
675: }
676: return widenPrimitive(other, src);
677: case LONG:
678: switch (srcKind) {
679: case DOUBLE:
680: case FLOAT:
681: case LONG:
682: return src;
683: }
684: return widenPrimitive(other, src);
685: default:
686: switch (srcKind) {
687: case DOUBLE:
688: case FLOAT:
689: case LONG:
690: case INT:
691: return src;
692: }
693: return widenPrimitive(JavaEntities.nameToBaseType("int"),
694: src);
695: }
696: }
697:
698: /**
699: * Perform unary numeric promotion.
700: *
701: * @return The converted type, or null if this kind of conversion does not apply.
702: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#170952">JLS 2 §5.6.1</a>
703: */
704: public static Type promoteUnaryNumeric(final Type src) {
705: assert JavaEntities.isGeneralRValueT(src);
706: final Type result = promoteUnaryNumericInternal(src);
707: assert null == result || JavaEntities.isGeneralRValueT(result)
708: && JavaEntities.resolveToRawRValue(result).isNumber();
709: return result;
710: }
711:
712: private static Type promoteUnaryNumericInternal(final Type src) {
713: final Type srcRaw = JavaEntities.resolveToRawRValue(src);
714: if (srcRaw.isNumber()) {
715: final NumberT.Kind srcKind = ((NumberT) srcRaw).getKind();
716: final Type tgt = JavaEntities.nameToBaseType("int");
717: switch (srcKind) {
718: case BYTE:
719: case SHORT:
720: case CHAR:
721: return widenPrimitive(tgt, src);
722: case INT:
723: case LONG:
724: case FLOAT:
725: case DOUBLE:
726: return src;
727: }
728: }
729: return null;
730: }
731:
732: /**
733: * Perform widening primitive conversion.
734: *
735: * @return The converted type, or null if this kind of conversion does not apply.
736: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25214">JLS 2 §5.1.2</a>
737: */
738: public static strictfp Type widenPrimitive(final Type tgt,
739: final Type src) {
740: assert JavaEntities.isGeneralRValueT(tgt)
741: && JavaEntities.isGeneralRValueT(src);
742: final Type result = widenPrimitiveInternal(tgt, src);
743: assert null == result || JavaEntities.isGeneralRValueT(result)
744: && JavaEntities.resolveToRawRValue(result).isNumber();
745: return result;
746: }
747:
748: private static strictfp Type widenPrimitiveInternal(final Type tgt,
749: final Type src) {
750: final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt);
751: final Type srcRaw = JavaEntities.resolveToRawRValue(src);
752: if (!JavaEntities.isPrimitiveT(srcRaw)
753: || !JavaEntities.isPrimitiveT(tgtRaw))
754: return null;
755: if (srcRaw.isBoolean() || tgtRaw.isBoolean())
756: return null;
757: final NumberT srcNum = (NumberT) srcRaw, tgtNum = (NumberT) tgtRaw;
758: final NumberT.Kind srcKind = srcNum.getKind(), tgtKind = tgtNum
759: .getKind();
760: switch (srcKind) {
761: case BYTE:
762: if (NumberT.Kind.SHORT != tgtKind
763: && NumberT.Kind.INT != tgtKind
764: && NumberT.Kind.LONG != tgtKind
765: && NumberT.Kind.FLOAT != tgtKind
766: && NumberT.Kind.DOUBLE != tgtKind)
767: return null;
768: break;
769: case SHORT:
770: if (NumberT.Kind.INT != tgtKind
771: && NumberT.Kind.LONG != tgtKind
772: && NumberT.Kind.FLOAT != tgtKind
773: && NumberT.Kind.DOUBLE != tgtKind)
774: return null;
775: break;
776: case CHAR:
777: if (NumberT.Kind.INT != tgtKind
778: && NumberT.Kind.LONG != tgtKind
779: && NumberT.Kind.FLOAT != tgtKind
780: && NumberT.Kind.DOUBLE != tgtKind)
781: return null;
782: break;
783: case INT:
784: if (NumberT.Kind.LONG != tgtKind
785: && NumberT.Kind.FLOAT != tgtKind
786: && NumberT.Kind.DOUBLE != tgtKind)
787: return null;
788: break;
789: case LONG:
790: if (NumberT.Kind.FLOAT != tgtKind
791: && NumberT.Kind.DOUBLE != tgtKind)
792: return null;
793: break;
794: case FLOAT:
795: if (NumberT.Kind.DOUBLE != tgtKind)
796: return null;
797: break;
798: case DOUBLE:
799: return null;
800: }
801: if (src.hasConstant()) {
802: final Object obj;
803: switch (srcKind) {
804: case BYTE: {
805: final byte v = ((Byte) src.getConstant().getValue())
806: .byteValue();
807: switch (tgtKind) {
808: case SHORT:
809: obj = new Short(v);
810: break;
811: case INT:
812: obj = new Integer(v);
813: break;
814: case LONG:
815: obj = new Long(v);
816: break;
817: case FLOAT:
818: obj = new Float(v);
819: break;
820: case DOUBLE:
821: obj = new Double(v);
822: break;
823: default:
824: obj = null;
825: }
826: break;
827: }
828: case SHORT: {
829: final short v = ((Short) src.getConstant().getValue())
830: .shortValue();
831: switch (tgtKind) {
832: case INT:
833: obj = new Integer(v);
834: break;
835: case LONG:
836: obj = new Long(v);
837: break;
838: case FLOAT:
839: obj = new Float(v);
840: break;
841: case DOUBLE:
842: obj = new Double(v);
843: break;
844: default:
845: obj = null;
846: }
847: break;
848: }
849: case CHAR: {
850: final char v = ((Character) src.getConstant()
851: .getValue()).charValue();
852: switch (tgtKind) {
853: case INT:
854: obj = new Integer(v);
855: break;
856: case LONG:
857: obj = new Long(v);
858: break;
859: case FLOAT:
860: obj = new Float(v);
861: break;
862: case DOUBLE:
863: obj = new Double(v);
864: break;
865: default:
866: obj = null;
867: }
868: break;
869: }
870: case INT: {
871: final int v = ((Integer) src.getConstant().getValue())
872: .intValue();
873: switch (tgtKind) {
874: case LONG:
875: obj = new Long(v);
876: break;
877: case FLOAT:
878: obj = new Float(v);
879: break;
880: case DOUBLE:
881: obj = new Double(v);
882: break;
883: default:
884: obj = null;
885: }
886: break;
887: }
888: case LONG: {
889: final long v = ((Long) src.getConstant().getValue())
890: .longValue();
891: switch (tgtKind) {
892: case FLOAT:
893: obj = new Float(v);
894: break;
895: case DOUBLE:
896: obj = new Double(v);
897: break;
898: default:
899: obj = null;
900: }
901: break;
902: }
903: case FLOAT: {
904: final float v = ((Float) src.getConstant().getValue())
905: .floatValue();
906: switch (tgtKind) {
907: case DOUBLE:
908: obj = new Double(v);
909: break;
910: default:
911: obj = null;
912: }
913: break;
914: }
915: default: {
916: obj = null;
917: break;
918: }
919: }
920: if (null == obj)
921: throw new Error();
922: return tgtRaw.annotate().constant(obj);
923: }
924: return tgt;
925: }
926:
927: /**
928: * Perform widening reference conversion. Assumes that src and tgt
929: * are either not aliases, or if aliases, are already resolved. May
930: * resolve other aliases, such as supertypes, method parameter and
931: * return types, etc.
932: *
933: * @return The converted type, or null if this kind of conversion does not apply.
934: * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25215">JLS 2 §5.1.4</a>
935: */
936: public static Type widenReference(final SymbolTable tab,
937: final List<File> paths, final Type tgt, final Type src) {
938: assert JavaEntities.isGeneralRValueT(src)
939: && JavaEntities.isGeneralRValueT(tgt);
940: final Type result = widenReferenceInternal(tab, paths, tgt, src);
941: assert null == result
942: || JavaEntities.isGeneralRValueT(result)
943: && !JavaEntities.isPrimitiveT(JavaEntities
944: .resolveToRawRValue(result));
945: return result;
946: }
947:
948: private static Type widenReferenceInternal(final SymbolTable tab,
949: final List<File> paths, final Type tgt, final Type src) {
950: if (isIdentical(tgt, src))
951: return null; // an identity conversion is not a widening conversion
952: final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt);
953: if (JavaEntities.isNullT(src))
954: if (JavaEntities.isWrappedClassT(tgt)
955: || JavaEntities.isWrappedInterfaceT(tgt)
956: || tgtRaw.isArray())
957: return tgt;
958: final Type srcRaw = JavaEntities.resolveToRawRValue(src);
959: if (srcRaw.isClass() || srcRaw.isInterface()) {
960: if (isIdentical(tgt, JavaEntities.tObject(tab)))
961: return tgt;
962: for (Iterator<Type> i = new SuperTypesIter(tab, paths, src); i
963: .hasNext();)
964: if (isIdentical(tgt, i.next()))
965: return tgt;
966: }
967: if (srcRaw.isArray()) {
968: if (isIdentical(tgt, JavaEntities.tObjectAlias(tab))
969: || isIdentical(tgt, JavaEntities.tCloneable(tab))
970: || isIdentical(tgt, JavaEntities.tSerializable(tab)))
971: return tgt;
972: if (tgtRaw.isArray()) {
973: final Type tgtElem = JavaEntities
974: .arrayElementType(tgtRaw.toArray());
975: final Type srcElem = JavaEntities
976: .arrayElementType(srcRaw.toArray());
977: if (isWiderReference(tab, paths, tgtElem, srcElem))
978: return tgt;
979: }
980: }
981: return null;
982: }
983: }
|