/* ____ ____ ____ ____ ______ *\
** / __// __ \/ __// __ \/ ____/ 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 java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Iterator;
import scalac.Global;
import scalac.ast.Tree;
import scalac.ast.Tree.Template;
import scalac.ast.TreeList;
import scalac.ast.TreeGen;
import scalac.ast.GenTreeCloner;
import scalac.ast.TreeSymbolCloner;
import scalac.ast.Transformer;
import scalac.symtab.Modifiers;
import scalac.symtab.Scope;
import scalac.symtab.Scope.SymbolIterator;
import scalac.symtab.Symbol;
import scalac.symtab.SymbolCloner;
import scalac.symtab.SymbolSubstTypeMap;
import scalac.symtab.Type;
import scalac.util.Name;
import scalac.util.Debug;
public class ClassExpander {
//########################################################################
// Private Fields
/** The global environment */
private final Global global;
/** A tree generator */
private final TreeGen gen;
/** The expanding class */
private final Symbol clasz;
/** The parents of the expanding class */
private final Type[] parents;
/** The members of the expanding class */
private final Scope members;
/** The template of the expanding class */
private final Template template;
/** The body of the expanding class */
private final LinkedList/*<Tree>*/ body;
/** The type map to apply to inlined symbols and trees */
private final SymbolSubstTypeMap map;
/** The symbol cloner to clone inlined symbols */
private final SymbolCloner cloner;
/** The state of this class (used to prevent misuses) */
private int state;
//########################################################################
// Public Constructors
public ClassExpander(Global global, Symbol clasz, Template template) {
this.global = global;
this.gen = global.treeGen;
this.clasz = clasz;
this.parents = Type.cloneArray(clasz.parents());
this.members = clasz.members(); // !!! .cloneScope();
this.template = gen.Template(template.pos, template.symbol(),
Tree.cloneArray(template.parents), template.body);
this.body = new LinkedList();
this.map = new SymbolSubstTypeMap();
this.cloner = new SymbolCloner(
global.freshNameCreator, new HashMap(), map.getSymbols());
this.state = parents.length;
this.mixinMemberCloner = new MixinMemberCloner(this);
this.superFixer = new SuperFixer(global);
}
//########################################################################
// Public Methods
// !!! remove this.parents
/** Inlines the ith mixin with given type, interface and body. */
public void inlineMixin(int i, Type type, Symbol iface, Template impl) {
assert 0 < i && i < state : "state = " + state + ", i = " + i;
switch (parents[i]) {
case TypeRef(Type prefix, Symbol mixin, Type[] args):
// relpace "This/Super(mixin)" by "This/Super(clasz)"
map.insertSymbol(mixin, clasz);
// owner of inlined value parameters and constructor is "clasz"
cloner.owners.put(mixin.primaryConstructor(), clasz);
// map mixin type parameters to mixin type arguments
map.insertType(type.symbol().typeParams(), type.typeArgs());
Tree.Apply constr = (Tree.Apply)template.parents[i];
Symbol[] vparams = mixin.valueParams();
inlineMixinVParams(vparams, constr.args, 0);
createMixedInMemberSymbols(mixin.nextInfo().members());
inlineMixedInCode(impl, vparams.length);
parents[i] = Type.TypeRef(prefix, iface, args);
template.parents[i] = gen.mkPrimaryConstr(constr.pos, parents[i]);
state = i;
return;
default:
throw Debug.abort("invalid base class type", parents[i]);
}
}
public Template getTemplate() {
assert 0 < state : "state = " + state;
Transformer superFixer = new Transformer(global) {
public Tree transform(Tree tree) {
switch (tree) {
case Select(Super(_, _), _):
Symbol symbol = map.lookupSymbol(tree.symbol());
if (symbol != null) {
Tree qualifier = gen.This(tree.pos, clasz);
assert qualifier.type().baseType(symbol.owner()) != Type.NoType: tree; // !!!
return gen.Select(qualifier, symbol);
}
}
return super.transform(tree);
}
};
body.addAll(Arrays.asList(this.superFixer.transform(
superFixer.transform(template.body))));
template.body = (Tree[])body.toArray(new Tree[body.size()]);
// !!! *1 fix ExpandMixinsPhase.transformInfo and remove next line
state = 0;
return template;
}
//########################################################################
// Private Methods
private void inlineMixinVParams(Symbol[] params, Tree[] args, int fstPos) {
for (int i = 0; i < params.length; i++) {
Symbol member = cloner.cloneSymbol(params[i], true);
member.flags &= ~Modifiers.PARAM;
member.flags |= Modifiers.PRIVATE;
members.enter(member);
}
// !!! remove double loop
// We need two loops because parameters may appear in their types.
for (int i = 0; i < params.length; i++) {
Symbol member = map.lookupSymbol(params[i]);
member.setType(map.apply(member.type()));
body.add(fstPos + i, gen.ValDef(member, args[i]));
}
}
private void createMixedInMemberSymbols(Scope symbols) {
// The map names is used to implement an all or nothing
// strategy for overloaded symbols.
Map/*<Name,Name>*/ names = new HashMap();
for (SymbolIterator i = symbols.iterator(true); i.hasNext();) {
Symbol member = i.next();
Name name = (Name)names.get(member.name);
boolean shadowed = name == null &&
members.lookup(member.name) != Symbol.NONE;
Symbol clone = cloner.cloneSymbol(member, shadowed);
if (name != null)
clone.name = name;
else
names.put(member.name, clone.name);
if (clone.name != member.name) clone.flags &= ~Modifiers.OVERRIDE;
clone.setType(clone.type().cloneTypeNoSubst(cloner));
members.enterOrOverload(clone);
}
// We need two loops because members may appear in their types.
for (SymbolIterator i = symbols.iterator(true); i.hasNext();) {
Symbol member = map.lookupSymbol(i.next());
if (member == null) continue;
member.setType(map.applyParams(member.type()));
}
}
private void inlineMixedInCode(Template mixin, int fstPos) {
cloner.owners.put(mixin.symbol(), template.symbol());
int pos = 0;
for (int i = 0; i < mixin.body.length; i++) {
Tree tree = mixin.body[i];
// Inline local code and members whose symbol has been cloned.
if (!tree.definesSymbol() ||
map.lookupSymbol(tree.symbol()) != null) {
body.add(fstPos + pos, mixinMemberCloner.transform(tree));
++pos;
}
}
}
private final GenTreeCloner mixinMemberCloner;
private class MixinMemberCloner extends GenTreeCloner {
public MixinMemberCloner(ClassExpander expander) {
super(expander.global, expander.map, expander.cloner);
}
public Symbol getSymbolFor(Tree tree) {
switch (tree) {
case Select(Super(_, _), _):
// !!! check
global.nextPhase();
Symbol symbol = tree.symbol().overridingSymbol(parents[0]);
global.prevPhase();
assert !symbol.isNone(): tree;
return symbol;
case Super(_, _):
case This(_):
return clasz;
default:
return super.getSymbolFor(tree);
}
}
}
private final Transformer superFixer;
private class SuperFixer extends Transformer {
private final Type parent;
public SuperFixer(Global global) {
super(global);
this.parent = clasz.parents()[0];
}
public Tree transform(Tree tree) {
switch (tree) {
case ClassDef(_, _, _, _, _, _):
return tree;
case Select(Super(_, _), _):
Tree qualifier = ((Tree.Select)tree).qualifier;
qualifier = gen.Super(qualifier.pos, clasz);
Symbol symbol = tree.symbol().overridingSymbol(parent);
assert !symbol.isNone(): tree + " -- " + parent + " -- " + Debug.show(clasz.parents()) + " -- " + Debug.show(clasz);
return gen.Select(tree.pos, qualifier, symbol);
default:
return super.transform(tree);
}
}
}
//########################################################################
}