package com.sun.tools.javac.code; import java.util.HashMap; import java.util.Map; import org.jmlspecs.openjml.JmlTokenKind; import org.jmlspecs.openjml.JmlTree; import org.jmlspecs.openjml.Strings; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.OperatorSymbol; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.comp.JmlAttr; import com.sun.tools.javac.jvm.ByteCodes; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Warner; /** Extends Types to provide JML specific behavior, in particular support for * JML primitive types. */ public class JmlTypes extends Types { /** The owning compilation context - not to be changed after construction */ final protected Context context; final protected Map<JmlTokenKind,JmlType> jmltypes = new HashMap<JmlTokenKind,JmlType>(); /** The singleton instance for the \TYPE JML type */ final public JmlType TYPE = new JmlType(JmlTokenKind.BSTYPEUC,"org.jmlspecs.utils.IJMLTYPE"); { jmltypes.put(JmlTokenKind.BSTYPEUC, TYPE); } /** The singleton instance for the \real JML type */ final public JmlType REAL = new JmlType(JmlTokenKind.BSREAL,Strings.jmlSpecsPackage + ".Real"); { jmltypes.put(JmlTokenKind.BSREAL, REAL); } /** The singleton instance for the \bigint JML type */ final public JmlType BIGINT = new JmlType(JmlTokenKind.BSBIGINT,"java.math.BigInteger"); { jmltypes.put(JmlTokenKind.BSBIGINT, BIGINT); } /** Returns the singleton instance of JmlTypes for this compilation context. */ public static JmlTypes instance(Context context) { JmlTypes instance = (JmlTypes)context.get(typesKey); if (instance == null) instance = new JmlTypes(context); return instance; } /** Called to register the class to be used in the tool chain. */ public static void preRegister(Context context) { context.put(Types.typesKey, new Context.Factory<Types>() { @Override public JmlTypes make(Context context) { return new JmlTypes(context); } }); } /** Constructs a new instance - should be used only by instance(), not called * directly; adds all function symbols for operations on JML primitive types. * @param context */ protected JmlTypes(Context context) { super(context); this.context = context; Symtab syms = Symtab.instance(context); syms.initType(BIGINT,"\\bigint"); syms.initType(TYPE,"\\TYPE"); syms.initType(REAL,"\\real"); enterBinop("==", TYPE, TYPE, syms.booleanType); enterBinop("!=", TYPE, TYPE, syms.booleanType); enterBinop("==", BIGINT, BIGINT, syms.booleanType); enterBinop("!=", BIGINT, BIGINT, syms.booleanType); enterBinop(">", BIGINT, BIGINT, syms.booleanType); enterBinop("<", BIGINT, BIGINT, syms.booleanType); enterBinop("<=", BIGINT, BIGINT, syms.booleanType); enterBinop(">=", BIGINT, BIGINT, syms.booleanType); enterUnop("---", BIGINT, BIGINT); // unary minus enterUnop("++", BIGINT, BIGINT); enterUnop("--", BIGINT, BIGINT); enterBinop("+", BIGINT, BIGINT, BIGINT); enterBinop("-", BIGINT, BIGINT, BIGINT); enterBinop("*", BIGINT, BIGINT, BIGINT); enterBinop("/", BIGINT, BIGINT, BIGINT); enterBinop("%", BIGINT, BIGINT, BIGINT); // FIXME - shift operators??? enterBinop("==", REAL, REAL, syms.booleanType); enterBinop("!=", REAL, REAL, syms.booleanType); enterBinop(">", REAL, REAL, syms.booleanType); enterBinop("<", REAL, REAL, syms.booleanType); enterBinop("<=", REAL, REAL, syms.booleanType); enterBinop(">=", REAL, REAL, syms.booleanType); enterUnop("---", REAL, REAL); // unary minus enterUnop("++", REAL, REAL); enterUnop("--", REAL, REAL); enterBinop("+", REAL, REAL, REAL); enterBinop("-", REAL, REAL, REAL); enterBinop("*", REAL, REAL, REAL); enterBinop("/", REAL, REAL, REAL); enterBinop("%", REAL, REAL, REAL); } /** Overrides Types.isSameType with functionality for JML primitive types. */ @Override public boolean isSameType(Type t, Type s) { if (t == s) return true; if (t instanceof JmlType || s instanceof JmlType) return false; return super.isSameType(t, s); } /** Overrides Types.disjointType with functionality for JML primitive types. */ @Override public boolean disjointType(Type t, Type s) { boolean bt = t instanceof JmlType; boolean bs = s instanceof JmlType; if (bt != bs) return true; if (!bt) return super.disjointType(t, s); return t != s; } /** Overrides Types.isAssignable with functionality for JML primitive types. */ @Override public boolean isAssignable(Type t, Type s, Warner warn) { if (s == t) return true; if (s == BIGINT) { if (isIntegral(t)) return true; if (repSym((JmlType)s) == t.tsym) return true; return false; } if (s == REAL) { if (isNumeric(t)) return true; if (repSym((JmlType)s) == t.tsym) return true; return false; } return super.isAssignable(t, s, warn); } /** True if the Java tag is a numeric type (not for JML types). */ public boolean isNumeric(Type t) { int i = t.getTag().ordinal(); return i >= TypeTag.BYTE.ordinal() && i <= TypeTag.DOUBLE.ordinal()|| t == BIGINT || t == REAL; } /** True if the Java tag is an integral type (not for JML types). */ public boolean isIntegral(Type t) { int i = t.getTag().ordinal(); return (i >= TypeTag.BYTE.ordinal() && i <= TypeTag.LONG.ordinal()) || t.getTag() == TypeTag.INT || t == BIGINT; } /** Overrides Types.isConvertible with functionality for JML primitive types. */ @Override public boolean isConvertible(Type t, Type s, Warner warn) { if (s instanceof JmlType) { if (s == BIGINT && isIntegral(t)) return true; if (s == BIGINT && repSym(BIGINT) == t.tsym) return true; if (s == REAL && isNumeric(t)) return true; if (s == REAL && repSym(REAL) == t.tsym) return true; } return super.isConvertible(t, s, warn); } /** Overrides Types.isSubtypeUnchecked with functionality for JML primitive types. */ @Override public boolean isSubtypeUnchecked(Type t, Type s, Warner warn) { if (s instanceof JmlType) { if (s == BIGINT) return isIntegral(t); if (s == REAL) return isNumeric(t); } return super.isSubtypeUnchecked(t, s, warn); } /** Overrides Types.boxedClass with functionality for JML primitive types. */ @Override public ClassSymbol boxedClass(Type t) { if (t instanceof JmlType) { return repSym((JmlType)t); } return reader.enterClass(syms.boxedName[t.getTag().ordinal()]); } /** Overrides Types.unboxedType with functionality for JML primitive types. */ @Override public Type unboxedType(Type t) { if (!allowBoxing) return Type.noType; if (t.tsym == repSym(BIGINT)) return BIGINT; if (t.tsym == repSym(REAL)) return REAL; if (t.tsym == repSym(TYPE)) return TYPE; return super.unboxedType(t); } /** Overrides Types.isSubtype with functionality for JML primitive types. */ @Override public boolean isSubtype(Type t, Type s, boolean capture) { if (t == s) return true; if (t instanceof JmlType || s instanceof JmlType) return false; return super.isSubtype(t, s, capture); } /** Overrides Types.containsType with functionality for JML primitive types. */ @Override public boolean containsType(Type t, Type s) { if (t == s) return true; if (t instanceof JmlType || s instanceof JmlType) return false; return super.containsType(t, s); } /** Local method to create a binary operation on JML types */ private OperatorSymbol enterBinop(String name, Type left, Type right, Type res) { OperatorSymbol opsym = new OperatorSymbol( Names.instance(context).fromString(name), new MethodType(List.of(left, right), res, List.<Type>nil(), null), ByteCodes.nop, Symtab.instance(context).predefClass); Symtab.instance(context).predefClass.members().enter(opsym); return opsym; } /** Local method to create a unary operation on JML types */ private OperatorSymbol enterUnop(String name, Type arg, Type res) { OperatorSymbol sym = new OperatorSymbol(names.fromString(name), new MethodType(List.of(arg), res, List.<Type>nil(), null), ByteCodes.nop, Symtab.instance(context).predefClass); Symtab.instance(context).predefClass.members().enter(sym); return sym; } /** Overrides Types.isCastable with functionality for JML primitive types; * true if Type t is castable to Type s. */ @Override public boolean isCastable(Type t, Type s, Warner warn) { if (s == t) return true; if (s == BIGINT) { if (isIntegral(t)) return true; return false; } if (t == BIGINT) { if (isIntegral(s)) return true; return false; } if (s == REAL) { if (isNumeric(t)) return true; if (t == BIGINT) return true; return false; } if (t == REAL) { if (isNumeric(s)) return true; if (s == BIGINT) return true; return false; } return super.isCastable(t, s, warn); } // /** Overrides Types.lowerBound with functionality for JML primitive types. */ // @Override // public Type lowerBound(Type t) { // if (t instanceof JmlType) return t; // return super.lowerBound(t); // } // // /** Overrides Types.upperBound with functionality for JML primitive types. */ // @Override // public Type upperBound(Type t) { // if (t instanceof JmlType) return t; // return super.upperBound(t); // } /** Returns an AST for the type representing the given JML primitive type */ public JCExpression repType(DiagnosticPosition pos, JmlType t) { ClassSymbol sym = repSym(t); return JmlTree.Maker.instance(context).at(pos).Type(sym.type); } /** Returns the ClassSymbol for the RAC representation of the given JML primitive type */ public ClassSymbol repSym(JmlType t) { if (t.repSym == null) { String fqName = t.fqName; t.repSym = JmlAttr.instance(context).createClass(fqName); } return t.repSym; } /** Returns true if the given type is a JML primitive type. */ public boolean isJmlType(Type t) { return t.getTag() == TypeTag.NONE || t.getTag() == TypeTag.UNKNOWN; // FIXME - needs review } /** Returns true if the given type is a JML primitive type. */ public boolean isJmlRepType(Type t) { return t.tsym == BIGINT.repSym || t.tsym == REAL.repSym || t.tsym == TYPE.repSym; // TODO - avoid having to list JML types } /** Returns true if the given token is the token for a JML primitive type. */ public boolean isJmlTypeToken(JmlTokenKind t) { return jmltypes.get(t) != null; } }