CodeGeneratorLexicalContext.java revision 1207:25109b6b055b
1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.codegen;
27
28import java.util.ArrayDeque;
29import java.util.Collection;
30import java.util.Collections;
31import java.util.Deque;
32import java.util.HashMap;
33import java.util.Map;
34import jdk.nashorn.internal.IntDeque;
35import jdk.nashorn.internal.codegen.types.Type;
36import jdk.nashorn.internal.ir.Block;
37import jdk.nashorn.internal.ir.Expression;
38import jdk.nashorn.internal.ir.FunctionNode;
39import jdk.nashorn.internal.ir.LexicalContext;
40import jdk.nashorn.internal.ir.LexicalContextNode;
41import jdk.nashorn.internal.ir.Node;
42import jdk.nashorn.internal.ir.Symbol;
43import jdk.nashorn.internal.ir.WithNode;
44
45/**
46 * A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new
47 * variables introduced into them at run time - a with block or a function directly containing an eval call.
48 * Furthermore, this class keeps track of current discard state, which the current method emitter being used is,
49 * the current compile unit, and local variable indexes
50 */
51final class CodeGeneratorLexicalContext extends LexicalContext {
52    private int dynamicScopeCount;
53
54    /** Map of shared scope call sites */
55    private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
56
57    /** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
58    private final Deque<CompileUnit> compileUnits = new ArrayDeque<>();
59
60    /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
61    private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>();
62
63    /** The discard stack - whenever we evaluate an expression that will be discarded, we push it on this stack. Various
64     * implementations of expression code emitter can choose to emit code that'll discard the expression themselves, or
65     * ignore it in which case CodeGenerator.loadAndDiscard() will explicitly emit a pop instruction. */
66    private final Deque<Expression> discard = new ArrayDeque<>();
67
68
69    private final Deque<Map<String, Collection<Label>>> unwarrantedOptimismHandlers = new ArrayDeque<>();
70    private final Deque<StringBuilder> slotTypesDescriptors = new ArrayDeque<>();
71    private final IntDeque splitNodes = new IntDeque();
72
73    /** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
74     *  currently on the lexical context stack. */
75    private int[] nextFreeSlots = new int[16];
76
77    /** size of next free slot vector */
78    private int nextFreeSlotsSize;
79
80    private boolean isWithBoundary(final Object node) {
81        return node instanceof Block && !isEmpty() && peek() instanceof WithNode;
82    }
83
84    @Override
85    public <T extends LexicalContextNode> T push(final T node) {
86        if (isWithBoundary(node)) {
87            dynamicScopeCount++;
88        } else if (node instanceof FunctionNode) {
89            if (((FunctionNode)node).inDynamicContext()) {
90                dynamicScopeCount++;
91            }
92            splitNodes.push(0);
93        }
94        return super.push(node);
95    }
96
97    void enterSplitNode() {
98        splitNodes.getAndIncrement();
99        pushFreeSlots(methodEmitters.peek().getUsedSlotsWithLiveTemporaries());
100    }
101
102    void exitSplitNode() {
103        final int count = splitNodes.decrementAndGet();
104        assert count >= 0;
105    }
106
107    @Override
108    public <T extends Node> T pop(final T node) {
109        final T popped = super.pop(node);
110        if (isWithBoundary(node)) {
111            dynamicScopeCount--;
112            assert dynamicScopeCount >= 0;
113        } else if (node instanceof FunctionNode) {
114            if (((FunctionNode)node).inDynamicContext()) {
115                dynamicScopeCount--;
116                assert dynamicScopeCount >= 0;
117            }
118            assert splitNodes.peek() == 0;
119            splitNodes.pop();
120        }
121        return popped;
122    }
123
124    boolean inDynamicScope() {
125        return dynamicScopeCount > 0;
126    }
127
128    boolean inSplitNode() {
129        return !splitNodes.isEmpty() && splitNodes.peek() > 0;
130    }
131
132    MethodEmitter pushMethodEmitter(final MethodEmitter newMethod) {
133        methodEmitters.push(newMethod);
134        return newMethod;
135    }
136
137    MethodEmitter popMethodEmitter(final MethodEmitter oldMethod) {
138        assert methodEmitters.peek() == oldMethod;
139        methodEmitters.pop();
140        return methodEmitters.isEmpty() ? null : methodEmitters.peek();
141    }
142
143    void pushUnwarrantedOptimismHandlers() {
144        unwarrantedOptimismHandlers.push(new HashMap<String, Collection<Label>>());
145        slotTypesDescriptors.push(new StringBuilder());
146    }
147
148    Map<String, Collection<Label>> getUnwarrantedOptimismHandlers() {
149        return unwarrantedOptimismHandlers.peek();
150    }
151
152    Map<String, Collection<Label>> popUnwarrantedOptimismHandlers() {
153        slotTypesDescriptors.pop();
154        return unwarrantedOptimismHandlers.pop();
155    }
156
157    CompileUnit pushCompileUnit(final CompileUnit newUnit) {
158        compileUnits.push(newUnit);
159        return newUnit;
160    }
161
162    CompileUnit popCompileUnit(final CompileUnit oldUnit) {
163        assert compileUnits.peek() == oldUnit;
164        final CompileUnit unit = compileUnits.pop();
165        assert unit.hasCode() : "compile unit popped without code";
166        unit.setUsed();
167        return compileUnits.isEmpty() ? null : compileUnits.peek();
168    }
169
170    boolean hasCompileUnits() {
171        return !compileUnits.isEmpty();
172    }
173
174    Collection<SharedScopeCall> getScopeCalls() {
175        return Collections.unmodifiableCollection(scopeCalls.values());
176    }
177
178    /**
179     * Get a shared static method representing a dynamic scope callsite.
180     *
181     * @param unit current compile unit
182     * @param symbol the symbol
183     * @param valueType the value type of the symbol
184     * @param returnType the return type
185     * @param paramTypes the parameter types
186     * @param flags the callsite flags
187     * @return an object representing a shared scope call
188     */
189    SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) {
190        final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags);
191        if (scopeCalls.containsKey(scopeCall)) {
192            return scopeCalls.get(scopeCall);
193        }
194        scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall"));
195        scopeCalls.put(scopeCall, scopeCall);
196        return scopeCall;
197    }
198
199    /**
200     * Get a shared static method representing a dynamic scope get access.
201     *
202     * @param unit current compile unit
203     * @param symbol the symbol
204     * @param valueType the type of the variable
205     * @param flags the callsite flags
206     * @return an object representing a shared scope call
207     */
208    SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags) {
209        return getScopeCall(unit, symbol, valueType, valueType, null, flags);
210    }
211
212    void onEnterBlock(final Block block) {
213        pushFreeSlots(assignSlots(block, isFunctionBody() ? 0 : getUsedSlotCount()));
214    }
215
216    private void pushFreeSlots(final int freeSlots) {
217        if (nextFreeSlotsSize == nextFreeSlots.length) {
218            final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
219            System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
220            nextFreeSlots = newNextFreeSlots;
221        }
222        nextFreeSlots[nextFreeSlotsSize++] = freeSlots;
223    }
224
225    int getUsedSlotCount() {
226        return nextFreeSlots[nextFreeSlotsSize - 1];
227    }
228
229    void releaseSlots() {
230        --nextFreeSlotsSize;
231        final int undefinedFromSlot = nextFreeSlotsSize == 0 ? 0 : nextFreeSlots[nextFreeSlotsSize - 1];
232        if(!slotTypesDescriptors.isEmpty()) {
233            slotTypesDescriptors.peek().setLength(undefinedFromSlot);
234        }
235        methodEmitters.peek().undefineLocalVariables(undefinedFromSlot, false);
236    }
237
238    private int assignSlots(final Block block, final int firstSlot) {
239        int fromSlot = firstSlot;
240        final MethodEmitter method = methodEmitters.peek();
241        for (final Symbol symbol : block.getSymbols()) {
242            if (symbol.hasSlot()) {
243                symbol.setFirstSlot(fromSlot);
244                final int toSlot = fromSlot + symbol.slotCount();
245                method.defineBlockLocalVariable(fromSlot, toSlot);
246                fromSlot = toSlot;
247            }
248        }
249        return fromSlot;
250    }
251
252    static Type getTypeForSlotDescriptor(final char typeDesc) {
253        // Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see
254        // MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor().
255        switch (typeDesc) {
256            case 'I':
257            case 'i':
258                return Type.INT;
259            case 'J':
260            case 'j':
261                return Type.LONG;
262            case 'D':
263            case 'd':
264                return Type.NUMBER;
265            case 'A':
266            case 'a':
267                return Type.OBJECT;
268            case 'U':
269            case 'u':
270                return Type.UNKNOWN;
271            default:
272                throw new AssertionError();
273        }
274    }
275
276    void pushDiscard(final Expression expr) {
277        discard.push(expr);
278    }
279
280    boolean popDiscardIfCurrent(final Expression expr) {
281        if (isCurrentDiscard(expr)) {
282            discard.pop();
283            return true;
284        }
285        return false;
286    }
287
288    boolean isCurrentDiscard(final Expression expr) {
289        return discard.peek() == expr;
290    }
291
292    int quickSlot(final Type type) {
293        return methodEmitters.peek().defineTemporaryLocalVariable(type.getSlots());
294    }
295}
296
297