1 /* 2 * Copyright (c) 2010, 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.runtime; 27 28 import static jdk.nashorn.internal.lookup.Lookup.MH; 29 30 import java.io.IOException; 31 import java.io.Serializable; 32 import java.lang.invoke.MethodHandle; 33 import java.lang.invoke.MethodHandles; 34 import java.lang.invoke.MethodType; 35 import java.util.Collections; 36 import java.util.HashSet; 37 import java.util.Map; 38 import java.util.Set; 39 import jdk.internal.dynalink.support.NameCodec; 40 import jdk.nashorn.internal.codegen.CompileUnit; 41 import jdk.nashorn.internal.codegen.Compiler; 42 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 43 import jdk.nashorn.internal.codegen.CompilerConstants; 44 import jdk.nashorn.internal.codegen.FunctionSignature; 45 import jdk.nashorn.internal.codegen.TypeMap; 46 import jdk.nashorn.internal.codegen.types.Type; 47 import jdk.nashorn.internal.ir.FunctionNode; 48 import jdk.nashorn.internal.ir.LexicalContext; 49 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 50 import jdk.nashorn.internal.objects.Global; 51 import jdk.nashorn.internal.parser.Parser; 52 import jdk.nashorn.internal.parser.Token; 53 import jdk.nashorn.internal.parser.TokenType; 54 import jdk.nashorn.internal.runtime.logging.DebugLogger; 55 import jdk.nashorn.internal.runtime.logging.Loggable; 56 import jdk.nashorn.internal.runtime.logging.Logger; 57 import jdk.nashorn.internal.runtime.options.Options; 58 import jdk.nashorn.internal.scripts.JS; 59 60 /** 61 * This is a subclass that represents a script function that may be regenerated, 62 * for example with specialization based on call site types, or lazily generated. 63 * The common denominator is that it can get new invokers during its lifespan, 64 * unlike {@code FinalScriptFunctionData} 65 */ 66 @Logger(name="recompile") 67 public final class RecompilableScriptFunctionData extends ScriptFunctionData implements Loggable { 68 /** Is lazy compilation enabled? TODO: this should be the default */ 69 public static final boolean LAZY_COMPILATION = Options.getBooleanProperty("nashorn.lazy"); 70 71 /** Prefix used for all recompiled script classes */ 72 public static final String RECOMPILATION_PREFIX = "Recompilation$"; 73 74 /** Unique function node id for this function node */ 75 private final int functionNodeId; 76 77 private final String functionName; 78 79 // TODO: try to eliminate the need for this somehow, either by allowing Source to change its name, allowing a 80 // function to internally replace its Source with one of a different name, or storing this additional field in the 81 // Source object. 82 private final String sourceURL; 83 84 /** The line number where this function begins. */ 85 private final int lineNumber; 86 87 /** Source from which FunctionNode was parsed. */ 88 private transient Source source; 89 90 /** Allows us to retrieve the method handle for this function once the code is compiled */ 91 private MethodLocator methodLocator; 92 93 /** Token of this function within the source. */ 94 private final long token; 95 96 /** Allocator map from makeMap() */ 97 private final PropertyMap allocatorMap; 98 99 /** Code installer used for all further recompilation/specialization of this ScriptFunction */ 100 private transient CodeInstaller<ScriptEnvironment> installer; 101 102 /** Name of class where allocator function resides */ 103 private final String allocatorClassName; 104 105 /** lazily generated allocator */ 106 private transient MethodHandle allocator; 107 108 private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions; 109 110 /** Id to parent function if one exists */ 111 private RecompilableScriptFunctionData parent; 112 113 private final boolean isDeclared; 114 private final boolean isAnonymous; 115 private final boolean needsCallee; 116 117 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 118 119 private transient DebugLogger log; 120 121 private final Map<String, Integer> externalScopeDepths; 122 123 private final Set<String> internalSymbols; 124 125 private static final int GET_SET_PREFIX_LENGTH = "*et ".length(); 126 127 private static final long serialVersionUID = 4914839316174633726L; 128 129 /** 130 * Constructor - public as scripts use it 131 * 132 * @param functionNode functionNode that represents this function code 133 * @param installer installer for code regeneration versions of this function 134 * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor 135 * @param allocatorMap allocator map to seed instances with, when constructing 136 * @param nestedFunctions nested function map 137 * @param sourceURL source URL 138 * @param externalScopeDepths external scope depths 139 * @param internalSymbols internal symbols to method, defined in its scope 140 */ 141 public RecompilableScriptFunctionData( 142 final FunctionNode functionNode, 143 final CodeInstaller<ScriptEnvironment> installer, 144 final String allocatorClassName, 145 final PropertyMap allocatorMap, 146 final Map<Integer, RecompilableScriptFunctionData> nestedFunctions, 147 final String sourceURL, 148 final Map<String, Integer> externalScopeDepths, 149 final Set<String> internalSymbols) { 150 151 super(functionName(functionNode), 152 Math.min(functionNode.getParameters().size(), MAX_ARITY), 153 getFlags(functionNode)); 154 155 this.functionName = functionNode.getName(); 156 this.lineNumber = functionNode.getLineNumber(); 157 this.isDeclared = functionNode.isDeclared(); 158 this.needsCallee = functionNode.needsCallee(); 159 this.isAnonymous = functionNode.isAnonymous(); 160 this.functionNodeId = functionNode.getId(); 161 this.source = functionNode.getSource(); 162 this.token = tokenFor(functionNode); 163 this.installer = installer; 164 this.sourceURL = sourceURL; 165 this.allocatorClassName = allocatorClassName; 166 this.allocatorMap = allocatorMap; 167 this.nestedFunctions = nestedFunctions; 168 this.externalScopeDepths = externalScopeDepths; 169 this.internalSymbols = new HashSet<>(internalSymbols); 170 171 for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) { 172 assert nfn.getParent() == null; 173 nfn.setParent(this); 174 } 175 176 createLogger(); 177 } 178 179 @Override 180 public DebugLogger getLogger() { 181 return log; 182 } 183 184 @Override 185 public DebugLogger initLogger(final Context ctxt) { 186 return ctxt.getLogger(this.getClass()); 187 } 188 189 /** 190 * Check if a symbol is internally defined in a function. For example 191 * if "undefined" is internally defined in the outermost program function, 192 * it has not been reassigned or overridden and can be optimized 193 * 194 * @param symbolName symbol name 195 * @return true if symbol is internal to this ScriptFunction 196 */ 197 198 public boolean hasInternalSymbol(final String symbolName) { 199 return internalSymbols.contains(symbolName); 200 } 201 202 /** 203 * Return the external symbol table 204 * @param symbolName symbol name 205 * @return the external symbol table with proto depths 206 */ 207 public int getExternalSymbolDepth(final String symbolName) { 208 final Map<String, Integer> map = externalScopeDepths; 209 if (map == null) { 210 return -1; 211 } 212 final Integer depth = map.get(symbolName); 213 if (depth == null) { 214 return -1; 215 } 216 return depth; 217 } 218 219 /** 220 * Get the parent of this RecompilableScriptFunctionData. If we are 221 * a nested function, we have a parent. Note that "null" return value 222 * can also mean that we have a parent but it is unknown, so this can 223 * only be used for conservative assumptions. 224 * @return parent data, or null if non exists and also null IF UNKNOWN. 225 */ 226 public RecompilableScriptFunctionData getParent() { 227 return parent; 228 } 229 230 void setParent(final RecompilableScriptFunctionData parent) { 231 this.parent = parent; 232 } 233 234 @Override 235 String toSource() { 236 if (source != null && token != 0) { 237 return source.getString(Token.descPosition(token), Token.descLength(token)); 238 } 239 240 return "function " + (name == null ? "" : name) + "() { [native code] }"; 241 } 242 243 public void setCodeAndSource(final Map<String, Class<?>> code, final Source source) { 244 this.source = source; 245 if (methodLocator != null) { 246 methodLocator.setClass(code.get(methodLocator.getClassName())); 247 } 248 } 249 250 @Override 251 public String toString() { 252 return super.toString() + '@' + functionNodeId; 253 } 254 255 @Override 256 public String toStringVerbose() { 257 final StringBuilder sb = new StringBuilder(); 258 259 sb.append("fnId=").append(functionNodeId).append(' '); 260 261 if (source != null) { 262 sb.append(source.getName()) 263 .append(':') 264 .append(lineNumber) 265 .append(' '); 266 } 267 268 return sb.toString() + super.toString(); 269 } 270 271 @Override 272 public String getFunctionName() { 273 return functionName; 274 } 275 276 @Override 277 public boolean inDynamicContext() { 278 return (flags & IN_DYNAMIC_CONTEXT) != 0; 279 } 280 281 private static String functionName(final FunctionNode fn) { 282 if (fn.isAnonymous()) { 283 return ""; 284 } 285 final FunctionNode.Kind kind = fn.getKind(); 286 if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) { 287 final String name = NameCodec.decode(fn.getIdent().getName()); 288 return name.substring(GET_SET_PREFIX_LENGTH); 289 } 290 return fn.getIdent().getName(); 291 } 292 293 private static long tokenFor(final FunctionNode fn) { 294 final int position = Token.descPosition(fn.getFirstToken()); 295 final long lastToken = fn.getLastToken(); 296 // EOL uses length field to store the line number 297 final int length = Token.descPosition(lastToken) - position + (Token.descType(lastToken) == TokenType.EOL ? 0 : Token.descLength(lastToken)); 298 299 return Token.toDesc(TokenType.FUNCTION, position, length); 300 } 301 302 private static int getFlags(final FunctionNode functionNode) { 303 int flags = IS_CONSTRUCTOR; 304 if (functionNode.isStrict()) { 305 flags |= IS_STRICT; 306 } 307 if (functionNode.needsCallee()) { 308 flags |= NEEDS_CALLEE; 309 } 310 if (functionNode.usesThis() || functionNode.hasEval()) { 311 flags |= USES_THIS; 312 } 313 if (functionNode.isVarArg()) { 314 flags |= IS_VARIABLE_ARITY; 315 } 316 if (functionNode.inDynamicContext()) { 317 flags |= IN_DYNAMIC_CONTEXT; 318 } 319 return flags; 320 } 321 322 @Override 323 PropertyMap getAllocatorMap() { 324 return allocatorMap; 325 } 326 327 @Override 328 ScriptObject allocate(final PropertyMap map) { 329 try { 330 ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try 331 return allocator == null ? null : (ScriptObject)allocator.invokeExact(map); 332 } catch (final RuntimeException | Error e) { 333 throw e; 334 } catch (final Throwable t) { 335 throw new RuntimeException(t); 336 } 337 } 338 339 private void ensureHasAllocator() throws ClassNotFoundException { 340 if (allocator == null && allocatorClassName != null) { 341 this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class)); 342 } 343 } 344 345 FunctionNode reparse() { 346 final boolean isProgram = functionNodeId == FunctionNode.FIRST_FUNCTION_ID; 347 // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node 348 final int descPosition = Token.descPosition(token); 349 final Context context = Context.getContextTrusted(); 350 final Parser parser = new Parser( 351 context.getEnv(), 352 source, 353 new Context.ThrowErrorManager(), 354 isStrict(), 355 functionNodeId - (isProgram ? 0 : 1), 356 lineNumber - 1, 357 context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive 358 359 if (isAnonymous) { 360 parser.setFunctionName(functionName); 361 } 362 363 final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true); 364 // Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a 365 // single function, extract it from the program. 366 return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName).setSourceURL(null, sourceURL); 367 } 368 369 TypeMap typeMap(final MethodType fnCallSiteType) { 370 if (fnCallSiteType == null) { 371 return null; 372 } 373 374 if (CompiledFunction.isVarArgsType(fnCallSiteType)) { 375 return null; 376 } 377 378 return new TypeMap(functionNodeId, explicitParams(fnCallSiteType), needsCallee()); 379 } 380 381 private static ScriptObject newLocals(final ScriptObject runtimeScope) { 382 final ScriptObject locals = Global.newEmptyInstance(); 383 locals.setProto(runtimeScope); 384 return locals; 385 } 386 387 private Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final ScriptObject runtimeScope) { 388 return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null); 389 } 390 391 Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType, final ScriptObject runtimeScope, final Map<Integer, Type> ipp, final int[] cep) { 392 final Context context = Context.getContextTrusted(); 393 return new Compiler( 394 context, 395 context.getEnv(), 396 installer, 397 functionNode.getSource(), // source 398 functionNode.getSourceURL(), 399 isStrict() | functionNode.isStrict(), // is strict 400 true, // is on demand 401 this, // compiledFunction, i.e. this RecompilableScriptFunctionData 402 typeMap(actualCallSiteType), // type map 403 ipp, // invalidated program points 404 cep, // continuation entry points 405 runtimeScope); // runtime scope 406 } 407 408 private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) { 409 // We're creating an empty script object for holding local variables. AssignSymbols will populate it with 410 // explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and 411 // CompilationEnvironment#declareLocalSymbol()). 412 413 if (log.isEnabled()) { 414 log.info("Type specialization of '", functionName, "' signature: ", actualCallSiteType); 415 } 416 417 final FunctionNode fn = reparse(); 418 return getCompiler(fn, actualCallSiteType, runtimeScope).compile(fn, CompilationPhases.COMPILE_ALL); 419 } 420 421 private MethodType explicitParams(final MethodType callSiteType) { 422 if (CompiledFunction.isVarArgsType(callSiteType)) { 423 return null; 424 } 425 426 final MethodType noCalleeThisType = callSiteType.dropParameterTypes(0, 2); // (callee, this) is always in call site type 427 final int callSiteParamCount = noCalleeThisType.parameterCount(); 428 429 // Widen parameters of reference types to Object as we currently don't care for specialization among reference 430 // types. E.g. call site saying (ScriptFunction, Object, String) should still link to (ScriptFunction, Object, Object) 431 final Class<?>[] paramTypes = noCalleeThisType.parameterArray(); 432 boolean changed = false; 433 for (int i = 0; i < paramTypes.length; ++i) { 434 final Class<?> paramType = paramTypes[i]; 435 if (!(paramType.isPrimitive() || paramType == Object.class)) { 436 paramTypes[i] = Object.class; 437 changed = true; 438 } 439 } 440 final MethodType generalized = changed ? MethodType.methodType(noCalleeThisType.returnType(), paramTypes) : noCalleeThisType; 441 442 if (callSiteParamCount < getArity()) { 443 return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(getArity() - callSiteParamCount, Object.class)); 444 } 445 return generalized; 446 } 447 448 private FunctionNode extractFunctionFromScript(final FunctionNode script) { 449 final Set<FunctionNode> fns = new HashSet<>(); 450 script.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 451 @Override 452 public boolean enterFunctionNode(final FunctionNode fn) { 453 fns.add(fn); 454 return false; 455 } 456 }); 457 assert fns.size() == 1 : "got back more than one method in recompilation"; 458 final FunctionNode f = fns.iterator().next(); 459 assert f.getId() == functionNodeId; 460 if (!isDeclared && f.isDeclared()) { 461 return f.clearFlag(null, FunctionNode.IS_DECLARED); 462 } 463 return f; 464 } 465 466 MethodHandle lookup(final FunctionNode fn) { 467 final MethodType type = new FunctionSignature(fn).getMethodType(); 468 log.info("Looking up ", DebugLogger.quote(fn.getName()), " type=", type); 469 return lookupWithExplicitType(fn, new FunctionSignature(fn).getMethodType()); 470 } 471 472 MethodHandle lookupWithExplicitType(final FunctionNode fn, final MethodType targetType) { 473 return lookupCodeMethod(fn.getCompileUnit(), targetType); 474 } 475 476 private MethodHandle lookupCodeMethod(final CompileUnit compileUnit, final MethodType targetType) { 477 return MH.findStatic(LOOKUP, compileUnit.getCode(), functionName, targetType); 478 } 479 480 /** 481 * Initializes this function data with the eagerly generated version of the code. This method can only be invoked 482 * by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it 483 * externally will result in an exception. 484 * @param functionNode the functionNode belonging to this data 485 */ 486 public void initializeCode(final FunctionNode functionNode) { 487 // Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit. 488 if(!(code.isEmpty() && functionNode.getCompileUnit().isInitializing(this, functionNode))) { 489 throw new IllegalStateException(functionNode.getName() + " id=" + functionNode.getId()); 490 } 491 addCode(functionNode); 492 methodLocator = new MethodLocator(functionNode); 493 } 494 495 private CompiledFunction addCode(final MethodHandle target, final int fnFlags) { 496 final CompiledFunction cfn = new CompiledFunction(target, this, fnFlags); 497 code.add(cfn); 498 return cfn; 499 } 500 501 private CompiledFunction addCode(final FunctionNode fn) { 502 return addCode(lookup(fn), fn.getFlags()); 503 } 504 505 /** 506 * Add code with specific call site type. It will adapt the type of the looked up method handle to fit the call site 507 * type. This is necessary because even if we request a specialization that takes an "int" parameter, we might end 508 * up getting one that takes a "double" etc. because of internal function logic causes widening (e.g. assignment of 509 * a wider value to the parameter variable). However, we use the method handle type for matching subsequent lookups 510 * for the same specialization, so we must adapt the handle to the expected type. 511 * @param fn the function 512 * @param callSiteType the call site type 513 * @return the compiled function object, with its type matching that of the call site type. 514 */ 515 private CompiledFunction addCode(final FunctionNode fn, final MethodType callSiteType) { 516 if (fn.isVarArg()) { 517 return addCode(fn); 518 } 519 520 final MethodHandle handle = lookup(fn); 521 final MethodType fromType = handle.type(); 522 MethodType toType = needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1); 523 toType = toType.changeReturnType(fromType.returnType()); 524 525 final int toCount = toType.parameterCount(); 526 final int fromCount = fromType.parameterCount(); 527 final int minCount = Math.min(fromCount, toCount); 528 for(int i = 0; i < minCount; ++i) { 529 final Class<?> fromParam = fromType.parameterType(i); 530 final Class<?> toParam = toType.parameterType(i); 531 // If method has an Object parameter, but call site had String, preserve it as Object. No need to narrow it 532 // artificially. Note that this is related to how CompiledFunction.matchesCallSite() works, specifically 533 // the fact that various reference types compare to equal (see "fnType.isEquivalentTo(csType)" there). 534 if (fromParam != toParam && !fromParam.isPrimitive() && !toParam.isPrimitive()) { 535 assert fromParam.isAssignableFrom(toParam); 536 toType = toType.changeParameterType(i, fromParam); 537 } 538 } 539 if (fromCount > toCount) { 540 toType = toType.appendParameterTypes(fromType.parameterList().subList(toCount, fromCount)); 541 } else if (fromCount < toCount) { 542 toType = toType.dropParameterTypes(fromCount, toCount); 543 } 544 545 return addCode(lookup(fn).asType(toType), fn.getFlags()); 546 } 547 548 549 @Override 550 CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) { 551 synchronized (code) { 552 CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope); 553 if (existingBest == null) { 554 if(code.isEmpty() && methodLocator != null) { 555 // This is a deserialized object, reconnect from method handle 556 existingBest = addCode(methodLocator.getMethodHandle(), methodLocator.getFunctionFlags()); 557 } else { 558 existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType); 559 } 560 } 561 562 assert existingBest != null; 563 //we are calling a vararg method with real args 564 boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType); 565 566 //if the best one is an apply to call, it has to match the callsite exactly 567 //or we need to regenerate 568 if (existingBest.isApplyToCall()) { 569 final CompiledFunction best = code.lookupExactApplyToCall(callSiteType); 570 if (best != null) { 571 return best; 572 } 573 applyToCall = true; 574 } 575 576 if (applyToCall) { 577 final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope); 578 if (fn.hasOptimisticApplyToCall()) { //did the specialization work 579 existingBest = addCode(fn, callSiteType); 580 } 581 } 582 583 return existingBest; 584 } 585 } 586 587 @Override 588 boolean isRecompilable() { 589 return true; 590 } 591 592 @Override 593 public boolean needsCallee() { 594 return needsCallee; 595 } 596 597 @Override 598 MethodType getGenericType() { 599 // 2 is for (callee, this) 600 if (isVariableArity()) { 601 return MethodType.genericMethodType(2, true); 602 } 603 return MethodType.genericMethodType(2 + getArity()); 604 } 605 606 /** 607 * Return a script function data based on a function id, either this function if 608 * the id matches or a nested function based on functionId. This goes down into 609 * nested functions until all leaves are exhausted. 610 * 611 * @param functionId function id 612 * @return script function data or null if invalid id 613 */ 614 public RecompilableScriptFunctionData getScriptFunctionData(final int functionId) { 615 if (functionId == functionNodeId) { 616 return this; 617 } 618 RecompilableScriptFunctionData data; 619 620 data = nestedFunctions == null ? null : nestedFunctions.get(functionId); 621 if (data != null) { 622 return data; 623 } 624 for (final RecompilableScriptFunctionData ndata : nestedFunctions.values()) { 625 data = ndata.getScriptFunctionData(functionId); 626 if (data != null) { 627 return data; 628 } 629 } 630 return null; 631 } 632 633 /** 634 * Get the uppermost parent, the program, for this data 635 * @return program 636 */ 637 public RecompilableScriptFunctionData getProgram() { 638 RecompilableScriptFunctionData program = this; 639 while (true) { 640 final RecompilableScriptFunctionData p = program.getParent(); 641 if (p == null) { 642 return program; 643 } 644 program = p; 645 } 646 } 647 648 /** 649 * Check whether a certain name is a global symbol, i.e. only exists as defined 650 * in outermost scope and not shadowed by being parameter or assignment in inner 651 * scopes 652 * 653 * @param functionNode function node to check 654 * @param symbolName symbol name 655 * @return true if global symbol 656 */ 657 public boolean isGlobalSymbol(final FunctionNode functionNode, final String symbolName) { 658 RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId()); 659 assert data != null; 660 661 do { 662 if (data.hasInternalSymbol(symbolName)) { 663 return false; 664 } 665 data = data.getParent(); 666 } while(data != null); 667 668 return true; 669 } 670 671 /** 672 * Helper class that allows us to retrieve the method handle for this function once it has been generated. 673 */ 674 private static class MethodLocator implements Serializable { 675 private transient Class<?> clazz; 676 private final String className; 677 private final String methodName; 678 private final MethodType methodType; 679 private final int functionFlags; 680 681 private static final long serialVersionUID = -5420835725902966692L; 682 683 MethodLocator(final FunctionNode functionNode) { 684 this.className = functionNode.getCompileUnit().getUnitClassName(); 685 this.methodName = functionNode.getName(); 686 this.methodType = new FunctionSignature(functionNode).getMethodType(); 687 this.functionFlags = functionNode.getFlags(); 688 689 assert className != null; 690 assert methodName != null; 691 } 692 693 void setClass(final Class<?> clazz) { 694 if (!JS.class.isAssignableFrom(clazz)) { 695 throw new IllegalArgumentException(); 696 } 697 this.clazz = clazz; 698 } 699 700 String getClassName() { 701 return className; 702 } 703 704 MethodHandle getMethodHandle() { 705 return MH.findStatic(LOOKUP, clazz, methodName, methodType); 706 } 707 708 int getFunctionFlags() { 709 return functionFlags; 710 } 711 } 712 713 private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { 714 in.defaultReadObject(); 715 createLogger(); 716 } 717 718 private void createLogger() { 719 log = initLogger(Context.getContextTrusted()); 720 } 721 }