summaryrefslogtreecommitdiff
path: root/sources/scalac/transformer/ExpandMixins.java
diff options
context:
space:
mode:
authorschinz <schinz@epfl.ch>2003-02-19 09:55:54 +0000
committerschinz <schinz@epfl.ch>2003-02-19 09:55:54 +0000
commit8ab0ae13ce49fb8d39a6c9af7873586da4e61c4a (patch)
tree69bb72f17b10f0f7a6adc553bf5ede8fde279b03 /sources/scalac/transformer/ExpandMixins.java
parent6cb8bc84c903322e4c87e545c23a648d7950fe9d (diff)
downloadscala-8ab0ae13ce49fb8d39a6c9af7873586da4e61c4a.tar.gz
scala-8ab0ae13ce49fb8d39a6c9af7873586da4e61c4a.tar.bz2
scala-8ab0ae13ce49fb8d39a6c9af7873586da4e61c4a.zip
*** empty log message ***
Diffstat (limited to 'sources/scalac/transformer/ExpandMixins.java')
-rw-r--r--sources/scalac/transformer/ExpandMixins.java426
1 files changed, 426 insertions, 0 deletions
diff --git a/sources/scalac/transformer/ExpandMixins.java b/sources/scalac/transformer/ExpandMixins.java
new file mode 100644
index 0000000000..088f392f63
--- /dev/null
+++ b/sources/scalac/transformer/ExpandMixins.java
@@ -0,0 +1,426 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $OldId: ExpandMixins.java,v 1.24 2002/11/11 16:08:50 schinz Exp $
+// $Id$
+
+package scalac.transformer;
+
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import java.util.*;
+import java.util.Arrays;
+import Tree.*;
+
+/**
+ * A transformer to expand mixins using code copying. We assume that
+ * links to outer classes have been made explicit by a previous phase.
+ *
+ * @author Michel Schinz
+ * @version 1.0
+ */
+
+// [...] do not copy hidden members which are not accessible via
+// "super"
+// [...] handle overloaded symbols
+
+public class ExpandMixins extends Transformer {
+ // Mapping from (class) symbols to their definition.
+ protected final Map/*<Symbol,Tree>*/ classDefs;
+
+ protected final FreshNameCreator freshNameCreator;
+ protected final Map interfaceToClass;
+ protected final Map classToInterface;
+
+ protected final static int PRIVATE_FINAL = Modifiers.FINAL | Modifiers.PRIVATE;
+
+ protected final TreeCopier treeCopier;
+ protected final Definitions defs;
+
+ public ExpandMixins(Global global, ExpandMixinsPhase descr) {
+ super(global, descr);
+ defs = global.definitions;
+
+ classToInterface = global.PHASE.ADDINTERFACES.classToInterface;
+ interfaceToClass = global.PHASE.ADDINTERFACES.interfaceToClass;
+
+ classDefs = descr.classDefs;
+
+ freshNameCreator = global.freshNameCreator;
+
+ treeCopier = new TreeCopier(global, descr, global.make) {
+ // Substitute symbols refering to this class only.
+ public boolean mustSubstituteSymbol(Tree tree) {
+ switch (tree) {
+ case Ident(_):
+ case Select(This(_), _):
+ return true;
+
+ default:
+ return mustCopySymbol(tree);
+ }
+ }
+ };
+ }
+
+ public void apply() {
+ ClassDefCollector collector = new ClassDefCollector(classDefs);
+
+ for (int i = 0; i < global.units.length; i++) {
+ Unit unit = global.units[i];
+ for (int j = 0; j < unit.body.length; ++j)
+ collector.traverse(unit.body[j]);
+ }
+
+ super.apply();
+ }
+
+ protected void typeSubst(Type type, ArrayList f, ArrayList a) {
+ switch (type) {
+ case TypeRef(Type pre, Symbol sym, Type[] args): {
+ Symbol s;
+ if (interfaceToClass.containsKey(sym))
+ s = (Symbol)interfaceToClass.get(sym);
+ else
+ s = sym;
+
+ f.addAll(Arrays.asList(s.typeParams()));
+ a.addAll(Arrays.asList(args));
+ typeSubst(pre, f, a);
+ } break;
+ default:
+ ; // nothing to do
+ }
+ }
+
+ protected Object[] typeSubst(Type type) {
+ ArrayList/*<Symbol[]>*/ f = new ArrayList();
+ ArrayList/*<Type[]>*/ a = new ArrayList();
+ typeSubst(type, f, a);
+ return new Object[] {
+ f.toArray(new Symbol[f.size()]), a.toArray(new Type[a.size()])
+ };
+ }
+
+ protected void getArgsSection(Tree tree, List s) {
+ switch(tree) {
+ case Apply(Tree fun, Tree[] args):
+ getArgsSection(fun, s);
+ s.add(args);
+ break;
+
+ default:
+ ; // nothing to do
+ }
+ }
+
+ protected Tree[][] getArgsSection(Tree tree) {
+ List s = new ArrayList();
+ getArgsSection(tree, s);
+ return (Tree[][])s.toArray(new Tree[s.size()][]);
+ }
+
+ protected Symbol renameSymbol(Map symbolMap, Symbol oldSymbol) {
+ Name newName = freshNameCreator.newName(oldSymbol.name);
+ Symbol newSymbol = oldSymbol.cloneSymbol();
+ newSymbol.name = newName;
+ symbolMap.put(oldSymbol, newSymbol);
+
+ return newSymbol;
+ }
+
+ protected Map/*<Template,Template>*/ expansions = new HashMap();
+
+ protected Template getMixinExpandedTemplate(Template tree, Symbol owner) {
+ if (! expansions.containsKey(tree))
+ expansions.put(tree, expandMixins(tree, owner));
+ return (Template)expansions.get(tree);
+ }
+
+ protected Template expandMixins(Template tree, Symbol owner) {
+ Type templType = tree.type;
+
+ List/*<Tree>*/ newBody = new ArrayList();
+ Scope newMembers = new Scope();
+
+ Map mixedInSymbols/*<Symbol,Symbol>*/ = new HashMap();
+
+ Symbol newTemplSymbol = tree.symbol().cloneSymbol();
+
+ // Start by copying the statement sequence.
+ Tree[] body = tree.body;
+ for (int i = 0; i < body.length; ++i) {
+ Tree stat = body[i];
+ newBody.add(transform(stat));
+
+ if (stat.hasSymbol()) {
+ Symbol sym = stat.symbol();
+ newMembers.enter(sym);
+ }
+ }
+
+ Type[] baseTypes = tree.type.parents();
+ global.log("baseTypes = <" + ArrayApply.toString(baseTypes) + ">");
+
+ // Then go over the mixins and mix them in.
+ for (int bcIndex = tree.parents.length - 1; bcIndex > 0; --bcIndex) {
+ Tree bc = tree.parents[bcIndex];
+
+ Symbol bcSym = baseTypes[bcIndex].symbol();
+ Type bcType = bcSym.type();
+
+ if ((bcSym.flags & Modifiers.INTERFACE) != 0)
+ continue;
+
+ assert classDefs.containsKey(bcSym) : bcSym;
+ ClassDef bcDef = (ClassDef)classDefs.get(bcSym);
+
+ Map symbolMap/*<Symbol,Symbol>*/ = new HashMap();
+
+ // Create substitution for mixin's type parameters.
+ Object[] ts = typeSubst(baseTypes[bcIndex]);
+ assert ts.length == 2;
+ final Symbol[] tpFormals = (Symbol[])ts[0];
+ final Type[] tpActuals = (Type[])ts[1];
+ assert tpFormals.length == tpActuals.length;
+ Type.Map typeMap = new Type.Map() {
+ public Type apply(Type t) {
+ return t.subst(tpFormals, tpActuals);
+ }
+ };
+
+ // Create private fields for mixin's value parameters.
+ Tree[][] actuals = getArgsSection(bc);
+ assert bcDef.vparams.length == actuals.length;
+ for (int s = 0; s < bcDef.vparams.length; ++s) {
+ ValDef[] sectionF = bcDef.vparams[s];
+ Tree[] sectionA = actuals[s];
+
+ assert sectionF.length == sectionA.length;
+
+ for (int p = 0; p < sectionF.length; ++p) {
+ // We do not need to copy the actual parameters,
+ // since they are removed from their original
+ // location anyway.
+ ValDef formal = sectionF[p];
+ Tree actual = sectionA[p];
+
+ Symbol memberSymbol =
+ renameSymbol(symbolMap, formal.symbol());
+ memberSymbol.setOwner(owner);
+ Type memberType = typeMap.apply(formal.tpe.type());
+ memberSymbol.updateInfo(memberType);
+
+ Tree memberDef = gen.ValDef(memberSymbol, actual);
+ newBody.add(memberDef);
+ }
+ }
+
+ Template mixin = getMixinExpandedTemplate(bcDef.impl, bcSym);
+ Tree[] mixinBody = mixin.body;
+ Set/*<Tree>*/ leftOutMembers = new HashSet();
+
+ // Pass 1: compute members to rename.
+ for (int m = 0; m < mixinBody.length; ++m) {
+ Tree member = mixinBody[m];
+
+ if (!member.hasSymbol())
+ continue;
+
+ Symbol memSym = member.symbol();
+ Name memName = memSym.name;
+
+ // Check if we have to import this member. To do this,
+ // we lookup the member both in the template and in
+ // the mixin, and if the result is the same, we import
+ // the member (otherwise it means it's shadowed).
+
+ Symbol memSymT = templType.lookupNonPrivate(memName);
+ Symbol memSymM = bcType.lookupNonPrivate(memName);
+
+ if (memSymT != memSymM) {
+ if ((memSym.flags & Modifiers.ABSTRACT) != 0)
+ leftOutMembers.add(member);
+ else
+ renameSymbol(symbolMap, memSym);
+ }
+ }
+
+ // Pass 2: copy members
+ for (int m = 0; m < mixinBody.length; ++m) {
+ Tree member = mixinBody[m];
+
+ if (leftOutMembers.contains(member))
+ continue;
+
+ treeCopier.pushSymbolSubst(symbolMap);
+ treeCopier.pushTypeSubst(tpFormals, tpActuals);
+ Tree newMember = treeCopier.copy(member);
+ treeCopier.popTypeSubst();
+ treeCopier.popSymbolSubst();
+
+ newBody.add(newMember);
+
+ if (newMember.hasSymbol()) {
+ Symbol sym = newMember.symbol();
+
+ sym.setOwner(owner);
+ newMembers.enter(sym);
+
+ mixedInSymbols.put(member.symbol(), newMember.symbol());
+ }
+ }
+ }
+
+ // Modify mixin base classes to refer to interfaces instead of
+ // real classes.
+ Type[] newBaseTypes = new Type[baseTypes.length];
+ Tree[] newBaseClasses = new Tree[tree.parents.length];
+ newBaseTypes[0] = baseTypes[0];
+ newBaseClasses[0] = tree.parents[0];
+ for (int i = 1; i < baseTypes.length; ++i) {
+ switch (baseTypes[i]) {
+ case TypeRef(Type pre, Symbol sym, Type[] args): {
+ if (!Modifiers.Helper.isInterface(sym.flags) && i > 0) {
+ assert classToInterface.containsKey(sym) : sym;
+ sym = (Symbol)classToInterface.get(sym);
+ }
+
+ newBaseClasses[i] =
+ gen.mkParentConstr(tree.pos,
+ new Type.TypeRef(pre, sym, args),
+ null);
+ newBaseTypes[i] = new Type.TypeRef(pre, sym, args);
+ } break;
+
+ default:
+ throw global.fail("invalid base class type", baseTypes[i]);
+ }
+ }
+
+ // Use correct symbols for mixed-in members.
+ SymbolFixer symbolFixer = new SymbolFixer(global, descr, mixedInSymbols);
+ Tree[] fixedBody =
+ symbolFixer.transform((Tree[])newBody.toArray(new Tree[newBody.size()]));
+ Template newTree = make.Template(tree.pos, newBaseClasses, fixedBody);
+ newTree.setSymbol(newTemplSymbol);
+ newTree.setType(Type.compoundType(newBaseTypes, newMembers, owner));
+
+ return newTree;
+ }
+
+ public Tree transform(Tree tree) {
+ switch (tree) {
+ case ClassDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Template impl):
+ if (Modifiers.Helper.isInterface(mods))
+ return super.transform(tree);
+ else {
+ global.log("expanding " + name);
+ Tree.ClassDef newClass = (Tree.ClassDef)
+ copy.ClassDef(tree,
+ mods,
+ name,
+ super.transform(tparams),
+ super.transform(vparams),
+ super.transform(tpe),
+ getMixinExpandedTemplate(impl, tree.symbol()));
+ newClass.symbol().setInfo(newClass.impl.type);
+ return newClass;
+ }
+
+ default:
+ Tree newTree = super.transform(tree);
+
+ switch (newTree) {
+ case Apply(Select(Tree qualifier, Name selector), Tree[] args): {
+ Tree fun = ((Tree.Apply)newTree).fun;
+ Symbol funOwnerSym = fun.symbol().owner();
+ Symbol qualSym = qualifier.type.symbol();
+ if (! (qualifier instanceof Tree.Super
+ || qualSym.isSubClass(funOwnerSym))) {
+ Type ownerTp = funOwnerSym.type();
+ Tree castQualifier =
+ Tree.Apply(Tree.TypeApply(Tree.Select(qualifier, Names.as),
+ new Tree[] {
+ gen.mkType(qualifier.pos, ownerTp)
+ }),
+ Tree.EMPTY_ARRAY);
+ return copy.Apply(newTree,
+ copy.Select(fun, castQualifier, selector),
+ args);
+ } else
+ return newTree;
+ }
+ default:
+ return newTree;
+ }
+ }
+ }
+
+ //########################################################################
+
+ // Return a hash table associating class definitions to (class) symbols.
+ protected static class ClassDefCollector extends Traverser {
+ private Map map;
+
+ public ClassDefCollector(Map map) {
+ this.map = map;
+ }
+
+ public void traverse(Tree tree) {
+ switch(tree) {
+ case ClassDef(_, _, _, _, _, _):
+ map.put(tree.symbol(), tree);
+ break;
+
+ default:
+ ; // nothing to do
+ }
+ super.traverse(tree);
+ }
+ }
+
+ //########################################################################
+
+ protected static class SymbolFixer extends Transformer {
+ protected final Map/*<Symbol,Symbol>*/ mixedInSymbols;
+
+ public SymbolFixer(Global global, PhaseDescriptor descr, Map mixedInSymbols) {
+ super(global, descr);
+ this.mixedInSymbols = mixedInSymbols;
+ }
+
+ public Tree transform(Tree tree) {
+ switch (tree) {
+ case Ident(_): {
+ Symbol sym = tree.symbol();
+ if (mixedInSymbols.containsKey(sym))
+ return gen.Ident((Symbol)mixedInSymbols.get(sym));
+ else
+ return super.transform(tree);
+ }
+
+ case Select(Super(Tree tpe), Name selector): {
+ Symbol sym = tree.symbol();
+ if (mixedInSymbols.containsKey(sym))
+ return gen.Ident((Symbol)mixedInSymbols.get(sym));
+ else
+ return super.transform(tree);
+ }
+
+ default:
+ return super.transform(tree);
+ }
+ }
+ }
+}