summaryrefslogtreecommitdiff
path: root/sources/scalac/transformer/AddInterfaces.java
blob: a54bcc95e8e9a2ccfd919f1a51ffdd8e5cf34869 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*     ____ ____  ____ ____  ______                                     *\
**    / __// __ \/ __// __ \/ ____/    SOcos COmpiles Scala             **
**  __\_ \/ /_/ / /__/ /_/ /\_ \       (c) 2002, LAMP/EPFL              **
** /_____/\____/\___/\____/____/                                        **
\*                                                                      */

// $Id$

// TODO find a good way to change symbol flags (PRIVATE, DEFERRED,
// INTERFACE). In particular find how to make sure that the
// modifications are not made too early, for example before the symbol
// is cloned.

package scalac.transformer;

import java.util.Map;
import java.util.HashMap;

import scalac.Global;
import scalac.ast.GenTransformer;
import scalac.ast.Tree;
import scalac.ast.Tree.Template;
import scalac.ast.TreeGen;
import scalac.ast.TreeList;
import scalac.symtab.Type;
import scalac.symtab.Scope;
import scalac.symtab.Symbol;
import scalac.symtab.SymbolSubstTypeMap;
import scalac.util.Debug;

/**
 * Add, for each class, an interface with the same name, to be used
 * later by mixin expansion. More specifically:
 *
 * - at the end of the name of every class, the string "$class" is
 *   added,
 *
 * - an interface with the original name of the class is created, and
 *   contains all directly bound members of the class (as abstract
 *   members),
 *
 * - the interface is added to the mixin base classes of the class.
 *
 * @author Michel Schinz
 * @version 1.0
 */
public class AddInterfaces extends GenTransformer {

    //#########################################################################
    // Private Fields

    /** The AddInterface phase */
    private final AddInterfacesPhase phase;

    /** The current class (null is none) */
    private Symbol clasz;

    /** The current member (null is none) */
    private Symbol member;

    /** The current class substitution (null is none) */
    private Type.Map classSubst;

    /** The current parameter substitution (null is none) */
    private SymbolSubstTypeMap paramSubst;

    //#########################################################################
    // Public Constructors

    /** Initializes this instance. */
    public AddInterfaces(Global global, AddInterfacesPhase phase) {
        super(global);
        this.phase = phase;
    }

    //#########################################################################
    // Public Methods

    /** Transforms the given symbol. */
    public Symbol getSymbolFor(Tree tree) {
        switch (tree) {
        case Create(_, _):
            return phase.getClassSymbol(tree.symbol());
        case Return(_):
            return member;
        case This(_):
        case Super(_, _):
            return clasz;
        case Select(Super(_, _), _):
            Symbol symbol = tree.symbol();
            if (symbol.isInitializer()) return getClassMember(symbol);
            return getClassMember(symbol, true);
        case Select(_, _):
            Symbol symbol = tree.symbol();
            if (symbol.isInitializer()) return getClassMember(symbol);
            return symbol;
        case Ident(_):
            Symbol symbol = tree.symbol();
            if (symbol.isInitializer()) return getClassMember(symbol);
            if (symbol.isParameter()) return getClassVParam(symbol);
            return symbol;
        default:
            return tree.symbol();
        }
    }

    /** Transforms the given type. */
    public Type transform(Type type) {
        if (classSubst != null) type = classSubst.apply(type);
        if (paramSubst != null) type = paramSubst.apply(type);
        return type;
    }

    /** Transforms the given trees. */
    public Tree[] transform(Tree[] trees) {
        if (member != null) return super.transform(trees);
        TreeList list = new TreeList();
        for (int i = 0; i < trees.length; i++) template(list, trees[i]);
        return list.toArray();
    }

    /** Transforms the given tree. */
    public Tree transform(Tree tree) {
        switch (tree) {
        case ValDef(_, _, _, _):
        case LabelDef(_, _, _):
            Symbol symbol = tree.symbol();
            if (symbol.owner() != member) {
                symbol.setOwner(member);
                symbol.updateInfo(transform(symbol.info()));
            }
            return super.transform(tree);
        case Select(Tree qualifier, _):
            Type prefix = qualifier.type();
            qualifier = transform(qualifier);
            Symbol symbol = getSymbolFor(tree);
            if (symbol.isJava() && !symbol.owner().isInterface()) {
                if (qualifier.type().widen().symbol().isInterface()) {
                    Type baseType = prefix.baseType(symbol.owner());
                    assert baseType != Type.NoType: tree;
                    qualifier = gen.mkAsInstanceOf(qualifier, baseType);
                }
            }
            return gen.Select(tree.pos, qualifier, symbol);
        default:
            return super.transform(tree);
        }
    }

    //#########################################################################
    // Private Methods

