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 26 package jdk.nashorn.internal.codegen; 27 28 import java.util.ArrayDeque; 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.Deque; 32 import java.util.HashMap; 33 import java.util.Map; 34 35 import jdk.nashorn.internal.codegen.types.Type; 36 import jdk.nashorn.internal.ir.Block; 37 import jdk.nashorn.internal.ir.FunctionNode; 38 import jdk.nashorn.internal.ir.LexicalContext; 39 import jdk.nashorn.internal.ir.LexicalContextNode; 40 import jdk.nashorn.internal.ir.Node; 41 import jdk.nashorn.internal.ir.Symbol; 42 import 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 */ 50 final 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 /** A stack tracking the next free local variable slot in the blocks. There's one entry for every block 67 * currently on the lexical context stack. */ 68 private int[] nextFreeSlots = new int[16]; 69 70 /** size of next free slot vector */ 71 private int nextFreeSlotsSize; 72 73 @Override 74 public <T extends LexicalContextNode> T push(final T node) { 75 if (isDynamicScopeBoundary(node)) { 76 ++dynamicScopeCount; 77 } 78 return super.push(node); 79 } 80 81 @Override 82 public <T extends LexicalContextNode> T pop(final T node) { 83 final T popped = super.pop(node); 84 if (isDynamicScopeBoundary(popped)) { 85 --dynamicScopeCount; 86 } 87 if (node instanceof Block) { 88 --nextFreeSlotsSize; 89 } 90 return popped; 91 } 92 93 private boolean isDynamicScopeBoundary(final LexicalContextNode node) { 94 if (node instanceof Block) { 95 // Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture 96 // processing of WithNode.expression too, but it should be unaffected. 97 return !isEmpty() && peek() instanceof WithNode; 98 } else if (node instanceof FunctionNode) { 99 // Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new 100 // variable into the function's scope), and it isn't strict (as evals in strict functions get an 101 // isolated scope). 102 return isFunctionDynamicScope((FunctionNode)node); 103 } 104 return false; 105 } 106 107 boolean inDynamicScope() { 108 return dynamicScopeCount > 0; 109 } 110 111 static boolean isFunctionDynamicScope(FunctionNode fn) { 112 return fn.hasEval() && !fn.isStrict(); 113 } 114 115 MethodEmitter pushMethodEmitter(final MethodEmitter newMethod) { 116 methodEmitters.push(newMethod); 117 return newMethod; 118 } 119 120 MethodEmitter popMethodEmitter(final MethodEmitter oldMethod) { 121 assert methodEmitters.peek() == oldMethod; 122 methodEmitters.pop(); 123 return methodEmitters.isEmpty() ? null : methodEmitters.peek(); 124 } 125 126 CompileUnit pushCompileUnit(final CompileUnit newUnit) { 127 compileUnits.push(newUnit); 128 return newUnit; 129 } 130 131 CompileUnit popCompileUnit(final CompileUnit oldUnit) { 132 assert compileUnits.peek() == oldUnit; 133 compileUnits.pop(); 134 return compileUnits.isEmpty() ? null : compileUnits.peek(); 135 } 136 137 boolean hasCompileUnits() { 138 return !compileUnits.isEmpty(); 139 } 140 141 Collection<SharedScopeCall> getScopeCalls() { 142 return Collections.unmodifiableCollection(scopeCalls.values()); 143 } 144 145 /** 146 * Get a shared static method representing a dynamic scope callsite. 147 * 148 * @param unit current compile unit 149 * @param symbol the symbol 150 * @param valueType the value type of the symbol 151 * @param returnType the return type 152 * @param paramTypes the parameter types 153 * @param flags the callsite flags 154 * @return an object representing a shared scope call 155 */ 156 SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) { 157 final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags); 158 if (scopeCalls.containsKey(scopeCall)) { 159 return scopeCalls.get(scopeCall); 160 } 161 scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall")); 162 scopeCalls.put(scopeCall, scopeCall); 163 return scopeCall; 164 } 165 166 /** 167 * Get a shared static method representing a dynamic scope get access. 168 * 169 * @param unit current compile unit 170 * @param type the type of the variable 171 * @param symbol the symbol 172 * @param flags the callsite flags 173 * @return an object representing a shared scope call 174 */ 175 SharedScopeCall getScopeGet(final CompileUnit unit, final Type type, final Symbol symbol, final int flags) { 176 final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags); 177 if (scopeCalls.containsKey(scopeCall)) { 178 return scopeCalls.get(scopeCall); 179 } 180 scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall")); 181 scopeCalls.put(scopeCall, scopeCall); 182 return scopeCall; 183 } 184 185 186 void nextFreeSlot(final Block block) { 187 final boolean isFunctionBody = isFunctionBody(); 188 189 final int nextFreeSlot; 190 if (isFunctionBody) { 191 // On entry to function, start with slot 0 192 nextFreeSlot = 0; 193 } else { 194 // Otherwise, continue from previous block's first free slot 195 nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1]; 196 } 197 if (nextFreeSlotsSize == nextFreeSlots.length) { 198 final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2]; 199 System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize); 200 nextFreeSlots = newNextFreeSlots; 201 } 202 nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot); 203 } 204 205 private static int assignSlots(final Block block, final int firstSlot) { 206 int nextSlot = firstSlot; 207 for (final Symbol symbol : block.getSymbols()) { 208 if (symbol.hasSlot()) { 209 symbol.setSlot(nextSlot); 210 nextSlot += symbol.slotCount(); 211 } 212 } 213 return nextSlot; 214 } 215 216 void pushDiscard(final Node node) { 217 discard.push(node); 218 } 219 220 Node popDiscard() { 221 return discard.pop(); 222 } 223 224 Node getCurrentDiscard() { 225 return discard.peek(); 226 } 227 228 int quickSlot(final Symbol symbol) { 229 final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1]; 230 nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount(); 231 return quickSlot; 232 } 233 234 } 235