/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** \* */ // $Id$ package scalac.ast; import java.io.*; import java.util.*; import scalac.*; import scalac.symtab.*; import scalac.util.*; import Tree.*; /** A helper class for building trees * * @author Martin Odersky, Christine Roeckl * @version 1.0 */ public class TreeGen implements Kinds, Modifiers { /********************************************************************************/ /********************************************************************************/ /** VARIABLES **/ /** the global environment */ protected Global global; /** the global definitions */ protected Definitions definitions; /** the tree factory */ public TreeFactory make; /************************************************************************/ /************************************************************************/ /** CONSTRUCTORS **/ public TreeGen(Global global, TreeFactory make) { this.global = global; this.definitions = global.definitions; this.make = make; } public TreeGen(Global global) { this(global, global.make); } /*************************************************************************/ /*************************************************************************/ /** METHODS **/ public Type deref(Type tp) { switch (tp) { case PolyType(Symbol[] tparams, Type restp): if (tparams.length == 0) return restp; } return tp; } /** Create a dummy symbol to be used for templates. */ public Symbol localDummy(int pos, Symbol owner) { return new TermSymbol(pos, Name.EMPTY, owner, 0) .setInfo(Type.NoType); } public Tree mkStable(Tree tree) { Symbol sym = tree.symbol(); if (sym.isStable()) { switch (tree) { case Ident(_): tree.setType(Type.singleType(sym.owner().thisType(), sym)); break; case Select(Tree qual, _): if (qual.type.isStable()) tree.setType(Type.singleType(qual.type, sym)); } } return tree; } public Tree mkRef(int pos, Type pre, Symbol sym) { if (pre.isSameAs(Type.localThisType) || pre.symbol().isRoot()) return Ident(pos, sym); else return Select(pos, mkStableId(pos, pre), sym); } public Tree mkRef(int pos, Symbol sym) { return mkRef(pos, sym.owner().thisType(), sym); } /** Build and attribute stable identifier tree corresponding to given prefix. */ public Tree mkStableId(int pos, Type pre) { switch (pre.expandModuleThis()) { case ThisType(Symbol sym): return make.This(pos, Ident(pos, sym)).setType(pre); case SingleType(Type pre1, Symbol sym): return mkStable(mkRef(pos, pre1, sym)); default: throw new ApplicationError(); } } /** Build and attribute tree corresponding to given type. */ public Tree mkType(int pos, Type type) { return TypeTerm(pos, type); } /** Build and attribute tree array corresponding to given type array. */ public Tree[] mkTypes(int pos, Type[] types) { Tree[] res = new Tree[types.length]; for (int i = 0; i < types.length; i++) { res[i] = mkType(pos, types[i]); } return res; } /** Build and attribute tree corresponding to given type. */ public Tree TypeTerm(int pos, Type type) { return make.TypeTerm(pos).setType(type); } /** Build and attribute tree corresponding to symbol's declaration. */ public Tree mkDef(int pos, Symbol sym) { switch (sym.kind) { case ERROR: return make.Bad(pos).setSymbol(Symbol.ERROR).setType(Type.ErrorType); case TYPE: case ALIAS: return TypeDef(pos, sym); case VAL: if (sym.isMethod()) return DefDef(pos, sym, Tree.Empty); else return Param(pos, sym); default: throw new ApplicationError(); } } /** Build and attribute tree array corresponding to given symbol's declarations. */ public Tree[] mkDefs(int pos, Symbol[] syms) { Tree[] res = new Tree[syms.length]; for (int i = 0; i < syms.length; i++) { res[i] = mkDef(pos, syms[i]); } return res; } /** Build a boolean constant tree. */ public Tree mkBooleanLit(int pos, boolean bool) { return make.Literal(pos, bool ? Boolean.TRUE : Boolean.FALSE). setType(definitions.BOOLEAN_TYPE); } /** Build a string literal */ public Tree mkStringLit(int pos, String str) { return make.Literal(pos, str).setType(definitions.JAVA_STRING_TYPE); } /** Build an integer literal */ public Tree mkIntLit(int pos, int value) { return make.Literal(pos, new Integer(value)).setType(definitions.INT_TYPE); } /** Build a tree to be used as a base class constructor for a template. */ public Tree mkParentConstr(int pos, Type parentType) { switch (parentType) { case TypeRef(Type pre, Symbol sym, Type[] args): Tree ref = mkRef(pos, pre, sym.constructor()); Tree constr = (args.length == 0) ? ref : TypeApply(ref, mkTypes(sym.pos, args)); return Apply(constr, Tree.EMPTY_ARRAY); default: throw global.fail("invalid parent type", parentType); } } /** Build an array of trees to be used as base classes for a template. */ public Tree[] mkParentConstrs(int pos, Type[] parents) { Tree[] constrs = new Tree[parents.length]; for (int i = 0; i < parents.length; ++i) constrs[i] = mkParentConstr(pos, parents[i]); return constrs; } /** Build parameter sections corresponding to type. */ public ValDef[][] mkParams(int pos, Type type) { switch (type) { case PolyType(Symbol[] tparams, Type restype): return mkParams(pos, restype); case MethodType(Symbol[] vparams, Type restype): ValDef[] params1 = mkParams(pos, vparams); ValDef[][] paramss = mkParams(pos, restype); if (paramss.length == 0) { return new ValDef[][]{params1}; } else { ValDef[][] paramss1 = new ValDef[paramss.length + 1][]; paramss1[0] = params1; System.arraycopy(paramss, 0, paramss1, 1, paramss.length); return paramss1; } default: return new ValDef[][]{}; } } /** Build parameter section corresponding to given array of symbols . */ public ValDef[] mkParams(int pos, Symbol[] symbols) { ValDef[] res = new ValDef[symbols.length]; for (int i = 0; i < symbols.length; i++) { res[i] = Param(pos, symbols[i]); } return res; } /** Build type parameter section corresponding to given array of symbols . */ public TypeDef[] mkTypeParams(int pos, Symbol[] symbols) { TypeDef[] res = new TypeDef[symbols.length]; for (int i = 0; i < symbols.length; i++) { res[i] = (TypeDef)TypeDef(pos, symbols[i]); } return res; } /** Build type definition corresponding to given symbol . */ public TypeDef TypeDef(int pos, Symbol sym) { Global.instance.nextPhase(); Type symtype = sym.info(); Global.instance.prevPhase(); return (TypeDef) make.TypeDef( pos, sym.flags & SOURCEFLAGS, sym.name, TypeTerm(pos, symtype), TypeTerm(pos, sym.loBound())) .setSymbol(sym).setType(definitions.UNIT_TYPE); } public Tree TypeDef(Symbol sym) { return TypeDef(sym.pos, sym); } /** Build parameter */ public ValDef Param(int pos, Symbol sym) { global.log("use of obsolete Param method in TreeGen"); return (ValDef)ValDef(pos, sym, Tree.Empty); } public ValDef Param(Symbol sym) { global.log("use of obsolete Param method in TreeGen"); return Param(sym.pos, sym); } public ValDef ValDef(int pos, Symbol sym) { return (ValDef)ValDef(pos, sym, Tree.Empty); } public ValDef ValDef(Symbol sym) { return Param(sym.pos, sym); } /** Build and attribute block with given statements, starting * at given position. The type is the type of the last * statement in the block. */ public Tree Block(int pos, Tree[] stats) { Type tp = (stats.length == 0) ? definitions.UNIT_TYPE : stats[stats.length - 1].type; return make.Block(pos, stats).setType(tp); } /** Build and attribute non-empty block with given statements. */ public Tree Block(Tree[] stats) { return Block(stats[0].pos, stats); } public Tree Typed(Tree tree, Type tp) { return make.Typed(tree.pos, tree, TypeTerm(tree.pos, tp)).setType(tp); } /** Build and attribute the assignment lhs = rhs */ public Tree Assign(int pos, Tree lhs, Tree rhs) { return make.Assign(pos, lhs, rhs).setType(definitions.UNIT_TYPE); } public Tree Assign(Tree lhs, Tree rhs) { return Assign(lhs.pos, lhs, rhs); } /** Build and attribute new B, given constructor expression B. */ public Tree New(Tree constr) { Template templ = make.Template( constr.pos, new Tree[]{constr}, Tree.EMPTY_ARRAY); templ.setType(constr.type); templ.setSymbol(localDummy(constr.pos, Symbol.NONE)); return make.New(constr.pos, templ).setType(constr.type); } /** Build an allocation new P.C[TARGS](ARGS) * given a (singleton) type P, class C, type arguments TARGS and arguments ARGS */ public Tree New(int pos, Type pre, Symbol clazz, Type[] targs, Tree[] args) { Tree constr = mkRef(pos, pre, clazz.constructor()); if (targs.length != 0) constr = TypeApply(constr, mkTypes(pos, targs)); Tree base = Apply(constr, args); return New(base); } /** Build a monomorphic allocation new P.C(ARGS) * given a prefix P, class C and arguments ARGS */ public Tree New(int pos, Type pre, Symbol clazz, Tree[] args) { return New(pos, pre, clazz, Type.EMPTY_ARRAY, args); } /** Build and attribute application node with given function * and argument trees. */ public Tree Apply(int pos, Tree fn, Tree[] args) { switch (fn.type) { case Type.MethodType(Symbol[] vparams, Type restpe): return make.Apply(pos, fn, args).setType(restpe); default: throw new ApplicationError("method type required", fn.type); } } public Tree Apply(Tree fn, Tree[] args) { return Apply(fn.pos, fn, args); } /** Build and attribute type application node with given function * and argument trees. */ public Tree TypeApply(int pos, Tree fn, Tree[] args) { switch (fn.type) { case Type.PolyType(Symbol[] tparams, Type restpe): return make.TypeApply(pos, fn, args) .setType(restpe.subst(tparams, Tree.typeOf(args))); default: throw new ApplicationError("poly type required", fn.type); } } public Tree TypeApply(Tree fn, Tree[] args) { return TypeApply(fn.pos, fn, args); } public Tree If(int pos, Tree cond, Tree thenpart, Tree elsepart) { return make.If(pos, cond, thenpart, elsepart).setType(thenpart.type); } public Tree If(Tree cond, Tree thenpart, Tree elsepart) { return If(cond.pos, cond, thenpart, elsepart); } /** Build and applied type node with given function * and argument trees. public Tree AppliedType(int pos, Tree fn, Tree[] args) { return make.AppliedType(pos, fn, args) .setType(Type.appliedType(fn.type, Tree.typeOf(args))); } public Tree AppliedType(Tree fn, Tree[] args) { return AppliedType(fn.pos, fn, args); } */ /** Build and attribute select node of given symbol. * It is assumed that the prefix is not empty. */ public Tree Select(int pos, Tree qual, Symbol sym) { assert sym.kind != NONE; Global.instance.nextPhase(); Type symtype = qual.type.memberType(sym); Global.instance.prevPhase(); return make.Select(pos, qual, sym.name) .setSymbol(sym).setType(deref(symtype)); } public Tree Select(Tree qual, Symbol sym) { return Select(qual.pos, qual, sym); } public Tree Select(Tree qual, Name name) { Symbol sym = qual.type.lookup(name); assert (sym.kind != NONE && sym != Symbol.ERROR) : name + " from " + qual.type; return Select(qual, sym); } /** Build and attribute ident node with given symbol. */ public Tree Ident(int pos, Symbol sym) { Global.instance.nextPhase(); Type symtype = sym.type(); Global.instance.prevPhase(); return make.Ident(pos, sym.name) .setSymbol(sym).setType(deref(symtype)); } public Tree Ident(Symbol sym) { return Ident(sym.pos, sym); } /** Build and attribute this node with given symbol. */ public Tree This(int pos, Symbol sym) { return make.This(pos, Ident(pos, sym)).setType(sym.thisType()); } /** Build and attribute super node with given type. */ public Tree Super(int pos, Type type) { return make.Super(pos, TypeTerm(pos, type)).setType(type); } /** Build and attribute value/variable/let definition node whose signature * corresponds to given symbol and which has given rhs. */ public Tree ValDef(int pos, Symbol sym, Tree rhs) { Global.instance.nextPhase(); Type symtype = sym.type(); Global.instance.prevPhase(); return make.ValDef(pos, sym.flags & SOURCEFLAGS, sym.name, TypeTerm(pos, symtype), rhs) .setSymbol(sym).setType(definitions.UNIT_TYPE); } public Tree ValDef(Symbol sym, Tree rhs) { return ValDef(sym.pos, sym, rhs); } /** Build and attribute value/variable/let definition node whose signature * corresponds to given symbol and which has given body. */ public Tree DefDef(int pos, Symbol sym, Tree body) { Global.instance.nextPhase(); Type symtype = sym.type(); Global.instance.prevPhase(); return make.DefDef(pos, sym.flags & SOURCEFLAGS, sym.name, mkTypeParams(pos, symtype.typeParams()), mkParams(pos, symtype), TypeTerm(pos, symtype.resultType()), body) .setSymbol(sym).setType(definitions.UNIT_TYPE); } public Tree DefDef(Symbol sym, Tree rhs) { return DefDef(sym.pos, sym, rhs); } /** Generate class definition from class symbol, parent constructors, and body. */ public Tree ClassDef(int pos, Symbol clazz, Tree[] constrs, Tree[] body) { Global.instance.nextPhase(); Type clazzinfo = clazz.info(); Type constrtype = clazz.constructor().info(); Global.instance.prevPhase(); switch (clazzinfo) { case CompoundType(Type[] parents, Scope members): Template templ = make.Template(pos, constrs, body); templ.setType(clazzinfo); templ.setSymbol(localDummy(pos, clazz.owner())); return make.ClassDef( pos, clazz.flags & SOURCEFLAGS, clazz.name, mkTypeParams(pos, constrtype.typeParams()), mkParams(pos, constrtype), Tree.Empty, templ) .setSymbol(clazz).setType(definitions.UNIT_TYPE); default: throw new ApplicationError(); } } public Tree ClassDef(Symbol clazz, Tree[] constrs, Tree[] body) { return ClassDef(clazz.pos, clazz, constrs, body); } /** Generate class definition from class symbol and body. * All parents must by parameterless, or take unit parameters. */ public Tree ClassDef(int pos, Symbol clazz, Tree[] body) { Global.instance.nextPhase(); Type clazzinfo = clazz.info(); Global.instance.prevPhase(); return ClassDef(pos, clazz, mkParentConstrs(pos, clazzinfo.parents()), body); } public Tree ClassDef(Symbol clazz, Tree[] body) { return ClassDef(clazz.pos, clazz, body); } /** Build the expansion of (() => expr) * This is: * { class $anon() extends scala.Function0 { def apply() = expr } ; new $anon() } */ public Tree mkUnitFunction(Tree expr, Type tp, Symbol owner) { int pos = expr.pos; Type f0t = definitions.functionType(Type.EMPTY_ARRAY, tp); ClassSymbol clazz = new ClassSymbol( pos, Names.ANON_CLASS_NAME.toTypeName(), owner, 0); clazz.setInfo(Type.compoundType(new Type[]{definitions.OBJECT_TYPE, f0t}, new Scope(), clazz)); clazz.constructor().setInfo( Type.MethodType(Symbol.EMPTY_ARRAY, clazz.typeConstructor())); Symbol applyMeth = new TermSymbol(pos, Names.apply, clazz, FINAL) .setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, tp)); clazz.info().members().enter(applyMeth); Tree applyDef = DefDef(applyMeth, changeOwner(expr, owner, applyMeth)); Tree classDef = ClassDef(clazz, new Tree[]{applyDef}); Tree alloc = New(pos, Type.localThisType, clazz, Tree.EMPTY_ARRAY); return Block(new Tree[]{classDef, alloc}); } /** Change owner of all defined symbols from `prevOwner' to `newOwner' */ public Tree changeOwner(Tree tree, final Symbol prevOwner, final Symbol newOwner) { Transformer lifter = new Transformer(global) { public Tree transform(Tree tree) { if (TreeInfo.isDefinition(tree)) { Symbol sym = tree.symbol(); if (sym != null && sym.owner() == prevOwner) { sym.setOwner(newOwner); if (sym.kind == Kinds.CLASS) sym.constructor().setOwner(newOwner); } } return super.transform(tree); } }; return lifter.transform(tree); } }