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