RecompilableScriptFunctionData.java revision 1264:9831c47f6279
170584Sobrien/*
270584Sobrien * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
370584Sobrien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
470584Sobrien *
570584Sobrien * This code is free software; you can redistribute it and/or modify it
670584Sobrien * under the terms of the GNU General Public License version 2 only, as
770584Sobrien * published by the Free Software Foundation.  Oracle designates this
870584Sobrien * particular file as subject to the "Classpath" exception as provided
970584Sobrien * by Oracle in the LICENSE file that accompanied this code.
1070584Sobrien *
1170584Sobrien * This code is distributed in the hope that it will be useful, but WITHOUT
1270584Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1370584Sobrien * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1470584Sobrien * version 2 for more details (a copy is included in the LICENSE file that
1570584Sobrien * accompanied this code).
1670584Sobrien *
1770584Sobrien * You should have received a copy of the GNU General Public License version
1870584Sobrien * 2 along with this work; if not, write to the Free Software Foundation,
1970584Sobrien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2070584Sobrien *
2170584Sobrien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2270584Sobrien * or visit www.oracle.com if you need additional information or have any
2370584Sobrien * questions.
2470584Sobrien */
2570584Sobrien
2670584Sobrienpackage jdk.nashorn.internal.runtime;
2770584Sobrien
2870584Sobrienimport static jdk.nashorn.internal.lookup.Lookup.MH;
2970584Sobrienimport java.io.IOException;
3070584Sobrienimport java.lang.invoke.MethodHandle;
3170584Sobrienimport java.lang.invoke.MethodHandles;
3270584Sobrienimport java.lang.invoke.MethodType;
3370584Sobrienimport java.util.Collection;
3470584Sobrienimport java.util.Collections;
3570584Sobrienimport java.util.HashSet;
3670584Sobrienimport java.util.Map;
3770584Sobrienimport java.util.Set;
3870584Sobrienimport java.util.TreeMap;
3970584Sobrienimport jdk.internal.dynalink.support.NameCodec;
4070584Sobrienimport jdk.nashorn.internal.codegen.Compiler;
41196994Sphkimport jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
42196994Sphkimport jdk.nashorn.internal.codegen.CompilerConstants;
43196994Sphkimport jdk.nashorn.internal.codegen.FunctionSignature;
4470584Sobrienimport jdk.nashorn.internal.codegen.Namespace;
45209975Snwhitehornimport jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
4670584Sobrienimport jdk.nashorn.internal.codegen.TypeMap;
4770584Sobrienimport jdk.nashorn.internal.codegen.types.Type;
48196994Sphkimport jdk.nashorn.internal.ir.FunctionNode;
4970584Sobrienimport jdk.nashorn.internal.ir.LexicalContext;
50186128Snwhitehornimport jdk.nashorn.internal.ir.visitor.NodeVisitor;
51186128Snwhitehornimport jdk.nashorn.internal.objects.Global;
52186128Snwhitehornimport jdk.nashorn.internal.parser.Parser;
5370584Sobrienimport jdk.nashorn.internal.parser.Token;
5470584Sobrienimport jdk.nashorn.internal.parser.TokenType;
5570584Sobrienimport jdk.nashorn.internal.runtime.logging.DebugLogger;
5670584Sobrienimport jdk.nashorn.internal.runtime.logging.Loggable;
57209975Snwhitehornimport jdk.nashorn.internal.runtime.logging.Logger;
58209975Snwhitehorn/**
59209975Snwhitehorn * This is a subclass that represents a script function that may be regenerated,
6070584Sobrien * for example with specialization based on call site types, or lazily generated.
6170584Sobrien * The common denominator is that it can get new invokers during its lifespan,
62209975Snwhitehorn * unlike {@code FinalScriptFunctionData}
6370584Sobrien */
64210369Skib@Logger(name="recompile")
65210369Skibpublic final class RecompilableScriptFunctionData extends ScriptFunctionData implements Loggable {
66210369Skib    /** Prefix used for all recompiled script classes */
67210369Skib    public static final String RECOMPILATION_PREFIX = "Recompilation$";
68210369Skib
6970584Sobrien    /** Unique function node id for this function node */
70177661Sjb    private final int functionNodeId;
71224207Sattilio
72291702Snwhitehorn    private final String functionName;
73224207Sattilio
7470584Sobrien    /** The line number where this function begins. */
7570584Sobrien    private final int lineNumber;
76177661Sjb
7770584Sobrien    /** Source from which FunctionNode was parsed. */
78250338Sattilio    private transient Source source;
79250338Sattilio
80250338Sattilio    /** Serialized, compressed form of the AST. Used by split functions as they can't be reparsed from source. */
81250338Sattilio    private final byte[] serializedAst;
8270584Sobrien
8370584Sobrien    /** Token of this function within the source. */
84195376Ssam    private final long token;
85195376Ssam
86195376Ssam    /**
87195376Ssam     * Represents the allocation strategy (property map, script object class, and method handle) for when
88195376Ssam     * this function is used as a constructor. Note that majority of functions (those not setting any this.*
89195376Ssam     * properties) will share a single canonical "default strategy" instance.
90209975Snwhitehorn     */
9170584Sobrien    private final AllocationStrategy allocationStrategy;
92191278Srwatson
93191278Srwatson    /**
94191278Srwatson     * Opaque object representing parser state at the end of the function. Used when reparsing outer function
95191278Srwatson     * to help with skipping parsing inner functions.
96191276Srwatson     */
97191276Srwatson    private final Object endParserState;
98191276Srwatson
9970584Sobrien    /** Code installer used for all further recompilation/specialization of this ScriptFunction */
100209975Snwhitehorn    private transient CodeInstaller<ScriptEnvironment> installer;
101292680Sjhibbits
10270584Sobrien    private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions;
10370584Sobrien
104197316Salc    /** Id to parent function if one exists */
105197316Salc    private RecompilableScriptFunctionData parent;
106118239Speter
107258078Sjhibbits    /** Copy of the {@link FunctionNode} flags. */
108258078Sjhibbits    private final int functionFlags;
109258078Sjhibbits
110258079Sjhibbits    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
111118239Speter
112258078Sjhibbits    private transient DebugLogger log;
113116355Salc
114286584Skib    private final Map<String, Integer> externalScopeDepths;
11570584Sobrien
11670584Sobrien    private final Set<String> internalSymbols;
11770584Sobrien
11870584Sobrien    private static final int GET_SET_PREFIX_LENGTH = "*et ".length();
119292680Sjhibbits
12070584Sobrien    private static final long serialVersionUID = 4914839316174633726L;
12170584Sobrien
122292680Sjhibbits    /**
123292680Sjhibbits     * Constructor - public as scripts use it
12470584Sobrien     *
125292680Sjhibbits     * @param functionNode        functionNode that represents this function code
126292680Sjhibbits     * @param installer           installer for code regeneration versions of this function
12770584Sobrien     * @param allocationStrategy  strategy for the allocation behavior when this function is used as a constructor
128209975Snwhitehorn     * @param nestedFunctions     nested function map
12970584Sobrien     * @param externalScopeDepths external scope depths
130292680Sjhibbits     * @param internalSymbols     internal symbols to method, defined in its scope
131292680Sjhibbits     * @param serializedAst       a serialized AST representation. Normally only used for split functions.
132196994Sphk     */
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        if (functionNode.getKind() == FunctionNode.Kind.GETTER || functionNode.getKind() == FunctionNode.Kind.SETTER) {
346            flags |= IS_PROPERTY_ACCESSOR;
347        }
348        return flags;
349    }
350
351    @Override
352    PropertyMap getAllocatorMap() {
353        return allocationStrategy.getAllocatorMap();
354    }
355
356    @Override
357    ScriptObject allocate(final PropertyMap map) {
358        return allocationStrategy.allocate(map);
359    }
360
361    boolean isSerialized() {
362        return serializedAst != null;
363    }
364
365    FunctionNode reparse() {
366        if (isSerialized()) {
367            return deserialize();
368        }
369
370        final int descPosition = Token.descPosition(token);
371        final Context context = Context.getContextTrusted();
372        final Parser parser = new Parser(
373            context.getEnv(),
374            source,
375            new Context.ThrowErrorManager(),
376            isStrict(),
377            // source starts at line 0, so even though lineNumber is the correct declaration line, back off
378            // one to make it exclusive
379            lineNumber - 1,
380            context.getLogger(Parser.class));
381
382        if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) {
383            parser.setFunctionName(functionName);
384        }
385        parser.setReparsedFunction(this);
386
387        final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition,
388                Token.descLength(token), isPropertyAccessor());
389        // Parser generates a program AST even if we're recompiling a single function, so when we are only
390        // recompiling a single function, extract it from the program.
391        return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName);
392    }
393
394    private FunctionNode deserialize() {
395        final ScriptEnvironment env = installer.getOwner();
396        final Timing timing = env._timing;
397        final long t1 = System.nanoTime();
398        try {
399            return AstDeserializer.deserialize(serializedAst).initializeDeserialized(source, new Namespace(env.getNamespace()));
400        } finally {
401            timing.accumulateTime("'Deserialize'", System.nanoTime() - t1);
402        }
403    }
404
405    private boolean getFunctionFlag(final int flag) {
406        return (functionFlags & flag) != 0;
407    }
408
409    private boolean isProgram() {
410        return getFunctionFlag(FunctionNode.IS_PROGRAM);
411    }
412
413    TypeMap typeMap(final MethodType fnCallSiteType) {
414        if (fnCallSiteType == null) {
415            return null;
416        }
417
418        if (CompiledFunction.isVarArgsType(fnCallSiteType)) {
419            return null;
420        }
421
422        return new TypeMap(functionNodeId, explicitParams(fnCallSiteType), needsCallee());
423    }
424
425    private static ScriptObject newLocals(final ScriptObject runtimeScope) {
426        final ScriptObject locals = Global.newEmptyInstance();
427        locals.setProto(runtimeScope);
428        return locals;
429    }
430
431    private Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
432        return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
433    }
434
435    /**
436     * Returns a code installer for installing new code. If we're using either optimistic typing or loader-per-compile,
437     * then asks for a code installer with a new class loader; otherwise just uses the current installer. We use
438     * a new class loader with optimistic typing so that deoptimized code can get reclaimed by GC.
439     * @return a code installer for installing new code.
440     */
441    private CodeInstaller<ScriptEnvironment> getInstallerForNewCode() {
442        final ScriptEnvironment env = installer.getOwner();
443        return env._optimistic_types || env._loader_per_compile ? installer.withNewLoader() : installer;
444    }
445
446    Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType,
447            final ScriptObject runtimeScope, final Map<Integer, Type> invalidatedProgramPoints,
448            final int[] continuationEntryPoints) {
449        final TypeMap typeMap = typeMap(actualCallSiteType);
450        final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
451        final Object typeInformationFile = OptimisticTypesPersistence.getLocationDescriptor(source, functionNodeId, paramTypes);
452        final Context context = Context.getContextTrusted();
453        return new Compiler(
454                context,
455                context.getEnv(),
456                getInstallerForNewCode(),
457                functionNode.getSource(),  // source
458                context.getErrorManager(),
459                isStrict() | functionNode.isStrict(), // is strict
460                true,       // is on demand
461                this,       // compiledFunction, i.e. this RecompilableScriptFunctionData
462                typeMap,    // type map
463                getEffectiveInvalidatedProgramPoints(invalidatedProgramPoints, typeInformationFile), // invalidated program points
464                typeInformationFile,
465                continuationEntryPoints, // continuation entry points
466                runtimeScope); // runtime scope
467    }
468
469    /**
470     * If the function being compiled already has its own invalidated program points map, use it. Otherwise, attempt to
471     * load invalidated program points map from the persistent type info cache.
472     * @param invalidatedProgramPoints the function's current invalidated program points map. Null if the function
473     * doesn't have it.
474     * @param typeInformationFile the object describing the location of the persisted type information.
475     * @return either the existing map, or a loaded map from the persistent type info cache, or a new empty map if
476     * neither an existing map or a persistent cached type info is available.
477     */
478    @SuppressWarnings("unused")
479    private static Map<Integer, Type> getEffectiveInvalidatedProgramPoints(
480            final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile) {
481        if(invalidatedProgramPoints != null) {
482            return invalidatedProgramPoints;
483        }
484        final Map<Integer, Type> loadedProgramPoints = OptimisticTypesPersistence.load(typeInformationFile);
485        return loadedProgramPoints != null ? loadedProgramPoints : new TreeMap<Integer, Type>();
486    }
487
488    private FunctionInitializer compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final boolean persist) {
489        // We're creating an empty script object for holding local variables. AssignSymbols will populate it with
490        // explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and
491        // CompilationEnvironment#declareLocalSymbol()).
492
493        if (log.isEnabled()) {
494            log.info("Parameter type specialization of '", functionName, "' signature: ", actualCallSiteType);
495        }
496
497        final boolean persistentCache = persist && usePersistentCodeCache();
498        String cacheKey = null;
499        if (persistentCache) {
500            final TypeMap typeMap = typeMap(actualCallSiteType);
501            final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
502            cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
503            final CodeInstaller<ScriptEnvironment> newInstaller = getInstallerForNewCode();
504            final StoredScript script = newInstaller.loadScript(source, cacheKey);
505
506            if (script != null) {
507                Compiler.updateCompilationId(script.getCompilationId());
508                return script.installFunction(this, newInstaller);
509            }
510        }
511
512        final FunctionNode fn = reparse();
513        final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope);
514        final FunctionNode compiledFn = compiler.compile(fn,
515                isSerialized() ? CompilationPhases.COMPILE_ALL_SERIALIZED : CompilationPhases.COMPILE_ALL);
516
517        if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) {
518            compiler.persistClassInfo(cacheKey, compiledFn);
519        }
520        return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());
521    }
522
523    boolean usePersistentCodeCache() {
524        return installer != null && installer.getOwner()._persistent_cache;
525    }
526
527    private MethodType explicitParams(final MethodType callSiteType) {
528        if (CompiledFunction.isVarArgsType(callSiteType)) {
529            return null;
530        }
531
532        final MethodType noCalleeThisType = callSiteType.dropParameterTypes(0, 2); // (callee, this) is always in call site type
533        final int callSiteParamCount = noCalleeThisType.parameterCount();
534
535        // Widen parameters of reference types to Object as we currently don't care for specialization among reference
536        // types. E.g. call site saying (ScriptFunction, Object, String) should still link to (ScriptFunction, Object, Object)
537        final Class<?>[] paramTypes = noCalleeThisType.parameterArray();
538        boolean changed = false;
539        for (int i = 0; i < paramTypes.length; ++i) {
540            final Class<?> paramType = paramTypes[i];
541            if (!(paramType.isPrimitive() || paramType == Object.class)) {
542                paramTypes[i] = Object.class;
543                changed = true;
544            }
545        }
546        final MethodType generalized = changed ? MethodType.methodType(noCalleeThisType.returnType(), paramTypes) : noCalleeThisType;
547
548        if (callSiteParamCount < getArity()) {
549            return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(getArity() - callSiteParamCount, Object.class));
550        }
551        return generalized;
552    }
553
554    private FunctionNode extractFunctionFromScript(final FunctionNode script) {
555        final Set<FunctionNode> fns = new HashSet<>();
556        script.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
557            @Override
558            public boolean enterFunctionNode(final FunctionNode fn) {
559                fns.add(fn);
560                return false;
561            }
562        });
563        assert fns.size() == 1 : "got back more than one method in recompilation";
564        final FunctionNode f = fns.iterator().next();
565        assert f.getId() == functionNodeId;
566        if (!getFunctionFlag(FunctionNode.IS_DECLARED) && f.isDeclared()) {
567            return f.clearFlag(null, FunctionNode.IS_DECLARED);
568        }
569        return f;
570    }
571
572    private void logLookup(final boolean shouldLog, final MethodType targetType) {
573        if (shouldLog && log.isEnabled()) {
574            log.info("Looking up ", DebugLogger.quote(functionName), " type=", targetType);
575        }
576    }
577
578    private MethodHandle lookup(final FunctionInitializer fnInit, final boolean shouldLog) {
579        final MethodType type = fnInit.getMethodType();
580        logLookup(shouldLog, type);
581        return lookupCodeMethod(fnInit.getCode(), type);
582    }
583
584    MethodHandle lookup(final FunctionNode fn) {
585        final MethodType type = new FunctionSignature(fn).getMethodType();
586        logLookup(true, type);
587        return lookupCodeMethod(fn.getCompileUnit().getCode(), type);
588    }
589
590    MethodHandle lookupCodeMethod(final Class<?> codeClass, final MethodType targetType) {
591        return MH.findStatic(LOOKUP, codeClass, functionName, targetType);
592    }
593
594    /**
595     * Initializes this function data with the eagerly generated version of the code. This method can only be invoked
596     * by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it
597     * externally will result in an exception.
598     *
599     * @param functionNode FunctionNode for this data
600     */
601    public void initializeCode(final FunctionNode functionNode) {
602        // Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit.
603        if (!code.isEmpty() || functionNode.getId() != functionNodeId || !functionNode.getCompileUnit().isInitializing(this, functionNode)) {
604            throw new IllegalStateException(name);
605        }
606        addCode(lookup(functionNode), null, null, functionNode.getFlags());
607    }
608
609    /**
610     * Initializes this function with the given function code initializer.
611     * @param initializer function code initializer
612     */
613    void initializeCode(final FunctionInitializer initializer) {
614        addCode(lookup(initializer, true), null, null, initializer.getFlags());
615    }
616
617    private CompiledFunction addCode(final MethodHandle target, final Map<Integer, Type> invalidatedProgramPoints,
618                                     final MethodType callSiteType, final int fnFlags) {
619        final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, callSiteType, fnFlags);
620        code.add(cfn);
621        return cfn;
622    }
623
624    /**
625     * Add code with specific call site type. It will adapt the type of the looked up method handle to fit the call site
626     * type. This is necessary because even if we request a specialization that takes an "int" parameter, we might end
627     * up getting one that takes a "double" etc. because of internal function logic causes widening (e.g. assignment of
628     * a wider value to the parameter variable). However, we use the method handle type for matching subsequent lookups
629     * for the same specialization, so we must adapt the handle to the expected type.
630     * @param fnInit the function
631     * @param callSiteType the call site type
632     * @return the compiled function object, with its type matching that of the call site type.
633     */
634    private CompiledFunction addCode(final FunctionInitializer fnInit, final MethodType callSiteType) {
635        if (isVariableArity()) {
636            return addCode(lookup(fnInit, true), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
637        }
638
639        final MethodHandle handle = lookup(fnInit, true);
640        final MethodType fromType = handle.type();
641        MethodType toType = needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1);
642        toType = toType.changeReturnType(fromType.returnType());
643
644        final int toCount = toType.parameterCount();
645        final int fromCount = fromType.parameterCount();
646        final int minCount = Math.min(fromCount, toCount);
647        for(int i = 0; i < minCount; ++i) {
648            final Class<?> fromParam = fromType.parameterType(i);
649            final Class<?>   toParam =   toType.parameterType(i);
650            // If method has an Object parameter, but call site had String, preserve it as Object. No need to narrow it
651            // artificially. Note that this is related to how CompiledFunction.matchesCallSite() works, specifically
652            // the fact that various reference types compare to equal (see "fnType.isEquivalentTo(csType)" there).
653            if (fromParam != toParam && !fromParam.isPrimitive() && !toParam.isPrimitive()) {
654                assert fromParam.isAssignableFrom(toParam);
655                toType = toType.changeParameterType(i, fromParam);
656            }
657        }
658        if (fromCount > toCount) {
659            toType = toType.appendParameterTypes(fromType.parameterList().subList(toCount, fromCount));
660        } else if (fromCount < toCount) {
661            toType = toType.dropParameterTypes(fromCount, toCount);
662        }
663
664        return addCode(lookup(fnInit, false).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
665    }
666
667    /**
668     * Returns the return type of a function specialization for particular parameter types.<br>
669     * <b>Be aware that the way this is implemented, it forces full materialization (compilation and installation) of
670     * code for that specialization.</b>
671     * @param callSiteType the parameter types at the call site. It must include the mandatory {@code callee} and
672     * {@code this} parameters, so it needs to start with at least {@code ScriptFunction.class} and
673     * {@code Object.class} class. Since the return type of the function is calculated from the code itself, it is
674     * irrelevant and should be set to {@code Object.class}.
675     * @param runtimeScope a current runtime scope. Can be null but when it's present it will be used as a source of
676     * current runtime values that can improve the compiler's type speculations (and thus reduce the need for later
677     * recompilations) if the specialization is not already present and thus needs to be freshly compiled.
678     * @return the return type of the function specialization.
679     */
680    public Class<?> getReturnType(final MethodType callSiteType, final ScriptObject runtimeScope) {
681        return getBest(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS).type().returnType();
682    }
683
684    @Override
685    synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
686        CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope, forbidden);
687        if (existingBest == null) {
688            existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, true), callSiteType);
689        }
690
691        assert existingBest != null;
692        //we are calling a vararg method with real args
693        boolean varArgWithRealArgs = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
694
695        //if the best one is an apply to call, it has to match the callsite exactly
696        //or we need to regenerate
697        if (existingBest.isApplyToCall()) {
698            final CompiledFunction best = lookupExactApplyToCall(callSiteType);
699            if (best != null) {
700                return best;
701            }
702            varArgWithRealArgs = true;
703        }
704
705        if (varArgWithRealArgs) {
706            // special case: we had an apply to call, but we failed to make it fit.
707            // Try to generate a specialized one for this callsite. It may
708            // be another apply to call specialization, or it may not, but whatever
709            // it is, it is a specialization that is guaranteed to fit
710            final FunctionInitializer fnInit = compileTypeSpecialization(callSiteType, runtimeScope, false);
711            existingBest = addCode(fnInit, callSiteType);
712        }
713
714        return existingBest;
715    }
716
717    @Override
718    boolean isRecompilable() {
719        return true;
720    }
721
722    @Override
723    public boolean needsCallee() {
724        return getFunctionFlag(FunctionNode.NEEDS_CALLEE);
725    }
726
727    /**
728     * Returns the {@link FunctionNode} flags associated with this function data.
729     * @return the {@link FunctionNode} flags associated with this function data.
730     */
731    public int getFunctionFlags() {
732        return functionFlags;
733    }
734
735    @Override
736    MethodType getGenericType() {
737        // 2 is for (callee, this)
738        if (isVariableArity()) {
739            return MethodType.genericMethodType(2, true);
740        }
741        return MethodType.genericMethodType(2 + getArity());
742    }
743
744    /**
745     * Return the function node id.
746     * @return the function node id
747     */
748    public int getFunctionNodeId() {
749        return functionNodeId;
750    }
751
752    /**
753     * Get the source for the script
754     * @return source
755     */
756    public Source getSource() {
757        return source;
758    }
759
760    /**
761     * Return a script function data based on a function id, either this function if
762     * the id matches or a nested function based on functionId. This goes down into
763     * nested functions until all leaves are exhausted.
764     *
765     * @param functionId function id
766     * @return script function data or null if invalid id
767     */
768    public RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
769        if (functionId == functionNodeId) {
770            return this;
771        }
772        RecompilableScriptFunctionData data;
773
774        data = nestedFunctions == null ? null : nestedFunctions.get(functionId);
775        if (data != null) {
776            return data;
777        }
778        for (final RecompilableScriptFunctionData ndata : nestedFunctions.values()) {
779            data = ndata.getScriptFunctionData(functionId);
780            if (data != null) {
781                return data;
782            }
783        }
784        return null;
785    }
786
787    /**
788     * Check whether a certain name is a global symbol, i.e. only exists as defined
789     * in outermost scope and not shadowed by being parameter or assignment in inner
790     * scopes
791     *
792     * @param functionNode function node to check
793     * @param symbolName symbol name
794     * @return true if global symbol
795     */
796    public boolean isGlobalSymbol(final FunctionNode functionNode, final String symbolName) {
797        RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId());
798        assert data != null;
799
800        do {
801            if (data.hasInternalSymbol(symbolName)) {
802                return false;
803            }
804            data = data.getParent();
805        } while(data != null);
806
807        return true;
808    }
809
810    /**
811     * Restores the {@link #getFunctionFlags()} flags to a function node. During on-demand compilation, we might need
812     * to restore flags to a function node that was otherwise not subjected to a full compile pipeline (e.g. its parse
813     * was skipped, or it's a nested function of a deserialized function.
814     * @param lc current lexical context
815     * @param fn the function node to restore flags onto
816     * @return the transformed function node
817     */
818    public FunctionNode restoreFlags(final LexicalContext lc, final FunctionNode fn) {
819        assert fn.getId() == functionNodeId;
820        FunctionNode newFn = fn.setFlags(lc, functionFlags);
821        // This compensates for missing markEval() in case the function contains an inner function
822        // that contains eval(), that now we didn't discover since we skipped the inner function.
823        if (newFn.hasNestedEval()) {
824            assert newFn.hasScopeBlock();
825            newFn = newFn.setBody(lc, newFn.getBody().setNeedsScope(null));
826        }
827        return newFn;
828    }
829
830    private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
831        in.defaultReadObject();
832        createLogger();
833    }
834
835    private void createLogger() {
836        log = initLogger(Context.getContextTrusted());
837    }
838}
839