RecompilableScriptFunctionData.java revision 1002:2f0161551858
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 context.getErrorManager(), 398 isStrict() | functionNode.isStrict(), // is strict 399 true, // is on demand 400 this, // compiledFunction, i.e. this RecompilableScriptFunctionData 401 typeMap, // type map 402 getEffectiveInvalidatedProgramPoints(invalidatedProgramPoints, typeInformationFile), // invalidated program points 403 typeInformationFile, 404 continuationEntryPoints, // continuation entry points 405 runtimeScope); // runtime scope 406 } 407 408 /** 409 * If the function being compiled already has its own invalidated program points map, use it. Otherwise, attempt to 410 * load invalidated program points map from the persistent type info cache. 411 * @param invalidatedProgramPoints the function's current invalidated program points map. Null if the function 412 * doesn't have it. 413 * @param typeInformationFile the object describing the location of the persisted type information. 414 * @return either the existing map, or a loaded map from the persistent type info cache, or a new empty map if 415 * neither an existing map or a persistent cached type info is available. 416 */ 417 private static Map<Integer, Type> getEffectiveInvalidatedProgramPoints( 418 final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile) { 419 if(invalidatedProgramPoints != null) { 420 return invalidatedProgramPoints; 421 } 422 final Map<Integer, Type> loadedProgramPoints = OptimisticTypesPersistence.load(typeInformationFile); 423 return loadedProgramPoints != null ? loadedProgramPoints : new TreeMap<Integer, Type>(); 424 } 425 426 private FunctionInitializer compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final boolean persist) { 427 // We're creating an empty script object for holding local variables. AssignSymbols will populate it with 428 // explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and 429 // CompilationEnvironment#declareLocalSymbol()). 430 431 if (log.isEnabled()) { 432 log.info("Type specialization of '", functionName, "' signature: ", actualCallSiteType); 433 } 434 435 final boolean persistentCache = usePersistentCodeCache() && persist; 436 String cacheKey = null; 437 if (persistentCache) { 438 final TypeMap typeMap = typeMap(actualCallSiteType); 439 final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId); 440 cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes); 441 final StoredScript script = installer.loadScript(source, cacheKey); 442 443 if (script != null) { 444 Compiler.updateCompilationId(script.getCompilationId()); 445 return install(script); 446 } 447 } 448 449 final FunctionNode fn = reparse(); 450 final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope); 451 final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL); 452 453 if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) { 454 compiler.persistClassInfo(cacheKey, compiledFn); 455 } 456 return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints()); 457 } 458 459 460 /** 461 * Install this script using the given {@code installer}. 462 * 463 * @param script the compiled script 464 * @return the function initializer 465 */ 466 private FunctionInitializer install(final StoredScript script) { 467 468 final Map<String, Class<?>> installedClasses = new HashMap<>(); 469 final String mainClassName = script.getMainClassName(); 470 final byte[] mainClassBytes = script.getClassBytes().get(mainClassName); 471 472 final Class<?> mainClass = installer.install(mainClassName, mainClassBytes); 473 474 installedClasses.put(mainClassName, mainClass); 475 476 for (final Map.Entry<String, byte[]> entry : script.getClassBytes().entrySet()) { 477 final String className = entry.getKey(); 478 final byte[] code = entry.getValue(); 479 480 if (className.equals(mainClassName)) { 481 continue; 482 } 483 484 installedClasses.put(className, installer.install(className, code)); 485 } 486 487 final Map<Integer, FunctionInitializer> initializers = script.getInitializers(); 488 assert initializers != null; 489 assert initializers.size() == 1; 490 final FunctionInitializer initializer = initializers.values().iterator().next(); 491 492 final Object[] constants = script.getConstants(); 493 for (int i = 0; i < constants.length; i++) { 494 if (constants[i] instanceof RecompilableScriptFunctionData) { 495 // replace deserialized function data with the ones we already have 496 constants[i] = getScriptFunctionData(((RecompilableScriptFunctionData) constants[i]).getFunctionNodeId()); 497 } 498 } 499 500 installer.initialize(installedClasses.values(), source, constants); 501 initializer.setCode(installedClasses.get(initializer.getClassName())); 502 return initializer; 503 } 504 505 boolean usePersistentCodeCache() { 506 final ScriptEnvironment env = installer.getOwner(); 507 return env._persistent_cache && env._optimistic_types; 508 } 509 510 private MethodType explicitParams(final MethodType callSiteType) { 511 if (CompiledFunction.isVarArgsType(callSiteType)) { 512 return null; 513 } 514 515 final MethodType noCalleeThisType = callSiteType.dropParameterTypes(0, 2); // (callee, this) is always in call site type 516 final int callSiteParamCount = noCalleeThisType.parameterCount(); 517 518 // Widen parameters of reference types to Object as we currently don't care for specialization among reference 519 // types. E.g. call site saying (ScriptFunction, Object, String) should still link to (ScriptFunction, Object, Object) 520 final Class<?>[] paramTypes = noCalleeThisType.parameterArray(); 521 boolean changed = false; 522 for (int i = 0; i < paramTypes.length; ++i) { 523 final Class<?> paramType = paramTypes[i]; 524 if (!(paramType.isPrimitive() || paramType == Object.class)) { 525 paramTypes[i] = Object.class; 526 changed = true; 527 } 528 } 529 final MethodType generalized = changed ? MethodType.methodType(noCalleeThisType.returnType(), paramTypes) : noCalleeThisType; 530 531 if (callSiteParamCount < getArity()) { 532 return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(getArity() - callSiteParamCount, Object.class)); 533 } 534 return generalized; 535 } 536 537 private FunctionNode extractFunctionFromScript(final FunctionNode script) { 538 final Set<FunctionNode> fns = new HashSet<>(); 539 script.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 540 @Override 541 public boolean enterFunctionNode(final FunctionNode fn) { 542 fns.add(fn); 543 return false; 544 } 545 }); 546 assert fns.size() == 1 : "got back more than one method in recompilation"; 547 final FunctionNode f = fns.iterator().next(); 548 assert f.getId() == functionNodeId; 549 if (!isDeclared && f.isDeclared()) { 550 return f.clearFlag(null, FunctionNode.IS_DECLARED); 551 } 552 return f; 553 } 554 555 MethodHandle lookup(final FunctionInitializer fnInit) { 556 final MethodType type = fnInit.getMethodType(); 557 return lookupCodeMethod(fnInit.getCode(), type); 558 } 559 560 MethodHandle lookup(final FunctionNode fn) { 561 final MethodType type = new FunctionSignature(fn).getMethodType(); 562 return lookupCodeMethod(fn.getCompileUnit().getCode(), type); 563 } 564 565 MethodHandle lookupCodeMethod(final Class<?> code, final MethodType targetType) { 566 log.info("Looking up ", DebugLogger.quote(name), " type=", targetType); 567 return MH.findStatic(LOOKUP, code, functionName, targetType); 568 } 569 570 /** 571 * Initializes this function data with the eagerly generated version of the code. This method can only be invoked 572 * by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it 573 * externally will result in an exception. 574 */ 575 public void initializeCode(final FunctionInitializer initializer) { 576 // Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit. 577 if(!code.isEmpty()) { 578 throw new IllegalStateException(name); 579 } 580 addCode(lookup(initializer), null, null, initializer.getFlags()); 581 } 582 583 private CompiledFunction addCode(final MethodHandle target, final Map<Integer, Type> invalidatedProgramPoints, 584 final MethodType callSiteType, final int fnFlags) { 585 final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, callSiteType, fnFlags); 586 code.add(cfn); 587 return cfn; 588 } 589 590 /** 591 * Add code with specific call site type. It will adapt the type of the looked up method handle to fit the call site 592 * type. This is necessary because even if we request a specialization that takes an "int" parameter, we might end 593 * up getting one that takes a "double" etc. because of internal function logic causes widening (e.g. assignment of 594 * a wider value to the parameter variable). However, we use the method handle type for matching subsequent lookups 595 * for the same specialization, so we must adapt the handle to the expected type. 596 * @param fnInit the function 597 * @param callSiteType the call site type 598 * @return the compiled function object, with its type matching that of the call site type. 599 */ 600 private CompiledFunction addCode(final FunctionInitializer fnInit, final MethodType callSiteType) { 601 if (isVariableArity()) { 602 return addCode(lookup(fnInit), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags()); 603 } 604 605 final MethodHandle handle = lookup(fnInit); 606 final MethodType fromType = handle.type(); 607 MethodType toType = needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1); 608 toType = toType.changeReturnType(fromType.returnType()); 609 610 final int toCount = toType.parameterCount(); 611 final int fromCount = fromType.parameterCount(); 612 final int minCount = Math.min(fromCount, toCount); 613 for(int i = 0; i < minCount; ++i) { 614 final Class<?> fromParam = fromType.parameterType(i); 615 final Class<?> toParam = toType.parameterType(i); 616 // If method has an Object parameter, but call site had String, preserve it as Object. No need to narrow it 617 // artificially. Note that this is related to how CompiledFunction.matchesCallSite() works, specifically 618 // the fact that various reference types compare to equal (see "fnType.isEquivalentTo(csType)" there). 619 if (fromParam != toParam && !fromParam.isPrimitive() && !toParam.isPrimitive()) { 620 assert fromParam.isAssignableFrom(toParam); 621 toType = toType.changeParameterType(i, fromParam); 622 } 623 } 624 if (fromCount > toCount) { 625 toType = toType.appendParameterTypes(fromType.parameterList().subList(toCount, fromCount)); 626 } else if (fromCount < toCount) { 627 toType = toType.dropParameterTypes(fromCount, toCount); 628 } 629 630 return addCode(lookup(fnInit).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags()); 631 } 632 633 634 @Override 635 synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) { 636 CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope); 637 if (existingBest == null) { 638 existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, true), callSiteType); 639 } 640 641 assert existingBest != null; 642 //we are calling a vararg method with real args 643 boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType); 644 645 //if the best one is an apply to call, it has to match the callsite exactly 646 //or we need to regenerate 647 if (existingBest.isApplyToCall()) { 648 final CompiledFunction best = lookupExactApplyToCall(callSiteType); 649 if (best != null) { 650 return best; 651 } 652 applyToCall = true; 653 } 654 655 if (applyToCall) { 656 final FunctionInitializer fnInit = compileTypeSpecialization(callSiteType, runtimeScope, false); 657 if ((fnInit.getFlags() & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0) { //did the specialization work 658 existingBest = addCode(fnInit, callSiteType); 659 } 660 } 661 662 return existingBest; 663 } 664 665 @Override 666 boolean isRecompilable() { 667 return true; 668 } 669 670 @Override 671 public boolean needsCallee() { 672 return needsCallee; 673 } 674 675 @Override 676 MethodType getGenericType() { 677 // 2 is for (callee, this) 678 if (isVariableArity()) { 679 return MethodType.genericMethodType(2, true); 680 } 681 return MethodType.genericMethodType(2 + getArity()); 682 } 683 684 /** 685 * Return the function node id. 686 * @return the function node id 687 */ 688 public int getFunctionNodeId() { 689 return functionNodeId; 690 } 691 692 public Source getSource() { 693 return source; 694 } 695 696 /** 697 * Return a script function data based on a function id, either this function if 698 * the id matches or a nested function based on functionId. This goes down into 699 * nested functions until all leaves are exhausted. 700 * 701 * @param functionId function id 702 * @return script function data or null if invalid id 703 */ 704 public RecompilableScriptFunctionData getScriptFunctionData(final int functionId) { 705 if (functionId == functionNodeId) { 706 return this; 707 } 708 RecompilableScriptFunctionData data; 709 710 data = nestedFunctions == null ? null : nestedFunctions.get(functionId); 711 if (data != null) { 712 return data; 713 } 714 for (final RecompilableScriptFunctionData ndata : nestedFunctions.values()) { 715 data = ndata.getScriptFunctionData(functionId); 716 if (data != null) { 717 return data; 718 } 719 } 720 return null; 721 } 722 723 /** 724 * Get the uppermost parent, the program, for this data 725 * @return program 726 */ 727 public RecompilableScriptFunctionData getProgram() { 728 RecompilableScriptFunctionData program = this; 729 while (true) { 730 final RecompilableScriptFunctionData p = program.getParent(); 731 if (p == null) { 732 return program; 733 } 734 program = p; 735 } 736 } 737 738 /** 739 * Check whether a certain name is a global symbol, i.e. only exists as defined 740 * in outermost scope and not shadowed by being parameter or assignment in inner 741 * scopes 742 * 743 * @param functionNode function node to check 744 * @param symbolName symbol name 745 * @return true if global symbol 746 */ 747 public boolean isGlobalSymbol(final FunctionNode functionNode, final String symbolName) { 748 RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId()); 749 assert data != null; 750 751 do { 752 if (data.hasInternalSymbol(symbolName)) { 753 return false; 754 } 755 data = data.getParent(); 756 } while(data != null); 757 758 return true; 759 } 760 761 private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { 762 in.defaultReadObject(); 763 createLogger(); 764 } 765 766 private void createLogger() { 767 log = initLogger(Context.getContextTrusted()); 768 } 769} 770