1 /* 2 * Copyright (c) 2014, 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 static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; 29 30 import java.util.HashMap; 31 import java.util.HashSet; 32 import java.util.Iterator; 33 import java.util.Map; 34 import java.util.Set; 35 import jdk.nashorn.internal.ir.Block; 36 import jdk.nashorn.internal.ir.FunctionNode; 37 import jdk.nashorn.internal.ir.IdentNode; 38 import jdk.nashorn.internal.ir.LexicalContext; 39 import jdk.nashorn.internal.ir.Node; 40 import jdk.nashorn.internal.ir.Symbol; 41 import jdk.nashorn.internal.ir.WithNode; 42 import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; 43 import jdk.nashorn.internal.runtime.Context; 44 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; 45 import jdk.nashorn.internal.runtime.logging.DebugLogger; 46 import jdk.nashorn.internal.runtime.logging.Loggable; 47 import jdk.nashorn.internal.runtime.logging.Logger; 48 49 /** 50 * Establishes depth of scope for non local symbols at the start of method. 51 * If this is a recompilation, the previous data from eager compilation is 52 * stored in the RecompilableScriptFunctionData and is transferred to the 53 * FunctionNode being compiled 54 */ 55 @Logger(name="scopedepths") 56 final class FindScopeDepths extends SimpleNodeVisitor implements Loggable { 57 58 private final Compiler compiler; 59 private final Map<Integer, Map<Integer, RecompilableScriptFunctionData>> fnIdToNestedFunctions = new HashMap<>(); 60 private final Map<Integer, Map<String, Integer>> externalSymbolDepths = new HashMap<>(); 61 private final Map<Integer, Set<String>> internalSymbols = new HashMap<>(); 62 private final Set<Block> withBodies = new HashSet<>(); 63 64 private final DebugLogger log; 65 66 private int dynamicScopeCount; 67 FindScopeDepths(final Compiler compiler)68 FindScopeDepths(final Compiler compiler) { 69 this.compiler = compiler; 70 this.log = initLogger(compiler.getContext()); 71 } 72 73 @Override getLogger()74 public DebugLogger getLogger() { 75 return log; 76 } 77 78 @Override initLogger(final Context context)79 public DebugLogger initLogger(final Context context) { 80 return context.getLogger(this.getClass()); 81 } 82 findScopesToStart(final LexicalContext lc, final FunctionNode fn, final Block block)83 static int findScopesToStart(final LexicalContext lc, final FunctionNode fn, final Block block) { 84 final Block bodyBlock = findBodyBlock(lc, fn, block); 85 final Iterator<Block> iter = lc.getBlocks(block); 86 Block b = iter.next(); 87 int scopesToStart = 0; 88 while (true) { 89 if (b.needsScope()) { 90 scopesToStart++; 91 } 92 if (b == bodyBlock) { 93 break; 94 } 95 b = iter.next(); 96 } 97 return scopesToStart; 98 } 99 findInternalDepth(final LexicalContext lc, final FunctionNode fn, final Block block, final Symbol symbol)100 static int findInternalDepth(final LexicalContext lc, final FunctionNode fn, final Block block, final Symbol symbol) { 101 final Block bodyBlock = findBodyBlock(lc, fn, block); 102 final Iterator<Block> iter = lc.getBlocks(block); 103 Block b = iter.next(); 104 int scopesToStart = 0; 105 while (true) { 106 if (definedInBlock(b, symbol)) { 107 return scopesToStart; 108 } 109 if (b.needsScope()) { 110 scopesToStart++; 111 } 112 if (b == bodyBlock) { 113 break; //don't go past body block, but process it 114 } 115 b = iter.next(); 116 } 117 return -1; 118 } 119 definedInBlock(final Block block, final Symbol symbol)120 private static boolean definedInBlock(final Block block, final Symbol symbol) { 121 if (symbol.isGlobal()) { 122 //globals cannot be defined anywhere else 123 124 return block.isGlobalScope(); 125 } 126 return block.getExistingSymbol(symbol.getName()) == symbol; 127 } 128 findBodyBlock(final LexicalContext lc, final FunctionNode fn, final Block block)129 static Block findBodyBlock(final LexicalContext lc, final FunctionNode fn, final Block block) { 130 final Iterator<Block> iter = lc.getBlocks(block); 131 while (iter.hasNext()) { 132 final Block next = iter.next(); 133 if (fn.getBody() == next) { 134 return next; 135 } 136 } 137 return null; 138 } 139 findGlobalBlock(final LexicalContext lc, final Block block)140 private static Block findGlobalBlock(final LexicalContext lc, final Block block) { 141 final Iterator<Block> iter = lc.getBlocks(block); 142 Block globalBlock = null; 143 while (iter.hasNext()) { 144 globalBlock = iter.next(); 145 } 146 return globalBlock; 147 } 148 isDynamicScopeBoundary(final FunctionNode fn)149 private static boolean isDynamicScopeBoundary(final FunctionNode fn) { 150 return fn.needsDynamicScope(); 151 } 152 isDynamicScopeBoundary(final Block block)153 private boolean isDynamicScopeBoundary(final Block block) { 154 return withBodies.contains(block); 155 } 156 157 @Override enterFunctionNode(final FunctionNode functionNode)158 public boolean enterFunctionNode(final FunctionNode functionNode) { 159 if (compiler.isOnDemandCompilation()) { 160 return true; 161 } 162 163 if (isDynamicScopeBoundary(functionNode)) { 164 increaseDynamicScopeCount(functionNode); 165 } 166 167 final int fnId = functionNode.getId(); 168 Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.get(fnId); 169 if (nestedFunctions == null) { 170 nestedFunctions = new HashMap<>(); 171 fnIdToNestedFunctions.put(fnId, nestedFunctions); 172 } 173 174 return true; 175 } 176 177 //external symbols hold the scope depth of sc11 from global at the start of the method 178 @Override leaveFunctionNode(final FunctionNode functionNode)179 public Node leaveFunctionNode(final FunctionNode functionNode) { 180 final String name = functionNode.getName(); 181 FunctionNode newFunctionNode = functionNode; 182 if (compiler.isOnDemandCompilation()) { 183 final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(newFunctionNode.getId()); 184 if (data.inDynamicContext()) { 185 log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope."); 186 newFunctionNode = newFunctionNode.setInDynamicContext(lc); 187 } 188 if (newFunctionNode == lc.getOutermostFunction() && !newFunctionNode.hasApplyToCallSpecialization()) { 189 data.setCachedAst(newFunctionNode); 190 } 191 return newFunctionNode; 192 } 193 194 if (inDynamicScope()) { 195 log.fine("Tagging ", quote(name), " as defined in dynamic scope"); 196 newFunctionNode = newFunctionNode.setInDynamicContext(lc); 197 } 198 199 //create recompilable scriptfunctiondata 200 final int fnId = newFunctionNode.getId(); 201 final Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.remove(fnId); 202 203 assert nestedFunctions != null; 204 // Generate the object class and property map in case this function is ever used as constructor 205 final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData( 206 newFunctionNode, 207 compiler.getCodeInstaller(), 208 ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()), 209 nestedFunctions, 210 externalSymbolDepths.get(fnId), 211 internalSymbols.get(fnId)); 212 213 if (lc.getOutermostFunction() != newFunctionNode) { 214 final FunctionNode parentFn = lc.getParentFunction(newFunctionNode); 215 if (parentFn != null) { 216 fnIdToNestedFunctions.get(parentFn.getId()).put(fnId, data); 217 } 218 } else { 219 compiler.setData(data); 220 } 221 222 if (isDynamicScopeBoundary(functionNode)) { 223 decreaseDynamicScopeCount(functionNode); 224 } 225 226 return newFunctionNode; 227 } 228 inDynamicScope()229 private boolean inDynamicScope() { 230 return dynamicScopeCount > 0; 231 } 232 increaseDynamicScopeCount(final Node node)233 private void increaseDynamicScopeCount(final Node node) { 234 assert dynamicScopeCount >= 0; 235 ++dynamicScopeCount; 236 if (log.isEnabled()) { 237 log.finest(quote(lc.getCurrentFunction().getName()), " ++dynamicScopeCount = ", dynamicScopeCount, " at: ", node, node.getClass()); 238 } 239 } 240 decreaseDynamicScopeCount(final Node node)241 private void decreaseDynamicScopeCount(final Node node) { 242 --dynamicScopeCount; 243 assert dynamicScopeCount >= 0; 244 if (log.isEnabled()) { 245 log.finest(quote(lc.getCurrentFunction().getName()), " --dynamicScopeCount = ", dynamicScopeCount, " at: ", node, node.getClass()); 246 } 247 } 248 249 @Override enterWithNode(final WithNode node)250 public boolean enterWithNode(final WithNode node) { 251 withBodies.add(node.getBody()); 252 return true; 253 } 254 255 @Override enterBlock(final Block block)256 public boolean enterBlock(final Block block) { 257 if (compiler.isOnDemandCompilation()) { 258 return true; 259 } 260 261 if (isDynamicScopeBoundary(block)) { 262 increaseDynamicScopeCount(block); 263 } 264 265 if (!lc.isFunctionBody()) { 266 return true; 267 } 268 269 //the below part only happens on eager compilation when we have the entire hierarchy 270 //block is a function body 271 final FunctionNode fn = lc.getCurrentFunction(); 272 273 //get all symbols that are referenced inside this function body 274 final Set<Symbol> symbols = new HashSet<>(); 275 block.accept(new SimpleNodeVisitor() { 276 @Override 277 public boolean enterIdentNode(final IdentNode identNode) { 278 final Symbol symbol = identNode.getSymbol(); 279 if (symbol != null && symbol.isScope()) { 280 //if this is an internal symbol, skip it. 281 symbols.add(symbol); 282 } 283 return true; 284 } 285 }); 286 287 final Map<String, Integer> internals = new HashMap<>(); 288 289 final Block globalBlock = findGlobalBlock(lc, block); 290 final Block bodyBlock = findBodyBlock(lc, fn, block); 291 292 assert globalBlock != null; 293 assert bodyBlock != null; 294 295 for (final Symbol symbol : symbols) { 296 Iterator<Block> iter; 297 298 final int internalDepth = findInternalDepth(lc, fn, block, symbol); 299 final boolean internal = internalDepth >= 0; 300 if (internal) { 301 internals.put(symbol.getName(), internalDepth); 302 } 303 304 // if not internal, we have to continue walking until we reach the top. We 305 // start outside the body and each new scope adds a depth count. When we 306 // find the symbol, we store its depth count 307 if (!internal) { 308 int depthAtStart = 0; 309 //not internal - keep looking. 310 iter = lc.getAncestorBlocks(bodyBlock); 311 while (iter.hasNext()) { 312 final Block b2 = iter.next(); 313 if (definedInBlock(b2, symbol)) { 314 addExternalSymbol(fn, symbol, depthAtStart); 315 break; 316 } 317 if (b2.needsScope()) { 318 depthAtStart++; 319 } 320 } 321 } 322 } 323 324 addInternalSymbols(fn, internals.keySet()); 325 326 if (log.isEnabled()) { 327 log.info(fn.getName() + " internals=" + internals + " externals=" + externalSymbolDepths.get(fn.getId())); 328 } 329 330 return true; 331 } 332 333 @Override leaveBlock(final Block block)334 public Node leaveBlock(final Block block) { 335 if (compiler.isOnDemandCompilation()) { 336 return block; 337 } 338 if (isDynamicScopeBoundary(block)) { 339 decreaseDynamicScopeCount(block); 340 } 341 return block; 342 } 343 addInternalSymbols(final FunctionNode functionNode, final Set<String> symbols)344 private void addInternalSymbols(final FunctionNode functionNode, final Set<String> symbols) { 345 final int fnId = functionNode.getId(); 346 assert internalSymbols.get(fnId) == null || internalSymbols.get(fnId).equals(symbols); //e.g. cloned finally block 347 internalSymbols.put(fnId, symbols); 348 } 349 addExternalSymbol(final FunctionNode functionNode, final Symbol symbol, final int depthAtStart)350 private void addExternalSymbol(final FunctionNode functionNode, final Symbol symbol, final int depthAtStart) { 351 final int fnId = functionNode.getId(); 352 Map<String, Integer> depths = externalSymbolDepths.get(fnId); 353 if (depths == null) { 354 depths = new HashMap<>(); 355 externalSymbolDepths.put(fnId, depths); 356 } 357 depths.put(symbol.getName(), depthAtStart); 358 } 359 360 } 361