package org.jmlspecs.openjml; import static com.sun.tools.javac.code.Flags.BLOCK; import static com.sun.tools.javac.code.Flags.FINAL; import static com.sun.tools.javac.code.Flags.PUBLIC; import static com.sun.tools.javac.code.Flags.STATIC; import static com.sun.tools.javac.code.Flags.STRICTFP; import java.util.Arrays; import java.util.Map; import javax.lang.model.type.TypeKind; import org.jmlspecs.annotation.NonNull; import org.jmlspecs.annotation.Nullable; import org.jmlspecs.openjml.JmlTree.JmlBinary; import org.jmlspecs.openjml.JmlTree.JmlMethodInvocation; import org.jmlspecs.openjml.JmlTree.JmlStatementExpr; import org.jmlspecs.openjml.esc.Label; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type.ClassType; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.code.Type.TypeVar; import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.comp.JmlAttr; import com.sun.tools.javac.comp.JmlResolve; import com.sun.tools.javac.jvm.ClassReader; import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCAssignOp; import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCConditional; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCLiteral; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCUnary; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWildcard; 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.ListBuffer; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Log.WriterKind; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Position; /** This class holds a number of utility functions that create fragments of AST trees * (using a factory); the created trees are fully type and symbol attributed and so * are to be used in tree transformations after type attribution is complete * and successful. It is the user's responsibility to ensure that the resulting * tree is legal (including flow checks) since there will be no further checking; * errors may easily result in crashes in code generation. * It is expected that these utilities will also be used by extension classes. * * @author David Cok * */ public class JmlTreeUtils { /** The key to use to retrieve the instance of this class from the Context object. */ //@ non_null public static final Context.Key<JmlTreeUtils> jmltreeutilsKey = new Context.Key<JmlTreeUtils>(); /** A method that returns the unique instance of this class for the given Context * (creating it if it does not already exist). * * @param context the Context whose JmlTreeUtils instance is wanted * @return the singleton instance (per Context) of this class */ //@ non_null public static JmlTreeUtils instance(Context context) { JmlTreeUtils instance = context.get(jmltreeutilsKey); if (instance == null) { instance = new JmlTreeUtils(context); // registers itself } return instance; } /** The qualified name of the Utils class that contains runtime utility methods */ @NonNull final public static String utilsClassQualifiedName = org.jmlspecs.utils.Utils.class.getCanonicalName(); /** The Context in which this object was constructed */ //@ non_null @NonNull final protected Context context; /** The Attr tool for this context */ @NonNull final protected JmlAttr attr; /** The Log tool for this context */ @NonNull final protected Log log; /** The symbol table from the compilation context, initialized in the constructor */ @NonNull final public Symtab syms; /** The Names table from the compilation context, initialized in the constructor */ @NonNull final public Names names; /** The Utils tool for this context */ @NonNull final protected Utils utils; /** The Resolve tool for this compilation context */ @NonNull final protected JmlResolve rs; /** The Types utilities object for this compilation context */ @NonNull final protected JmlTypes types; // /** The Env in which to do resolving */ // @NonNull protected Env<AttrContext> attrEnv; /** The factory used to create AST nodes, initialized in the constructor */ @NonNull final public JmlTree.Maker factory; // Cached values of all of these symbols final public ClassSymbol utilsClass; final public JCIdent utilsClassIdent; final public Symbol andSymbol; final public Symbol orSymbol; final public Symbol intbitandSymbol; final public Symbol longbitandSymbol; final public Symbol bitorSymbol; final public Symbol notSymbol; final public Symbol objecteqSymbol; final public Symbol objectneSymbol; final public Symbol booleqSymbol; final public Symbol boolneSymbol; final public Symbol intminusSymbol; final public Symbol intplusSymbol; final public Symbol inteqSymbol; final public Symbol intneqSymbol; final public Symbol intgtSymbol; final public Symbol intltSymbol; final public Symbol intleSymbol; final public Symbol longeqSymbol; final public Symbol longleSymbol; final public Symbol longltSymbol; final public Symbol longminusSymbol; final public Symbol longplusSymbol; final public JCLiteral trueLit; final public JCLiteral falseLit; final public JCLiteral zero; final public JCLiteral one; final public JCLiteral longone; final public JCLiteral nullLit; final public JCLiteral maxIntLit; final public ClassSymbol assertionFailureClass; final public Name resultName; final public Name caughtException; final public Name TYPEName; /** Creates an instance in association with the given Context; * do not call the constructor * directly, except from derived classes. * * @param context The compilation context */ protected JmlTreeUtils(Context context) { this.context = context; context.put(jmltreeutilsKey, this); // self register this.attr = JmlAttr.instance(context); this.log = Log.instance(context); this.utils = Utils.instance(context); this.factory = JmlTree.Maker.instance(context); this.names = Names.instance(context); this.rs = JmlResolve.instance(context); this.syms = Symtab.instance(context); this.types = JmlTypes.instance(context); ClassReader reader = ClassReader.instance(context); Name utilsName = names.fromString(utilsClassQualifiedName); // flatname this.utilsClass = reader.enterClass(utilsName); utilsClassIdent = factory.Ident(utilsName); // FIXME - should this be some sort of Qualified Ident - a simple Ident seems to work utilsClassIdent.type = utilsClass.type; // ident containing flatname utilsClassIdent.sym = utilsClassIdent.type.tsym; andSymbol = findOpSymbol(JCTree.Tag.AND,syms.booleanType); orSymbol = findOpSymbol(JCTree.Tag.OR,syms.booleanType); intbitandSymbol = findOpSymbol(JCTree.Tag.BITAND,syms.intType); longbitandSymbol = findOpSymbol(JCTree.Tag.BITAND,syms.longType); bitorSymbol = findOpSymbol(JCTree.Tag.BITOR,syms.booleanType); notSymbol = findOpSymbol(JCTree.Tag.NOT,syms.booleanType); objecteqSymbol = findOpSymbol(JCTree.Tag.EQ,syms.objectType); objectneSymbol = findOpSymbol(JCTree.Tag.NE,syms.objectType); booleqSymbol = findOpSymbol(JCTree.Tag.EQ,syms.booleanType); boolneSymbol = findOpSymbol(JCTree.Tag.NE,syms.booleanType); intminusSymbol = findOpSymbol(JCTree.Tag.MINUS,syms.intType); intplusSymbol = findOpSymbol(JCTree.Tag.PLUS,syms.intType); inteqSymbol = findOpSymbol(JCTree.Tag.EQ,syms.intType); intneqSymbol = findOpSymbol(JCTree.Tag.NE,syms.intType); intgtSymbol = findOpSymbol(JCTree.Tag.GT,syms.intType); intltSymbol = findOpSymbol(JCTree.Tag.LT,syms.intType); intleSymbol = findOpSymbol(JCTree.Tag.LE,syms.intType); longleSymbol = findOpSymbol(JCTree.Tag.LE,syms.longType); longltSymbol = findOpSymbol(JCTree.Tag.LT,syms.longType); longeqSymbol = findOpSymbol(JCTree.Tag.EQ,syms.longType); longminusSymbol = findOpSymbol(JCTree.Tag.MINUS,syms.longType); longplusSymbol = findOpSymbol(JCTree.Tag.PLUS,syms.longType); trueLit = makeLit(0,syms.booleanType,1); falseLit = makeLit(0,syms.booleanType,0); zero = makeLit(0,syms.intType,0); one = makeLit(0,syms.intType,1); longone = makeLit(0,syms.longType,Long.valueOf(1L)); nullLit = makeLit(0,syms.botType, null); maxIntLit = makeLit(0,syms.intType,Integer.MAX_VALUE); assertionFailureClass = reader.enterClass(names.fromString(utilsClassQualifiedName+"$JmlAssertionFailure")); this.resultName = attr.resultName; this.caughtException = names.fromString("_JML$$$caughtException"); // FIXME - do we need this? this.TYPEName = names.fromString("TYPE"); } /** This sets the end position of newnode to be the same as that of srcnode; * the nodes are assumed to reference the same source file. */ public void copyEndPosition(JCTree newnode, JCTree srcnode) { EndPosTable z = log.currentSource().getEndPosTable(); if (z != null) { int end = srcnode.getEndPosition(z); z.storeEnd(newnode, end); } } /** Finds the Symbol for the operator given an optag (e.g. JCTree.Tag.AND) and an * argument type. Note that for object equality, the argument type must be * Object, not another reference class - better to use makeEqObject in that * case. * @param optag the optag of the builtin operator, e.g. JCTree.Tag.AND * @param argtype the argument type * @return the symbol of the operator */ public Symbol findOpSymbol(JCTree.Tag optag, Type argtype) { Name opName = TreeInfo.instance(context).operatorName(optag); Scope.Entry e = syms.predefClass.members().lookup(opName); //Type unboxedArgtype = unboxedType(argtype); if (true) {// || unboxedArgtype == argtype) { while (e != null && e.sym != null) { MethodType mt = (MethodType)e.sym.type; if (types.isSameType(mt.argtypes.head,argtype)) return e.sym; e = e.next(); } if (argtype != syms.objectType && !argtype.isPrimitive()) return findOpSymbol(optag,syms.objectType); // } else { // argtype = unboxedArgtype; // while (e != null && e.sym != null) { // MethodType mt = (MethodType)e.sym.type; // if (types.isSameType(mt.argtypes.head,argtype)) return e.sym; // e = e.next(); // } } throw new JmlInternalError("The operation symbol " + opName + " for type " + argtype + " could not be resolved"); } // FIXME - duplicated in JmlAssertionAdder protected Type unboxedType(Type t) { Type tt = types.unboxedType(t); if (tt == Type.noType) tt = t; return tt; } /** Returns an attributed AST for "org.jmlspecs.utils.Utils.<methodName>" */ public JCFieldAccess findUtilsMethod(int pos, String methodName) { Name n = names.fromString(methodName); // Presumes there is just one method with the given name - no overloading // by argument type Scope.Entry e = utilsClass.members().lookup(n); Symbol ms = e.sym; if (ms == null) { throw new JmlInternalError("Method " + methodName + " not found in Utils"); } JCFieldAccess m = factory.Select(utilsClassIdent,n); m.pos = pos; m.sym = ms; m.type = m.sym.type; return m; } public Symbol getSym(JCTree tree) { if (tree instanceof JCMethodInvocation) tree = ((JCMethodInvocation)tree).meth; if (tree instanceof JCIdent) { return ((JCIdent)tree).sym; } else if (tree instanceof JCFieldAccess) { return ((JCFieldAccess)tree).sym; } else { return null; } } // FIXME _ document; does this work correctly for this and super? /** Returns true if the argument is a reference type name (e.g., A or tt.A) * rather than an identifier for a variable or field or other * kind of expression. */ public boolean isATypeTree(JCExpression tree) { if (tree instanceof JCIdent) { Name n = ((JCIdent)tree).name; if (n == names._this || n == names._super) return false; // this (and super I think) are ClassSymbol, not VarSymbol return !(((JCIdent)tree).sym instanceof VarSymbol); } if (tree instanceof JCFieldAccess) { return !(((JCFieldAccess)tree).sym instanceof VarSymbol); } return false; } /** Makes an attributed JCTree for a class literal corresponding to the given type. */ public JCExpression makeType(int pos, Type type) { // factory.Type does produce an attributed tree - after all we start knowing the type JCExpression tree = factory.at(pos).Type(type); return tree; } /** Make an attributed tree representing a literal - NOT FOR BOOLEAN or NULL or CHARACTER values. * @param pos The node position * @param type The literal's type. * @param value The literal's value; use 0 or 1 for Boolean; use an int for char literals. */ public JCLiteral makeLit(int pos, Type type, Object value) { return factory.at(pos).Literal(type.getTag(), value).setType(type.constType(value)); } /** Returns true if the argument is a boolean Literal with value true */ public boolean isTrueLit(JCTree tree) { if (tree == trueLit) return true; if (!(tree instanceof JCLiteral)) return false; if (tree.type.getTag() != TypeTag.BOOLEAN) return false; return (Boolean)((JCLiteral)tree).getValue(); } /** Returns true if the argument is a boolean Literal with value true */ public boolean isFalseLit(JCTree tree) { if (tree == falseLit) return true; if (!(tree instanceof JCLiteral)) return false; if (tree.type.getTag() != TypeTag.BOOLEAN) return false; return !(Boolean)((JCLiteral)tree).getValue(); } /** Makes an attributed AST that is a copy of a given literal AST, * but with the new position. */ public JCLiteral makeDuplicateLiteral(DiagnosticPosition pos, JCLiteral lit) { // Note that lit.typetag can be different from lit.type.tag - e.g for null values return factory.at(pos).Literal(lit.typetag, lit.value).setType(lit.type.constType(lit.value)); } public JCLiteral makeDuplicateLiteral(int pos, JCLiteral lit) { // Note that lit.typetag can be different from lit.type.tag - e.g for null values return factory.at(pos).Literal(lit.typetag, lit.value).setType(lit.type.constType(lit.value)); } /** Make an attributed tree representing an integer literal. */ public JCLiteral makeIntLiteral(int pos, int value) { return factory.at(pos).Literal(TypeTag.INT, value).setType(syms.intType.constType(value)); } /** Make an attributed tree representing an integer literal. */ public JCLiteral makeIntLiteral(DiagnosticPosition pos, int value) { return factory.at(pos).Literal(TypeTag.INT, value).setType(syms.intType.constType(value)); } /** Make an attributed tree representing an long literal. */ public JCLiteral makeLongLiteral(int pos, long value) { return factory.at(pos).Literal(TypeTag.LONG, value).setType(syms.longType.constType(value)); } /** Make an attributed tree representing an long literal. */ public JCLiteral makeLongLiteral(DiagnosticPosition pos, long value) { return factory.at(pos).Literal(TypeTag.LONG, value).setType(syms.longType.constType(value)); } /** Make an attributed tree representing a null literal. */ public JCLiteral makeNullLiteral(int pos) { return makeDuplicateLiteral(pos,nullLit); } /** Make an attributed tree representing a null literal. */ public JCLiteral makeNullLiteral(DiagnosticPosition pos) { return makeDuplicateLiteral(pos,nullLit); } /** Makes a constant boolean literal AST node. * @param pos the position to use for the node * @param value the boolean value of the constant node * @return the AST node */ public JCLiteral makeBooleanLiteral(int pos, boolean value) { int v = value?1:0; JCLiteral r = factory.at(pos).Literal(TypeTag.BOOLEAN,v); r.type = syms.booleanType.constType(v); return r; } /** Makes a constant String literal AST node. * @param pos the position to use for the node * @param value the String value of the constant node * @return the AST node */ public JCLiteral makeStringLiteral(int pos, String value) { JCLiteral r = factory.at(pos).Literal(TypeTag.CLASS,value); r.type = syms.stringType.constType(value); return r; } /** Make a zero-equivalent constant node of the given type * @param type the type of the node, e.g. syms.intType * @return the AST node */ public JCLiteral makeZeroEquivalentLit(int pos, Type type) { // if (type == types.BIGINT) { // return makeLit(pos,type,0); // // } else if (type == types.REAL) { // return makeLit(pos,type,0.0); // // } else if (type == types.TYPE) { // // FIXME - ??? // return makeNullLiteral(pos); // // } else { switch (type.getTag()) { case CHAR: return makeLit(pos,type,0); // Character literal requires an int value case LONG: return makeLit(pos,type,(long)0); case INT: return makeLit(pos,type,0); case SHORT: return makeLit(pos,type,(short)0); case BYTE: return makeLit(pos,type,(byte)0); case BOOLEAN: return makeLit(pos,type,0); // Boolean literal requires an int value case FLOAT: return makeLit(pos,type,0.0f); case DOUBLE: return makeLit(pos,type,0.0); case CLASS: case ARRAY: default: return makeNullLiteral(pos); } // } } // FIXME - the following method appears to be misnamed /** Makes an AST for a primitive type literal, e.g. "int" * @param s the text string corresponding to the type * @return the AST */ public JCExpression makePrimitiveClassLiteralExpression(String s) { Name n = names.fromString(s); // FIXME - pass in a Name? // The following only ever loads the class once, despite multiple calls Type type = ClassReader.instance(context).enterClass(n).type; // TODO - don't call instance all the time JCIdent id = factory.Ident(n); id.pos = Position.NOPOS; id.type = type; id.sym = type.tsym; JCFieldAccess f = factory.Select(id,TYPEName); f.pos = Position.NOPOS; f.type = syms.objectType; Scope.Entry e = type.tsym.members().lookup(TYPEName); f.sym = e.sym; return f; } /** Makes a new AST for an identifier that references the given symbol * @param sym the symbol for which to make an identifier * @return the AST */ public JCIdent makeIdent(int pos, Symbol sym) { JCIdent id = factory.Ident(sym); id.pos = pos; // id.type is set in Ident return id; } /** Makes a new AST for an identifier that references the given symbol * @param sym the symbol for which to make an identifier * @return the AST */ public JCIdent makeIdent(DiagnosticPosition pos, Symbol sym) { JCIdent id = factory.Ident(sym); id.pos = pos.getPreferredPosition(); // id.type is set in Ident return id; } /** Makes a new AST for an identifier that references the given symbol * @param sym the symbol for which to make an identifier * @return the AST */ public JCIdent makeIdent(int pos, Name name, Symbol sym) { JCIdent id = sym != null ? factory.Ident(sym) : factory.Ident(name); id.name = name; id.pos = pos; // id.type is set in Ident, if sym is not null return id; } /** Makes a new AST and VarSymbol for an identifier with the given name and type * @param sym the symbol for which to make an identifier * @return the AST */ public JCIdent makeIdent(int pos, String name, Type type) { VarSymbol sym = makeVarSymbol(0,names.fromString(name),type,pos); return makeIdent(pos,sym); } /** Makes an AST for a field selection (attributed) * @param sym the symbol for which to make an identifier * @return the AST */ public JCFieldAccess makeSelect(int pos, JCExpression lhs, Symbol sym) { JCFieldAccess fa = factory.Select(lhs, sym.name); fa.pos = pos; fa.type = sym.type; fa.sym = sym; return fa; } /** Makes an AST for a field selection (attributed) * @param sym the symbol for which to make an identifier * @return the AST */ public JCFieldAccess makeSelect(int pos, JCExpression lhs, Name name) { JCFieldAccess fa = factory.Select(lhs, name); fa.pos = pos; fa.type = null; fa.sym = null; return fa; } /** Makes an attributed assignment expression; the expression type is the type of the lhs. */ public JCAssign makeAssign(int pos, JCExpression lhs, JCExpression rhs) { JCAssign tree = factory.at(pos).Assign(lhs, rhs); tree.type = lhs.type; return tree; } /** Makes an attributed assignment expression; the expression type is the type of the lhs. */ public JCExpressionStatement makeAssignStat(int pos, JCExpression lhs, JCExpression rhs) { JCAssign tree = factory.at(pos).Assign(lhs, rhs); tree.type = lhs.type; return factory.Exec(tree); } /** Makes an attributed assignment-op expression; the expression type is the type of the lhs. */ public JCAssignOp makeAssignOp(int pos, JCTree.Tag op, JCExpression lhs, JCExpression rhs) { JCAssignOp asn = factory.at(pos).Assignop(op, lhs, rhs); asn.setType(lhs.type); asn.operator = findOpSymbol(op.noAssignOp(),asn.lhs.type); return asn; } /** Makes a JML assume statement */ public JmlStatementExpr makeAssume(DiagnosticPosition pos, Label label, JCExpression expr) { JmlStatementExpr e = factory.at(pos).JmlExpressionStatement(JmlTokenKind.ASSUME, label, expr); e.associatedPos = Position.NOPOS; e.associatedSource = null; return e; } /** Makes a JML assert statement */ public JmlStatementExpr makeAssert(DiagnosticPosition pos, Label label, JCExpression expr) { JmlStatementExpr e = factory.at(pos).JmlExpressionStatement(JmlTokenKind.ASSERT, label, expr); e.associatedPos = Position.NOPOS; e.associatedSource = null; return e; } /** Returns the 'larger' of the two types as numeric types are compared; * not appropriate for Boolean types; floats test larger than long */ public Type maxType(Type lhs, Type rhs) { Type t = lhs.getTag().ordinal() >= rhs.getTag().ordinal() || rhs.getTag() == TypeTag.BOT ? lhs : rhs; if (TypeTag.INT.ordinal() > t.getTag().ordinal()) t = syms.intType; return t; } public boolean isIntegral(TypeTag tag) { return tag == TypeTag.INT || tag.ordinal() <= TypeTag.LONG.ordinal(); } public Type opType(Type lhs, Type rhs) { Type lhsu = unboxedType(lhs); Type rhsu = unboxedType(rhs); if (lhsu.getTag() == TypeTag.BOOLEAN) return syms.booleanType; if (!lhsu.isPrimitive() || !rhsu.isPrimitive()) return syms.stringType; if (lhs == types.REAL || rhs == types.REAL) return types.REAL; if (lhs == types.BIGINT || rhs == types.BIGINT) return types.BIGINT; if (lhs == types.TYPE || rhs == types.TYPE) return types.TYPE; TypeTag ltag = lhsu.getTag(); TypeTag rtag = rhsu.getTag(); if (ltag == TypeTag.DOUBLE) return lhs; if (rtag == TypeTag.DOUBLE) return rhs; if (ltag == TypeTag.FLOAT) return lhs; if (rtag == TypeTag.FLOAT) return rhs; if (ltag == TypeTag.LONG) return lhs; if (rtag == TypeTag.LONG) return rhs; return syms.intType; } /** Makes a Java unary operator node; it may be constant-folded * @param pos the pseudo source code location of the node * @param optag the unary operator, e.g. JCTree.Tag.NOT, JCTree.Tag.NEG, JCTree.Tag.COMPL, ... * @param expr the argument expression * @return the new node */ public JCExpression makeUnary(DiagnosticPosition pos, JCTree.Tag optag, JCExpression expr) { JCUnary e = factory.at(pos).Unary(optag,expr); e.operator = findOpSymbol(optag,expr.type); e.type = e.operator.type.getReturnType(); copyEndPosition(e,expr); return e; } public JCExpression makeUnary(int pos, JCTree.Tag optag, JCExpression expr) { JCUnary e = factory.at(pos).Unary(optag,expr); e.operator = findOpSymbol(optag,expr.type); e.type = e.operator.type.getReturnType(); copyEndPosition(e,expr); return e; } /** Makes a Java unary operator node, to be used when the opsymbol is * already known. * @param pos the pseudo source code location of the node * @param optag the unary operator, e.g. JCTree.NOT, JCTree.NEG, JCTree.COMPL, ... * @param opsymbol the symbol corresponding to the optag * @param expr the argument expression * @return the new node */ public JCExpression makeUnary(int pos, JCTree.Tag optag, Symbol opsymbol, JCExpression expr) { JCUnary e = factory.at(pos).Unary(optag,expr); e.operator = opsymbol; e.type = e.operator.type.getReturnType(); copyEndPosition(e,expr); return e; } /** Make an attributed unary NOT(!) expression * @param pos The position at which to put the new AST. * @param arg The operator's argument. */ public JCExpression makeNot(DiagnosticPosition pos, JCExpression arg) { return makeUnary(pos,JCTree.Tag.NOT,arg); } public JCExpression makeNot(int pos, JCExpression arg) { return makeUnary(pos,JCTree.Tag.NOT,arg); } /** Make an attributed binary expression. * @param pos The pseudo-position at which to place the node * @param optag The operator's operation tag (e.g. JCTree.PLUS). * @param opSymbol The symbol for the operation * @param lhs The operator's left argument. * @param rhs The operator's right argument. */ public JCBinary makeBinary(DiagnosticPosition pos, JCTree.Tag optag, Symbol opSymbol, JCExpression lhs, JCExpression rhs) { JCBinary tree = factory.at(pos).Binary(optag, lhs, rhs); tree.operator = opSymbol; tree.type = tree.operator.type.getReturnType(); //copyEndPosition(tree,rhs); return tree; } public JCBinary makeBinary(int pos, JCTree.Tag optag, Symbol opSymbol, JCExpression lhs, JCExpression rhs) { JCBinary tree = factory.at(pos).Binary(optag, lhs, rhs); tree.operator = opSymbol; tree.type = tree.operator.type.getReturnType(); //copyEndPosition(tree,rhs); return tree; } /** Makes an attributed Java binary operator node (with boolean result) * @param pos the pseudo source code location of the node * @param optag the binary operator (producing a boolean result), e.g. JCTree.EQ * @param lhs the left-hand expression * @param rhs the right-hand expression * @return the new node */ public JCBinary makeBinary(DiagnosticPosition pos, JCTree.Tag optag, JCExpression lhs, JCExpression rhs) { return makeBinary(pos,optag,findOpSymbol(optag,opType(lhs.type.baseType(),rhs.type.baseType())),lhs,rhs); } public JCBinary makeBinary(int pos, JCTree.Tag optag, JCExpression lhs, JCExpression rhs) { return makeBinary(pos,optag,findOpSymbol(optag,opType(lhs.type.baseType(),rhs.type.baseType())),lhs,rhs); } /** Produces an Equality AST node; presumes that the lhs and rhs have the * same type. * @param pos the position of the node * @param lhs the left argument * @param rhs the right argument * @return the AST */ public JCBinary makeEquality(int pos, JCExpression lhs, JCExpression rhs) { JCBinary tree = factory.at(pos).Binary(JCTree.Tag.EQ, lhs, rhs); Type t = lhs.type; if (t.isPrimitive() && TypeTag.INT.ordinal() > t.getTag().ordinal()) t = syms.intType; tree.operator = findOpSymbol(JCTree.Tag.EQ, t); tree.type = syms.booleanType; return tree; } /** Makes a JML binary operator node (with boolean result) * @param pos the pseudo source code location of the node * @param op the binary operator (producing a boolean result), e.g. JmlToken.IMPLIES * @param lhs the left-hand expression * @param rhs the right-hand expression * @return the new node */ public JmlBinary makeJmlBinary(int pos, JmlTokenKind op, JCExpression lhs, JCExpression rhs) { JmlBinary e = factory.at(pos).JmlBinary(op,lhs,rhs); e.type = syms.booleanType; copyEndPosition(e,rhs); return e; } public JCConditional makeConditional(int pos, JCExpression cond, JCExpression trueexpr, JCExpression falseexpr) { JCConditional e = factory.at(pos).Conditional(cond,trueexpr,falseexpr); e.type = trueexpr.type; copyEndPosition(e,falseexpr); return e; } /** Makes an attributed AST for a short-circuit boolean AND expression */ public JCExpression makeAnd(DiagnosticPosition pos, JCExpression lhs, JCExpression rhs) { if (lhs == null) { System.out.println("BAD AND"); } return makeBinary(pos,JCTree.Tag.AND,andSymbol,lhs,rhs); } public JCExpression makeAnd(int pos, JCExpression lhs, JCExpression rhs) { if (lhs == null) { System.out.println("BAD AND"); } return makeBinary(pos,JCTree.Tag.AND,andSymbol,lhs,rhs); } /** Makes an attributed AST for a short-circuit boolean AND expression, simplifying literal true or false */ public JCExpression makeAndSimp(int pos, JCExpression lhs, JCExpression rhs) { if (isTrueLit(rhs) || isFalseLit(lhs)) return lhs; if (isTrueLit(lhs) || isFalseLit(rhs)) return rhs; return makeBinary(pos,JCTree.Tag.AND,andSymbol,lhs,rhs); } /** Makes an attributed AST for a short-circuit boolean OR expression */ public JCExpression makeOr(int pos, JCExpression lhs, JCExpression rhs) { return makeBinary(pos,JCTree.Tag.OR,orSymbol,lhs,rhs); } /** Makes an attributed AST for a short-circuit boolean OR expression, simplifying literal true or false */ public JCExpression makeOrSimp(int pos, JCExpression lhs, JCExpression rhs) { if (isFalseLit(rhs) || isTrueLit(lhs)) return lhs; if (isFalseLit(lhs) || isTrueLit(rhs)) return rhs; return makeBinary(pos,JCTree.Tag.OR,orSymbol,lhs,rhs); } /** Makes an attributed attributed AST for a non-short-circuit boolean OR expression */ public JCExpression makeBitOr(int pos, JCExpression lhs, JCExpression rhs) { return makeBinary(pos,JCTree.Tag.BITOR,bitorSymbol,lhs,rhs); } /** Makes an attributed AST for the Java equivalent of a JML IMPLIES expression */ public JCExpression makeImplies(int pos, JCExpression lhs, JCExpression rhs) { return makeBinary(pos,JCTree.Tag.OR,orSymbol, makeNot(pos,lhs), rhs); } /** Makes an attributed AST for a reference equality (==) expression */ public JCBinary makeEqObject(int pos, JCExpression lhs, JCExpression rhs) { return makeBinary(pos,JCTree.Tag.EQ,objecteqSymbol,lhs, rhs); } /** Makes an attributed AST for a reference inequality (!=) expression */ public JCBinary makeNeqObject(int pos, JCExpression lhs, JCExpression rhs) { return makeBinary(pos,JCTree.Tag.NE,objectneSymbol,lhs, rhs); } /** Makes an attributed AST for a reference inequality (!=) expression */ public JCBinary makeNotNull(int pos, JCExpression lhs) { return makeBinary(pos,JCTree.Tag.NE,objectneSymbol,lhs, makeNullLiteral(pos)); } /** Makes an attributed AST for a reference inequality (!=) expression */ public JCBinary makeEqNull(int pos, JCExpression lhs) { return makeBinary(pos,JCTree.Tag.EQ,objecteqSymbol,lhs, makeNullLiteral(pos)); } /** Makes an attributed AST for the length operation on an array. */ public JCFieldAccess makeLength(DiagnosticPosition pos, JCExpression array) { JCFieldAccess fa = (JCFieldAccess)factory.at(pos).Select(array, syms.lengthVar); fa.type = syms.intType; return fa; } // /** Makes the AST for a catch block; the name of the exception variable is // * that of the 'caughtException' name defined in the constructor; the catch // * block itself is initialized with no statements; the type of the exception // * is java.lang.Exception. // * @param owner the symbol of the enclosing method // * @return the new AST // */ // public JCCatch makeCatcher(Symbol owner) { // return makeCatcher(owner,syms.exceptionType); // } /** Makes the AST for a catch block; the name of the exception variable is * that of the 'caughtException' name defined in the constructor; the catch * block itself is initialized with no statements. * @param owner TBD * @param exceptionType the type of the exception caught in the statement * @return the new AST */ public JCCatch makeCatcher(Symbol owner, Type exceptionType) { JCVariableDecl v = makeVarDef(exceptionType,caughtException,owner,Position.NOPOS); return factory.at(Position.NOPOS).Catch(v,factory.Block(0,List.<JCStatement>nil())); } /** Makes an AST for an int variable declaration with initialization and no * modifiers and no position. * @param name the name of the new variable * @param initializer the (possibly null) initializer expression * @param owner the owner of the declaration (e.g. a method or a class) * @return the new AST */ public JCVariableDecl makeIntVarDef(Name name, JCExpression initializer, Symbol owner) { Type type = syms.intType; JCExpression tid = factory.Type(type); // sets tid.type tid.pos = Position.NOPOS; JCModifiers mods = factory.at(Position.NOPOS).Modifiers(0); JCVariableDecl d = factory.VarDef(mods,name,tid,initializer); VarSymbol v = new VarSymbol(0, d.name, type, owner); d.pos = Position.NOPOS; d.sym = v; d.type = type; return d; } // FIXME - might be a problem having no owner /** Creates a new VarSymbol with the given name and type and modifier flags * (and no owner); * the declaration position is 'pos'. */ public VarSymbol makeVarSymbol(long flags, @NonNull Name name, @NonNull Type type, int pos) { VarSymbol v = new VarSymbol(flags,name,type.baseType(),null); // FIXME - explain why baseType is needed v.pos = pos; return v; } /** Makes a new variable declaration for new helper variables in the AST translation; * a new VarSymbol is also created in conjunction with the variable; the variable * is created with no modifiers and no owner. * @param name the variable name, as it might be used in program text * @param type the variable type * @param init the initialization expression as it would appear in a declaration (null for no initialization) * @param pos the pseudo source code location for the new node * @return a new JCVariableDecl node */ public JCVariableDecl makeVariableDecl(Name name, Type type, @Nullable JCExpression init, int pos) { VarSymbol vsym = new VarSymbol(0, name, type.baseType(), null); vsym.pos = pos; JCVariableDecl decl = factory.at(pos).VarDef(vsym,init); return decl; } /** Makes an attributed variable declaration along with a new VarSymbol (which is not * put into the symbol table); the declaration has no modifiers; it is * initialized to a zero-equivalent value; no position set. * @param type the type of the new variable (should be an attributed AST) * @param name the name of the new variable * @param owner the owner of the new variable (e.g. a MethodSymbol or ClassSymbol) * @return the AST for the declaration */ public JCVariableDecl makeVarDefZeroInit(JCExpression type, Name name, Symbol owner) { int flags = 0; JCModifiers mods = factory.at(Position.NOPOS).Modifiers(0); JCExpression zeroEquiv = makeZeroEquivalentLit(Position.NOPOS,type.type); JCVariableDecl d = factory.VarDef(mods,name,type,zeroEquiv); VarSymbol v = new VarSymbol(flags, d.name, d.vartype.type.baseType(), owner); v.pos = Position.NOPOS; d.pos = Position.NOPOS; d.sym = v; d.type = type.type; return d; } /** Makes an attributed variable declaration for the given VarSymbol; * the declaration has no modifiers; position * is set to that of the init expression. */ public JCVariableDecl makeVariableDecl(VarSymbol var, @Nullable JCExpression init) { JCVariableDecl d = factory.VarDef(var,init); if (init != null) d.pos = init.pos; return d; } /** Makes an attributed variable declaration along with a new VarSymbol (which is not * put into the symbol table); the declaration has no modifiers; position * is set to that of the init expression. * @param type the type of the new variable * @param name the name of the new variable * @param owner the owner of the new variable (e.g. a MethodSymbol or ClassSymbol) * @param init the initialization expression for the new AST * @return the AST for the declaration */ public JCVariableDecl makeVarDef(Type type, Name name, Symbol owner, @NonNull JCExpression init) { int modifierFlags = 0; // We use type.baseType() here to remove any constType in case the // expression the type came from is a literal. This made the difference // in making the racnew2.testLblConst test work. // TODO - figure out why - something in code generation VarSymbol v = new VarSymbol(modifierFlags, name, type.baseType(), owner); v.pos = init.getStartPosition(); JCVariableDecl d = factory.VarDef(v,init); d.pos = v.pos; return d; } public JCVariableDecl makeStaticVarDef(Type type, Name name, Symbol owner, @NonNull JCExpression init) { int modifierFlags = Flags.STATIC; // We use type.baseType() here to remove any constType in case the // expression the type came from is a literal. This made the difference // in making the racnew2.testLblConst test work. // TODO - figure out why - something in code generation VarSymbol v = new VarSymbol(modifierFlags, name, type.baseType(), owner); v.pos = init.getStartPosition(); JCVariableDecl d = factory.VarDef(v,init); d.pos = v.pos; return d; } /** Makes an attributed variable declaration along with a new VarSymbol (which is not * put into the symbol table); the declaration has no modifiers and no initialization. * @param type the type of the new variable * @param name the name of the new variable * @param owner the owner of the new variable (e.g. a MethodSymbol or ClassSymbol) * @param pos the position to set * @return the AST for the declaration */ public JCVariableDecl makeVarDef(Type type, Name name, Symbol owner, int pos) { int modifierFlags = 0; VarSymbol v = new VarSymbol(modifierFlags, name, type, owner); v.pos = pos; JCVariableDecl d = factory.VarDef(v,null); d.pos = pos; return d; } /** Makes an \old expression */ public JCMethodInvocation makeOld(int pos, JCExpression arg, JCIdent label) { JCMethodInvocation m; if (label == null || label.toString().isEmpty()) { m = factory.at(pos).JmlMethodInvocation(JmlTokenKind.BSOLD, List.<JCExpression>of(arg)); } else { JCIdent id = factory.at(pos).Ident(label.name); id.type = null; // Should never refer to the label's type id.sym = null; // Should never refer to the label's symbol m = factory.at(pos).JmlMethodInvocation(JmlTokenKind.BSOLD, List.<JCExpression>of(arg, id)); } m.type = arg.type; return m; } /** Makes an \old expression */ public JCMethodInvocation makeOld(DiagnosticPosition pos, JCExpression arg) { JCMethodInvocation m; m = factory.at(pos).JmlMethodInvocation(JmlTokenKind.BSOLD, List.<JCExpression>of(arg)); m.type = arg.type; return m; } /** Makes a \past expression */ public JCMethodInvocation makePast(int pos, JCExpression arg, JCIdent label) { JCMethodInvocation m; if (label.toString().isEmpty()) { m = factory.JmlMethodInvocation(JmlTokenKind.BSPAST, List.<JCExpression>of(arg)); } else { JCIdent id = factory.at(pos).Ident(label.name); id.type = null; // Should never refer to the label's type id.sym = null; // Should never refer to the label's symbol m = factory.JmlMethodInvocation(JmlTokenKind.BSPAST, List.<JCExpression>of(arg, id)); } m.type = arg.type; return m; } public JCExpression makeThrownPredicate(DiagnosticPosition pos, JCIdent exceptionId, JCMethodDecl methodDecl) { JCExpression rex = makeType(pos.getPreferredPosition(),syms.runtimeExceptionType); JCExpression condd = factory.at(pos).TypeTest(exceptionId, rex).setType(syms.booleanType); for (JCExpression ex: methodDecl.thrown) { if (pos == null) pos = ex.pos(); JCExpression tc = factory.at(ex.pos()).TypeTest(exceptionId, ex).setType(syms.booleanType); condd = makeOr(ex.pos, condd, tc); } return condd; } public JCExpression makeThrownPredicate(DiagnosticPosition pos, JCIdent exceptionId, MethodSymbol sym) { int p = pos.getPreferredPosition(); JCExpression rex = makeType(p,syms.runtimeExceptionType); JCExpression ex = makeType(p,syms.exceptionType); JCExpression condd = factory.at(pos).TypeTest(exceptionId, rex).setType(syms.booleanType); JCExpression conde = factory.at(pos).TypeTest(exceptionId, ex).setType(syms.booleanType); condd = makeAnd(p,condd,conde); // FIXME - why this redundancy? for (Type t: sym.getThrownTypes()) { JCExpression tc = factory.at(pos).TypeTest(exceptionId, makeType(p,t)).setType(syms.booleanType); condd = makeOr(p, condd, tc); } return condd; } /** Makes a Java method invocation using the given MethodSymbol, on the given receiver, * with the given arguments, at the given position; no varargs, no typeargs. */ public JCMethodInvocation makeMethodInvocation(DiagnosticPosition pos, JCExpression receiver, MethodSymbol sym, JCExpression ... args) { JCExpression meth = factory.at(pos).Ident(sym); if (receiver != null) meth = makeSelect(pos.getPreferredPosition(), receiver, sym); List<JCExpression> nargs; if (args.length == 0) { nargs = List.<JCExpression>nil(); } else { ListBuffer<JCExpression> a = new ListBuffer<JCExpression>(); for (JCExpression arg: args) a.add(arg); nargs = a.toList(); } JCMethodInvocation call = factory.at(pos).Apply(List.<JCExpression>nil(), meth, nargs); call.type = sym.type.getReturnType(); call.varargsElement = null; return call; } public JCMethodInvocation makeMethodInvocation(DiagnosticPosition pos, JCExpression receiver, MethodSymbol sym, List<JCExpression> nargs) { JCExpression meth = factory.at(pos).Ident(sym); if (receiver != null) meth = makeSelect(pos.getPreferredPosition(), receiver, sym); JCMethodInvocation call = factory.at(pos).Apply(List.<JCExpression>nil(), meth, nargs); call.type = sym.type.getReturnType(); call.varargsElement = null; return call; } /** Makes a Java method invocation using the given MethodSymbol, on the given receiver, * with the given arguments, at the given position; no varargs, no typeargs. */ public JmlMethodInvocation makeJmlMethodInvocation(DiagnosticPosition pos, JmlTokenKind token, Type type, JCExpression ... args) { ListBuffer<JCExpression> a = new ListBuffer<JCExpression>(); a.appendArray(args); JmlMethodInvocation call = factory.at(pos).JmlMethodInvocation(token, a.toList()); call.type = type; call.meth = null; call.typeargs = null; call.varargsElement = null; return call; } // FIXME _ document public JCMethodDecl makeMethodDefNoArg(JCModifiers mods, Name methodName, Type resultType, ClassSymbol ownerClass) { MethodType mtype = new MethodType(List.<Type>nil(),resultType,List.<Type>nil(),ownerClass); MethodSymbol msym = new MethodSymbol( mods.flags, methodName, mtype, ownerClass); JCMethodDecl mdecl = factory.MethodDef( msym, factory.Block(0,List.<JCStatement>nil())); ownerClass.members_field.enter(msym); return mdecl; } /** Makes a new MethodSymbol, given its various properties */ public MethodSymbol makeMethodSym(JCModifiers mods, Name methodName, Type resultType, TypeSymbol ownerClass, List<Type> argtypes) { MethodType mtype = new MethodType(List.<Type>nil(),resultType,argtypes,ownerClass); return new MethodSymbol( mods.flags, methodName, mtype, ownerClass); } public JCTree.JCInstanceOf makeInstanceOf(int pos, JCExpression expr, JCExpression clazz) { if (clazz.toString().equals("\\bigint")) Utils.stop(); JCTree.JCInstanceOf t = factory.at(pos).TypeTest(expr, clazz); t.type = syms.booleanType; return t; } public JCTree.JCInstanceOf makeInstanceOf(int pos, JCExpression expr, Type t) { return makeInstanceOf(pos,expr,makeType(pos,t)); } /** Makes a JML \typeof expression, with the given expression as the argument */ public JCExpression makeTypeof(JCExpression e) { JmlMethodInvocation typeof = factory.at(e.pos).JmlMethodInvocation(JmlTokenKind.BSTYPEOF,e); typeof.type = types.TYPE; return typeof; } /** Makes a JML \typeof expression, with the given expression as the argument */ public JCExpression makeTypelc(JCExpression e) { JmlMethodInvocation typeof = factory.at(e.pos).JmlMethodInvocation(JmlTokenKind.BSTYPELC,e); typeof.type = types.TYPE; return typeof; } /** Makes an equivalent of \erasure(\typeof ) expression, with the given expression as the argument */ public JCExpression makeJavaTypelc(JCExpression e) { JmlMethodInvocation type = factory.at(e.pos).JmlMethodInvocation(JmlTokenKind.BSTYPELC,e); type.javaType = true; type.type = syms.classType; return type; } public JCExpression makeElemtype(JCExpression e) { JmlMethodInvocation elem = factory.at(e.pos).JmlMethodInvocation(JmlTokenKind.BSELEMTYPE,e); elem.type = types.TYPE; return elem; } public JCExpression makeSubtype(JCExpression e1, JCExpression e2) { JmlMethodInvocation e = factory.at(e1.pos).JmlMethodInvocation(JmlTokenKind.SUBTYPE_OF,e1,e2); e.type = syms.booleanType; return e; } /** Returns the AST for ( \typeof(id) == \type(type) && id instanceof 'erasure of type') */ public JCExpression makeDynamicTypeEquality(DiagnosticPosition pos, JCExpression id, Type type) { int p = pos.getPreferredPosition(); JCExpression lhs = makeTypeof(id); JmlMethodInvocation rhs = factory.at(p).JmlMethodInvocation(JmlTokenKind.BSTYPELC,makeType(p,type)); rhs.type = JmlTypes.instance(context).TYPE; JCExpression expr = makeEqObject(p,lhs,rhs); expr = makeAnd(p,expr, makeJmlMethodInvocation(pos,JmlTokenKind.SUBTYPE_OF,syms.booleanType,lhs,rhs)); { Type t = types.erasure(type); if (!t.isPrimitive() && t.getKind() != TypeKind.ARRAY) { JCTree.JCInstanceOf tt = makeInstanceOf(p,id,types.erasure(type)); expr = makeAnd(p,tt,expr); } } if (type.getTag() == TypeTag.ARRAY) { Type compType = ((Type.ArrayType)type).getComponentType(); JmlMethodInvocation ct = factory.at(p).JmlMethodInvocation(JmlTokenKind.BSTYPELC,makeType(p,compType)); JCExpression e = makeTypeof(id); e = factory.at(p).JmlMethodInvocation(JmlTokenKind.BSELEMTYPE,e); e = makeEqObject(p, e, ct); expr = makeAnd(p,expr,e); } if (!type.isPrimitive() ) { JCExpression ex = makeEqNull(id.pos, id); expr = makeOr(p,ex,expr); } return expr; } // requires id to have a reference type /** Returns the AST for id == null || ( \typeof(id) <: \type(type) && id instanceof 'erasure of type') */ public JCExpression makeDynamicTypeInEquality(DiagnosticPosition pos, JCExpression id, Type type) { int p = pos.getPreferredPosition(); JCExpression nn = makeEqObject(p,id,nullLit); return makeOr(p,nn,makeNonNullDynamicTypeInEquality(pos, id, type)); } /** Returns the AST for \typeof(id) <: \type(type) && id instanceof 'erasure of type' */ public JCExpression makeNonNullDynamicTypeInEquality(DiagnosticPosition pos, JCExpression id, Type type) { int p = pos.getPreferredPosition(); if (type.getKind().isPrimitive()) return trueLit; JCExpression lhs = makeTypeof(id); // FIXME - copy? JmlMethodInvocation rhs = factory.at(p).JmlMethodInvocation(JmlTokenKind.BSTYPELC,makeType(p,type)); rhs.type = JmlTypes.instance(context).TYPE; JCExpression expr = makeJmlMethodInvocation(pos,JmlTokenKind.SUBTYPE_OF,syms.booleanType,lhs,rhs); { if (type.getKind() != TypeKind.ARRAY) { JCTree.JCInstanceOf tt = makeInstanceOf(p,id,types.erasure(type)); expr = makeAnd(p,tt,expr); } else { Type comptype = ((Type.ArrayType)type).elemtype; JCExpression e = makeTypeof(id); e = makeJmlMethodInvocation(pos,JmlTokenKind.BSELEMTYPE,e.type,e); JmlMethodInvocation tt = factory.at(p).JmlMethodInvocation(JmlTokenKind.BSTYPELC,makeType(p,comptype)); tt.type = JmlTypes.instance(context).TYPE; if (comptype.isPrimitive()) e = makeEquality(p,e,tt); else e = makeJmlMethodInvocation(pos,JmlTokenKind.SUBTYPE_OF,syms.booleanType,e,tt); expr = makeAnd(p,expr,e); } } return expr; } /** Creates an AST for an invocation of a (static) method in org.jmlspecs.utils.Utils, * with the given name and arguments. * @param pos the node position of the new AST * @param methodName the name of the method to call * @param args the expressions that are the arguments of the call * @return the resulting AST */ public JCMethodInvocation makeUtilsMethodCall(int pos, String methodName, List<JCExpression> args) { // presumes the arguments are all properly attributed JCFieldAccess meth = findUtilsMethod(pos,methodName); ListBuffer<JCExpression> list = new ListBuffer<JCExpression>(); list.addAll(args); JCMethodInvocation call = factory.at(pos).Apply(List.<JCExpression>nil(),meth,list.toList()); call.type = ((MethodType)meth.type).getReturnType(); return call; } /** Creates an AST for an invocation of a (static) method in org.jmlspecs.utils.Utils, * with the given name and arguments. * @param pos the node position of the new AST * @param methodName the name of the method to call * @param args the expressions that are the arguments of the call * @return the resulting AST */ public JCMethodInvocation makeUtilsMethodCall(int pos, String methodName, JCExpression... args) { // presumes the arguments are all properly attributed factory.at(pos); JCFieldAccess meth = findUtilsMethod(pos,methodName); ListBuffer<JCExpression> list = new ListBuffer<JCExpression>(); list.appendArray(args); JCMethodInvocation call = factory.Apply(List.<JCExpression>nil(),meth,list.toList()); if (meth.type instanceof Type.ErrorType) { log.error("esc.incomplete.typechecking",meth.sym.toString()); throw new JmlInternalAbort(); } else if (meth.type instanceof MethodType) call.type = ((MethodType)meth.type).getReturnType(); else call.type = ((Type.ForAll)meth.type).getReturnType(); return call; } public JCExpression copyArray(int pos, JCExpression ad) { Type t = ((Type.ArrayType)ad.type).getComponentType(); JCExpression a = null; switch (t.getTag()) { case INT: a = makeUtilsMethodCall(pos,"copyIntArray",ad); break; case BOOLEAN: a = makeUtilsMethodCall(pos,"copyBooleanArray",ad); break; case CLASS: a = makeUtilsMethodCall(pos,"copyArray",ad); break; case SHORT: a = makeUtilsMethodCall(pos,"copyShortArray",ad); break; case CHAR: a = makeUtilsMethodCall(pos,"copyCharArray",ad); break; case BYTE: a = makeUtilsMethodCall(pos,"copyByteArray",ad); break; case FLOAT: a = makeUtilsMethodCall(pos,"copyFloatArray",ad); break; case DOUBLE: a = makeUtilsMethodCall(pos,"copyDoubleArray",ad); break; default: a = null; // FIXME - error } return a; } // FIXME - review & document - for ESC public JCExpression makeDotClass(int pos, Type type) { if (type.tsym instanceof ClassSymbol) type = ((ClassSymbol)type.tsym).erasure(Types.instance(context)); JCExpression tt = makeType(pos,type); JCFieldAccess result = factory.Select(tt,names._class); result.pos = pos; Type t = syms.classType; List<Type> typeargs = List.of(type); t = new ClassType(t.getEnclosingType(), typeargs, t.tsym); result.sym = new VarSymbol( STATIC | PUBLIC | FINAL, names._class, t, type.tsym); result.type = result.sym.type; return result; } // FIXME - review & document - translates a type into ESC logic public JCExpression trType(int pos, Type type) { JCTree tree = factory.at(pos).Type(type); return trType(pos,tree); } // FIXME - review & document public JCExpression trType(int pos, JCTree type) { JCExpression result = null; if (type instanceof JCTypeApply) { // Convert a literal generic type, e.g. Vector<String> // into a function that creates type objects: // Utils.makeType(Vector.class,\type(String)); JCExpression headType = ((JCTypeApply)type).clazz; // t.type is the actual Java type of the head (e.g. java.util.Vector) // What we want is a Java class literal headType = makeDotClass(type.pos,headType.type); ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); args.append(headType); for (JCExpression tt: ((JCTypeApply)type).arguments) args.append(trType(tt.pos,tt)); int n = args.size()-1; if (n <= 2) { result = makeUtilsMethodCall(pos,"makeTYPE"+n,args.toList()); } else { // FIXME - we need to make an array argument here. result = makeUtilsMethodCall(pos,"makeTYPE",args.toList()); } } else if (type instanceof JCIdent) { if (type.type instanceof TypeVar) { // This is a generic type variable result = (JCIdent)type; } else { JCExpression headType = (JCIdent)type; // t.type is the actual Java type of the head (e.g. java.util.Vector) // What we want is a Java class literal headType = makeDotClass(type.pos,headType.type); result = makeUtilsMethodCall(pos,"makeTYPE0",headType); } } else if (type instanceof JCFieldAccess) { JCExpression headType = (JCFieldAccess)type; // t.type is the actual Java type of the head (e.g. java.util.Vector) // What we want is a Java class literal headType = makeDotClass(type.pos,headType.type); result = makeUtilsMethodCall(pos,"makeTYPE0",headType); } else if (type instanceof JCArrayTypeTree) { JCExpression headType = (JCArrayTypeTree)type; // t.type is the actual Java type of the head (e.g. java.util.Vector) // What we want is a Java class literal headType = makeDotClass(type.pos,headType.type); result = makeUtilsMethodCall(pos,"makeTYPE0",headType); } else if (type instanceof JCPrimitiveTypeTree) { // FIXME - this does not work JCExpression headType = (JCPrimitiveTypeTree)type; headType = makeDotClass(type.pos,headType.type); result = makeUtilsMethodCall(pos,"makeTYPE0",headType); } else if (type instanceof JCWildcard) { result = (JCWildcard)type; // FIXME - is this right? } else { log.getWriter(WriterKind.NOTICE).println("NOT IMPLEMENTED (JmlTreeUtils) - " + type.getClass()); //result = type; // Unknown - FIXME - error } return result; } public JCExpression convertToString(JCExpression that) { String n; switch(that.type.getTag()) { case CLASS: n = "toStringObject"; break; case INT: n = "toStringInt"; break; case LONG: n = "toStringLong"; break; case BOOLEAN: n = "toStringBoolean"; break; case SHORT: n = "toStringShort"; break; case BYTE: n = "toStringByte"; break; case DOUBLE: n = "toStringDouble"; break; case FLOAT: n = "toStringFloat"; break; case CHAR: n = "toStringChar"; break; default: log.warning("jml.internal", "Missing case in Utils.convertToType"); return null; } JCExpression e = makeUtilsMethodCall(that.pos,n,that); e.type = syms.stringType; return e; } }