/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** \* */ // $Id$ package scalac.transformer; import scalac.Global; import scalac.util.Name; import scalac.util.Names; import scalac.ast.Tree; import Tree.*; import scalac.ast.Transformer; import scalac.symtab.Type; import scalac.symtab.Symbol; import scalac.symtab.TermSymbol; import scalac.symtab.Scope; import scalac.symtab.Kinds; import scalac.symtab.Modifiers; import java.util.HashMap; import java.util.HashSet; import java.util.ArrayList; import scalac.util.Debug; /** * 1. For each class add a single method (DefDef) designated as * constructor whose parameters are the class parameters and the * owner is the constructed class. The constructor contains: * * - call to the constructor of the supertype with the appropriate arguments * - initialize the fields corresponding to the class ValDefs * - the bodies of the class level expressions; * they are also removed from the ClassDef body * * 2. Additional constructors are assigned new symbols owned by the class * * 3. The right hand sides of the ValDefs are left empty * * 4. Substitute the appropriate constructor symbol into the * 'new' expressions and constructor applications * * @author Nikolay Mihaylov * @version 1.2 */ public class AddConstructors extends Transformer { // True iff we generate code for INT backend. protected final boolean forINT; // True iff we generate code for JVM backend. protected final boolean forJVM; protected final HashMap/**/ constructors; public AddConstructors(Global global, HashMap constructors) { super(global); this.constructors = constructors; this.forINT = global.target == global.TARGET_INT; this.forJVM = (global.target == global.TARGET_JVM || global.target == global.TARGET_JVM_BCEL); } /** return new constructor symbol if it isn't already defined */ Symbol getConstructor(Symbol classConstr) { assert classConstr.isConstructor() : "Class constructor expected: " + Debug.show(classConstr); Symbol owner = classConstr.constructorClass(); Symbol[] tparamSyms = classConstr.typeParams(); Symbol[] paramSyms = classConstr.valueParams(); Symbol constr = (Symbol) constructors.get(classConstr); if (constr == null) { assert !owner.isInterface() : Debug.show(owner) + " is interface"; int flags = classConstr.flags & (Modifiers.PRIVATE | Modifiers.PROTECTED); constr = new TermSymbol(classConstr.pos, classConstr.name, owner, flags); Type constrType = Type.MethodType (paramSyms, forJVM ? global.definitions.UNIT_TYPE : owner.type()); if (tparamSyms.length != 0) constrType = Type.PolyType(tparamSyms, constrType); constr.setInfo(constrType); constructors.put(classConstr, constr); constructors.put(constr, constr); owner.members().enterOrOverload(constr); } return constr; } /** process the tree */ public Tree transform(Tree tree) { final Symbol treeSym = tree.symbol(); switch (tree) { case ClassDef(_, _, _, _, _, Template impl): if (treeSym.isInterface()) return super.transform(tree); // expressions that go before the call to the super constructor final ArrayList constrBody = new ArrayList(); // expressions that go after the call to the super constructor final ArrayList constrBody2 = new ArrayList(); // the body of the class after the transformation final ArrayList classBody = new ArrayList(); // the Symbol of the new primary constructor final Symbol constrSym = getConstructor(treeSym.primaryConstructor()); assert constrSym.owner() == treeSym : "Wrong owner of the constructor: \n\tfound: " + Debug.show(constrSym.owner()) + "\n\texpected: " + Debug.show(treeSym); for (int i = 0; i < impl.body.length; i++) { Tree t = impl.body[i]; if (t.definesSymbol()) { Symbol sym = t.symbol(); switch (t) { // for ValDefs move the initialization code to the constructor case ValDef(_, _, _, Tree rhs): if (rhs != Tree.Empty) { Tree lhs = gen.Select(gen.This(t.pos, treeSym), sym); Tree assign = gen.Assign(t.pos, lhs, transform(rhs)); t = gen.ValDef(sym, Tree.Empty); if (rhs.hasSymbol() && rhs.symbol().isParameter()) { constrBody.add(assign); } else { constrBody2.add(assign); } } break; // assign new symbols to the alternative constructors case DefDef(_, _, _, _, _, Tree rhs): if (sym.isConstructor()) { // add result expression consistent with the // result type of the constructor Tree result = forJVM ? gen.mkUnitLit(t.pos) : gen.This(t.pos, treeSym); rhs = gen.mkBlock(new Tree[] { rhs, result }); t = gen.DefDef(getConstructor(sym), rhs); } break; } // do not transform(t). It will be done at the end classBody.add(t); } else { // move class-level expressions into the constructor constrBody2.add(transform(impl.body[i])); } } // inline the call to the super constructor if ( !forINT || !treeSym.parents()[0].symbol().isJava()) { switch (impl.parents[0]) { case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] args): int pos = impl.parents[0].pos; Tree superConstr = gen.Select (gen.Super(pos, treeSym), getConstructor(fun.symbol())); constrBody.add(gen.mkApplyTV(superConstr, transform(targs), transform(args))); break; case Apply(Tree fun, Tree[] args): int pos = impl.parents[0].pos; Tree superConstr = gen.Select (gen.Super(pos, treeSym), getConstructor(fun.symbol())); constrBody.add(gen.mkApply_V(superConstr, transform(args))); break; default: throw Debug.abort("illegal case", impl.parents[0]); } } // inline initialization of module values if (forINT && treeSym.isModuleClass()) { Symbol module = treeSym.module(); if (module.isGlobalModule()) { constrBody.add( gen.Assign( gen.mkRef(tree.pos, module), gen.This(tree.pos, treeSym))); } else { Symbol owner = module.owner(); Name module_eqname = module.name.append(Names._EQ); Symbol module_eq = owner.lookup(module_eqname); assert module != Symbol.NONE :Debug.show(treeSym.module()); if (owner.isModuleClass() && owner.module().isStable()) { constrBody.add( gen.mkApply_V( gen.mkRef(tree.pos, module_eq), new Tree[] {gen.This(tree.pos, treeSym)})); } else { // !!! module_eq must be accessed via some outer field } } } // add valdefs and class-level expression to the constructorr body constrBody.addAll(constrBody2); // add result expression consistent with the // result type of the constructor if (! forJVM) constrBody.add(gen.This(tree.pos, treeSym)); else constrBody.add(gen.mkUnitLit(tree.pos)); Tree constrTree = constrBody.size() > 1 ? gen.Block((Tree[])constrBody. toArray(new Tree[constrBody.size()])): (Tree) constrBody.get(0); classBody.add(gen.DefDef(constrSym, constrTree)); Tree[] newBody = (Tree[]) classBody.toArray(Tree.EMPTY_ARRAY); // transform the bodies of all members in order to substitute // the constructor references with the new ones for (int i = 0; i < newBody.length - 1; i ++) newBody[i] = transform(newBody[i]); return gen.ClassDef(treeSym, newBody); // Substitute the constructor into the 'new' expressions case New(Template(Tree[] baseClasses, _)): Tree base = baseClasses[0]; switch (base) { case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] args): return gen.New( tree.pos, gen.mkApplyTV( base.pos, gen.Ident(fun.pos, getConstructor(fun.symbol())), transform(targs), transform(args))); case Apply(Tree fun, Tree[] args): return gen.New( tree.pos, gen.mkApply_V( base.pos, gen.Ident(fun.pos, getConstructor(fun.symbol())), transform(args))); default: assert false; } break; // Substitute the constructor aplication; // this only occurs within constructors that terminate // by a call to another constructor of the same class case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] args): Symbol constr = (Symbol)constructors.get(fun.symbol()); if (constr != null) return gen.mkApplyTV( tree.pos, gen.Select(fun.pos, gen.This(fun.pos, constr.owner()), constr), transform(targs), transform(args)); break; case Apply(Tree fun, Tree[] args): Symbol constr = (Symbol)constructors.get(fun.symbol()); if (constr != null) return gen.mkApply_V( tree.pos, gen.Select(fun.pos, gen.This(fun.pos, constr.owner()), constr), transform(args)); break; } // switch(tree) return super.transform(tree); } // transform() } // class AddConstructors