/* * xtc - The eXTensible Compiler * Copyright (C) 2006-2007 IBM Corp. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.lang; import java.io.File; import java.util.Iterator; import java.util.List; import xtc.lang.JavaEntities.SuperTypesIter; import xtc.type.IntegerT; import xtc.type.InterfaceT; import xtc.type.MethodT; import xtc.type.NumberT; import xtc.type.Type; import xtc.util.SymbolTable; /** * Java type conversions and promotions. * * @author Martin Hirzel * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#27529">JLS 2 §5</a> */ public final class JavaTypeConverter { /** * Perform assignment conversion. Assumes that src and tgt are * either not aliases, or if aliases, are already resolved. May * resolve other aliases, such as supertypes, method parameter and * return types, etc. * * @return The converted type, or null if this kind of conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#184206">JLS 2 §5.2</a> */ public static Type convertAssigning(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { assert JavaEntities.isGeneralRValueT(tgt) && JavaEntities.isGeneralRValueT(src); final Type result = convertAssigningInternal(tab, paths, tgt, src); assert null == result || JavaEntities.isWrappedRValueT(result); return result; } private static Type convertAssigningInternal(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { final Type resIdentity = convertIdentity(tgt, src); if (null != resIdentity) return resIdentity; final Type resWidenPrimitive = widenPrimitive(tgt, src); if (null != resWidenPrimitive) return resWidenPrimitive; final Type resWidenReference = widenReference(tab, paths, tgt, src); if (null != resWidenReference) return resWidenReference; if (src.hasConstant()) { final Type srcRaw = JavaEntities.resolveToRawRValue(src); final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt); if (srcRaw.isInteger() && tgtRaw.isInteger()) { final NumberT srcInt = (IntegerT) srcRaw, tgtInt = (IntegerT) tgtRaw; switch (srcInt.getKind()) { case BYTE: case SHORT: case CHAR: case INT: switch (tgtInt.getKind()) { case BYTE: case SHORT: case CHAR: final Type resNarrowPrimitive = narrowPrimitive(tgt, src); final Object srcObj = src.getConstant().getValue(); final Object resObj = resNarrowPrimitive.getConstant().getValue(); final long srcVal = srcObj instanceof Number ? ((Number)srcObj).longValue() : ((Character)srcObj).charValue(); final long resVal = resObj instanceof Number ? ((Number)resObj).longValue() : ((Character)resObj).charValue(); if (srcVal == resVal) return resNarrowPrimitive; } } } } return null; } /** * Perform casting conversion. Assumes that src and tgt are either * not aliases, or if aliases, are already resolved. May resolve * other aliases, such as supertypes, method parameter and return * types, etc. * * @return The converted type, or null if this kind of conversion does not * apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#20232">JLS 2 §5.5</a> */ public static Type convertCasting(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { assert JavaEntities.isGeneralRValueT(tgt) && JavaEntities.isGeneralRValueT(src); final Type result = convertCastingInternal(tab, paths, tgt, src); assert null == result || JavaEntities.isGeneralRValueT(result); return result; } private static Type convertCastingInternal(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { final Type resIdentity = convertIdentity(tgt, src); if (null != resIdentity) return resIdentity; final Type resWidenPrimitive = widenPrimitive(tgt, src); if (null != resWidenPrimitive) return resWidenPrimitive; final Type resNarrowPrimitive = narrowPrimitive(tgt, src); if (null != resNarrowPrimitive) return resNarrowPrimitive; final Type resWidenReference = widenReference(tab, paths, tgt, src); if (null != resWidenReference) return resWidenReference; final Type resNarrowReference = narrowReference(tab, paths, tgt, src); if (null != resNarrowReference) return resNarrowReference; return null; } /** * Perform identity conversion. Assumes that src and tgt are either * not aliases, or if aliases, are already resolved. * * @return The converted type, or null if this kind of conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25209">JLS 2 §5.1.1</a> */ public static Type convertIdentity(final Type tgt, final Type src) { assert JavaEntities.isGeneralRValueT(tgt) && JavaEntities.isGeneralRValueT(src); final Type result = isIdentical(tgt, src) ? src : null; assert null == result || JavaEntities.isGeneralRValueT(result); return result; } /** * Perform method invocation conversion. Assumes that src and tgt * are either not aliases, or if aliases, are already resolved. May * resolve other aliases, such as supertypes, method parameter and * return types, etc. * * @return The converted type, or null if this kind of conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#12687">JLS 2 §5.3</a> */ public static Type convertParameterPassing(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { assert JavaEntities.isGeneralRValueT(tgt) && JavaEntities.isGeneralRValueT(src) : "tgt == " + tgt + ", src == " + src; final Type result = convertParameterPassingInternal(tab, paths, tgt, src); assert null == result || JavaEntities.isGeneralRValueT(result); return result; } private static Type convertParameterPassingInternal(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { if (isIdentical(tgt, src) || isWiderPrimitive(tgt, src) || isWiderReference(tab, paths, tgt, src)) return tgt; return null; } /** * Perform string conversion. May resolve JavaEntities.tString(tab) * if that is an alias. * * @return The converted type, or null if this kind of conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#176886">JLS 2 §5.1.6</a> * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#186035">JLS 2 §5.4</a> * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#39990">JLS 2 §15.18.1</a> */ public static Type convertString(final SymbolTable tab, final Type src) { assert JavaEntities.isGeneralRValueT(src); final Type result = convertStringInternal(tab, src); assert null == result || JavaEntities.isWrappedRValueT(result); return result; } private static Type convertStringInternal(final SymbolTable tab, final Type src) { final Type tgt = JavaEntities.tString(tab); if (src.hasConstant()) { if (JavaEntities.isNullT(src)) return tgt.annotate().constant("null"); return tgt.annotate().constant(src.getConstant().getValue().toString()); } return tgt; } public static boolean isAssignable(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { return null != convertAssigning(tab, paths, tgt, src); } public static boolean isCastable(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { return null != convertCasting(tab, paths, tgt, src); } public static boolean isIdentical(final Type x, final Type y) { if (x.isVoid() || y.isVoid()) return x.isVoid() && y.isVoid(); assert JavaEntities.isGeneralRValueT(x) && JavaEntities.isGeneralRValueT(y); final Type rawX = JavaEntities.resolveToRawRValue(x); final Type rawY = JavaEntities.resolveToRawRValue(y); if (rawX.isArray()) { if (!rawY.isArray()) return false; final Type elemX = JavaEntities.arrayElementType(rawX.toArray()); final Type elemY = JavaEntities.arrayElementType(rawY.toArray()); return isIdentical(elemX, elemY); } return rawX == rawY; } public static boolean isNarrowerPrimitive(final Type tgt, final Type src) { return null != narrowPrimitive(tgt, src); } public static boolean isNarrowerReference(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { return null != narrowReference(tab, paths, tgt, src); } public static boolean isParameterPassable(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { return null != convertParameterPassing(tab, paths, tgt, src); } public static boolean isPromotableBinaryNumeric(final Type other, final Type src) { final Type t1 = promoteBinaryNumeric(other, src); final Type t2 = promoteBinaryNumeric(src, other); return null != t1 && null != t2; } /** * Is src return-type substitutable for tgt? This method implements * Java 3 Language Specification §8.4.5, unlike the rest of * this type checker, which deals with Java 2 only. */ public static boolean isReturnTypeSubstitutable(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { if (null == tgt || null == src) return null == tgt && null == src; if (JavaEntities.isPrimitiveT(src)) return isIdentical(src, tgt); if (JavaEntities.isReferenceT(src)) return isIdentical(src, tgt) || isWiderReference(tab, paths, src, tgt); //contravariant if (src.isVoid()) return tgt.isVoid(); assert false; return false; } public static boolean isWiderPrimitive(final Type tgt, final Type src) { return null != widenPrimitive(tgt, src); } public static boolean isWiderReference(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { return null != widenReference(tab, paths, tgt, src); } /** * Perform narrowing primitive conversion. * * @return The converted type, or null if this kind of conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25363">JLS 2 §5.1.3</a> */ public static strictfp Type narrowPrimitive(final Type tgt, final Type src) { assert JavaEntities.isGeneralRValueT(tgt) && JavaEntities.isGeneralRValueT(src); final Type result = narrowPrimitiveInternal(tgt, src); assert null == result || JavaEntities.isGeneralRValueT(result) && JavaEntities.resolveToRawRValue(result).isNumber(); return result; } private static strictfp Type narrowPrimitiveInternal(final Type tgt, final Type src) { final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt); final Type srcRaw = JavaEntities.resolveToRawRValue(src); if (!srcRaw.isNumber() || !tgtRaw.isNumber()) return null; final NumberT srcNum = (NumberT) srcRaw, tgtNum = (NumberT) tgtRaw; final NumberT.Kind srcKind = srcNum.getKind(), tgtKind = tgtNum.getKind(); switch (srcKind) { case BYTE: if (NumberT.Kind.CHAR != tgtKind) return null; break; case SHORT: if (NumberT.Kind.BYTE != tgtKind && NumberT.Kind.CHAR != tgtKind) return null; break; case CHAR: if (NumberT.Kind.BYTE != tgtKind && NumberT.Kind.SHORT != tgtKind) return null; break; case INT: if (NumberT.Kind.BYTE != tgtKind && NumberT.Kind.SHORT != tgtKind && NumberT.Kind.CHAR != tgtKind) return null; break; case LONG: if (NumberT.Kind.BYTE != tgtKind && NumberT.Kind.SHORT != tgtKind && NumberT.Kind.CHAR != tgtKind && NumberT.Kind.INT != tgtKind) return null; break; case FLOAT: if (NumberT.Kind.BYTE != tgtKind && NumberT.Kind.SHORT != tgtKind && NumberT.Kind.CHAR != tgtKind && NumberT.Kind.INT != tgtKind && NumberT.Kind.LONG != tgtKind) return null; break; case DOUBLE: if (NumberT.Kind.BYTE != tgtKind && NumberT.Kind.SHORT != tgtKind && NumberT.Kind.CHAR != tgtKind && NumberT.Kind.INT != tgtKind && NumberT.Kind.LONG != tgtKind && NumberT.Kind.FLOAT != tgtKind) return null; } if (src.hasConstant()) { final Object obj; switch (srcKind) { case BYTE: { final byte v = ((Byte) src.getConstant().getValue()).byteValue(); switch (tgtKind) { case CHAR: obj = new Character((char) v); break; default: obj = null; } break; } case SHORT: { final short v = ((Short) src.getConstant().getValue()).shortValue(); switch (tgtKind) { case BYTE: obj = new Byte((byte) v); break; case CHAR: obj = new Character((char) v); break; default: obj = null; } break; } case CHAR: { final char v = ((Character) src.getConstant().getValue()).charValue(); switch (tgtKind) { case BYTE: obj = new Byte((byte) v); break; case SHORT: obj = new Short((short) v); break; default: obj = null; } break; } case INT: { final int v = ((Integer) src.getConstant().getValue()).intValue(); switch (tgtKind) { case BYTE: obj = new Byte((byte) v); break; case SHORT: obj = new Short((short) v); break; case CHAR: obj = new Character((char) v); break; default: obj = null; } break; } case LONG: { final long v = ((Long) src.getConstant().getValue()).longValue(); switch (tgtKind) { case BYTE: obj = new Byte((byte) v); break; case SHORT: obj = new Short((short) v); break; case CHAR: obj = new Character((char) v); break; case INT: obj = new Integer((int) v); break; default: obj = null; } break; } case FLOAT: { final float v = ((Float) src.getConstant().getValue()).floatValue(); switch (tgtKind) { case BYTE: obj = new Byte((byte) v); break; case SHORT: obj = new Short((short) v); break; case CHAR: obj = new Character((char) v); break; case INT: obj = new Integer((int) v); break; case LONG: obj = new Long((long) v); break; default: obj = null; } break; } case DOUBLE: { final double v = ((Double) src.getConstant().getValue()).doubleValue(); switch (tgtKind) { case BYTE: obj = new Byte((byte) v); break; case SHORT: obj = new Short((short) v); break; case CHAR: obj = new Character((char) v); break; case INT: obj = new Integer((int) v); break; case LONG: obj = new Long((long) v); break; case FLOAT: obj = new Float((float) v); break; default: obj = null; } break; } default: { obj = null; break; } } if (null == obj) throw new Error(); return tgtRaw.annotate().constant(obj); } return tgt; } /** * Perform narrowing reference conversion. Assumes that src and tgt * are either not aliases, or if aliases, are already resolved. May * resolve other aliases, such as supertypes, method parameter and * return types, etc. * * @return The converted type, or null if this kind of conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25379">JLS 2 §5.1.5</a> */ public static Type narrowReference(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { assert JavaEntities.isGeneralRValueT(tgt) && JavaEntities.isGeneralRValueT(src); final Type result = narrowReferenceInternal(tab, paths, tgt, src); assert null == result || JavaEntities.isGeneralRValueT(result) && !JavaEntities.isPrimitiveT(JavaEntities.resolveToRawRValue(result)); return result; } private static Type narrowReferenceInternal(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { if (isIdentical(tgt, src)) return null; // an identity conversion is not a narrowing conversion final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt); if (isIdentical(JavaEntities.tObject(tab), src)) if (JavaEntities.isWrappedClassT(tgt) || JavaEntities.isWrappedInterfaceT(tgt) || tgtRaw.isArray()) return tgt; if (JavaEntities.isWrappedClassT(src) || JavaEntities.isWrappedInterfaceT(src)) for (final Iterator<Type> i = new SuperTypesIter(tab, paths, src); i.hasNext();) if (isIdentical(tgt, i.next())) return null; boolean tgtInheritsFromSrc = false; if (JavaEntities.isWrappedClassT(tgt) || JavaEntities.isWrappedInterfaceT(tgt)) for (final Iterator<Type> i = new SuperTypesIter(tab, paths, tgt); i.hasNext();) if (isIdentical(i.next(), src)) { tgtInheritsFromSrc = true; break; } if (JavaEntities.isWrappedClassT(src)) { if (JavaEntities.isWrappedClassT(tgt) && tgtInheritsFromSrc) return tgt; if (JavaEntities.isWrappedInterfaceT(tgt) && !src.hasAttribute(JavaEntities.nameToModifier("final"))) return tgt; } final Type srcRaw = JavaEntities.resolveToRawRValue(src); if (JavaEntities.isWrappedInterfaceT(src)) { if (JavaEntities.isWrappedClassT(tgt)) if (tgtInheritsFromSrc || !tgt.hasAttribute(JavaEntities.nameToModifier("final"))) return tgt; if (JavaEntities.isWrappedInterfaceT(tgt)) { final List<Type> srcMethods = ((InterfaceT) srcRaw).getMethods(); final List<Type> tgtMethods = ((InterfaceT) tgtRaw).getMethods(); if (100 < srcMethods.size() * tgtMethods.size()) throw new Error("implement sub-quadratic algorithm here"); for (final Type wrappedMethA : srcMethods) for (final Type wrappedMethB : tgtMethods) { final MethodT methA = wrappedMethA.toMethod(); final MethodT methB = wrappedMethB.toMethod(); if (methA.getName().equals(methB.getName()) && !isIdentical(methA.getResult(), methB.getResult())) { final List<Type> lParamA = methA.getParameters(); final List<Type> lParamB = methB.getParameters(); if (lParamA.size() == lParamB.size()) { final Iterator<Type> iParamA = lParamA.iterator(); final Iterator<Type> iParamB = lParamB.iterator(); boolean sameSignature = true; while (sameSignature && iParamA.hasNext()) { final Type a = JavaEntities.dereference(iParamA.next()); final Type b = JavaEntities.dereference(iParamB.next()); sameSignature = isIdentical(a, b); } if (sameSignature) return null; } } } return tgt; } } if (tgtRaw.isArray() && srcRaw.isArray()) { final Type tgtElem = JavaEntities.arrayElementType(tgtRaw.toArray()); final Type srcElem = JavaEntities.arrayElementType(srcRaw.toArray()); if (isNarrowerReference(tab, paths, tgtElem, srcElem)) return tgt; } return null; } /** * Perform binary numeric promotion. Given a binary expression "a op * b", this method should be called twice: once with src=a and * other=b, and once with src=b and other=a. * * @param src The type of the operand that is being promoted. * @param other The type of the other operand. * @return The converted type of the src operand, or null if this kind of * conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#170983">JLS 2 §5.6.2</a> */ public static Type promoteBinaryNumeric(final Type other, final Type src) { assert JavaEntities.isGeneralRValueT(other) && JavaEntities.isGeneralRValueT(src); final Type result = promoteBinaryNumericInternal(other, src); assert null == result || JavaEntities.isGeneralRValueT(result) && JavaEntities.resolveToRawRValue(result).isNumber(); return result; } private static Type promoteBinaryNumericInternal(final Type other, final Type src) { final Type srcRaw = JavaEntities.resolveToRawRValue(src); final Type otherRaw = JavaEntities.resolveToRawRValue(other); if (!srcRaw.isNumber() || !otherRaw.isNumber()) return null; final NumberT srcNum = (NumberT) srcRaw, otherNum = (NumberT) otherRaw; final NumberT.Kind srcKind = srcNum.getKind(),otherKind = otherNum.getKind(); switch (otherKind) { case DOUBLE: switch (srcKind) { case DOUBLE: return src; } return widenPrimitive(other, src); case FLOAT: switch (srcKind) { case DOUBLE: case FLOAT: return src; } return widenPrimitive(other, src); case LONG: switch (srcKind) { case DOUBLE: case FLOAT: case LONG: return src; } return widenPrimitive(other, src); default: switch (srcKind) { case DOUBLE: case FLOAT: case LONG: case INT: return src; } return widenPrimitive(JavaEntities.nameToBaseType("int"), src); } } /** * Perform unary numeric promotion. * * @return The converted type, or null if this kind of conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#170952">JLS 2 §5.6.1</a> */ public static Type promoteUnaryNumeric(final Type src) { assert JavaEntities.isGeneralRValueT(src); final Type result = promoteUnaryNumericInternal(src); assert null == result || JavaEntities.isGeneralRValueT(result) && JavaEntities.resolveToRawRValue(result).isNumber(); return result; } private static Type promoteUnaryNumericInternal(final Type src) { final Type srcRaw = JavaEntities.resolveToRawRValue(src); if (srcRaw.isNumber()) { final NumberT.Kind srcKind = ((NumberT) srcRaw).getKind(); final Type tgt = JavaEntities.nameToBaseType("int"); switch (srcKind) { case BYTE: case SHORT: case CHAR: return widenPrimitive(tgt, src); case INT: case LONG: case FLOAT: case DOUBLE: return src; } } return null; } /** * Perform widening primitive conversion. * * @return The converted type, or null if this kind of conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25214">JLS 2 §5.1.2</a> */ public static strictfp Type widenPrimitive(final Type tgt, final Type src) { assert JavaEntities.isGeneralRValueT(tgt) && JavaEntities.isGeneralRValueT(src); final Type result = widenPrimitiveInternal(tgt, src); assert null == result || JavaEntities.isGeneralRValueT(result) && JavaEntities.resolveToRawRValue(result).isNumber(); return result; } private static strictfp Type widenPrimitiveInternal(final Type tgt, final Type src) { final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt); final Type srcRaw = JavaEntities.resolveToRawRValue(src); if (!JavaEntities.isPrimitiveT(srcRaw) || !JavaEntities.isPrimitiveT(tgtRaw)) return null; if (srcRaw.isBoolean() || tgtRaw.isBoolean()) return null; final NumberT srcNum = (NumberT) srcRaw, tgtNum = (NumberT) tgtRaw; final NumberT.Kind srcKind = srcNum.getKind(), tgtKind = tgtNum.getKind(); switch (srcKind) { case BYTE: if (NumberT.Kind.SHORT != tgtKind && NumberT.Kind.INT != tgtKind && NumberT.Kind.LONG != tgtKind && NumberT.Kind.FLOAT != tgtKind && NumberT.Kind.DOUBLE != tgtKind) return null; break; case SHORT: if (NumberT.Kind.INT != tgtKind && NumberT.Kind.LONG != tgtKind && NumberT.Kind.FLOAT != tgtKind && NumberT.Kind.DOUBLE != tgtKind) return null; break; case CHAR: if (NumberT.Kind.INT != tgtKind && NumberT.Kind.LONG != tgtKind && NumberT.Kind.FLOAT != tgtKind && NumberT.Kind.DOUBLE != tgtKind) return null; break; case INT: if (NumberT.Kind.LONG != tgtKind && NumberT.Kind.FLOAT != tgtKind && NumberT.Kind.DOUBLE != tgtKind) return null; break; case LONG: if (NumberT.Kind.FLOAT != tgtKind && NumberT.Kind.DOUBLE != tgtKind) return null; break; case FLOAT: if (NumberT.Kind.DOUBLE != tgtKind) return null; break; case DOUBLE: return null; } if (src.hasConstant()) { final Object obj; switch (srcKind) { case BYTE: { final byte v = ((Byte) src.getConstant().getValue()).byteValue(); switch (tgtKind) { case SHORT: obj = new Short(v); break; case INT: obj = new Integer(v); break; case LONG: obj = new Long(v); break; case FLOAT: obj = new Float(v); break; case DOUBLE: obj = new Double(v); break; default: obj = null; } break; } case SHORT: { final short v = ((Short) src.getConstant().getValue()).shortValue(); switch (tgtKind) { case INT: obj = new Integer(v); break; case LONG: obj = new Long(v); break; case FLOAT: obj = new Float(v); break; case DOUBLE: obj = new Double(v); break; default: obj = null; } break; } case CHAR: { final char v = ((Character) src.getConstant().getValue()).charValue(); switch (tgtKind) { case INT: obj = new Integer(v); break; case LONG: obj = new Long(v); break; case FLOAT: obj = new Float(v); break; case DOUBLE: obj = new Double(v); break; default: obj = null; } break; } case INT: { final int v = ((Integer) src.getConstant().getValue()).intValue(); switch (tgtKind) { case LONG: obj = new Long(v); break; case FLOAT: obj = new Float(v); break; case DOUBLE: obj = new Double(v); break; default: obj = null; } break; } case LONG: { final long v = ((Long) src.getConstant().getValue()).longValue(); switch (tgtKind) { case FLOAT: obj = new Float(v); break; case DOUBLE: obj = new Double(v); break; default: obj = null; } break; } case FLOAT: { final float v = ((Float) src.getConstant().getValue()).floatValue(); switch (tgtKind) { case DOUBLE: obj = new Double(v); break; default: obj = null; } break; } default: { obj = null; break; } } if (null == obj) throw new Error(); return tgtRaw.annotate().constant(obj); } return tgt; } /** * Perform widening reference conversion. Assumes that src and tgt * are either not aliases, or if aliases, are already resolved. May * resolve other aliases, such as supertypes, method parameter and * return types, etc. * * @return The converted type, or null if this kind of conversion does not apply. * @see <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25215">JLS 2 §5.1.4</a> */ public static Type widenReference(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { assert JavaEntities.isGeneralRValueT(src) && JavaEntities.isGeneralRValueT(tgt); final Type result = widenReferenceInternal(tab, paths, tgt, src); assert null == result || JavaEntities.isGeneralRValueT(result) && !JavaEntities.isPrimitiveT(JavaEntities.resolveToRawRValue(result)); return result; } private static Type widenReferenceInternal(final SymbolTable tab, final List<File> paths, final Type tgt, final Type src) { if (isIdentical(tgt, src)) return null; // an identity conversion is not a widening conversion final Type tgtRaw = JavaEntities.resolveToRawRValue(tgt); if (JavaEntities.isNullT(src)) if (JavaEntities.isWrappedClassT(tgt) || JavaEntities.isWrappedInterfaceT(tgt) || tgtRaw.isArray()) return tgt; final Type srcRaw = JavaEntities.resolveToRawRValue(src); if (srcRaw.isClass() || srcRaw.isInterface()) { if (isIdentical(tgt, JavaEntities.tObject(tab))) return tgt; for (Iterator<Type> i = new SuperTypesIter(tab, paths, src); i.hasNext();) if (isIdentical(tgt, i.next())) return tgt; } if (srcRaw.isArray()) { if (isIdentical(tgt, JavaEntities.tObjectAlias(tab)) || isIdentical(tgt, JavaEntities.tCloneable(tab)) || isIdentical(tgt, JavaEntities.tSerializable(tab))) return tgt; if (tgtRaw.isArray()) { final Type tgtElem = JavaEntities.arrayElementType(tgtRaw.toArray()); final Type srcElem = JavaEntities.arrayElementType(srcRaw.toArray()); if (isWiderReference(tab, paths, tgtElem, srcElem)) return tgt; } } return null; } }