    /** Transforms the given template and adds it to given list. */
    private void template(TreeList trees, Tree tree) {
        switch (tree) {
        case Empty:
            return;
        case PackageDef(_, _):
            trees.append(super.transform(tree));
            return;
        case ClassDef(_, _, _, _, _, Template(_, Tree[] body)):
            TreeList list = new TreeList(transform(body));
            this.clasz = tree.symbol();
            Map methods = new HashMap();
            if (phase.needInterface(clasz)) {
                Symbol clone = phase.getClassSymbol(clasz);
                trees.append(getClassTree(clasz, list, methods));
                list = new TreeList();
                this.classSubst = new Type.SubstThisMap(clasz, clone);
                this.paramSubst = phase.getClassSubst(clone);
                this.clasz = clone;
            }
            for (int i = 0; i < body.length; i++) member(methods, body[i]);
            trees.append(getClassTree(clasz, list, methods));
            assert methods.isEmpty(): Debug.show(methods.keySet().toArray());
            this.paramSubst = null;
            this.classSubst = null;
            this.clasz = null;
            return;
        case DefDef(_, _, _, _, _, _):
        case ValDef(_, _, _, _):
            return;
        default:
            throw Debug.abort("illegal tree", tree);
        }
    }

    /**
     * Transforms the given class member. Methods with a non-empty
     * body are added to the given method map. All other members are
     * dropped.
     */
    private void member(Map methods, Tree tree) {
        switch (tree) {
        case ClassDef(_, _, _, _, _, _):
            return;
        case DefDef(_, _, _, _, _, Tree rhs):
            if (rhs == Tree.Empty) return;
            Symbol symbol = tree.symbol();
            this.member = getClassMember(symbol);
            if (member != symbol) {
                paramSubst.insertSymbol(
                    symbol.typeParams(), member.nextTypeParams());
                paramSubst.insertSymbol(
                    symbol.valueParams(), member.nextValueParams());
            }
            methods.put(member, gen.DefDef(member, transform(rhs)));
            if (member != symbol) {
                paramSubst.removeSymbol(symbol.valueParams());
                paramSubst.removeSymbol(symbol.typeParams());
            }
            this.member = null;
            return;
        case ValDef(_, _, _, Tree rhs):
            assert rhs == Tree.Empty: tree;
            return;
        default:
            throw Debug.abort("illegal tree", tree);
        }
    }

    /**
     * Returns the tree of the given class whose body is built by
     * adding to the given body the class members. Non-abstract
     * methods are removed from the ngiven method map. All other
     * members are generated from their symbol.
     */
    private Tree getClassTree(Symbol clasz, TreeList body, Map methods) {
        Scope members = clasz.nextInfo().members();
	/*
        for (Scope.SymbolIterator i = members.iterator(false); i.hasNext(); ) {
	    Symbol sym = i.next();
	    System.out.println(clasz + " defines " + sym + ":" + sym.getType());
	}
	*/
        for (Scope.SymbolIterator i = members.iterator(true); i.hasNext(); ) {
            Symbol member = i.next();
            if (!member.isTerm()) continue;
            body.append(getMemberTree(clasz, member, methods));
        }
        return gen.ClassDef(clasz, body.toArray());
    }

    /**
     * Returns the tree of the given member. Non-abstract methods are
     * removed from the given method map. All other members are
     * generated from their symbol.
     */
    private Tree getMemberTree(Symbol clasz, Symbol member, Map methods) {
        if (!member.isMethod()) return gen.ValDef(member, Tree.Empty);
        if (member.isDeferred()) return gen.DefDef(member, Tree.Empty);
        Tree method = (Tree)methods.remove(member);
        assert method != null: Debug.show(clasz + "." + member + ":" + member.info() + member.locationString());
        return method;
    }

    /** Returns the symbol of given parameter in current class. */
    private Symbol getClassVParam(Symbol vparam) {
        if (paramSubst == null) return vparam;
        Symbol clone = (Symbol)paramSubst.lookupSymbol(vparam);
        assert clone != null: Debug.show(vparam, clasz, member);
        return clone;
    }

    /** Returns the symbol of given member in current class. */
    private Symbol getClassMember(Symbol member) {
        return getClassMember(member, false);
    }
    // !!! Try to remove version with lazy argument. It is currently
    // needed for super calls to abstract method (possible in mixins).
    private Symbol getClassMember(Symbol member, boolean lazy) {
        Symbol owner = member.owner();
        assert owner.isClass(): Debug.show(member);
        if (!phase.needInterface(owner)) return member;
        Symbol clasz = phase.getClassSymbol(owner);
        Symbol clone = (Symbol)phase.getClassMemberMap(clasz).get(member);
        assert clone != null || lazy: Debug.show(member, " not in ", clasz);
        return clone != null ? clone : member;
    }

    //#########################################################################
}