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