RecompilableScriptFunctionData.java revision 1726:5d68f5155dde
1/*
2 * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.runtime;
27
28import static jdk.nashorn.internal.lookup.Lookup.MH;
29
30import java.io.IOException;
31import java.lang.invoke.MethodHandle;
32import java.lang.invoke.MethodHandles;
33import java.lang.invoke.MethodType;
34import java.lang.ref.Reference;
35import java.lang.ref.SoftReference;
36import java.util.Collection;
37import java.util.Collections;
38import java.util.HashSet;
39import java.util.IdentityHashMap;
40import java.util.Map;
41import java.util.Set;
42import java.util.TreeMap;
43import java.util.concurrent.ExecutorService;
44import java.util.concurrent.LinkedBlockingDeque;
45import java.util.concurrent.ThreadPoolExecutor;
46import java.util.concurrent.TimeUnit;
47import jdk.nashorn.internal.codegen.Compiler;
48import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
49import jdk.nashorn.internal.codegen.CompilerConstants;
50import jdk.nashorn.internal.codegen.FunctionSignature;
51import jdk.nashorn.internal.codegen.Namespace;
52import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
53import jdk.nashorn.internal.codegen.TypeMap;
54import jdk.nashorn.internal.codegen.types.Type;
55import jdk.nashorn.internal.ir.Block;
56import jdk.nashorn.internal.ir.ForNode;
57import jdk.nashorn.internal.ir.FunctionNode;
58import jdk.nashorn.internal.ir.IdentNode;
59import jdk.nashorn.internal.ir.LexicalContext;
60import jdk.nashorn.internal.ir.Node;
61import jdk.nashorn.internal.ir.SwitchNode;
62import jdk.nashorn.internal.ir.Symbol;
63import jdk.nashorn.internal.ir.TryNode;
64import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
65import jdk.nashorn.internal.objects.Global;
66import jdk.nashorn.internal.parser.Parser;
67import jdk.nashorn.internal.parser.Token;
68import jdk.nashorn.internal.parser.TokenType;
69import jdk.nashorn.internal.runtime.linker.NameCodec;
70import jdk.nashorn.internal.runtime.logging.DebugLogger;
71import jdk.nashorn.internal.runtime.logging.Loggable;
72import jdk.nashorn.internal.runtime.logging.Logger;
73import jdk.nashorn.internal.runtime.options.Options;
74/**
75 * This is a subclass that represents a script function that may be regenerated,
76 * for example with specialization based on call site types, or lazily generated.
77 * The common denominator is that it can get new invokers during its lifespan,
78 * unlike {@code FinalScriptFunctionData}
79 */
80@Logger(name="recompile")
81public final class RecompilableScriptFunctionData extends ScriptFunctionData implements Loggable {
82    /** Prefix used for all recompiled script classes */
83    public static final String RECOMPILATION_PREFIX = "Recompilation$";
84
85    private static final ExecutorService astSerializerExecutorService = createAstSerializerExecutorService();
86
87    /** Unique function node id for this function node */
88    private final int functionNodeId;
89
90    private final String functionName;
91
92    /** The line number where this function begins. */
93    private final int lineNumber;
94
95    /** Source from which FunctionNode was parsed. */
96    private transient Source source;
97
98    /**
99     * Cached form of the AST. Either a {@code SerializedAst} object used by split functions as they can't be
100     * reparsed from source, or a soft reference to a {@code FunctionNode} for other functions (it is safe
101     * to be cleared as they can be reparsed).
102     */
103    private volatile Object cachedAst;
104
105    /** Token of this function within the source. */
106    private final long token;
107
108    /**
109     * Represents the allocation strategy (property map, script object class, and method handle) for when
110     * this function is used as a constructor. Note that majority of functions (those not setting any this.*
111     * properties) will share a single canonical "default strategy" instance.
112     */
113    private final AllocationStrategy allocationStrategy;
114
115    /**
116     * Opaque object representing parser state at the end of the function. Used when reparsing outer function
117     * to help with skipping parsing inner functions.
118     */
119    private final Object endParserState;
120
121    /** Code installer used for all further recompilation/specialization of this ScriptFunction */
122    private transient CodeInstaller installer;
123
124    private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions;
125
126    /** Id to parent function if one exists */
127    private RecompilableScriptFunctionData parent;
128
129    /** Copy of the {@link FunctionNode} flags. */
130    private final int functionFlags;
131
132    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
133
134    private transient DebugLogger log;
135
136    private final Map<String, Integer> externalScopeDepths;
137
138    private final Set<String> internalSymbols;
139
140    private static final int GET_SET_PREFIX_LENGTH = "*et ".length();
141
142    private static final long serialVersionUID = 4914839316174633726L;
143
144    /**
145     * Constructor - public as scripts use it
146     *
147     * @param functionNode        functionNode that represents this function code
148     * @param installer           installer for code regeneration versions of this function
149     * @param allocationStrategy  strategy for the allocation behavior when this function is used as a constructor
150     * @param nestedFunctions     nested function map
151     * @param externalScopeDepths external scope depths
152     * @param internalSymbols     internal symbols to method, defined in its scope
153     */
154    public RecompilableScriptFunctionData(
155        final FunctionNode functionNode,
156        final CodeInstaller installer,
157        final AllocationStrategy allocationStrategy,
158        final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
159        final Map<String, Integer> externalScopeDepths,
160        final Set<String> internalSymbols) {
161
162        super(functionName(functionNode),
163              Math.min(functionNode.getParameters().size(), MAX_ARITY),
164              getDataFlags(functionNode));
165
166        this.functionName        = functionNode.getName();
167        this.lineNumber          = functionNode.getLineNumber();
168        this.functionFlags       = functionNode.getFlags() | (functionNode.needsCallee() ? FunctionNode.NEEDS_CALLEE : 0);
169        this.functionNodeId      = functionNode.getId();
170        this.source              = functionNode.getSource();
171        this.endParserState      = functionNode.getEndParserState();
172        this.token               = tokenFor(functionNode);
173        this.installer           = installer;
174        this.allocationStrategy  = allocationStrategy;
175        this.nestedFunctions     = smallMap(nestedFunctions);
176        this.externalScopeDepths = smallMap(externalScopeDepths);
177        this.internalSymbols     = smallSet(new HashSet<>(internalSymbols));
178
179        for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) {
180            assert nfn.getParent() == null;
181            nfn.setParent(this);
182        }
183
184        createLogger();
185    }
186
187    private static <K, V> Map<K, V> smallMap(final Map<K, V> map) {
188        if (map == null || map.isEmpty()) {
189            return Collections.emptyMap();
190        } else if (map.size() == 1) {
191            final Map.Entry<K, V> entry = map.entrySet().iterator().next();
192            return Collections.singletonMap(entry.getKey(), entry.getValue());
193        } else {
194            return map;
195        }
196    }
197
198    private static <T> Set<T> smallSet(final Set<T> set) {
199        if (set == null || set.isEmpty()) {
200            return Collections.emptySet();
201        } else if (set.size() == 1) {
202            return Collections.singleton(set.iterator().next());
203        } else {
204            return set;
205        }
206    }
207
208    @Override
209    public DebugLogger getLogger() {
210        return log;
211    }
212
213    @Override
214    public DebugLogger initLogger(final Context ctxt) {
215        return ctxt.getLogger(this.getClass());
216    }
217
218    /**
219     * Check if a symbol is internally defined in a function. For example
220     * if "undefined" is internally defined in the outermost program function,
221     * it has not been reassigned or overridden and can be optimized
222     *
223     * @param symbolName symbol name
224     * @return true if symbol is internal to this ScriptFunction
225     */
226
227    public boolean hasInternalSymbol(final String symbolName) {
228        return internalSymbols.contains(symbolName);
229    }
230
231    /**
232     * Return the external symbol table
233     * @param symbolName symbol name
234     * @return the external symbol table with proto depths
235     */
236    public int getExternalSymbolDepth(final String symbolName) {
237        final Integer depth = externalScopeDepths.get(symbolName);
238        return depth == null ? -1 : depth;
239    }
240
241    /**
242     * Returns the names of all external symbols this function uses.
243     * @return the names of all external symbols this function uses.
244     */
245    public Set<String> getExternalSymbolNames() {
246        return Collections.unmodifiableSet(externalScopeDepths.keySet());
247    }
248
249    /**
250     * Returns the opaque object representing the parser state at the end of this function's body, used to
251     * skip parsing this function when reparsing its containing outer function.
252     * @return the object representing the end parser state
253     */
254    public Object getEndParserState() {
255        return endParserState;
256    }
257
258    /**
259     * Get the parent of this RecompilableScriptFunctionData. If we are
260     * a nested function, we have a parent. Note that "null" return value
261     * can also mean that we have a parent but it is unknown, so this can
262     * only be used for conservative assumptions.
263     * @return parent data, or null if non exists and also null IF UNKNOWN.
264     */
265    public RecompilableScriptFunctionData getParent() {
266        return parent;
267    }
268
269    void setParent(final RecompilableScriptFunctionData parent) {
270        this.parent = parent;
271    }
272
273    @Override
274    String toSource() {
275        if (source != null && token != 0) {
276            return source.getString(Token.descPosition(token), Token.descLength(token));
277        }
278
279        return "function " + (name == null ? "" : name) + "() { [native code] }";
280    }
281
282    /**
283     * Initialize transient fields on deserialized instances
284     *
285     * @param src source
286     * @param inst code installer
287     */
288    public void initTransients(final Source src, final CodeInstaller inst) {
289        if (this.source == null && this.installer == null) {
290            this.source    = src;
291            this.installer = inst;
292        } else if (this.source != src || !this.installer.isCompatibleWith(inst)) {
293            // Existing values must be same as those passed as parameters
294            throw new IllegalArgumentException();
295        }
296    }
297
298    @Override
299    public String toString() {
300        return super.toString() + '@' + functionNodeId;
301    }
302
303    @Override
304    public String toStringVerbose() {
305        final StringBuilder sb = new StringBuilder();
306
307        sb.append("fnId=").append(functionNodeId).append(' ');
308
309        if (source != null) {
310            sb.append(source.getName())
311                .append(':')
312                .append(lineNumber)
313                .append(' ');
314        }
315
316        return sb.toString() + super.toString();
317    }
318
319    @Override
320    public String getFunctionName() {
321        return functionName;
322    }
323
324    @Override
325    public boolean inDynamicContext() {
326        return getFunctionFlag(FunctionNode.IN_DYNAMIC_CONTEXT);
327    }
328
329    private static String functionName(final FunctionNode fn) {
330        if (fn.isAnonymous()) {
331            return "";
332        }
333        final FunctionNode.Kind kind = fn.getKind();
334        if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
335            final String name = NameCodec.decode(fn.getIdent().getName());
336            return name.substring(GET_SET_PREFIX_LENGTH);
337        }
338        return fn.getIdent().getName();
339    }
340
341    private static long tokenFor(final FunctionNode fn) {
342        final int  position  = Token.descPosition(fn.getFirstToken());
343        final long lastToken = Token.withDelimiter(fn.getLastToken());
344        // EOL uses length field to store the line number
345        final int  length    = Token.descPosition(lastToken) - position + (Token.descType(lastToken) == TokenType.EOL ? 0 : Token.descLength(lastToken));
346
347        return Token.toDesc(TokenType.FUNCTION, position, length);
348    }
349
350    private static int getDataFlags(final FunctionNode functionNode) {
351        int flags = IS_CONSTRUCTOR;
352        if (functionNode.isStrict()) {
353            flags |= IS_STRICT;
354        }
355        if (functionNode.needsCallee()) {
356            flags |= NEEDS_CALLEE;
357        }
358        if (functionNode.usesThis() || functionNode.hasEval()) {
359            flags |= USES_THIS;
360        }
361        if (functionNode.isVarArg()) {
362            flags |= IS_VARIABLE_ARITY;
363        }
364        if (functionNode.getKind() == FunctionNode.Kind.GETTER || functionNode.getKind() == FunctionNode.Kind.SETTER) {
365            flags |= IS_PROPERTY_ACCESSOR;
366        }
367        if (functionNode.isMethod() || functionNode.isClassConstructor()) {
368            flags |= IS_ES6_METHOD;
369        }
370        return flags;
371    }
372
373    @Override
374    PropertyMap getAllocatorMap(final ScriptObject prototype) {
375        return allocationStrategy.getAllocatorMap(prototype);
376    }
377
378    @Override
379    ScriptObject allocate(final PropertyMap map) {
380        return allocationStrategy.allocate(map);
381    }
382
383    FunctionNode reparse() {
384        final FunctionNode cachedFunction = getCachedAst();
385        if (cachedFunction != null) {
386            assert cachedFunction.isCached();
387            return cachedFunction;
388        }
389
390        final int descPosition = Token.descPosition(token);
391        final Context context = Context.getContextTrusted();
392        final Parser parser = new Parser(
393            context.getEnv(),
394            source,
395            new Context.ThrowErrorManager(),
396            isStrict(),
397            // source starts at line 0, so even though lineNumber is the correct declaration line, back off
398            // one to make it exclusive
399            lineNumber - 1,
400            context.getLogger(Parser.class));
401
402        if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) {
403            parser.setFunctionName(functionName);
404        }
405        parser.setReparsedFunction(this);
406
407        final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition,
408                Token.descLength(token), flags);
409        // Parser generates a program AST even if we're recompiling a single function, so when we are only
410        // recompiling a single function, extract it from the program.
411        return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName);
412    }
413
414    private FunctionNode getCachedAst() {
415        final Object lCachedAst = cachedAst;
416        // Are we softly caching the AST?
417        if (lCachedAst instanceof Reference<?>) {
418            final FunctionNode fn = (FunctionNode)((Reference<?>)lCachedAst).get();
419            if (fn != null) {
420                // Yes we are - this is fast
421                return cloneSymbols(fn);
422            }
423        // Are we strongly caching a serialized AST (for split functions only)?
424        } else if (lCachedAst instanceof SerializedAst) {
425            final SerializedAst serializedAst = (SerializedAst)lCachedAst;
426            // Even so, are we also softly caching the AST?
427            final FunctionNode cachedFn = serializedAst.cachedAst.get();
428            if (cachedFn != null) {
429                // Yes we are - this is fast
430                return cloneSymbols(cachedFn);
431            }
432            final FunctionNode deserializedFn = deserialize(serializedAst.serializedAst);
433            // Softly cache after deserialization, maybe next time we won't need to deserialize
434            serializedAst.cachedAst = new SoftReference<>(deserializedFn);
435            return deserializedFn;
436        }
437        // No cached representation; return null for reparsing
438        return null;
439    }
440
441    /**
442     * Sets the AST to cache in this function
443     * @param astToCache the new AST to cache
444     */
445    public void setCachedAst(final FunctionNode astToCache) {
446        assert astToCache.getId() == functionNodeId; // same function
447        assert !(cachedAst instanceof SerializedAst); // Can't overwrite serialized AST
448
449        final boolean isSplit = astToCache.isSplit();
450        // If we're caching a split function, we're doing it in the eager pass, hence there can be no other
451        // cached representation already. In other words, isSplit implies cachedAst == null.
452        assert !isSplit || cachedAst == null; //
453
454        final FunctionNode symbolClonedAst = cloneSymbols(astToCache);
455        final Reference<FunctionNode> ref = new SoftReference<>(symbolClonedAst);
456        cachedAst = ref;
457
458        // Asynchronously serialize split functions.
459        if (isSplit) {
460            astSerializerExecutorService.execute(() -> {
461                cachedAst = new SerializedAst(symbolClonedAst, ref);
462            });
463        }
464    }
465
466    /**
467     * Creates the AST serializer executor service used for in-memory serialization of split functions' ASTs.
468     * It is created with an unbounded queue (so it can queue any number of pending tasks). Its core and max
469     * threads is the same, but they are all allowed to time out so when there's no work, they can all go
470     * away. The threads will be daemons, and they will time out if idle for a minute. Their priority is also
471     * slightly lower than normal priority as we'd prefer the CPU to keep running the program; serializing
472     * split function is a memory conservation measure (it allows us to release the AST), it can wait a bit.
473     * @return an executor service with above described characteristics.
474     */
475    private static ExecutorService createAstSerializerExecutorService() {
476        final int threads = Math.max(1, Options.getIntProperty("nashorn.serialize.threads", Runtime.getRuntime().availableProcessors() / 2));
477        final ThreadPoolExecutor service = new ThreadPoolExecutor(threads, threads, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<>(),
478            (r) -> {
479                final Thread t = new Thread(r, "Nashorn AST Serializer");
480                t.setDaemon(true);
481                t.setPriority(Thread.NORM_PRIORITY - 1);
482                return t;
483            });
484        service.allowCoreThreadTimeOut(true);
485        return service;
486    }
487
488    /**
489     * A tuple of a serialized AST and a soft reference to a deserialized AST. This is used to cache split
490     * functions. Since split functions are altered from their source form, they can't be reparsed from
491     * source. While we could just use the {@code byte[]} representation in {@link RecompilableScriptFunctionData#cachedAst}
492     * we're using this tuple instead to also keep a deserialized AST around in memory to cut down on
493     * deserialization costs.
494     */
495    private static class SerializedAst {
496        private final byte[] serializedAst;
497        private volatile Reference<FunctionNode> cachedAst;
498
499        SerializedAst(final FunctionNode fn, final Reference<FunctionNode> cachedAst) {
500            this.serializedAst = AstSerializer.serialize(fn);
501            this.cachedAst = cachedAst;
502        }
503    }
504
505    private FunctionNode deserialize(final byte[] serializedAst) {
506        final ScriptEnvironment env = installer.getContext().getEnv();
507        final Timing timing = env._timing;
508        final long t1 = System.nanoTime();
509        try {
510            return AstDeserializer.deserialize(serializedAst).initializeDeserialized(source, new Namespace(env.getNamespace()));
511        } finally {
512            timing.accumulateTime("'Deserialize'", System.nanoTime() - t1);
513        }
514    }
515
516    private FunctionNode cloneSymbols(final FunctionNode fn) {
517        final IdentityHashMap<Symbol, Symbol> symbolReplacements = new IdentityHashMap<>();
518        final boolean cached = fn.isCached();
519        // blockDefinedSymbols is used to re-mark symbols defined outside the function as global. We only
520        // need to do this when we cache an eagerly parsed function (which currently means a split one, as we
521        // don't cache non-split functions from the eager pass); those already cached, or those not split
522        // don't need this step.
523        final Set<Symbol> blockDefinedSymbols = fn.isSplit() && !cached ? Collections.newSetFromMap(new IdentityHashMap<>()) : null;
524        FunctionNode newFn = (FunctionNode)fn.accept(new SimpleNodeVisitor() {
525            private Symbol getReplacement(final Symbol original) {
526                if (original == null) {
527                    return null;
528                }
529                final Symbol existingReplacement = symbolReplacements.get(original);
530                if (existingReplacement != null) {
531                    return existingReplacement;
532                }
533                final Symbol newReplacement = original.clone();
534                symbolReplacements.put(original, newReplacement);
535                return newReplacement;
536            }
537
538            @Override
539            public Node leaveIdentNode(final IdentNode identNode) {
540                final Symbol oldSymbol = identNode.getSymbol();
541                if (oldSymbol != null) {
542                    final Symbol replacement = getReplacement(oldSymbol);
543                    return identNode.setSymbol(replacement);
544                }
545                return identNode;
546            }
547
548            @Override
549            public Node leaveForNode(final ForNode forNode) {
550                return ensureUniqueLabels(forNode.setIterator(lc, getReplacement(forNode.getIterator())));
551            }
552
553            @Override
554            public Node leaveSwitchNode(final SwitchNode switchNode) {
555                return ensureUniqueLabels(switchNode.setTag(lc, getReplacement(switchNode.getTag())));
556            }
557
558            @Override
559            public Node leaveTryNode(final TryNode tryNode) {
560                return ensureUniqueLabels(tryNode.setException(lc, getReplacement(tryNode.getException())));
561            }
562
563            @Override
564            public boolean enterBlock(final Block block) {
565                for(final Symbol symbol: block.getSymbols()) {
566                    final Symbol replacement = getReplacement(symbol);
567                    if (blockDefinedSymbols != null) {
568                        blockDefinedSymbols.add(replacement);
569                    }
570                }
571                return true;
572            }
573
574            @Override
575            public Node leaveBlock(final Block block) {
576                return ensureUniqueLabels(block.replaceSymbols(lc, symbolReplacements));
577            }
578
579            @Override
580            public Node leaveFunctionNode(final FunctionNode functionNode) {
581                return functionNode.setParameters(lc, functionNode.visitParameters(this));
582            }
583
584            @Override
585            protected Node leaveDefault(final Node node) {
586                return ensureUniqueLabels(node);
587            };
588
589            private Node ensureUniqueLabels(final Node node) {
590                // If we're returning a cached AST, we must also ensure unique labels
591                return cached ? node.ensureUniqueLabels(lc) : node;
592            }
593        });
594
595        if (blockDefinedSymbols != null) {
596            // Mark all symbols not defined in blocks as globals
597            Block newBody = null;
598            for(final Symbol symbol: symbolReplacements.values()) {
599                if(!blockDefinedSymbols.contains(symbol)) {
600                    assert symbol.isScope(); // must be scope
601                    assert externalScopeDepths.containsKey(symbol.getName()); // must be known to us as an external
602                    // Register it in the function body symbol table as a new global symbol
603                    symbol.setFlags((symbol.getFlags() & ~Symbol.KINDMASK) | Symbol.IS_GLOBAL);
604                    if (newBody == null) {
605                        newBody = newFn.getBody().copyWithNewSymbols();
606                        newFn = newFn.setBody(null, newBody);
607                    }
608                    assert newBody.getExistingSymbol(symbol.getName()) == null; // must not be defined in the body already
609                    newBody.putSymbol(symbol);
610                }
611            }
612        }
613        return newFn.setCached(null);
614    }
615
616    private boolean getFunctionFlag(final int flag) {
617        return (functionFlags & flag) != 0;
618    }
619
620    private boolean isProgram() {
621        return getFunctionFlag(FunctionNode.IS_PROGRAM);
622    }
623
624    TypeMap typeMap(final MethodType fnCallSiteType) {
625        if (fnCallSiteType == null) {
626            return null;
627        }
628
629        if (CompiledFunction.isVarArgsType(fnCallSiteType)) {
630            return null;
631        }
632
633        return new TypeMap(functionNodeId, explicitParams(fnCallSiteType), needsCallee());
634    }
635
636    private static ScriptObject newLocals(final ScriptObject runtimeScope) {
637        final ScriptObject locals = Global.newEmptyInstance();
638        locals.setProto(runtimeScope);
639        return locals;
640    }
641
642    private Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
643        return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
644    }
645
646    /**
647     * Returns a code installer for installing new code. If we're using either optimistic typing or loader-per-compile,
648     * then asks for a code installer with a new class loader; otherwise just uses the current installer. We use
649     * a new class loader with optimistic typing so that deoptimized code can get reclaimed by GC.
650     * @return a code installer for installing new code.
651     */
652    private CodeInstaller getInstallerForNewCode() {
653        final ScriptEnvironment env = installer.getContext().getEnv();
654        return env._optimistic_types || env._loader_per_compile ? installer.getOnDemandCompilationInstaller() : installer;
655    }
656
657    Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType,
658            final ScriptObject runtimeScope, final Map<Integer, Type> invalidatedProgramPoints,
659            final int[] continuationEntryPoints) {
660        final TypeMap typeMap = typeMap(actualCallSiteType);
661        final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
662        final Object typeInformationFile = OptimisticTypesPersistence.getLocationDescriptor(source, functionNodeId, paramTypes);
663        return Compiler.forOnDemandCompilation(
664                getInstallerForNewCode(),
665                functionNode.getSource(),  // source
666                isStrict() | functionNode.isStrict(), // is strict
667                this,       // compiledFunction, i.e. this RecompilableScriptFunctionData
668                typeMap,    // type map
669                getEffectiveInvalidatedProgramPoints(invalidatedProgramPoints, typeInformationFile), // invalidated program points
670                typeInformationFile,
671                continuationEntryPoints, // continuation entry points
672                runtimeScope); // runtime scope
673    }
674
675    /**
676     * If the function being compiled already has its own invalidated program points map, use it. Otherwise, attempt to
677     * load invalidated program points map from the persistent type info cache.
678     * @param invalidatedProgramPoints the function's current invalidated program points map. Null if the function
679     * doesn't have it.
680     * @param typeInformationFile the object describing the location of the persisted type information.
681     * @return either the existing map, or a loaded map from the persistent type info cache, or a new empty map if
682     * neither an existing map or a persistent cached type info is available.
683     */
684    @SuppressWarnings("unused")
685    private static Map<Integer, Type> getEffectiveInvalidatedProgramPoints(
686            final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile) {
687        if(invalidatedProgramPoints != null) {
688            return invalidatedProgramPoints;
689        }
690        final Map<Integer, Type> loadedProgramPoints = OptimisticTypesPersistence.load(typeInformationFile);
691        return loadedProgramPoints != null ? loadedProgramPoints : new TreeMap<Integer, Type>();
692    }
693
694    private FunctionInitializer compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final boolean persist) {
695        // We're creating an empty script object for holding local variables. AssignSymbols will populate it with
696        // explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and
697        // CompilationEnvironment#declareLocalSymbol()).
698
699        if (log.isEnabled()) {
700            log.info("Parameter type specialization of '", functionName, "' signature: ", actualCallSiteType);
701        }
702
703        final boolean persistentCache = persist && usePersistentCodeCache();
704        String cacheKey = null;
705        if (persistentCache) {
706            final TypeMap typeMap = typeMap(actualCallSiteType);
707            final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
708            cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
709            final CodeInstaller newInstaller = getInstallerForNewCode();
710            final StoredScript script = newInstaller.loadScript(source, cacheKey);
711
712            if (script != null) {
713                Compiler.updateCompilationId(script.getCompilationId());
714                return script.installFunction(this, newInstaller);
715            }
716        }
717
718        final FunctionNode fn = reparse();
719        final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope);
720        final FunctionNode compiledFn = compiler.compile(fn,
721                fn.isCached() ? CompilationPhases.COMPILE_ALL_CACHED : CompilationPhases.COMPILE_ALL);
722
723        if (persist && !compiledFn.hasApplyToCallSpecialization()) {
724            compiler.persistClassInfo(cacheKey, compiledFn);
725        }
726        return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());
727    }
728
729    boolean usePersistentCodeCache() {
730        return installer != null && installer.getContext().getEnv()._persistent_cache;
731    }
732
733    private MethodType explicitParams(final MethodType callSiteType) {
734        if (CompiledFunction.isVarArgsType(callSiteType)) {
735            return null;
736        }
737
738        final MethodType noCalleeThisType = callSiteType.dropParameterTypes(0, 2); // (callee, this) is always in call site type
739        final int callSiteParamCount = noCalleeThisType.parameterCount();
740
741        // Widen parameters of reference types to Object as we currently don't care for specialization among reference
742        // types. E.g. call site saying (ScriptFunction, Object, String) should still link to (ScriptFunction, Object, Object)
743        final Class<?>[] paramTypes = noCalleeThisType.parameterArray();
744        boolean changed = false;
745        for (int i = 0; i < paramTypes.length; ++i) {
746            final Class<?> paramType = paramTypes[i];
747            if (!(paramType.isPrimitive() || paramType == Object.class)) {
748                paramTypes[i] = Object.class;
749                changed = true;
750            }
751        }
752        final MethodType generalized = changed ? MethodType.methodType(noCalleeThisType.returnType(), paramTypes) : noCalleeThisType;
753
754        if (callSiteParamCount < getArity()) {
755            return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(getArity() - callSiteParamCount, Object.class));
756        }
757        return generalized;
758    }
759
760    private FunctionNode extractFunctionFromScript(final FunctionNode script) {
761        final Set<FunctionNode> fns = new HashSet<>();
762        script.getBody().accept(new SimpleNodeVisitor() {
763            @Override
764            public boolean enterFunctionNode(final FunctionNode fn) {
765                fns.add(fn);
766                return false;
767            }
768        });
769        assert fns.size() == 1 : "got back more than one method in recompilation";
770        final FunctionNode f = fns.iterator().next();
771        assert f.getId() == functionNodeId;
772        if (!getFunctionFlag(FunctionNode.IS_DECLARED) && f.isDeclared()) {
773            return f.clearFlag(null, FunctionNode.IS_DECLARED);
774        }
775        return f;
776    }
777
778    private void logLookup(final boolean shouldLog, final MethodType targetType) {
779        if (shouldLog && log.isEnabled()) {
780            log.info("Looking up ", DebugLogger.quote(functionName), " type=", targetType);
781        }
782    }
783
784    private MethodHandle lookup(final FunctionInitializer fnInit, final boolean shouldLog) {
785        final MethodType type = fnInit.getMethodType();
786        logLookup(shouldLog, type);
787        return lookupCodeMethod(fnInit.getCode(), type);
788    }
789
790    MethodHandle lookup(final FunctionNode fn) {
791        final MethodType type = new FunctionSignature(fn).getMethodType();
792        logLookup(true, type);
793        return lookupCodeMethod(fn.getCompileUnit().getCode(), type);
794    }
795
796    MethodHandle lookupCodeMethod(final Class<?> codeClass, final MethodType targetType) {
797        return MH.findStatic(LOOKUP, codeClass, functionName, targetType);
798    }
799
800    /**
801     * Initializes this function data with the eagerly generated version of the code. This method can only be invoked
802     * by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it
803     * externally will result in an exception.
804     *
805     * @param functionNode FunctionNode for this data
806     */
807    public void initializeCode(final FunctionNode functionNode) {
808        // Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit.
809        if (!code.isEmpty() || functionNode.getId() != functionNodeId || !functionNode.getCompileUnit().isInitializing(this, functionNode)) {
810            throw new IllegalStateException(name);
811        }
812        addCode(lookup(functionNode), null, null, functionNode.getFlags());
813    }
814
815    /**
816     * Initializes this function with the given function code initializer.
817     * @param initializer function code initializer
818     */
819    void initializeCode(final FunctionInitializer initializer) {
820        addCode(lookup(initializer, true), null, null, initializer.getFlags());
821    }
822
823    private CompiledFunction addCode(final MethodHandle target, final Map<Integer, Type> invalidatedProgramPoints,
824                                     final MethodType callSiteType, final int fnFlags) {
825        final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, callSiteType, fnFlags);
826        assert noDuplicateCode(cfn) : "duplicate code";
827        code.add(cfn);
828        return cfn;
829    }
830
831    /**
832     * Add code with specific call site type. It will adapt the type of the looked up method handle to fit the call site
833     * type. This is necessary because even if we request a specialization that takes an "int" parameter, we might end
834     * up getting one that takes a "double" etc. because of internal function logic causes widening (e.g. assignment of
835     * a wider value to the parameter variable). However, we use the method handle type for matching subsequent lookups
836     * for the same specialization, so we must adapt the handle to the expected type.
837     * @param fnInit the function
838     * @param callSiteType the call site type
839     * @return the compiled function object, with its type matching that of the call site type.
840     */
841    private CompiledFunction addCode(final FunctionInitializer fnInit, final MethodType callSiteType) {
842        if (isVariableArity()) {
843            return addCode(lookup(fnInit, true), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
844        }
845
846        final MethodHandle handle = lookup(fnInit, true);
847        final MethodType fromType = handle.type();
848        MethodType toType = needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1);
849        toType = toType.changeReturnType(fromType.returnType());
850
851        final int toCount = toType.parameterCount();
852        final int fromCount = fromType.parameterCount();
853        final int minCount = Math.min(fromCount, toCount);
854        for(int i = 0; i < minCount; ++i) {
855            final Class<?> fromParam = fromType.parameterType(i);
856            final Class<?>   toParam =   toType.parameterType(i);
857            // If method has an Object parameter, but call site had String, preserve it as Object. No need to narrow it
858            // artificially. Note that this is related to how CompiledFunction.matchesCallSite() works, specifically
859            // the fact that various reference types compare to equal (see "fnType.isEquivalentTo(csType)" there).
860            if (fromParam != toParam && !fromParam.isPrimitive() && !toParam.isPrimitive()) {
861                assert fromParam.isAssignableFrom(toParam);
862                toType = toType.changeParameterType(i, fromParam);
863            }
864        }
865        if (fromCount > toCount) {
866            toType = toType.appendParameterTypes(fromType.parameterList().subList(toCount, fromCount));
867        } else if (fromCount < toCount) {
868            toType = toType.dropParameterTypes(fromCount, toCount);
869        }
870
871        return addCode(lookup(fnInit, false).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
872    }
873
874    /**
875     * Returns the return type of a function specialization for particular parameter types.<br>
876     * <b>Be aware that the way this is implemented, it forces full materialization (compilation and installation) of
877     * code for that specialization.</b>
878     * @param callSiteType the parameter types at the call site. It must include the mandatory {@code callee} and
879     * {@code this} parameters, so it needs to start with at least {@code ScriptFunction.class} and
880     * {@code Object.class} class. Since the return type of the function is calculated from the code itself, it is
881     * irrelevant and should be set to {@code Object.class}.
882     * @param runtimeScope a current runtime scope. Can be null but when it's present it will be used as a source of
883     * current runtime values that can improve the compiler's type speculations (and thus reduce the need for later
884     * recompilations) if the specialization is not already present and thus needs to be freshly compiled.
885     * @return the return type of the function specialization.
886     */
887    public Class<?> getReturnType(final MethodType callSiteType, final ScriptObject runtimeScope) {
888        return getBest(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS).type().returnType();
889    }
890
891    @Override
892    synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden, final boolean linkLogicOkay) {
893        assert isValidCallSite(callSiteType) : callSiteType;
894
895        CompiledFunction existingBest = pickFunction(callSiteType, false);
896        if (existingBest == null) {
897            existingBest = pickFunction(callSiteType, true); // try vararg last
898        }
899        if (existingBest == null) {
900            existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, true), callSiteType);
901        }
902
903        assert existingBest != null;
904
905        //if the best one is an apply to call, it has to match the callsite exactly
906        //or we need to regenerate
907        if (existingBest.isApplyToCall()) {
908            final CompiledFunction best = lookupExactApplyToCall(callSiteType);
909            if (best != null) {
910                return best;
911            }
912
913            // special case: we had an apply to call, but we failed to make it fit.
914            // Try to generate a specialized one for this callsite. It may
915            // be another apply to call specialization, or it may not, but whatever
916            // it is, it is a specialization that is guaranteed to fit
917            existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, false), callSiteType);
918        }
919
920        return existingBest;
921    }
922
923    @Override
924    public boolean needsCallee() {
925        return getFunctionFlag(FunctionNode.NEEDS_CALLEE);
926    }
927
928    /**
929     * Returns the {@link FunctionNode} flags associated with this function data.
930     * @return the {@link FunctionNode} flags associated with this function data.
931     */
932    public int getFunctionFlags() {
933        return functionFlags;
934    }
935
936    @Override
937    MethodType getGenericType() {
938        // 2 is for (callee, this)
939        if (isVariableArity()) {
940            return MethodType.genericMethodType(2, true);
941        }
942        return MethodType.genericMethodType(2 + getArity());
943    }
944
945    /**
946     * Return the function node id.
947     * @return the function node id
948     */
949    public int getFunctionNodeId() {
950        return functionNodeId;
951    }
952
953    /**
954     * Get the source for the script
955     * @return source
956     */
957    public Source getSource() {
958        return source;
959    }
960
961    /**
962     * Return a script function data based on a function id, either this function if
963     * the id matches or a nested function based on functionId. This goes down into
964     * nested functions until all leaves are exhausted.
965     *
966     * @param functionId function id
967     * @return script function data or null if invalid id
968     */
969    public RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
970        if (functionId == functionNodeId) {
971            return this;
972        }
973        RecompilableScriptFunctionData data;
974
975        data = nestedFunctions == null ? null : nestedFunctions.get(functionId);
976        if (data != null) {
977            return data;
978        }
979        for (final RecompilableScriptFunctionData ndata : nestedFunctions.values()) {
980            data = ndata.getScriptFunctionData(functionId);
981            if (data != null) {
982                return data;
983            }
984        }
985        return null;
986    }
987
988    /**
989     * Check whether a certain name is a global symbol, i.e. only exists as defined
990     * in outermost scope and not shadowed by being parameter or assignment in inner
991     * scopes
992     *
993     * @param functionNode function node to check
994     * @param symbolName symbol name
995     * @return true if global symbol
996     */
997    public boolean isGlobalSymbol(final FunctionNode functionNode, final String symbolName) {
998        RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId());
999        assert data != null;
1000
1001        do {
1002            if (data.hasInternalSymbol(symbolName)) {
1003                return false;
1004            }
1005            data = data.getParent();
1006        } while(data != null);
1007
1008        return true;
1009    }
1010
1011    /**
1012     * Restores the {@link #getFunctionFlags()} flags to a function node. During on-demand compilation, we might need
1013     * to restore flags to a function node that was otherwise not subjected to a full compile pipeline (e.g. its parse
1014     * was skipped, or it's a nested function of a deserialized function.
1015     * @param lc current lexical context
1016     * @param fn the function node to restore flags onto
1017     * @return the transformed function node
1018     */
1019    public FunctionNode restoreFlags(final LexicalContext lc, final FunctionNode fn) {
1020        assert fn.getId() == functionNodeId;
1021        FunctionNode newFn = fn.setFlags(lc, functionFlags);
1022        // This compensates for missing markEval() in case the function contains an inner function
1023        // that contains eval(), that now we didn't discover since we skipped the inner function.
1024        if (newFn.hasNestedEval()) {
1025            assert newFn.hasScopeBlock();
1026            newFn = newFn.setBody(lc, newFn.getBody().setNeedsScope(null));
1027        }
1028        return newFn;
1029    }
1030
1031    // Make sure code does not contain a compiled function with the same signature as compiledFunction
1032    private boolean noDuplicateCode(final CompiledFunction compiledFunction) {
1033        for (final CompiledFunction cf : code) {
1034            if (cf.type().equals(compiledFunction.type())) {
1035                return false;
1036            }
1037        }
1038        return true;
1039    }
1040
1041    private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
1042        in.defaultReadObject();
1043        createLogger();
1044    }
1045
1046    private void createLogger() {
1047        log = initLogger(Context.getContextTrusted());
1048    }
1049}
1050