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