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