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