/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2005, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** \* */ // $Id$ package scalac.ast; import scalac.Global; import scalac.ast.Tree.*; import scalac.atree.AConstant; import scalac.symtab.*; import scalac.util.Debug; import scalac.util.Name; import scalac.util.Names; import scalac.util.TypeNames; /** * This class provides method to build attributed trees. * * @author Martin Odersky, Christine Roeckl * @version 1.0 */ public class TreeGen implements Kinds, Modifiers, TypeTags { //######################################################################## // Private Fields /** The global environment */ private final Global global; /** The global definitions */ private final Definitions definitions; /** The tree factory */ private final TreeFactory make; /** Initializes this instance. */ public TreeGen(Global global) { this(global, global.make); } /** Initializes this instance. */ public TreeGen(Global global, TreeFactory make) { this.global = global; this.definitions = global.definitions; this.make = make; } //######################################################################## // Public Methods - Building types /** Builds type references corresponding to given symbols. */ public Tree[] mkTypeRefs(int pos, Symbol[] syms) { if (syms.length == 0) return Tree.EMPTY_ARRAY; Tree[] trees = new Tree[syms.length]; for (int i = 0; i < trees.length; i++) trees[i] = mkTypeRef(pos, syms[i]); return trees; } /** Builds a type reference corresponding to given symbol. */ public Tree mkTypeRef(int pos, Symbol sym) { assert sym.kind == TYPE: Debug.show(sym); sym.flags |= ACCESSED; return mkType(pos, sym.nextType()); } /** Builds trees corresponding to given types. */ public Tree[] mkTypes(int pos, Type[] types) { if (types.length == 0) return Tree.EMPTY_ARRAY; Tree[] trees = new Tree[types.length]; for (int i = 0; i < trees.length; i++) trees[i] = mkType(pos, types[i]); return trees; } /** Builds a tree corresponding to given type. */ public Tree mkType(int pos, Type type) { return TypeTerm(pos, type); } /** Builds a TypeTerm node corresponding to given type. */ public TypeTerm TypeTerm(int pos, Type type) { TypeTerm tree = make.TypeTerm(pos); tree.setType(type); return tree; } //######################################################################## // Public Methods - Building constants /** Builds a unit literal. */ public Tree mkUnitLit(int pos) { return Literal(pos, AConstant.UNIT); } /** Builds a boolean literal. */ public Tree mkBooleanLit(int pos, boolean value) { return Literal(pos, AConstant.BOOLEAN(value)); } /** Builds a byte literal. */ public Tree mkByteLit(int pos, byte value) { return Literal(pos, AConstant.BYTE(value)); } /** Builds a short literal. */ public Tree mkShortLit(int pos, short value) { return Literal(pos, AConstant.SHORT(value)); } /** Builds a character literal. */ public Tree mkCharLit(int pos, char value) { return Literal(pos, AConstant.CHAR(value)); } /** Builds an integer literal */ public Tree mkIntLit(int pos, int value) { return Literal(pos, AConstant.INT(value)); } /** Builds a long literal. */ public Tree mkLongLit(int pos, long value) { return Literal(pos, AConstant.LONG(value)); } /** Builds a float literal. */ public Tree mkFloatLit(int pos, float value) { return Literal(pos, AConstant.FLOAT(value)); } /** Builds a double literal. */ public Tree mkDoubleLit(int pos, double value) { return Literal(pos, AConstant.DOUBLE(value)); } /** Builds a string literal. */ public Tree mkStringLit(int pos, String value) { assert value != null; return Literal(pos, AConstant.STRING(value)); } /** Builds a symbol name literal. */ public Tree mkSymbolNameLit(int pos, Symbol value) { return Literal(pos, AConstant.SYMBOL_NAME(value)); } /** Builds a null literal. */ public Tree mkNullLit(int pos) { return Literal(pos, AConstant.NULL); } /** Builds a zero literal. */ public Tree mkZeroLit(int pos) { return Literal(pos, AConstant.ZERO); } /** Builds a default zero value according to given type tag. */ public Tree mkDefaultValue(int pos, int tag) { switch (tag) { case UNIT : return mkUnitLit(pos); case BOOLEAN: return mkBooleanLit(pos, false); case BYTE : return mkByteLit(pos, (byte)0); case SHORT : return mkShortLit(pos, (short)0); case CHAR : return mkCharLit(pos, '\0'); case INT : return mkIntLit(pos, 0); case LONG : return mkLongLit(pos, 0l); case FLOAT : return mkFloatLit(pos, 0f); case DOUBLE : return mkDoubleLit(pos, 0d); default : throw Debug.abort("unknown type tag: " + tag); } } /** Builds a default zero value according to given type. */ public Tree mkDefaultValue(int pos, Type type) { switch (type.unbox()) { case UnboxedType(int tag): return mkDefaultValue(pos, tag); } if (definitions.ALLREF_TYPE().isSubType(type)) return mkNullLit(pos); return mkZeroLit(pos); } /** Builds a Literal node of given value. */ public Literal Literal(int pos, AConstant value) { Literal tree = make.Literal(pos, value); global.nextPhase(); tree.setType(definitions.atyper.type(value)); global.prevPhase(); return tree; } //######################################################################## // Public Methods - Building references /** * Builds a reference to primary constructor of given class with * given qualifier. */ public Tree mkPrimaryConstructorRef(int pos, Tree qualifier, Symbol clasz){ return mkRef(pos, qualifier, primaryConstructorOf(clasz)); } public Tree mkPrimaryConstructorRef(Tree qualifier, Symbol clasz) { return mkPrimaryConstructorRef(qualifier.pos, qualifier, clasz); } /** * Builds a reference to primary constructor of given class with * given stable prefix. */ public Tree mkPrimaryConstructorRef(int pos, Type stable, Symbol clasz) { return mkRef(pos, stable, primaryConstructorOf(clasz)); } /** * Builds a local reference to primary constructor of given class. */ public Tree mkPrimaryConstructorLocalRef(int pos, Symbol clasz) { return mkLocalRef(pos, primaryConstructorOf(clasz)); } /** * Builds a global reference to primary constructor of given * class. */ public Tree mkPrimaryConstructorGlobalRef(int pos, Symbol clasz) { return mkGlobalRef(pos, primaryConstructorOf(clasz)); } /** Builds local references to given symbols. */ public Tree[] mkLocalRefs(int pos, Symbol[] symbols) { if (symbols.length == 0) return Tree.EMPTY_ARRAY; Tree[] trees = new Tree[symbols.length]; for (int i = 0; i < trees.length; i++) trees[i] = mkLocalRef(pos, symbols[i]); return trees; } /** Builds global references to given symbols. */ public Tree[] mkGlobalRefs(int pos, Symbol[] symbols) { if (symbols.length == 0) return Tree.EMPTY_ARRAY; Tree[] trees = new Tree[symbols.length]; for (int i = 0; i < trees.length; i++) trees[i] = mkGlobalRef(pos, symbols[i]); return trees; } /** Builds a reference to given symbol with given qualifier. */ public Tree mkRef(int pos, Tree qualifier, Symbol symbol) { return Select(pos, qualifier, symbol); } public Tree mkRef(Tree qualifier, Symbol symbol) { return mkRef(qualifier.pos, qualifier, symbol); } /** * Builds a reference to given symbol with given stable prefix. */ public Tree mkRef(int pos, Type stable, Symbol symbol) { Tree qualifier = mkQualifier(pos, stable); if (qualifier == Tree.Empty) return Ident(pos, symbol); return mkRef(pos, qualifier, symbol); } /** Builds a local reference to given symbol. */ public Tree mkLocalRef(int pos, Symbol symbol) { assert symbol.isTerm(): Debug.show(symbol); Type prefix = symbol.hasStaticAttribute() ? Type.NoPrefix : symbol.owner().thisType(); return mkRef(pos, prefix, symbol); } /** Builds a global reference to given symbol. */ public Tree mkGlobalRef(int pos, Symbol symbol) { assert symbol.isTerm(): Debug.show(symbol); global.nextPhase(); Type prefix = symbol.owner().staticPrefix(); global.prevPhase(); return mkRef(pos, prefix, symbol); } /** Builds a qualifier corresponding to given stable prefix. */ public Tree mkQualifier(int pos, Type stable) { switch (stable) { case NoPrefix: return Tree.Empty; case ThisType(Symbol clasz): return This(pos, clasz); case SingleType(Type prefix, Symbol member): Tree qualifier = mkRef(pos, prefix, member); switch (qualifier.type()) { case MethodType(Symbol[] params, _): assert params.length == 0: qualifier.type(); return Apply(pos, qualifier); default: return qualifier; } default: throw Debug.abort("illegal case", stable); } } /** Builds a This node corresponding to given class. */ public This This(int pos, Symbol clazz) { assert clazz.isClassType(): Debug.show(clazz); This tree = make.This(pos, clazz); global.nextPhase(); tree.setType(clazz.thisType()); global.prevPhase(); return tree; } /** Builds a Super node corresponding to given class. */ public Super Super(int pos, Symbol clazz) { assert clazz.isClass(): Debug.show(clazz); Super tree = make.Super(pos, clazz, TypeNames.EMPTY); global.nextPhase(); tree.setType(clazz.thisType()); global.prevPhase(); return tree; } /** Builds an Ident node corresponding to given symbol. */ public Ident Ident(int pos, Symbol sym) { assert sym.isTerm(): Debug.show(sym); sym.flags |= ACCESSED; Ident tree = make.Ident(pos, sym); global.nextPhase(); Type type; if (sym.isInitializer()) { type = sym.type(); Symbol[] tparams = sym.owner().typeParams(); if (tparams.length != 0) type = Type.PolyType(tparams, type); } else { type = sym.owner().thisType().memberStabilizedType(sym); } tree.setType(type); global.prevPhase(); return tree; } /** * Builds a Select node corresponding to given symbol selected * from given qualifier. */ public Select Select(int pos, Tree qualifier, Symbol sym) { assert sym.isTerm(): Debug.show(sym); sym.flags |= ACCESSED | SELECTOR; Select tree = make.Select(pos, sym, qualifier); global.nextPhase(); tree.setType(qualifier.type.memberStabilizedType(sym)); global.prevPhase(); return tree; } public Select Select(Tree qualifier, Symbol sym) { return Select(qualifier.pos, qualifier, sym); } //######################################################################## // Public Methods - Building applications /** * Builds an application with given function, type arguments and * value arguments. */ public Tree mkApplyTV(int pos, Tree fn, Type[] targs, Tree[] vargs) { if (targs.length != 0) fn = TypeApply(pos, fn, mkTypes(pos, targs)); if (vargs.length == 0 && fn.getType().isObjectType()) return fn; else return Apply(pos, fn, vargs); } public Tree mkApplyTV(Tree fn, Type[] targs, Tree[] vargs) { return mkApplyTV(fn.pos, fn, targs, vargs); } public Tree mkApplyTV(int pos, Tree fn, Tree[] targs, Tree[] vargs) { if (targs.length != 0) fn = TypeApply(pos, fn, targs); if (vargs.length == 0 && fn.getType().isObjectType()) return fn; else return Apply(pos, fn, vargs); } public Tree mkApplyTV(Tree fn, Tree[] targs, Tree[] vargs) { return mkApplyTV(fn.pos, fn, targs, vargs); } /** * Builds an application with given function and type arguments * and with no value arguments. */ public Tree mkApplyT_(int pos, Tree fn, Type[] targs) { return mkApplyTV(pos, fn, targs, Tree.EMPTY_ARRAY); } public Tree mkApplyT_(Tree fn, Type[] targs) { return mkApplyT_(fn.pos, fn, targs); } public Tree mkApplyT_(int pos, Tree fn, Tree[] targs) { return mkApplyTV(pos, fn, targs, Tree.EMPTY_ARRAY); } public Tree mkApplyT_(Tree fn, Tree[] targs) { return mkApplyT_(fn.pos, fn, targs); } /** * Builds an application with given function, no type arguments * and given value arguments. */ public Tree mkApply_V(int pos, Tree fn, Tree[] vargs) { return mkApplyTV(pos, fn, Tree.EMPTY_ARRAY, vargs); } public Tree mkApply_V(Tree fn, Tree[] vargs) { return mkApply_V(fn.pos, fn, vargs); } /** * Builds an application with given function and no type arguments * and no value arguments. */ public Tree mkApply__(int pos, Tree fn) { return mkApplyTV(pos, fn, Tree.EMPTY_ARRAY, Tree.EMPTY_ARRAY); } public Tree mkApply__(Tree fn) { return mkApply__(fn.pos, fn); } /** Builds a TypeApply node with given function and arguments. */ public TypeApply TypeApply(int pos, Tree fn, Type[] targs) { return TypeApply(pos, fn, mkTypes(pos, targs)); } public TypeApply TypeApply(Tree fn, Type[] targs) { return TypeApply(fn.pos, fn, targs); } public TypeApply TypeApply(int pos, Tree fn, Tree[] targs) { Type fnType = fn.type(); switch (fnType) { case Type.OverloadedType(_, _): // TreeGen only builds trees, names must already be resolved throw Debug.abort("unresolved name", fn + " - " + fnType); case Type.PolyType(Symbol[] tparams, Type restpe): global.nextPhase(); restpe = restpe.subst(tparams, Tree.typeOf(targs)); global.prevPhase(); return (TypeApply)make.TypeApply(pos, fn, targs).setType(restpe); default: throw Debug.abort("illegal case", fn + " - " + fnType + " - " + fnType.getClass()); } } public TypeApply TypeApply(Tree fn, Tree[] targs) { return TypeApply(fn.pos, fn, targs); } /** Builds an Apply node with given function and arguments. */ public Apply Apply(int pos, Tree fn, Tree[] vargs) { Type fnType = fn.type(); switch (fnType) { case Type.OverloadedType(_, _): // TreeGen only builds trees, names must already be resolved throw Debug.abort("unresolved name", fn + " - " + fnType); case Type.MethodType(Symbol[] vparams, Type restpe): return (Apply)make.Apply(pos, fn, vargs).setType(restpe); default: throw Debug.abort("illegal case", fn + " - " + fnType + " - " + fnType.getClass()); } } public Apply Apply(Tree fn, Tree[] vargs) { return Apply(fn.pos, fn, vargs); } /** Builds an Apply node with given function and no arguments. */ public Apply Apply(int pos, Tree fn) { return Apply(pos, fn, Tree.EMPTY_ARRAY); } public Apply Apply(Tree fn) { return Apply(fn.pos, fn); } //######################################################################## // Public Methods - Building new instances /** * Builds a new instance creation with given qualifier, * initializer, type arguments and value arguments. */ public Tree mkNewTV(int pos, Tree qual, Symbol init, Type[] targs, Tree[] vargs) { return mkNewTV(pos, qual, init, mkTypes(pos, targs), vargs); } public Tree mkNewTV(Tree qual, Symbol init, Type[] targs, Tree[] vargs) { return mkNewTV(qual.pos, qual, init, targs, vargs); } public Tree mkNewTV(int pos, Tree qual, Symbol init, Tree[] targs, Tree[] vargs) { assert init.isInitializer(): Debug.show(init); Tree instance = Create(pos, qual, init.owner(), targs); return New(pos, Apply(pos, Select(pos, instance, init), vargs)); } public Tree mkNewTV(Tree qual, Symbol init, Tree[] targs, Tree[] vargs) { return mkNewTV(qual.pos, qual, init, targs, vargs); } /** * Builds a new instance creation with given qualifier, * initializer and type arguments and no value arguments. */ public Tree mkNewT_(int pos, Tree qual, Symbol init, Type[] targs) { return mkNewTV(pos, qual, init, targs, Tree.EMPTY_ARRAY); } public Tree mkNewT_(Tree qual, Symbol init, Type[] targs) { return mkNewT_(qual.pos, qual, init, targs); } public Tree mkNewT_(int pos, Tree qual, Symbol init, Tree[] targs) { return mkNewTV(pos, qual, init, targs, Tree.EMPTY_ARRAY); } public Tree mkNewT_(Tree qual, Symbol init, Tree[] targs) { return mkNewT_(qual.pos, qual, init, targs); } /** * Builds a new instance creation with given qualifier, * initializer and value arguments and no type arguments. */ public Tree mkNew_V(int pos, Tree qual, Symbol init, Tree[] vargs) { return mkNewTV(pos, qual, init, Tree.EMPTY_ARRAY, vargs); } public Tree mkNew_V(Tree qual, Symbol init, Tree[] vargs) { return mkNew_V(qual.pos, qual, init, vargs); } /** * Builds a new instance creation with given qualifier, and * initializer and no type arguments and no value arguments. */ public Tree mkNew__(int pos, Tree qual, Symbol init) { return mkNewTV(pos, qual, init, Tree.EMPTY_ARRAY, Tree.EMPTY_ARRAY); } public Tree mkNew__(Tree qual, Symbol init) { return mkNew__(qual.pos, qual, init); } /** Builds a New node corresponding to "new ". */ public New New(int pos, Tree init) { New tree = make.New(pos, init); tree.setType(init.type()); // after AddConstructor use type of Create node switch (init) { case Apply(Select(Tree qual, _), _): if (qual instanceof Tree.Create) tree.setType(qual.type()); } return tree; } public New New(Tree init) { return New(init.pos, init); } /** * Builds a Create node with given qualifier, clasz and type * arguments. */ public Create Create(int pos, Tree qual, Symbol clasz, Type[] targs) { return Create(pos, qual, clasz, mkTypes(pos, targs)); } public Create Create(Tree qual, Symbol clasz, Type[] targs) { return Create(qual.pos, qual, clasz, targs); } public Create Create(int pos, Tree qual, Symbol clasz, Tree[] targs) { assert clasz.isClass(): Debug.show(clasz); Create tree = make.Create(pos, clasz, qual, targs); global.nextPhase(); assert targs.length == clasz.typeParams().length: tree; Type prefix = qual == Tree.Empty ? Type.NoPrefix:qual.type(); tree.setType(Type.typeRef(prefix, clasz, Tree.typeOf(targs))); global.prevPhase(); return tree; } public Create Create(Tree qual, Symbol clasz, Tree[] targs) { return Create(qual.pos, qual, clasz, targs); } /** * Builds a Create node with given qualifier, clasz and no type * arguments. */ public Create Create(int pos, Tree qual, Symbol clasz) { return Create(pos, qual, clasz, Tree.EMPTY_ARRAY); } public Create Create(Tree qual, Symbol clasz) { return Create(qual.pos, qual, clasz); } /** Builds a Create node with given type. */ public Create Create(int pos, Type type) { switch (type) { case TypeRef(Type prefix, Symbol clasz, Type[] targs): return Create(pos, mkQualifier(pos, prefix), clasz, targs); default: throw Debug.abort("illegal case", type); } } //######################################################################## // Public Methods - Building expressions - Simple nodes /** Flattens the given tree array by inlining Block nodes. */ public Tree[] flatten_(Tree[] trees) { boolean copy = false; int length = 0; for (int i = 0; i < trees.length; i++) { switch (trees[i]) { case Empty: copy = true; length -= 1; continue; case Block(Tree[] stats, Tree value): copy = true; length += stats.length + 1; continue; } length += 1; } if (!copy) return trees; Tree[] clone = new Tree[length]; for (int i = 0, o = 0; i < trees.length; i++) { switch (trees[i]) { case Empty: continue; case Block(Tree[] stats, Tree value): for (int j = 0; j < stats.length; j++) clone[o++] = stats[j]; clone[o++] = value; continue; } clone[o++] = trees[i]; } return clone; } /** Builds an import of all names of given qualifier. */ public Import mkImportAll(int pos, Tree qualifier) { return Import(pos, qualifier, new Name[]{Names.IMPORT_WILDCARD}); } public Import mkImportAll(Tree qualifier) { return mkImportAll(qualifier.pos, qualifier); } public Import mkImportAll(int pos, Symbol qualifier) { return mkImportAll(pos, mkGlobalRef(pos, qualifier)); } /** Builds an instance test with given value and type. */ public Tree mkIsInstanceOf(int pos, Tree value, Type type, boolean erased) { Symbol sym = erased ? definitions.ANY_IS_ERASED : definitions.ANY_IS; return mkApplyT_(pos, Select(value, sym), new Type[]{type}); } public Tree mkIsInstanceOf(int pos, Tree value, Type type) { boolean afterTAV = global.currentPhase.id >= global.PHASE.TYPESASVALUES.id(); return mkIsInstanceOf(pos, value, type, afterTAV); } public Tree mkIsInstanceOf(Tree value, Type type) { return mkIsInstanceOf(value.pos, value, type); } /** Builds a cast with given value and type. */ public Tree mkAsInstanceOf(int pos, Tree value, Type type, boolean erased) { Symbol sym = erased ? definitions.ANY_AS_ERASED : definitions.ANY_AS; return mkApplyT_(pos, Select(value, sym), new Type[]{type}); } public Tree mkAsInstanceOf(int pos, Tree value, Type type) { boolean afterTAV = global.currentPhase.id >= global.PHASE.TYPESASVALUES.id(); return mkAsInstanceOf(pos, value, type, afterTAV); } public Tree mkAsInstanceOf(Tree value, Type type) { return mkAsInstanceOf(value.pos, value, type); } /** Builds an expression of type Unit with given statements. */ public Tree mkUnitBlock(int pos, Tree[] stats) { return mkBlock(pos, stats, mkUnitLit(pos)); } /** Builds an expression of type Unit with given statement. */ public Tree mkUnitBlock(int pos, Tree stat) { return mkUnitBlock(pos, new Tree[]{stat}); } public Tree mkUnitBlock(Tree stat) { return mkUnitBlock(stat.pos, stat); } /** Builds an expression with given statements and value. */ public Tree mkBlock(int pos, Tree[] stats, Tree value) { if (stats.length == 0) return value; return Block(pos, stats, value); // !!! add flatten? } public Tree mkBlock(Tree[] stats, Tree value) { return mkBlock((stats.length!=0 ? stats[0] : value).pos, stats, value); } /** Builds an expression with given statement and value. */ public Tree mkBlock(int pos, Tree stat, Tree value) { switch (stat) { case Empty: return value; case Block(Tree[] block_stats, Tree block_value): Tree[] stats = Tree.cloneArray(block_stats, 1); stats[block_stats.length] = block_value; return Block(stat.pos, stats, value); default: return Block(pos, new Tree[]{stat}, value); } } public Tree mkBlock(Tree stat, Tree value) { return mkBlock(stat.pos, stat, value); } /** Builds an Import node with given qualifier and names. */ public Import Import(int pos, Tree qualifier, Name[] names) { Import tree = make.Import(pos, qualifier.symbol(), qualifier, names); tree.setType(Type.NoType); return tree; } public Import Import(Tree qualifier, Name[] names) { return Import(qualifier.pos, qualifier, names); } public Import Import(int pos, Symbol qualifier, Name[] names) { return Import(pos, mkGlobalRef(pos, qualifier), names); } /** Builds a Template node with given symbol, parents and body. */ public Template Template(int pos, Symbol local, Tree[]parents, Tree[]body){ Template tree = make.Template(pos, local, parents, body); tree.setType(Type.NoType); return tree; } /** Builds a Block node with given statements and value. */ public Block Block(int pos, Tree[] stats, Tree value) { inline: switch (value) { case Block(Tree[] value_stats, Tree value_value): int count = 0; for (int i = 0; i < value_stats.length; i++) { if (value_stats[i].definesSymbol()) break inline; if (value_stats[i] != Tree.Empty) count++; } Tree[] array = Tree.cloneArray(stats, count); for (int i = 0, j = stats.length; i < value_stats.length; i++) { if (value_stats[i] != Tree.Empty) array[j++] = value_stats[i]; } stats = array; value = value_value; } Block tree = make.Block(pos, stats, value); tree.setType(value.type()); return tree; } public Block Block(Tree[] stats, Tree value) { return Block((stats.length != 0 ? stats[0] : value).pos, stats, value); } /** Builds an Assign node corresponding to " = ". */ public Assign Assign(int pos, Tree lhs, Tree rhs) { Assign tree = make.Assign(pos, lhs, rhs); global.nextPhase(); tree.setType(definitions.void_TYPE()); global.prevPhase(); return tree; } public Assign Assign(Tree lhs, Tree rhs) { return Assign(lhs.pos, lhs, rhs); } /** Builds an If node with given components and type. */ public If If(int pos, Tree cond, Tree thenpart, Tree elsepart, Type type) { //if (!thenpart.type.isSubType(type)) System.out.println(type + "!>:(" + thenpart.type + "," + elsepart.type + ")");//debug assert assertTreeSubTypeOf(thenpart, type); assert assertTreeSubTypeOf(elsepart, type); If tree = make.If(pos, cond, thenpart, elsepart); tree.setType(type); return tree; } public If If(Tree cond, Tree thenpart, Tree elsepart, Type type) { return If(cond.pos, cond, thenpart, elsepart, type); } /** Builds an If node with given condition and branches. */ public If If(int pos, Tree cond, Tree thenpart, Tree elsepart) { global.nextPhase(); Type type = thenpart.getType().isSameAs(elsepart.getType()) ? thenpart.type : Type.lub(new Type[] {thenpart.getType(), elsepart.getType()}); global.prevPhase(); return If(pos, cond, thenpart, elsepart, type); } public If If(Tree cond, Tree thenpart, Tree elsepart) { return If(cond.pos, cond, thenpart, elsepart); } /** Builds a Switch node with given components and type. * @param tags a sorted array of tags */ public Switch Switch(int pos, Tree test, int[] tags, Tree[] bodies, Tree otherwise, Type type) { for (int i = 0; i < bodies.length; i++) { assert assertTreeSubTypeOf(bodies[i], type); assert (i==0) || ( tags[i-1] < tags[i] ) : "expecting sorted tags"; } assert assertTreeSubTypeOf(otherwise, type); Switch tree = make.Switch(pos, test, tags, bodies, otherwise); tree.setType(type); return tree; } public Switch Switch(Tree test, int[] tags, Tree[] bodies, Tree otherwise, Type type) { return Switch(test.pos, test, tags, bodies, otherwise, type); } /** Builds a Switch node with given components. */ public Switch Switch(int pos, Tree test, int[] tags, Tree[] bodies, Tree otherwise) { Type[] types = new Type[bodies.length + 1]; for (int i = 0; i < bodies.length; i++) types[i] = bodies[i].getType(); types[bodies.length] = otherwise.getType(); global.nextPhase(); Type type = Type.lub(types); global.prevPhase(); return Switch(pos, test, tags, bodies, otherwise, type); } public Switch Switch(Tree test, int[] tags, Tree[] bodies, Tree otherwise){ return Switch(test.pos, test, tags, bodies, otherwise); } /** Builds a Return node of given method with given value. */ public Return Return(int pos, Symbol method, Tree value) { Return tree = make.Return(pos, method, value); tree.setType(definitions.ALL_TYPE()); return tree; } public Return Return(Symbol method, Tree value) { return Return(value.pos, method, value); } /** Builds a Typed nodes with given value and type. */ public Typed Typed(int pos, Tree value, Tree type) { Typed tree = make.Typed(pos, value, type); tree.setType(type.type()); return tree; } public Typed Typed(Tree value, Tree type) { return Typed(value.pos, value, type); } public Typed Typed(int pos, Tree value, Type type) { return Typed(pos, value, mkType(pos, type)); } public Typed Typed(Tree value, Type type) { return Typed(value.pos, value, type); } //######################################################################## // Public Methods - Building expressions - Lists /** Builds an empty list. */ public Tree mkNil(int pos) { return mkGlobalRef(pos, definitions.NIL()); } /** Builds a list with given element type, head and tail. */ public Tree mkNewCons(int pos, Type element, Tree head, Tree tail) { // !!! these checks can be removed once they are done in Apply global.nextPhase(); assert head.type().isSubType(element): element + " -- " + head + " : " + head.type; assert tail.type().isSubType(definitions.LIST_TYPE(element)): element + " -- " + tail + " : " + tail.type; global.prevPhase(); return New( mkApplyTV( mkPrimaryConstructorGlobalRef(pos, definitions.CONS_CLASS()), new Type[]{element}, new Tree[]{head, tail})); } public Tree mkNewCons(Type element, Tree head, Tree tail) { return mkNewCons(head.pos, element, head, tail); } /** Builds a list with given element type and values. */ public Tree mkNewList(int pos, Type element, Tree[] values) { Tree list = mkNil(pos); for (int i = values.length - 1; 0 <= i; i--) list = mkNewCons(pos, element, values[i], list); return list; } //######################################################################## // Public Methods - Building expressions - Arrays /** Builds a new array tree with given element type and length. */ public Tree mkNewArray(int pos, Type element, Tree length) { return New( mkApplyTV( mkPrimaryConstructorGlobalRef(pos, definitions.ARRAY_CLASS), new Type[]{element}, new Tree[]{length})); } public Tree mkNewArray(Type element, Tree length) { return mkNewArray(length.pos, element, length); } public Tree mkNewArray(int pos, Type element, int length) { return mkNewArray(pos, element, mkIntLit(pos, length)); } /** * Builds a new array tree with given element type and values. The * owner must be the owner of the created code. It is needed to * create a local variable. */ public Tree mkNewArray(int pos, Type element, Tree[] values, Symbol owner){ if (values.length == 0) return mkNewArray(pos, element, 0); Tree[] trees = new Tree[1 + values.length]; Symbol array = newLocal( owner, pos, FINAL, "array", definitions.array_TYPE(element)); trees[0] = ValDef(array, mkNewArray(pos, element, values.length)); for (int i = 0; i < values.length; i++) trees[1 + i] = mkArraySet(Ident(pos, array), i, values[i]); return Block(pos, trees, Ident(pos, array)); } /** Builds an array length operation. */ public Tree mkArrayLength(int pos, Tree array) { Tree function = Select(pos, array, definitions.ARRAY_LENGTH()); return Apply(pos, function); } public Tree mkArrayLength(Tree array) { return mkArrayLength(array.pos, array); } /** Builds an array get operation. */ public Tree mkArrayGet(int pos, Tree array, Tree index) { Tree function = Select(pos, array, definitions.ARRAY_GET()); return Apply(pos, function, new Tree[] {index}); } public Tree mkArrayGet(Tree array, Tree index) { return mkArrayGet(array.pos, array, index); } public Tree mkArrayGet(int pos, Tree array, int index) { return mkArrayGet(pos, array, mkIntLit(pos, index)); } public Tree mkArrayGet(Tree array, int index) { return mkArrayGet(array.pos, array, index); } /** Builds an array set operation. */ public Tree mkArraySet(int pos, Tree array, Tree index, Tree value) { Tree function = Select(pos, array, definitions.ARRAY_SET()); return Apply(pos, function, new Tree[] {index, value}); } public Tree mkArraySet(Tree array, Tree index, Tree value) { return mkArraySet(array.pos, array, index, value); } public Tree mkArraySet(int pos, Tree array, int index, Tree value) { return mkArraySet(pos, array, mkIntLit(pos, index), value); } public Tree mkArraySet(Tree array, int index, Tree value) { return mkArraySet(array.pos, array, index, value); } //######################################################################## // Public Methods - Building definitions /** Builds the type parameter section of given symbol. */ public AbsTypeDef[] mkTypeParamsOf(Symbol sym) { Symbol[] tparams = sym.nextTypeParams(); AbsTypeDef[] trees = new AbsTypeDef[tparams.length]; for (int i = 0; i < tparams.length; i++) trees[i] = mkTypeParam(tparams[i]); return trees; } /** Builds the value parameter section of given symbol. */ public ValDef[][] mkParamsOf(Symbol sym) { global.nextPhase(); if (sym.isClass()) sym = sym.primaryConstructor(); Type type = sym.type(); global.prevPhase(); ValDef[][] treess = Tree.ValDef_EMPTY_ARRAY_ARRAY; while (true) { switch (type) { case PolyType(_, Type result): type = result; continue; case MethodType(Symbol[] vparams, Type result): ValDef[] trees = new ValDef[vparams.length]; for (int i = 0; i < vparams.length; i++) trees[i] = mkParam(vparams[i]); ValDef[][] array = new ValDef[treess.length + 1][]; for (int i = 0; i < treess.length; i++) array[i] = treess[i]; array[treess.length] = trees; treess = array; type = result; continue; default: return treess; } } } /** Builds the type parameter corresponding to given symbol. */ public AbsTypeDef mkTypeParam(Symbol sym) { return AbsTypeDef(sym); } /** Builds the value parameter corresponding to given symbol. */ public ValDef mkParam(Symbol sym) { return ValDef(sym, Tree.Empty); } /** Builds a PackageDef with given package and template. */ public PackageDef PackageDef(Tree peckage, Template template) { PackageDef tree = make.PackageDef(peckage.pos, peckage, template); tree.setType(Type.NoType); return tree; } public PackageDef PackageDef(Symbol peckage, Template template) { return PackageDef(mkGlobalRef(peckage.pos, peckage), template); } /** Builds a PackageDef with given package and body. */ public PackageDef PackageDef(Tree peckage, Tree[] body) { return PackageDef( peckage, Template(peckage.pos, Symbol.NONE, Tree.EMPTY_ARRAY, body)); } public PackageDef PackageDef(Symbol peckage, Tree[] body) { return PackageDef(mkGlobalRef(peckage.pos, peckage), body); } /** Builds a ClassDef node for given class with given template. */ public ClassDef ClassDef(Symbol clazz, Template template) { Tree type = Tree.Empty; if (clazz.thisSym() != clazz) { global.nextPhase(); type = mkType(clazz.pos, clazz.typeOfThis()); global.prevPhase(); } ClassDef tree = make.ClassDef( clazz.pos, clazz, mkTypeParamsOf(clazz), mkParamsOf(clazz), type, template); tree.setType(Type.NoType); return tree; } /** * Builds a ClassDef node for given class with given parent * constructors, local symbol and body. */ public ClassDef ClassDef(Symbol clazz, Tree[] constrs, Symbol local, Tree[] body) { return ClassDef(clazz, Template(clazz.pos, local, constrs, body)); } /** Builds a ClassDef node for given class with given body. */ public Tree ClassDef(Symbol clazz, Tree[] body) { for (int i = 0; i < body.length; i++) { assert body[i] == Tree.Empty || body[i].definesSymbol() && body[i].symbol().owner() ==clazz: "\nclass : " + Debug.show(clazz) + "\nmember: " + Debug.show(body[i].symbol()) + "\nmember: " + body[i]; } Global.instance.nextPhase(); Type[] parents = clazz.parents(); Global.instance.prevPhase(); Tree[] constrs = new Tree[parents.length]; for (int i = 0; i < constrs.length; i++) { switch (parents[i]) { case TypeRef(Type prefix, Symbol parent, Type[] targs): constrs[i] = mkApplyT_( mkPrimaryConstructorRef(clazz.pos, prefix, parent), targs); continue; default: throw Debug.abort("invalid type", parents[i]); } } return ClassDef(clazz, constrs, Symbol.NONE, body); } /** Builds a ValDef node for given symbol and with given rhs. */ public ValDef ValDef(Symbol sym, Tree rhs) { ValDef tree = make.ValDef( sym.pos, sym, TypeTerm(sym.pos, sym.nextType()), rhs); tree.setType(Type.NoType); return tree; } /** Builds a DefDef node for given symbol with given body. */ public DefDef DefDef(Symbol sym, Tree body) { DefDef tree = make.DefDef( sym.pos, sym, mkTypeParamsOf(sym), mkParamsOf(sym), TypeTerm(sym.pos, sym.nextType().resultType()), body); tree.setType(Type.NoType); return tree; } /** Builds an AbsTypeDef node for given symbol. */ public AbsTypeDef AbsTypeDef(Symbol sym) { AbsTypeDef tree = make.AbsTypeDef( sym.pos, sym, TypeTerm(sym.pos, sym.isViewBounded() ? sym.vuBound() : sym.nextInfo()), TypeTerm(sym.pos, sym.loBound())); tree.setType(Type.NoType); return tree; } /** Builds an AliasTypeDef node for given symbol. */ public AliasTypeDef AliasTypeDef(Symbol sym) { AliasTypeDef tree = make.AliasTypeDef( sym.pos, sym, mkTypeParamsOf(sym), TypeTerm(sym.pos, sym.nextInfo())); tree.setType(Type.NoType); return tree; } /** Builds an CaseDef node with given pattern, guard and body. */ public CaseDef CaseDef(Tree pattern, Tree guard, Tree body) { CaseDef tree = make.CaseDef(pattern.pos, pattern, guard, body); tree.setType(body.getType()); return tree; } /** Builds an CaseDef node with given pattern and body. */ public CaseDef CaseDef(Tree pattern, Tree body) { return CaseDef(pattern, Tree.Empty, body); } /** * Builds a LabelDef node for given label with given parameters * and body. */ public LabelDef LabelDef(Symbol label, Ident[] params, Tree body) { LabelDef tree = make.LabelDef(label.pos, label, params, body); tree.setType(label.nextType().resultType()); return tree; } public LabelDef LabelDef(Symbol label, Symbol[] params, Tree body) { Ident[] idents = new Ident[params.length]; for (int i = 0; i < params.length; i++) idents[i] = Ident(params[i].pos, params[i]); return LabelDef(label, idents, body); } //######################################################################## // Private Methods /** Asserts type of given tree is a subtype of given type. */ private boolean assertTreeSubTypeOf(Tree tree, Type expected) { global.nextPhase(); assert tree.getType().isSubType(expected): "\ntree : " + tree + "\ntype : " + tree.getType() + "\nexpected: " + expected; global.prevPhase(); return true; } /** Creates a new local variable and initializes it. */ private Symbol newLocal(Symbol owner, int pos, int flags, String prefix, Type type) { Name name = Names.LOCAL(owner); // !!! create unique names ? flags |= Modifiers.SYNTHETIC; Symbol local = owner.newVariable(pos, flags, name); global.nextPhase(); local.setType(type); global.prevPhase(); return local; } /** Returns the primary constructor of given class. */ private Symbol primaryConstructorOf(Symbol clasz) { global.nextPhase(); Symbol constr = clasz.primaryConstructor(); global.prevPhase(); return constr; } //######################################################################## //######################################################################## //######################################################################## //######################################################################## // !!! not yet reviewed /** Build the expansion of (() => expr) */ public Tree mkUnitFunction(Tree expr, Type type, Symbol owner, boolean tagMethod) { return mkFunction (expr.pos, Tree.ValDef_EMPTY_ARRAY, expr, type, owner, tagMethod); } /** Build the expansion of ((vparams_1, ..., vparams_n) => body) * with result type `restype', where `owner' is the previous owner * of `body'. * This is: * { class $anon() extends scala.Object with * scala.Function_N[T_1, ..., T_n, restype] { * def apply(vparams_1, ..., vparams_n) = body1 * } * new $anon() * } * where * vparams_i: T_i * `body1' results from `body' by changing owner of all defined * symbols in `body' from `owner' to the apply method. */ public Tree mkFunction(int pos, ValDef[] vparams, Tree body, Type restype, Symbol owner, boolean tagMethod) { int n = vparams.length; Symbol[] params = new Symbol[n]; Type[] argtypes = new Type[n]; for (int i = 0; i < n; i++) { params[i] = vparams[i].symbol(); argtypes[i] = params[i].type(); } Type[] parentTypes = { definitions.ANYREF_TYPE(), definitions.FUNCTION_TYPE(argtypes, restype) }; Name name = Names.ANON_CLASS_NAME.toTypeName(); ClassSymbol clazz = owner.newAnonymousClass(pos, name); clazz.setInfo(Type.compoundType(parentTypes, new Scope(), clazz)); clazz.allConstructors().setInfo( Type.MethodType(Symbol.EMPTY_ARRAY, clazz.typeConstructor())); global.addAttribute(clazz, definitions.SCALA_SERIALIZABLE_CONSTR); Symbol applyMeth = clazz.newMethod(pos, FINAL, Names.apply) .setInfo(Type.MethodType(params, restype)); clazz.info().members().enter(applyMeth); for (int i = 0; i < params.length; i++) { params[i].setOwner(applyMeth); } changeOwner(body, owner, applyMeth); Tree[] memberTrees = new Tree[tagMethod ? 2 : 1]; memberTrees[0] = DefDef(applyMeth, body); if (tagMethod) memberTrees[1] = mkTagMethod(clazz); Tree classDef = ClassDef(clazz, memberTrees); Tree alloc = New(mkApply__(mkPrimaryConstructorLocalRef(pos, clazz))); return mkBlock(classDef, Typed(alloc, parentTypes[1])); // !!! Typed } public Tree mkTagMethod(ClassSymbol clazz) { int flags = clazz.isSubClass(definitions.SCALAOBJECT_CLASS) ? OVERRIDE : 0; Symbol tagSym = clazz.newMethod(clazz.pos, flags, Names.tag) .setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, definitions.int_TYPE())); clazz.info().members().enter(tagSym); int tagValue = clazz.isCaseClass() ? clazz.tag() : 0; return DefDef(tagSym, mkIntLit(clazz.pos, tagValue)); } public Tree mkPartialFunction(int pos, Tree applyVisitor, Tree isDefinedAtVisitor, Type pattype, Type restype, Symbol owner) { Name name = Names.ANON_CLASS_NAME.toTypeName(); Symbol clazz = owner.newAnonymousClass(pos, name); Type[] parentTypes = { definitions.ANYREF_TYPE(), definitions.PARTIALFUNCTION_TYPE(pattype, restype)}; clazz.setInfo(Type.compoundType(parentTypes, new Scope(), clazz)); clazz.allConstructors().setInfo( Type.MethodType(Symbol.EMPTY_ARRAY, clazz.typeConstructor())); Tree[] memberTrees = { makeVisitorMethod(pos, Names.apply, applyVisitor, pattype, restype, clazz, owner), makeVisitorMethod(pos, Names.isDefinedAt, isDefinedAtVisitor, pattype, definitions.boolean_TYPE(), clazz, owner)}; Tree classDef = ClassDef(clazz, memberTrees); Tree alloc = New(mkApply__(mkPrimaryConstructorLocalRef(pos, clazz))); return mkBlock(classDef, Typed(alloc, parentTypes[1])); // !!! Typed } //where private Tree makeVisitorMethod(int pos, Name name, Tree visitor, Type pattype, Type restype, Symbol clazz, Symbol prevOwner) { Symbol meth = clazz.newMethod(pos, FINAL, name); Symbol param = meth.newVParam(pos, 0, Name.fromString("x$"), pattype); meth.setInfo(Type.MethodType(new Symbol[]{param}, restype)); clazz.info().members().enter(meth); changeOwner(visitor, prevOwner, meth); Tree body = mkApplyTV( Select(Ident(pos, param), definitions.ANY_MATCH), new Tree[]{mkType(pos, pattype), mkType(pos, restype)}, new Tree[]{visitor}); return DefDef(meth, body); } /** Change owner of all defined symbols from `prevOwner' to `newOwner' */ public void changeOwner(Tree tree, final Symbol prevOwner, final Symbol newOwner) { Traverser lifter = new Traverser() { public void traverse(Tree tree) { if (tree.definesSymbol()) { Symbol sym = tree.symbol(); if (sym != null && sym.owner() == prevOwner) { sym.setOwner(newOwner); } } super.traverse(tree); } }; lifter.traverse(tree); } /** Build a postfix function application */ public Tree postfixApply(Tree obj, Tree fn, Symbol owner) { if (TreeInfo.isPureExpr(obj) || TreeInfo.isPureExpr(fn)) { return Apply(Select(fn, definitions.FUNCTION_APPLY(1)), new Tree[]{obj}); } else { Symbol tmp = newLocal(owner, obj.pos, FINAL, "postfix", obj.type); Tree expr = postfixApply(Ident(obj.pos, tmp), fn, owner); return mkBlock(ValDef(tmp, obj), expr); } } // for insert debug printing code public Tree Console_print(int pos, String str) { return Console_print( pos, mkStringLit( pos, str )); } public Tree Console_print(int pos, Tree arg) { return Apply( Select( pos, mkGlobalRef(pos, definitions.CONSOLE), definitions.CONSOLE_PRINT()), new Tree[] { arg }); } }