CodeGeneratorLexicalContext.java revision 953:221a84ef44c0
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.FunctionNode;
38import jdk.nashorn.internal.ir.LexicalContext;
39import jdk.nashorn.internal.ir.LexicalContextNode;
40import jdk.nashorn.internal.ir.Node;
41import jdk.nashorn.internal.ir.Symbol;
42import jdk.nashorn.internal.ir.WithNode;
43
44/**
45 * A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new
46 * variables introduced into them at run time - a with block or a function directly containing an eval call.
47 * Furthermore, this class keeps track of current discard state, which the current method emitter being used is,
48 * the current compile unit, and local variable indexes
49 */
50final class CodeGeneratorLexicalContext extends LexicalContext {
51    private int dynamicScopeCount;
52
53    /** Map of shared scope call sites */
54    private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
55
56    /** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
57    private final Deque<CompileUnit> compileUnits = new ArrayDeque<>();
58
59    /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
60    private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>();
61
62    /** The discard stack - whenever we enter a discard node we keep track of its return value status -
63     *  i.e. should we keep it or throw it away */
64    private final Deque<Node> discard = new ArrayDeque<>();
65
66    private final Deque<Map<String, Collection<Label>>> unwarrantedOptimismHandlers = new ArrayDeque<>();
67    private final Deque<StringBuilder> slotTypesDescriptors = new ArrayDeque<>();
68    private final IntDeque splitNodes = new IntDeque();
69
70    /** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
71     *  currently on the lexical context stack. */
72    private int[] nextFreeSlots = new int[16];
73
74    /** size of next free slot vector */
75    private int nextFreeSlotsSize;
76
77    private boolean isWithBoundary(final LexicalContextNode node) {
78        return node instanceof Block && !isEmpty() && peek() instanceof WithNode;
79    }
80
81    @Override
82    public <T extends LexicalContextNode> T push(final T node) {
83        if (isWithBoundary(node)) {
84            dynamicScopeCount++;
85        } else if (node instanceof FunctionNode) {
86            if (((FunctionNode)node).inDynamicContext()) {
87                dynamicScopeCount++;
88            }
89            splitNodes.push(0);
90        }
91        return super.push(node);
92    }
93
94    void enterSplitNode() {
95        splitNodes.getAndIncrement();
96        pushFreeSlots(methodEmitters.peek().getUsedSlotsWithLiveTemporaries());
97    }
98
99    void exitSplitNode() {
100        final int count = splitNodes.decrementAndGet();
101        assert count >= 0;
102    }
103
104    @Override
105    public <T extends LexicalContextNode> T pop(final T node) {
106        final T popped = super.pop(node);
107        if (isWithBoundary(node)) {
108            dynamicScopeCount--;
109            assert dynamicScopeCount >= 0;
110        } else if (node instanceof FunctionNode) {
111            if (((FunctionNode)node).inDynamicContext()) {
112                dynamicScopeCount--;
113                assert dynamicScopeCount >= 0;
114            }
115            assert splitNodes.peek() == 0;
116            splitNodes.pop();
117        }
118        return popped;
119    }
120
121    boolean inDynamicScope() {
122        return dynamicScopeCount > 0;
123    }
124
125    boolean inSplitNode() {
126        return !splitNodes.isEmpty() && splitNodes.peek() > 0;
127    }
128
129    MethodEmitter pushMethodEmitter(final MethodEmitter newMethod) {
130        methodEmitters.push(newMethod);
131        return newMethod;
132    }
133
134    MethodEmitter popMethodEmitter(final MethodEmitter oldMethod) {
135        assert methodEmitters.peek() == oldMethod;
136        methodEmitters.pop();
137        return methodEmitters.isEmpty() ? null : methodEmitters.peek();
138    }
139
140    void pushUnwarrantedOptimismHandlers() {
141        unwarrantedOptimismHandlers.push(new HashMap<String, Collection<Label>>());
142        slotTypesDescriptors.push(new StringBuilder());
143    }
144
145    Map<String, Collection<Label>> getUnwarrantedOptimismHandlers() {
146        return unwarrantedOptimismHandlers.peek();
147    }
148
149    Map<String, Collection<Label>> popUnwarrantedOptimismHandlers() {
150        slotTypesDescriptors.pop();
151        return unwarrantedOptimismHandlers.pop();
152    }
153
154    CompileUnit pushCompileUnit(final CompileUnit newUnit) {
155        compileUnits.push(newUnit);
156        return newUnit;
157    }
158
159    CompileUnit popCompileUnit(final CompileUnit oldUnit) {
160        assert compileUnits.peek() == oldUnit;
161        compileUnits.pop();
162        return compileUnits.isEmpty() ? null : compileUnits.peek();
163    }
164
165    boolean hasCompileUnits() {
166        return !compileUnits.isEmpty();
167    }
168
169    Collection<SharedScopeCall> getScopeCalls() {
170        return Collections.unmodifiableCollection(scopeCalls.values());
171    }
172
173    /**
174     * Get a shared static method representing a dynamic scope callsite.
175     *
176     * @param unit current compile unit
177     * @param symbol the symbol
178     * @param valueType the value type of the symbol
179     * @param returnType the return type
180     * @param paramTypes the parameter types
181     * @param flags the callsite flags
182     * @return an object representing a shared scope call
183     */
184    SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) {
185        final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags);
186        if (scopeCalls.containsKey(scopeCall)) {
187            return scopeCalls.get(scopeCall);
188        }
189        scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall"));
190        scopeCalls.put(scopeCall, scopeCall);
191        return scopeCall;
192    }
193
194    /**
195     * Get a shared static method representing a dynamic scope get access.
196     *
197     * @param unit current compile unit
198     * @param symbol the symbol
199     * @param valueType the type of the variable
200     * @param flags the callsite flags
201     * @return an object representing a shared scope call
202     */
203    SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags) {
204        return getScopeCall(unit, symbol, valueType, valueType, null, flags);
205    }
206
207    void onEnterBlock(final Block block) {
208        pushFreeSlots(assignSlots(block, isFunctionBody() ? 0 : getUsedSlotCount()));
209    }
210
211    private void pushFreeSlots(final int freeSlots) {
212        if (nextFreeSlotsSize == nextFreeSlots.length) {
213            final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
214            System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
215            nextFreeSlots = newNextFreeSlots;
216        }
217        nextFreeSlots[nextFreeSlotsSize++] = freeSlots;
218    }
219
220    int getUsedSlotCount() {
221        return nextFreeSlots[nextFreeSlotsSize - 1];
222    }
223
224    void releaseSlots() {
225        --nextFreeSlotsSize;
226        final int undefinedFromSlot = nextFreeSlotsSize == 0 ? 0 : nextFreeSlots[nextFreeSlotsSize - 1];
227        if(!slotTypesDescriptors.isEmpty()) {
228            slotTypesDescriptors.peek().setLength(undefinedFromSlot);
229        }
230        methodEmitters.peek().undefineLocalVariables(undefinedFromSlot, false);
231    }
232
233    private int assignSlots(final Block block, final int firstSlot) {
234        int fromSlot = firstSlot;
235        final MethodEmitter method = methodEmitters.peek();
236        for (final Symbol symbol : block.getSymbols()) {
237            if (symbol.hasSlot()) {
238                symbol.setFirstSlot(fromSlot);
239                final int toSlot = fromSlot + symbol.slotCount();
240                method.defineBlockLocalVariable(fromSlot, toSlot);
241                fromSlot = toSlot;
242            }
243        }
244        return fromSlot;
245    }
246
247    static Type getTypeForSlotDescriptor(final char typeDesc) {
248        // Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see
249        // MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor().
250        switch(typeDesc) {
251            case 'I':
252            case 'i':
253                return Type.INT;
254            case 'J':
255            case 'j':
256                return Type.LONG;
257            case 'D':
258            case 'd':
259                return Type.NUMBER;
260            case 'A':
261            case 'a':
262                return Type.OBJECT;
263            case 'U':
264            case 'u':
265                return Type.UNKNOWN;
266            default:
267                throw new AssertionError();
268        }
269    }
270
271    void pushDiscard(final Node node) {
272        discard.push(node);
273    }
274
275    Node popDiscard() {
276        return discard.pop();
277    }
278
279    Node getCurrentDiscard() {
280        return discard.peek();
281    }
282
283    int quickSlot(final Type type) {
284        return methodEmitters.peek().defineTemporaryLocalVariable(type.getSlots());
285    }
286}
287
288