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