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 import jdk.nashorn.internal.IntDeque; 35 import jdk.nashorn.internal.codegen.types.Type; 36 import jdk.nashorn.internal.ir.Block; 37 import jdk.nashorn.internal.ir.Expression; 38 import jdk.nashorn.internal.ir.FunctionNode; 39 import jdk.nashorn.internal.ir.LexicalContext; 40 import jdk.nashorn.internal.ir.LexicalContextNode; 41 import jdk.nashorn.internal.ir.Node; 42 import jdk.nashorn.internal.ir.Symbol; 43 import 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 */ 51 final 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 splitLiterals = 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 splitLiterals.push(0); 93 } 94 return super.push(node); 95 } 96 97 void enterSplitLiteral() { 98 splitLiterals.getAndIncrement(); 99 pushFreeSlots(methodEmitters.peek().getUsedSlotsWithLiveTemporaries()); 100 } 101 102 void exitSplitLiteral() { 103 final int count = splitLiterals.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 splitLiterals.peek() == 0; 119 splitLiterals.pop(); 120 } 121 return popped; 122 } 123 124 boolean inDynamicScope() { 125 return dynamicScopeCount > 0; 126 } 127 128 boolean inSplitLiteral() { 129 return !splitLiterals.isEmpty() && splitLiterals.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 * @param isOptimistic is this an optimistic call 188 * @return an object representing a shared scope call 189 */ 190 SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, 191 final Type returnType, final Type[] paramTypes, final int flags, 192 final boolean isOptimistic) { 193 final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags, 194 isOptimistic); 195 if (scopeCalls.containsKey(scopeCall)) { 196 return scopeCalls.get(scopeCall); 197 } 198 scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall")); 199 scopeCalls.put(scopeCall, scopeCall); 200 return scopeCall; 201 } 202 203 /** 204 * Get a shared static method representing a dynamic scope get access. 205 * 206 * @param unit current compile unit 207 * @param symbol the symbol 208 * @param valueType the type of the variable 209 * @param flags the callsite flags 210 * @param isOptimistic is this an optimistic get 211 * @return an object representing a shared scope get 212 */ 213 SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags, 214 final boolean isOptimistic) { 215 return getScopeCall(unit, symbol, valueType, valueType, null, flags, isOptimistic); 216 } 217 218 void onEnterBlock(final Block block) { 219 pushFreeSlots(assignSlots(block, isFunctionBody() ? 0 : getUsedSlotCount())); 220 } 221 222 private void pushFreeSlots(final int freeSlots) { 223 if (nextFreeSlotsSize == nextFreeSlots.length) { 224 final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2]; 225 System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize); 226 nextFreeSlots = newNextFreeSlots; 227 } 228 nextFreeSlots[nextFreeSlotsSize++] = freeSlots; 229 } 230 231 int getUsedSlotCount() { 232 return nextFreeSlots[nextFreeSlotsSize - 1]; 233 } 234 235 void releaseSlots() { 236 --nextFreeSlotsSize; 237 final int undefinedFromSlot = nextFreeSlotsSize == 0 ? 0 : nextFreeSlots[nextFreeSlotsSize - 1]; 238 if(!slotTypesDescriptors.isEmpty()) { 239 slotTypesDescriptors.peek().setLength(undefinedFromSlot); 240 } 241 methodEmitters.peek().undefineLocalVariables(undefinedFromSlot, false); 242 } 243 244 private int assignSlots(final Block block, final int firstSlot) { 245 int fromSlot = firstSlot; 246 final MethodEmitter method = methodEmitters.peek(); 247 for (final Symbol symbol : block.getSymbols()) { 248 if (symbol.hasSlot()) { 249 symbol.setFirstSlot(fromSlot); 250 final int toSlot = fromSlot + symbol.slotCount(); 251 method.defineBlockLocalVariable(fromSlot, toSlot); 252 fromSlot = toSlot; 253 } 254 } 255 return fromSlot; 256 } 257 258 static Type getTypeForSlotDescriptor(final char typeDesc) { 259 // Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see 260 // MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor(). 261 switch (typeDesc) { 262 case 'I': 263 case 'i': 264 return Type.INT; 265 case 'J': 266 case 'j': 267 return Type.LONG; 268 case 'D': 269 case 'd': 270 return Type.NUMBER; 271 case 'A': 272 case 'a': 273 return Type.OBJECT; 274 case 'U': 275 case 'u': 276 return Type.UNKNOWN; 277 default: 278 throw new AssertionError(); 279 } 280 } 281 282 void pushDiscard(final Expression expr) { 283 discard.push(expr); 284 } 285 286 boolean popDiscardIfCurrent(final Expression expr) { 287 if (isCurrentDiscard(expr)) { 288 discard.pop(); 289 return true; 290 } 291 return false; 292 } 293 294 boolean isCurrentDiscard(final Expression expr) { 295 return discard.peek() == expr; 296 } 297 298 int quickSlot(final Type type) { 299 return methodEmitters.peek().defineTemporaryLocalVariable(type.getSlots()); 300 } 301 } 302