1 /*
2  * Copyright (c) 2010, 2013, 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 package jdk.nashorn.internal.runtime;
26 
27 import static jdk.nashorn.internal.lookup.Lookup.MH;
28 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
29 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
30 
31 import java.lang.invoke.CallSite;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.lang.invoke.MutableCallSite;
36 import java.lang.invoke.SwitchPoint;
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.TreeMap;
44 import java.util.function.Supplier;
45 import java.util.logging.Level;
46 import jdk.internal.dynalink.linker.GuardedInvocation;
47 import jdk.nashorn.internal.codegen.Compiler;
48 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
49 import jdk.nashorn.internal.codegen.TypeMap;
50 import jdk.nashorn.internal.codegen.types.ArrayType;
51 import jdk.nashorn.internal.codegen.types.Type;
52 import jdk.nashorn.internal.ir.FunctionNode;
53 import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
54 import jdk.nashorn.internal.runtime.events.RecompilationEvent;
55 import jdk.nashorn.internal.runtime.linker.Bootstrap;
56 import jdk.nashorn.internal.runtime.logging.DebugLogger;
57 
58 /**
59  * An version of a JavaScript function, native or JavaScript.
60  * Supports lazily generating a constructor version of the invocation.
61  */
62 final class CompiledFunction {
63 
64     private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
65     private static final MethodHandle RELINK_COMPOSABLE_INVOKER = findOwnMH("relinkComposableInvoker", void.class, CallSite.class, CompiledFunction.class, boolean.class);
66     private static final MethodHandle HANDLE_REWRITE_EXCEPTION = findOwnMH("handleRewriteException", MethodHandle.class, CompiledFunction.class, OptimismInfo.class, RewriteException.class);
67     private static final MethodHandle RESTOF_INVOKER = MethodHandles.exactInvoker(MethodType.methodType(Object.class, RewriteException.class));
68 
69     private final DebugLogger log;
70 
71     static final Collection<CompiledFunction> NO_FUNCTIONS = Collections.emptySet();
72 
73     /**
74      * The method type may be more specific than the invoker, if. e.g.
75      * the invoker is guarded, and a guard with a generic object only
76      * fallback, while the target is more specific, we still need the
77      * more specific type for sorting
78      */
79     private MethodHandle invoker;
80     private MethodHandle constructor;
81     private OptimismInfo optimismInfo;
82     private final int flags; // from FunctionNode
83     private final MethodType callSiteType;
84 
85     private final Specialization specialization;
86 
CompiledFunction(final MethodHandle invoker)87     CompiledFunction(final MethodHandle invoker) {
88         this(invoker, null, null);
89     }
90 
createBuiltInConstructor(final MethodHandle invoker, final Specialization specialization)91     static CompiledFunction createBuiltInConstructor(final MethodHandle invoker, final Specialization specialization) {
92         return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)), specialization);
93     }
94 
CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final Specialization specialization)95     CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final Specialization specialization) {
96         this(invoker, constructor, 0, null, specialization, DebugLogger.DISABLED_LOGGER);
97     }
98 
CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final Specialization specialization, final DebugLogger log)99     CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final Specialization specialization, final DebugLogger log) {
100         this.specialization = specialization;
101         if (specialization != null && specialization.isOptimistic()) {
102             /*
103              * An optimistic builtin with isOptimistic=true works like any optimistic generated function, i.e. it
104              * can throw unwarranted optimism exceptions. As native functions trivially can't have parts of them
105              * regenerated as "restOf" methods, this only works if the methods are atomic/functional in their behavior
106              * and doesn't modify state before an UOE can be thrown. If they aren't, we can reexecute a wider version
107              * of the same builtin in a recompilation handler for FinalScriptFunctionData. There are several
108              * candidate methods in Native* that would benefit from this, but I haven't had time to implement any
109              * of them currently. In order to fit in with the relinking framework, the current thinking is
110              * that the methods still take a program point to fit in with other optimistic functions, but
111              * it is set to "first", which is the beginning of the method. The relinker can tell the difference
112              * between builtin and JavaScript functions. This might change. TODO
113              */
114             this.invoker = MH.insertArguments(invoker, invoker.type().parameterCount() - 1, UnwarrantedOptimismException.FIRST_PROGRAM_POINT);
115             throw new AssertionError("Optimistic (UnwarrantedOptimismException throwing) builtin functions are currently not in use");
116         }
117         this.invoker = invoker;
118         this.constructor = constructor;
119         this.flags = flags;
120         this.callSiteType = callSiteType;
121         this.log = log;
122     }
123 
CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData, final Map<Integer, Type> invalidatedProgramPoints, final MethodType callSiteType, final int flags)124     CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData,
125             final Map<Integer, Type> invalidatedProgramPoints, final MethodType callSiteType, final int flags) {
126         this(invoker, null, flags, callSiteType, null, functionData.getLogger());
127         if ((flags & FunctionNode.IS_DEOPTIMIZABLE) != 0) {
128             optimismInfo = new OptimismInfo(functionData, invalidatedProgramPoints);
129         } else {
130             optimismInfo = null;
131         }
132     }
133 
createBuiltInConstructor(final MethodHandle invoker)134     static CompiledFunction createBuiltInConstructor(final MethodHandle invoker) {
135         return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)), null);
136     }
137 
isSpecialization()138     boolean isSpecialization() {
139         return specialization != null;
140     }
141 
hasLinkLogic()142     boolean hasLinkLogic() {
143         return getLinkLogicClass() != null;
144     }
145 
getLinkLogicClass()146     Class<? extends LinkLogic> getLinkLogicClass() {
147         if (isSpecialization()) {
148             final Class<? extends LinkLogic> linkLogicClass = specialization.getLinkLogicClass();
149             assert !LinkLogic.isEmpty(linkLogicClass) : "empty link logic classes should have been removed by nasgen";
150             return linkLogicClass;
151         }
152         return null;
153     }
154 
getFlags()155     int getFlags() {
156         return flags;
157     }
158 
159     /**
160      * An optimistic specialization is one that can throw UnwarrantedOptimismException.
161      * This is allowed for native methods, as long as they are functional, i.e. don't change
162      * any state between entering and throwing the UOE. Then we can re-execute a wider version
163      * of the method in the continuation. Rest-of method generation for optimistic builtins is
164      * of course not possible, but this approach works and fits into the same relinking
165      * framework
166      *
167      * @return true if optimistic builtin
168      */
isOptimistic()169     boolean isOptimistic() {
170         return isSpecialization() ? specialization.isOptimistic() : false;
171     }
172 
isApplyToCall()173     boolean isApplyToCall() {
174         return (flags & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0;
175     }
176 
isVarArg()177     boolean isVarArg() {
178         return isVarArgsType(invoker.type());
179     }
180 
181     @Override
toString()182     public String toString() {
183         final StringBuilder sb = new StringBuilder();
184         final Class<? extends LinkLogic> linkLogicClass = getLinkLogicClass();
185 
186         sb.append("[invokerType=").
187             append(invoker.type()).
188             append(" ctor=").
189             append(constructor).
190             append(" weight=").
191             append(weight()).
192             append(" linkLogic=").
193             append(linkLogicClass != null ? linkLogicClass.getSimpleName() : "none");
194 
195         return sb.toString();
196     }
197 
needsCallee()198     boolean needsCallee() {
199         return ScriptFunctionData.needsCallee(invoker);
200     }
201 
202     /**
203      * Returns an invoker method handle for this function. Note that the handle is safely composable in
204      * the sense that you can compose it with other handles using any combinators even if you can't affect call site
205      * invalidation. If this compiled function is non-optimistic, then it returns the same value as
206      * {@link #getInvokerOrConstructor(boolean)}. However, if the function is optimistic, then this handle will
207      * incur an overhead as it will add an intermediate internal call site that can relink itself when the function
208      * needs to regenerate its code to always point at the latest generated code version.
209      * @return a guaranteed composable invoker method handle for this function.
210      */
createComposableInvoker()211     MethodHandle createComposableInvoker() {
212         return createComposableInvoker(false);
213     }
214 
215     /**
216      * Returns an invoker method handle for this function when invoked as a constructor. Note that the handle should be
217      * considered non-composable in the sense that you can only compose it with other handles using any combinators if
218      * you can ensure that the composition is guarded by {@link #getOptimisticAssumptionsSwitchPoint()} if it's
219      * non-null, and that you can relink the call site it is set into as a target if the switch point is invalidated. In
220      * all other cases, use {@link #createComposableConstructor()}.
221      * @return a direct constructor method handle for this function.
222      */
getConstructor()223     private MethodHandle getConstructor() {
224         if (constructor == null) {
225             constructor = createConstructorFromInvoker(createInvokerForPessimisticCaller());
226         }
227 
228         return constructor;
229     }
230 
231     /**
232      * Creates a version of the invoker intended for a pessimistic caller (return type is Object, no caller optimistic
233      * program point available).
234      * @return a version of the invoker intended for a pessimistic caller.
235      */
createInvokerForPessimisticCaller()236     private MethodHandle createInvokerForPessimisticCaller() {
237         return createInvoker(Object.class, INVALID_PROGRAM_POINT);
238     }
239 
240     /**
241      * Compose a constructor from an invoker.
242      *
243      * @param invoker         invoker
244      * @return the composed constructor
245      */
createConstructorFromInvoker(final MethodHandle invoker)246     private static MethodHandle createConstructorFromInvoker(final MethodHandle invoker) {
247         final boolean needsCallee = ScriptFunctionData.needsCallee(invoker);
248         // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
249         // "this" in the first argument position is what allows the elegant folded composition of
250         // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
251         // always returns Object.
252         final MethodHandle swapped = needsCallee ? swapCalleeAndThis(invoker) : invoker;
253 
254         final MethodHandle returnsObject = MH.asType(swapped, swapped.type().changeReturnType(Object.class));
255 
256         final MethodType ctorType = returnsObject.type();
257 
258         // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually
259         // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it.
260         // (this, [callee, ]args...) => ([callee, ]args...)
261         final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray();
262 
263         // Fold constructor into newFilter that replaces the return value from the constructor with the originally
264         // allocated value when the originally allocated value is a JS primitive (String, Boolean, Number).
265         // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...)
266         final MethodHandle filtered = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), returnsObject);
267 
268         // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
269         if (needsCallee) {
270             // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and
271             // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...),
272             // or...
273             return MH.foldArguments(filtered, ScriptFunction.ALLOCATE);
274         }
275 
276         // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
277         // (this, args...) filter (callee) => (callee, args...)
278         return MH.filterArguments(filtered, 0, ScriptFunction.ALLOCATE);
279     }
280 
281     /**
282      * Permutes the parameters in the method handle from {@code (callee, this, ...)} to {@code (this, callee, ...)}.
283      * Used when creating a constructor handle.
284      * @param mh a method handle with order of arguments {@code (callee, this, ...)}
285      * @return a method handle with order of arguments {@code (this, callee, ...)}
286      */
swapCalleeAndThis(final MethodHandle mh)287     private static MethodHandle swapCalleeAndThis(final MethodHandle mh) {
288         final MethodType type = mh.type();
289         assert type.parameterType(0) == ScriptFunction.class : type;
290         assert type.parameterType(1) == Object.class : type;
291         final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
292         final int[] reorder = new int[type.parameterCount()];
293         reorder[0] = 1;
294         assert reorder[1] == 0;
295         for (int i = 2; i < reorder.length; ++i) {
296             reorder[i] = i;
297         }
298         return MethodHandles.permuteArguments(mh, newType, reorder);
299     }
300 
301     /**
302      * Returns an invoker method handle for this function when invoked as a constructor. Note that the handle is safely
303      * composable in the sense that you can compose it with other handles using any combinators even if you can't affect
304      * call site invalidation. If this compiled function is non-optimistic, then it returns the same value as
305      * {@link #getConstructor()}. However, if the function is optimistic, then this handle will incur an overhead as it
306      * will add an intermediate internal call site that can relink itself when the function needs to regenerate its code
307      * to always point at the latest generated code version.
308      * @return a guaranteed composable constructor method handle for this function.
309      */
createComposableConstructor()310     MethodHandle createComposableConstructor() {
311         return createComposableInvoker(true);
312     }
313 
hasConstructor()314     boolean hasConstructor() {
315         return constructor != null;
316     }
317 
type()318     MethodType type() {
319         return invoker.type();
320     }
321 
weight()322     int weight() {
323         return weight(type());
324     }
325 
weight(final MethodType type)326     private static int weight(final MethodType type) {
327         if (isVarArgsType(type)) {
328             return Integer.MAX_VALUE; //if there is a varargs it should be the heavist and last fallback
329         }
330 
331         int weight = Type.typeFor(type.returnType()).getWeight();
332         for (int i = 0 ; i < type.parameterCount() ; i++) {
333             final Class<?> paramType = type.parameterType(i);
334             final int pweight = Type.typeFor(paramType).getWeight() * 2; //params are more important than call types as return values are always specialized
335             weight += pweight;
336         }
337 
338         weight += type.parameterCount(); //more params outweigh few parameters
339 
340         return weight;
341     }
342 
isVarArgsType(final MethodType type)343     static boolean isVarArgsType(final MethodType type) {
344         assert type.parameterCount() >= 1 : type;
345         return type.parameterType(type.parameterCount() - 1) == Object[].class;
346     }
347 
moreGenericThan(final MethodType mt0, final MethodType mt1)348     static boolean moreGenericThan(final MethodType mt0, final MethodType mt1) {
349         return weight(mt0) > weight(mt1);
350     }
351 
betterThanFinal(final CompiledFunction other, final MethodType callSiteMethodType)352     boolean betterThanFinal(final CompiledFunction other, final MethodType callSiteMethodType) {
353         // Prefer anything over nothing, as we can't compile new versions.
354         if (other == null) {
355             return true;
356         }
357         return betterThanFinal(this, other, callSiteMethodType);
358     }
359 
betterThanFinal(final CompiledFunction cf, final CompiledFunction other, final MethodType callSiteMethodType)360     private static boolean betterThanFinal(final CompiledFunction cf, final CompiledFunction other, final MethodType callSiteMethodType) {
361         final MethodType thisMethodType  = cf.type();
362         final MethodType otherMethodType = other.type();
363         final int thisParamCount = getParamCount(thisMethodType);
364         final int otherParamCount = getParamCount(otherMethodType);
365         final int callSiteRawParamCount = getParamCount(callSiteMethodType);
366         final boolean csVarArg = callSiteRawParamCount == Integer.MAX_VALUE;
367         // Subtract 1 for callee for non-vararg call sites
368         final int callSiteParamCount = csVarArg ? callSiteRawParamCount : callSiteRawParamCount - 1;
369 
370         // Prefer the function that discards less parameters
371         final int thisDiscardsParams = Math.max(callSiteParamCount - thisParamCount, 0);
372         final int otherDiscardsParams = Math.max(callSiteParamCount - otherParamCount, 0);
373         if(thisDiscardsParams < otherDiscardsParams) {
374             return true;
375         }
376         if(thisDiscardsParams > otherDiscardsParams) {
377             return false;
378         }
379 
380         final boolean thisVarArg = thisParamCount == Integer.MAX_VALUE;
381         final boolean otherVarArg = otherParamCount == Integer.MAX_VALUE;
382         if(!(thisVarArg && otherVarArg && csVarArg)) {
383             // At least one of them isn't vararg
384             final Type[] thisType = toTypeWithoutCallee(thisMethodType, 0); // Never has callee
385             final Type[] otherType = toTypeWithoutCallee(otherMethodType, 0); // Never has callee
386             final Type[] callSiteType = toTypeWithoutCallee(callSiteMethodType, 1); // Always has callee
387 
388             int narrowWeightDelta = 0;
389             int widenWeightDelta = 0;
390             final int minParamsCount = Math.min(Math.min(thisParamCount, otherParamCount), callSiteParamCount);
391             for(int i = 0; i < minParamsCount; ++i) {
392                 final int callSiteParamWeight = getParamType(i, callSiteType, csVarArg).getWeight();
393                 // Delta is negative for narrowing, positive for widening
394                 final int thisParamWeightDelta = getParamType(i, thisType, thisVarArg).getWeight() - callSiteParamWeight;
395                 final int otherParamWeightDelta = getParamType(i, otherType, otherVarArg).getWeight() - callSiteParamWeight;
396                 // Only count absolute values of narrowings
397                 narrowWeightDelta += Math.max(-thisParamWeightDelta, 0) - Math.max(-otherParamWeightDelta, 0);
398                 // Only count absolute values of widenings
399                 widenWeightDelta += Math.max(thisParamWeightDelta, 0) - Math.max(otherParamWeightDelta, 0);
400             }
401 
402             // If both functions accept more arguments than what is passed at the call site, account for ability
403             // to receive Undefined un-narrowed in the remaining arguments.
404             if(!thisVarArg) {
405                 for(int i = callSiteParamCount; i < thisParamCount; ++i) {
406                     narrowWeightDelta += Math.max(Type.OBJECT.getWeight() - thisType[i].getWeight(), 0);
407                 }
408             }
409             if(!otherVarArg) {
410                 for(int i = callSiteParamCount; i < otherParamCount; ++i) {
411                     narrowWeightDelta -= Math.max(Type.OBJECT.getWeight() - otherType[i].getWeight(), 0);
412                 }
413             }
414 
415             // Prefer function that narrows less
416             if(narrowWeightDelta < 0) {
417                 return true;
418             }
419             if(narrowWeightDelta > 0) {
420                 return false;
421             }
422 
423             // Prefer function that widens less
424             if(widenWeightDelta < 0) {
425                 return true;
426             }
427             if(widenWeightDelta > 0) {
428                 return false;
429             }
430         }
431 
432         // Prefer the function that exactly matches the arity of the call site.
433         if(thisParamCount == callSiteParamCount && otherParamCount != callSiteParamCount) {
434             return true;
435         }
436         if(thisParamCount != callSiteParamCount && otherParamCount == callSiteParamCount) {
437             return false;
438         }
439 
440         // Otherwise, neither function matches arity exactly. We also know that at this point, they both can receive
441         // more arguments than call site, otherwise we would've already chosen the one that discards less parameters.
442         // Note that variable arity methods are preferred, as they actually match the call site arity better, since they
443         // really have arbitrary arity.
444         if(thisVarArg) {
445             if(!otherVarArg) {
446                 return true; //
447             }
448         } else if(otherVarArg) {
449             return false;
450         }
451 
452         // Neither is variable arity; chose the one that has less extra parameters.
453         final int fnParamDelta = thisParamCount - otherParamCount;
454         if(fnParamDelta < 0) {
455             return true;
456         }
457         if(fnParamDelta > 0) {
458             return false;
459         }
460 
461         final int callSiteRetWeight = Type.typeFor(callSiteMethodType.returnType()).getWeight();
462         // Delta is negative for narrower return type, positive for wider return type
463         final int thisRetWeightDelta = Type.typeFor(thisMethodType.returnType()).getWeight() - callSiteRetWeight;
464         final int otherRetWeightDelta = Type.typeFor(otherMethodType.returnType()).getWeight() - callSiteRetWeight;
465 
466         // Prefer function that returns a less wide return type
467         final int widenRetDelta = Math.max(thisRetWeightDelta, 0) - Math.max(otherRetWeightDelta, 0);
468         if(widenRetDelta < 0) {
469             return true;
470         }
471         if(widenRetDelta > 0) {
472             return false;
473         }
474 
475         // Prefer function that returns a less narrow return type
476         final int narrowRetDelta = Math.max(-thisRetWeightDelta, 0) - Math.max(-otherRetWeightDelta, 0);
477         if(narrowRetDelta < 0) {
478             return true;
479         }
480         if(narrowRetDelta > 0) {
481             return false;
482         }
483 
484         //if they are equal, pick the specialized one first
485         if (cf.isSpecialization() != other.isSpecialization()) {
486             return cf.isSpecialization(); //always pick the specialized version if we can
487         }
488 
489         if (cf.isSpecialization() && other.isSpecialization()) {
490             return cf.getLinkLogicClass() != null; //pick link logic specialization above generic specializations
491         }
492 
493         // Signatures are identical
494         throw new AssertionError(thisMethodType + " identically applicable to " + otherMethodType + " for " + callSiteMethodType);
495     }
496 
toTypeWithoutCallee(final MethodType type, final int thisIndex)497     private static Type[] toTypeWithoutCallee(final MethodType type, final int thisIndex) {
498         final int paramCount = type.parameterCount();
499         final Type[] t = new Type[paramCount - thisIndex];
500         for(int i = thisIndex; i < paramCount; ++i) {
501             t[i - thisIndex] = Type.typeFor(type.parameterType(i));
502         }
503         return t;
504     }
505 
getParamType(final int i, final Type[] paramTypes, final boolean isVarArg)506     private static Type getParamType(final int i, final Type[] paramTypes, final boolean isVarArg) {
507         final int fixParamCount = paramTypes.length - (isVarArg ? 1 : 0);
508         if(i < fixParamCount) {
509             return paramTypes[i];
510         }
511         assert isVarArg;
512         return ((ArrayType)paramTypes[paramTypes.length - 1]).getElementType();
513     }
514 
matchesCallSite(final MethodType other, final boolean pickVarArg)515     boolean matchesCallSite(final MethodType other, final boolean pickVarArg) {
516         if (other.equals(this.callSiteType)) {
517             return true;
518         }
519         final MethodType type  = type();
520         final int fnParamCount = getParamCount(type);
521         final boolean isVarArg = fnParamCount == Integer.MAX_VALUE;
522         if (isVarArg) {
523             return pickVarArg;
524         }
525 
526         final int csParamCount = getParamCount(other);
527         final boolean csIsVarArg = csParamCount == Integer.MAX_VALUE;
528         if (csIsVarArg && isApplyToCall()) {
529             return false; // apply2call function must be called with exact number of parameters
530         }
531         final int thisThisIndex = needsCallee() ? 1 : 0; // Index of "this" parameter in this function's type
532 
533         final int fnParamCountNoCallee = fnParamCount - thisThisIndex;
534         final int minParams = Math.min(csParamCount - 1, fnParamCountNoCallee); // callSiteType always has callee, so subtract 1
535         // We must match all incoming parameters, including "this". "this" will usually be Object, but there
536         // are exceptions, e.g. when calling functions with primitive "this" in strict mode or through call/apply.
537         for(int i = 0; i < minParams; ++i) {
538             final Type fnType = Type.typeFor(type.parameterType(i + thisThisIndex));
539             final Type csType = csIsVarArg ? Type.OBJECT : Type.typeFor(other.parameterType(i + 1));
540             if(!fnType.isEquivalentTo(csType)) {
541                 return false;
542             }
543         }
544 
545         // Must match any undefined parameters to Object type.
546         for(int i = minParams; i < fnParamCountNoCallee; ++i) {
547             if(!Type.typeFor(type.parameterType(i + thisThisIndex)).isEquivalentTo(Type.OBJECT)) {
548                 return false;
549             }
550         }
551 
552         return true;
553     }
554 
getParamCount(final MethodType type)555     private static int getParamCount(final MethodType type) {
556         final int paramCount = type.parameterCount();
557         return type.parameterType(paramCount - 1).isArray() ? Integer.MAX_VALUE : paramCount;
558     }
559 
canBeDeoptimized()560     private boolean canBeDeoptimized() {
561         return optimismInfo != null;
562     }
563 
createComposableInvoker(final boolean isConstructor)564     private MethodHandle createComposableInvoker(final boolean isConstructor) {
565         final MethodHandle handle = getInvokerOrConstructor(isConstructor);
566 
567         // If compiled function is not optimistic, it can't ever change its invoker/constructor, so just return them
568         // directly.
569         if(!canBeDeoptimized()) {
570             return handle;
571         }
572 
573         // Otherwise, we need a new level of indirection; need to introduce a mutable call site that can relink itself
574         // to the compiled function's changed target whenever the optimistic assumptions are invalidated.
575         final CallSite cs = new MutableCallSite(handle.type());
576         relinkComposableInvoker(cs, this, isConstructor);
577         return cs.dynamicInvoker();
578     }
579 
580     private static class HandleAndAssumptions {
581         final MethodHandle handle;
582         final SwitchPoint assumptions;
583 
HandleAndAssumptions(final MethodHandle handle, final SwitchPoint assumptions)584         HandleAndAssumptions(final MethodHandle handle, final SwitchPoint assumptions) {
585             this.handle = handle;
586             this.assumptions = assumptions;
587         }
588 
createInvocation()589         GuardedInvocation createInvocation() {
590             return new GuardedInvocation(handle, assumptions);
591         }
592     }
593 
594     /**
595      * Returns a pair of an invocation created with a passed-in supplier and a non-invalidated switch point for
596      * optimistic assumptions (or null for the switch point if the function can not be deoptimized). While the method
597      * makes a best effort to return a non-invalidated switch point (compensating for possible deoptimizing
598      * recompilation happening on another thread) it is still possible that by the time this method returns the
599      * switchpoint has been invalidated by a {@code RewriteException} triggered on another thread for this function.
600      * This is not a problem, though, as these switch points are always used to produce call sites that fall back to
601      * relinking when they are invalidated, and in this case the execution will end up here again. What this method
602      * basically does is minimize such busy-loop relinking while the function is being recompiled on a different thread.
603      * @param invocationSupplier the supplier that constructs the actual invocation method handle; should use the
604      * {@code CompiledFunction} method itself in some capacity.
605      * @return a tuple object containing the method handle as created by the supplier and an optimistic assumptions
606      * switch point that is guaranteed to not have been invalidated before the call to this method (or null if the
607      * function can't be further deoptimized).
608      */
getValidOptimisticInvocation(final Supplier<MethodHandle> invocationSupplier)609     private synchronized HandleAndAssumptions getValidOptimisticInvocation(final Supplier<MethodHandle> invocationSupplier) {
610         for(;;) {
611             final MethodHandle handle = invocationSupplier.get();
612             final SwitchPoint assumptions = canBeDeoptimized() ? optimismInfo.optimisticAssumptions : null;
613             if(assumptions != null && assumptions.hasBeenInvalidated()) {
614                 // We can be in a situation where one thread is in the middle of a deoptimizing compilation when we hit
615                 // this and thus, it has invalidated the old switch point, but hasn't created the new one yet. Note that
616                 // the behavior of invalidating the old switch point before recompilation, and only creating the new one
617                 // after recompilation is by design. If we didn't wait here for the recompilation to complete, we would
618                 // be busy looping through the fallback path of the invalidated switch point, relinking the call site
619                 // again with the same invalidated switch point, invoking the fallback, etc. stealing CPU cycles from
620                 // the recompilation task we're dependent on. This can still happen if the switch point gets invalidated
621                 // after we grabbed it here, in which case we'll indeed do one busy relink immediately.
622                 try {
623                     wait();
624                 } catch (final InterruptedException e) {
625                     // Intentionally ignored. There's nothing meaningful we can do if we're interrupted
626                 }
627             } else {
628                 return new HandleAndAssumptions(handle, assumptions);
629             }
630         }
631     }
632 
relinkComposableInvoker(final CallSite cs, final CompiledFunction inv, final boolean constructor)633     private static void relinkComposableInvoker(final CallSite cs, final CompiledFunction inv, final boolean constructor) {
634         final HandleAndAssumptions handleAndAssumptions = inv.getValidOptimisticInvocation(new Supplier<MethodHandle>() {
635             @Override
636             public MethodHandle get() {
637                 return inv.getInvokerOrConstructor(constructor);
638             }
639         });
640         final MethodHandle handle = handleAndAssumptions.handle;
641         final SwitchPoint assumptions = handleAndAssumptions.assumptions;
642         final MethodHandle target;
643         if(assumptions == null) {
644             target = handle;
645         } else {
646             final MethodHandle relink = MethodHandles.insertArguments(RELINK_COMPOSABLE_INVOKER, 0, cs, inv, constructor);
647             target = assumptions.guardWithTest(handle, MethodHandles.foldArguments(cs.dynamicInvoker(), relink));
648         }
649         cs.setTarget(target.asType(cs.type()));
650     }
651 
getInvokerOrConstructor(final boolean selectCtor)652     private MethodHandle getInvokerOrConstructor(final boolean selectCtor) {
653         return selectCtor ? getConstructor() : createInvokerForPessimisticCaller();
654     }
655 
656     /**
657      * Returns a guarded invocation for this function when not invoked as a constructor. The guarded invocation has no
658      * guard but it potentially has an optimistic assumptions switch point. As such, it will probably not be used as a
659      * final guarded invocation, but rather as a holder for an invocation handle and switch point to be decomposed and
660      * reassembled into a different final invocation by the user of this method. Any recompositions should take care to
661      * continue to use the switch point. If that is not possible, use {@link #createComposableInvoker()} instead.
662      * @return a guarded invocation for an ordinary (non-constructor) invocation of this function.
663      */
createFunctionInvocation(final Class<?> callSiteReturnType, final int callerProgramPoint)664     GuardedInvocation createFunctionInvocation(final Class<?> callSiteReturnType, final int callerProgramPoint) {
665         return getValidOptimisticInvocation(new Supplier<MethodHandle>() {
666             @Override
667             public MethodHandle get() {
668                 return createInvoker(callSiteReturnType, callerProgramPoint);
669             }
670         }).createInvocation();
671     }
672 
673     /**
674      * Returns a guarded invocation for this function when invoked as a constructor. The guarded invocation has no guard
675      * but it potentially has an optimistic assumptions switch point. As such, it will probably not be used as a final
676      * guarded invocation, but rather as a holder for an invocation handle and switch point to be decomposed and
677      * reassembled into a different final invocation by the user of this method. Any recompositions should take care to
678      * continue to use the switch point. If that is not possible, use {@link #createComposableConstructor()} instead.
679      * @return a guarded invocation for invocation of this function as a constructor.
680      */
681     GuardedInvocation createConstructorInvocation() {
682         return getValidOptimisticInvocation(new Supplier<MethodHandle>() {
683             @Override
684             public MethodHandle get() {
685                 return getConstructor();
686             }
687         }).createInvocation();
688     }
689 
690     private MethodHandle createInvoker(final Class<?> callSiteReturnType, final int callerProgramPoint) {
691         final boolean isOptimistic = canBeDeoptimized();
692         MethodHandle handleRewriteException = isOptimistic ? createRewriteExceptionHandler() : null;
693 
694         MethodHandle inv = invoker;
695         if(isValid(callerProgramPoint)) {
696             inv = OptimisticReturnFilters.filterOptimisticReturnValue(inv, callSiteReturnType, callerProgramPoint);
697             inv = changeReturnType(inv, callSiteReturnType);
698             if(callSiteReturnType.isPrimitive() && handleRewriteException != null) {
699                 // because handleRewriteException always returns Object
700                 handleRewriteException = OptimisticReturnFilters.filterOptimisticReturnValue(handleRewriteException,
701                         callSiteReturnType, callerProgramPoint);
702             }
703         } else if(isOptimistic) {
704             // Required so that rewrite exception has the same return type. It'd be okay to do it even if we weren't
705             // optimistic, but it isn't necessary as the linker upstream will eventually convert the return type.
706             inv = changeReturnType(inv, callSiteReturnType);
707         }
708 
709         if(isOptimistic) {
710             assert handleRewriteException != null;
711             final MethodHandle typedHandleRewriteException = changeReturnType(handleRewriteException, inv.type().returnType());
712             return MH.catchException(inv, RewriteException.class, typedHandleRewriteException);
713         }
714         return inv;
715     }
716 
717     private MethodHandle createRewriteExceptionHandler() {
718         return MH.foldArguments(RESTOF_INVOKER, MH.insertArguments(HANDLE_REWRITE_EXCEPTION, 0, this, optimismInfo));
719     }
720 
721     private static MethodHandle changeReturnType(final MethodHandle mh, final Class<?> newReturnType) {
722         return Bootstrap.getLinkerServices().asType(mh, mh.type().changeReturnType(newReturnType));
723     }
724 
725     @SuppressWarnings("unused")
726     private static MethodHandle handleRewriteException(final CompiledFunction function, final OptimismInfo oldOptimismInfo, final RewriteException re) {
727         return function.handleRewriteException(oldOptimismInfo, re);
728     }
729 
730     /**
731      * Debug function for printing out all invalidated program points and their
732      * invalidation mapping to next type
733      * @param ipp
734      * @return string describing the ipp map
735      */
736     private static List<String> toStringInvalidations(final Map<Integer, Type> ipp) {
737         if (ipp == null) {
738             return Collections.emptyList();
739         }
740 
741         final List<String> list = new ArrayList<>();
742 
743         for (final Iterator<Map.Entry<Integer, Type>> iter = ipp.entrySet().iterator(); iter.hasNext(); ) {
744             final Map.Entry<Integer, Type> entry = iter.next();
745             final char bct = entry.getValue().getBytecodeStackType();
746             final String type;
747 
748             switch (entry.getValue().getBytecodeStackType()) {
749             case 'A':
750                 type = "object";
751                 break;
752             case 'I':
753                 type = "int";
754                 break;
755             case 'J':
756                 type = "long";
757                 break;
758             case 'D':
759                 type = "double";
760                 break;
761             default:
762                 type = String.valueOf(bct);
763                 break;
764             }
765 
766             final StringBuilder sb = new StringBuilder();
767             sb.append('[').
768                     append("program point: ").
769                     append(entry.getKey()).
770                     append(" -> ").
771                     append(type).
772                     append(']');
773 
774             list.add(sb.toString());
775         }
776 
777         return list;
778     }
779 
780     private void logRecompile(final String reason, final FunctionNode fn, final MethodType type, final Map<Integer, Type> ipp) {
781         if (log.isEnabled()) {
782             log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", type);
783             log.indent();
784             for (final String str : toStringInvalidations(ipp)) {
785                 log.fine(str);
786             }
787             log.unindent();
788         }
789     }
790 
791     /**
792      * Handles a {@link RewriteException} raised during the execution of this function by recompiling (if needed) the
793      * function with an optimistic assumption invalidated at the program point indicated by the exception, and then
794      * executing a rest-of method to complete the execution with the deoptimized version.
795      * @param oldOptInfo the optimism info of this function. We must store it explicitly as a bound argument in the
796      * method handle, otherwise it could be null for handling a rewrite exception in an outer invocation of a recursive
797      * function when recursive invocations of the function have completely deoptimized it.
798      * @param re the rewrite exception that was raised
799      * @return the method handle for the rest-of method, for folding composition.
800      */
801     private synchronized MethodHandle handleRewriteException(final OptimismInfo oldOptInfo, final RewriteException re) {
802         if (log.isEnabled()) {
803             log.info(
804                     new RecompilationEvent(
805                         Level.INFO,
806                         re,
807                         re.getReturnValueNonDestructive()),
808                     "caught RewriteException ",
809                     re.getMessageShort());
810             log.indent();
811         }
812 
813         final MethodType type = type();
814 
815         // Compiler needs a call site type as its input, which always has a callee parameter, so we must add it if
816         // this function doesn't have a callee parameter.
817         final MethodType ct = type.parameterType(0) == ScriptFunction.class ?
818                 type :
819                 type.insertParameterTypes(0, ScriptFunction.class);
820         final OptimismInfo currentOptInfo = optimismInfo;
821         final boolean shouldRecompile = currentOptInfo != null && currentOptInfo.requestRecompile(re);
822 
823         // Effective optimism info, for subsequent use. We'll normally try to use the current (latest) one, but if it
824         // isn't available, we'll use the old one bound into the call site.
825         final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo;
826         FunctionNode fn = effectiveOptInfo.reparse();
827         final boolean cached = fn.isCached();
828         final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of
829 
830         if (!shouldRecompile) {
831             // It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already
832             // recompiled a deoptimized version for an inner invocation.
833             // We still need to do the rest of from the beginning
834             logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
835             return restOfHandle(effectiveOptInfo, compiler.compile(fn, cached ? CompilationPhases.COMPILE_CACHED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null);
836         }
837 
838         logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
839         fn = compiler.compile(fn, cached ? CompilationPhases.RECOMPILE_CACHED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE);
840         log.fine("Reusable IR generated");
841 
842         // compile the rest of the function, and install it
843         log.info("Generating and installing bytecode from reusable IR...");
844         logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
845         final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL);
846 
847         if (effectiveOptInfo.data.usePersistentCodeCache()) {
848             final RecompilableScriptFunctionData data = effectiveOptInfo.data;
849             final int functionNodeId = data.getFunctionNodeId();
850             final TypeMap typeMap = data.typeMap(ct);
851             final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
852             final String cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
853             compiler.persistClassInfo(cacheKey, normalFn);
854         }
855 
856         final boolean canBeDeoptimized = normalFn.canBeDeoptimized();
857 
858         if (log.isEnabled()) {
859             log.unindent();
860             log.info("Done.");
861 
862             log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ") ", canBeDeoptimized ? "can still be deoptimized." : " is completely deoptimized.");
863             log.finest("Looking up invoker...");
864         }
865 
866         final MethodHandle newInvoker = effectiveOptInfo.data.lookup(fn);
867         invoker     = newInvoker.asType(type.changeReturnType(newInvoker.type().returnType()));
868         constructor = null; // Will be regenerated when needed
869 
870         log.info("Done: ", invoker);
871         final MethodHandle restOf = restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL_RESTOF), canBeDeoptimized);
872 
873         // Note that we only adjust the switch point after we set the invoker/constructor. This is important.
874         if (canBeDeoptimized) {
875             effectiveOptInfo.newOptimisticAssumptions(); // Otherwise, set a new switch point.
876         } else {
877             optimismInfo = null; // If we got to a point where we no longer have optimistic assumptions, let the optimism info go.
878         }
879         notifyAll();
880 
881         return restOf;
882     }
883 
884     private MethodHandle restOfHandle(final OptimismInfo info, final FunctionNode restOfFunction, final boolean canBeDeoptimized) {
885         assert info != null;
886         assert restOfFunction.getCompileUnit().getUnitClassName().contains("restOf");
887         final MethodHandle restOf =
888                 changeReturnType(
889                         info.data.lookupCodeMethod(
890                                 restOfFunction.getCompileUnit().getCode(),
891                                 MH.type(restOfFunction.getReturnType().getTypeClass(),
892                                         RewriteException.class)),
893                         Object.class);
894 
895         if (!canBeDeoptimized) {
896             return restOf;
897         }
898 
899         // If rest-of is itself optimistic, we must make sure that we can repeat a deoptimization if it, too hits an exception.
900         return MH.catchException(restOf, RewriteException.class, createRewriteExceptionHandler());
901 
902     }
903 
904     private static class OptimismInfo {
905         // TODO: this is pointing to its owning ScriptFunctionData. Re-evaluate if that's okay.
906         private final RecompilableScriptFunctionData data;
907         private final Map<Integer, Type> invalidatedProgramPoints;
908         private SwitchPoint optimisticAssumptions;
909         private final DebugLogger log;
910 
911         OptimismInfo(final RecompilableScriptFunctionData data, final Map<Integer, Type> invalidatedProgramPoints) {
912             this.data = data;
913             this.log  = data.getLogger();
914             this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new TreeMap<Integer, Type>() : invalidatedProgramPoints;
915             newOptimisticAssumptions();
916         }
917 
918         private void newOptimisticAssumptions() {
919             optimisticAssumptions = new SwitchPoint();
920         }
921 
922         boolean requestRecompile(final RewriteException e) {
923             final Type retType            = e.getReturnType();
924             final Type previousFailedType = invalidatedProgramPoints.put(e.getProgramPoint(), retType);
925 
926             if (previousFailedType != null && !previousFailedType.narrowerThan(retType)) {
927                 final StackTraceElement[] stack      = e.getStackTrace();
928                 final String              functionId = stack.length == 0 ?
929                         data.getName() :
930                         stack[0].getClassName() + "." + stack[0].getMethodName();
931 
932                 log.info("RewriteException for an already invalidated program point ", e.getProgramPoint(), " in ", functionId, ". This is okay for a recursive function invocation, but a bug otherwise.");
933 
934                 return false;
935             }
936 
937             SwitchPoint.invalidateAll(new SwitchPoint[] { optimisticAssumptions });
938 
939             return true;
940         }
941 
942         Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final RewriteException e) {
943             return data.getCompiler(fn, actualCallSiteType, e.getRuntimeScope(), invalidatedProgramPoints, getEntryPoints(e));
944         }
945 
946         private static int[] getEntryPoints(final RewriteException e) {
947             final int[] prevEntryPoints = e.getPreviousContinuationEntryPoints();
948             final int[] entryPoints;
949             if (prevEntryPoints == null) {
950                 entryPoints = new int[1];
951             } else {
952                 final int l = prevEntryPoints.length;
953                 entryPoints = new int[l + 1];
954                 System.arraycopy(prevEntryPoints, 0, entryPoints, 1, l);
955             }
956             entryPoints[0] = e.getProgramPoint();
957             return entryPoints;
958         }
959 
960         FunctionNode reparse() {
961             return data.reparse();
962         }
963     }
964 
965     @SuppressWarnings("unused")
966     private static Object newFilter(final Object result, final Object allocation) {
967         return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
968     }
969 
970     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
971         return MH.findStatic(MethodHandles.lookup(), CompiledFunction.class, name, MH.type(rtype, types));
972     }
973 }
974