/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** \* */ // $Id$ package scalac.atree; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import scalac.Unit; import scalac.ast.Tree; import scalac.ast.Tree.Ident; import scalac.ast.Tree.Template; import scalac.symtab.Definitions; import scalac.symtab.Symbol; import scalac.symtab.TermSymbol; import scalac.symtab.Type; import scalac.util.Debug; import scalac.util.Name; /** This class translates syntax trees into attributed trees. */ public class ATreeFromSTree { //######################################################################## // Private Fields /** The global definitions */ private final Definitions definitions; /** The attributed tree factory */ private final ATreeFactory make; /** A mapping from primitive classes to initialization state */ private final Map/**/ states; /** A mapping from primitive methods to generators */ private final Map/**/ generators; //######################################################################## // Public Constructors /** Initializes this instance. */ public ATreeFromSTree(Definitions definitions) { this.definitions = definitions; this.make = new ATreeFactory(); this.states = new HashMap(); this.generators = new HashMap(); Symbol[] classes = { definitions.ANY_CLASS, definitions.JAVA_OBJECT_CLASS, definitions.JAVA_STRING_CLASS, definitions.JAVA_THROWABLE_CLASS, definitions.ARRAY_CLASS, definitions.UNIT_CLASS, definitions.BOOLEAN_CLASS, definitions.BYTE_CLASS, definitions.SHORT_CLASS, definitions.CHAR_CLASS, definitions.INT_CLASS, definitions.LONG_CLASS, definitions.FLOAT_CLASS, definitions.DOUBLE_CLASS, }; for (int i = 0; i < classes.length; i++) states.put(classes[i], Boolean.FALSE); } //######################################################################## // Public Methods - Translating units /** Translates the unit's body and stores the result in it. */ public void translate(Unit unit) { template(unit.repository = new ARepository(), unit.body); } //######################################################################## // Private Methods - Translating templates /** Translates the templates and adds them to the repository. */ private void template(ARepository repository, Tree[] trees) { for (int i = 0; i < trees.length; i++) template(repository, trees[i]); } /** Translates the template and adds it to the repository. */ private void template(ARepository repository, Tree tree) { switch (tree) { case Empty: return; case ClassDef(_, _, _, _, _, Template(_, Tree[] body)): AClass clasz = new AClass(tree.symbol()); // !!! add static field to global modules repository.addClass(clasz); member(clasz, body); return; case PackageDef(_, Template(_, Tree[] body)): template(repository, body); return; default: throw Debug.abort("illegal case", tree); } } //######################################################################## // Private Methods - Translating members /** Translates the members and adds them to the class. */ private void member(AClass clasz, Tree[] trees) { for (int i = 0; i < trees.length; i++) member(clasz, trees[i]); } /** Translates the member and adds it to the class. */ private void member(AClass clasz, Tree tree) { switch (tree) { case Empty: return; case ClassDef(_, _, _, _, _, _): template(clasz, tree); return; case ValDef(_, _, _, Tree rhs): AField field = new AField(tree.symbol(), false); clasz.addField(field); return; case DefDef(_, _, _, _, _, Tree rhs): AMethod method = new AMethod(tree.symbol(), false); clasz.addMethod(method); if (!method.isAbstract()) method.setCode(expression(rhs)); return; default: throw Debug.abort("illegal case", tree); } } //######################################################################## // Private Methods - Translating statements /** Translates the statements. */ private ACode[] statement(List locals, Tree[] trees, int start, int count){ List codes = new ArrayList(); for (int i = start; i < start + count; i++) { ACode code = statement(locals, trees[i]); if (code != ACode.Void) codes.add(code); } return (ACode[])codes.toArray(new ACode[codes.size()]); } /** Translates the statement. */ private ACode statement(List locals, Tree tree) { switch (tree) { case Empty: return make.Void; case ValDef(_, _, _, Tree rhs): Symbol symbol = tree.symbol(); locals.add(symbol); ALocation location = ALocation.Local(symbol, false); return make.Store(tree, location, expression(rhs)); default: return ACode.Drop(expression(tree), tree.type()); } } //######################################################################## // Private Methods - Translating expressions /** Translates the expressions. */ private ACode[] expression(Tree[] trees) { ACode[] codes = new ACode[trees.length]; for (int i = 0; i < codes.length; i++) codes[i] = expression(trees[i]); return codes; } /** Translates the expression. */ private ACode expression(Tree tree) { switch (tree) { case LabelDef(_, Ident[] idents, Tree rhs): Symbol[] locals = Tree.symbolOf(idents); return make.Label(tree, tree.symbol(), locals, expression(rhs)); case Block(Tree[] statements): if (statements.length == 0) return make.Void; int statement_count = statements.length - 1; List locals = new ArrayList(); ACode[] codes = statement(locals,statements,0, statement_count); ACode value = expression(statements[statement_count]); if (locals.size() == 0 && codes.length == 0) return value; Symbol[] symbols = (Symbol[])locals.toArray(new Symbol[locals.size()]); return make.Block(tree, symbols, codes, value); case Assign(Tree lhs, Tree rhs): return make.Block(tree, Symbol.EMPTY_ARRAY, new ACode[] { make.Store(tree, location(lhs), expression(rhs))}, make.Void); case If(Tree cond, Tree thenp, Tree elsep): ACode test = expression(cond); return make.If(tree, test, expression(thenp), expression(elsep)); case Switch(Tree test, int[] tags, Tree[] bodies, Tree otherwise): int[][] tagss = new int[tags.length][]; for (int i = 0; i < tagss.length; i++) tagss[i] = new int[] {tags[i]}; ACode[] codes = new ACode[bodies.length + 1]; for (int i = 0; i < bodies.length; i++) codes[i] = expression(bodies[i]); codes[tags.length] = expression(otherwise); return make.Switch(tree, expression(test), tagss, codes); case Return(Tree value): return make.Return(tree, tree.symbol(), expression(value)); case Throw(Tree value): return make.Throw(tree, expression(value)); case New(Template(Tree[] bases, _)): switch (bases[0]) { case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs): return apply(tree, method(fun), targs, vargs); case Apply(Tree fun, Tree[] vargs): return apply(tree, method(fun), Tree.EMPTY_ARRAY, vargs); default: throw Debug.abort("illegal case", bases[0]); } case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs): return apply(tree, fun, targs, vargs); case Apply(Tree fun, Tree[] vargs): return apply(tree, fun, Tree.EMPTY_ARRAY, vargs); case Super(_, _): case This(_): return make.This(tree, tree.symbol()); case Select(_, _): case Ident(_): return make.Load(tree, location(tree)); case Literal(AConstant value): return make.Constant(tree, value); default: throw Debug.abort("illegal case", tree); } } /** Translates the application. */ private ACode apply(Tree tree, Tree fun, Tree[] targs, Tree[] vargs) { switch (fun) { case Ident(_): return make.Goto(tree, fun.symbol(), expression(vargs)); default: return apply(tree, method(fun), targs, vargs); } } /** Translates the application. */ private ACode apply(Tree tree, AFunction function,Tree[]targs,Tree[]vargs){ Type[] types = Tree.typeOf(targs); switch (function) { case Method(ACode object, Symbol method, AInvokeStyle style): if (!style.isDynamic()) break; Symbol clasz = method.owner(); Object state = states.get(clasz); if (state == null) break; if (state != Boolean.TRUE) addGeneratorsOf(clasz); Object generator = generators.get(method); if (generator == null) break; return generate((Generator)generator, tree, object, types, vargs); } return make.Apply(tree, function, types, expression(vargs)); } //######################################################################## // Private Methods - Translating functions /** Translates the method. */ private AFunction method(Tree tree) { Symbol symbol = tree.symbol(); switch (tree) { case Select(Tree qualifier, _): if (symbol.isJava() && symbol.owner().isModuleClass()) return AFunction.Method(make.Void, symbol, AInvokeStyle.StaticClass); // !!! qualifier is ignored ! ACode object = expression(qualifier); return AFunction.Method(object, symbol, invokeStyle(qualifier)); case Ident(_): return AFunction.Method(make.Void, symbol, AInvokeStyle.New); default: throw Debug.abort("illegal case", tree); } } /** Returns the InvokeStyle to use for the qualifier. */ private AInvokeStyle invokeStyle(Tree qualifier) { switch (qualifier) { case Super(_, _): return AInvokeStyle.StaticInstance; default: return AInvokeStyle.Dynamic; } } //######################################################################## // Private Methods - Translating locations /** Translates the location. */ private ALocation location(Tree tree) { Symbol symbol = tree.symbol(); switch (tree) { case Select(Tree qualifier, _): if (symbol.isModule()) return ALocation.Module(symbol); // !!! qualifier is ignored ! if (symbol.isJava() && symbol.owner().isModuleClass()) return ALocation.Field(make.Void, symbol, true); // !!! qualifier is ignored ! return ALocation.Field(expression(qualifier), symbol, false); case Ident(_): if (symbol.isModule()) return ALocation.Module(symbol); return ALocation.Local(symbol, symbol.isParameter()); default: throw Debug.abort("illegal case", tree); } } //######################################################################## // Private Methods - Translating constants /** Translates the constant. */ private AConstant constant(Object value) { if (value instanceof Boolean ) return make.BOOLEAN((Boolean )value); if (value instanceof Byte ) return make.BYTE (((Byte )value)); if (value instanceof Short ) return make.SHORT ((Short )value); if (value instanceof Character) return make.CHAR ((Character)value); if (value instanceof Integer ) return make.INT ((Integer )value); if (value instanceof Long ) return make.LONG ((Long )value); if (value instanceof Float ) return make.FLOAT ((Float )value); if (value instanceof Double ) return make.DOUBLE ((Double )value); if (value instanceof String ) return make.STRING ((String )value); throw Debug.abort("illegal constant", value +" -- "+ value.getClass()); } //######################################################################## // Private Methods - Generating code for primitive methods /** Applies generator to given object and arguments. */ private ACode generate(Generator generator, Tree tree, ACode object, Type[] targs, Tree[] vargs) { switch (generator) { case ANYID: assert targs.length == 0 && vargs.length == 1: tree; return make.EQ(tree, ATypeKind.REF, object, expression(vargs[0])); case ANYEQ: Symbol lf = newLocal(tree, definitions.ANY_TYPE()); Symbol rg = newLocal(tree, definitions.ANY_TYPE()); return make.Block(tree, new Symbol[] {lf, rg}, new ACode[] { store(tree, lf, object), store(tree, rg, expression(vargs[0]))}, make.If(tree, make.EQ(tree, ATypeKind.REF, load(tree, lf)), make.EQ(tree, ATypeKind.REF, load(tree, rg)), make.Apply(tree, AFunction.Method( load(tree, lf), definitions.ANY_EQUALS, AInvokeStyle.Dynamic), Type.EMPTY_ARRAY, new ACode[] {load(tree, rg)}))); case ANYNE: Symbol lf = newLocal(tree, definitions.ANY_TYPE()); Symbol rg = newLocal(tree, definitions.ANY_TYPE()); return make.Block(tree, new Symbol[] {lf, rg}, new ACode[] { store(tree, lf, object), store(tree, rg, expression(vargs[0]))}, make.If(tree, make.EQ(tree, ATypeKind.REF, load(tree, lf)), make.NE(tree, ATypeKind.REF, load(tree, rg)), make.NOT(tree, ATypeKind.BOOL, make.Apply(tree, AFunction.Method( load(tree, lf), definitions.ANY_EQUALS, AInvokeStyle.Dynamic), Type.EMPTY_ARRAY, new ACode[] {load(tree, rg)})))); case ISAS(boolean cast): assert targs.length == 1 && vargs.length == 0: tree; return make.IsAs(tree, object, targs[0], cast); case SYNCHRONIZED: assert targs.length == 1 && vargs.length == 1: tree; return make.Synchronized(tree, object, expression(vargs[0])); case THROW: assert targs.length == 0 && vargs.length == 0: tree; return make.Throw(tree, object); case CONCAT(ATypeKind prefix): assert targs.length == 0 && vargs.length == 1: tree; ATypeKind suffix = kind(vargs[0].type()); ACode argument = expression(vargs[0]); return make.CONCAT(tree, prefix, suffix, object, argument); default: throw Debug.abort("unknown case", generator); } } /** Generates a load operation with given variable. */ private ACode load(Tree tree, Symbol local) { assert local.owner().isNone(): Debug.show(local); return make.Load(tree, ALocation.Local(local, false)); } /** Generates a store operation with given variable and value. */ private ACode store(Tree tree, Symbol local, ACode value) { assert local.owner().isNone(): Debug.show(local); return make.Store(tree, ALocation.Local(local, false), value); } /** Creates a variable with tree's position and given type. */ private Symbol newLocal(Tree tree, Type type) { Symbol owner = Symbol.NONE; // !!! Name name = Name.fromString("local"); // !!! return new TermSymbol(tree.pos, name, owner, 0).setType(type); } /** Returns the type kind of given type. */ private ATypeKind kind(Type type) { switch (type) { case SingleType(_, _): case ConstantType(_, _): return kind(type.singleDeref()); case TypeRef(_, Symbol clasz, _): if (clasz == definitions.BOOLEAN_CLASS) return ATypeKind.BOOL; if (clasz == definitions.BYTE_CLASS) return ATypeKind.I1; if (clasz == definitions.SHORT_CLASS) return ATypeKind.I2; if (clasz == definitions.CHAR_CLASS) return ATypeKind.U2; if (clasz == definitions.INT_CLASS) return ATypeKind.I4; if (clasz == definitions.LONG_CLASS) return ATypeKind.I8; if (clasz == definitions.FLOAT_CLASS) return ATypeKind.R4; if (clasz == definitions.DOUBLE_CLASS) return ATypeKind.R8; if (clasz == definitions.JAVA_STRING_CLASS) return ATypeKind.STR; return ATypeKind.REF; default: return ATypeKind.REF; } } //######################################################################## // Private Methods - Collecting primitive methods /** Associates generators to primitive methods of given class. */ private void addGeneratorsOf(Symbol clasz) { if (clasz == definitions.ANY_CLASS) { addGenerator(definitions.ANY_EQ, Generator.ANYID); addGenerator(definitions.ANY_EQEQ, Generator.ANYEQ); addGenerator(definitions.ANY_BANGEQ, Generator.ANYNE); addGenerator(definitions.ANY_IS, Generator.ISAS(false)); addGenerator(definitions.ANY_AS, Generator.ISAS(true)); } if (clasz == definitions.JAVA_OBJECT_CLASS) { addGenerator(definitions.ANYREF_SYNCHRONIZED, Generator.SYNCHRONIZED); } if (clasz == definitions.JAVA_STRING_CLASS) { addGenerator(definitions.JAVA_STRING_PLUS, Generator.CONCAT(ATypeKind.STR)); } if (clasz == definitions.JAVA_THROWABLE_CLASS) { addGenerator(definitions.JAVA_THROWABLE_THROW, Generator.THROW); } if (clasz == definitions.ARRAY_CLASS) { // !!! addAll(defs.ARRAY_CLASS, Names.length, Primitive.LENGTH, 1); // !!! addAll(defs.ARRAY_CLASS, Names.apply, Primitive.APPLY, 2); // !!! addAll(defs.ARRAY_CLASS, Names.update, Primitive.UPDATE, 1); } if (clasz == definitions.UNIT_CLASS) { // !!! } if (clasz == definitions.BOOLEAN_CLASS) { // !!! } if (clasz == definitions.BYTE_CLASS) { // !!! } if (clasz == definitions.SHORT_CLASS) { // !!! } if (clasz == definitions.CHAR_CLASS) { // !!! } if (clasz == definitions.INT_CLASS) { // !!! } if (clasz == definitions.LONG_CLASS) { // !!! } if (clasz == definitions.FLOAT_CLASS) { // !!! } if (clasz == definitions.DOUBLE_CLASS) { // !!! } states.put(clasz, Boolean.TRUE); } /** Associates given generator to given primitive method. */ private void addGenerator(Symbol method, Generator generator) { generators.put(method, generator); } //######################################################################## // Private Class - Code generators /** Code generators for primitive methods. */ private static class Generator { case ANYID; case ANYEQ; case ANYNE; case ISAS(boolean cast); case SYNCHRONIZED; case THROW; case CONCAT(ATypeKind prefix); } //######################################################################## }