1 package jdk.nashorn.internal.objects; |
|
2 |
|
3 import static jdk.nashorn.internal.lookup.Lookup.MH; |
|
4 |
|
5 import java.lang.invoke.MethodHandle; |
|
6 import java.lang.invoke.MethodHandles; |
|
7 import java.lang.invoke.MethodType; |
|
8 import jdk.nashorn.internal.codegen.CompilationException; |
|
9 import jdk.nashorn.internal.codegen.Compiler; |
|
10 import jdk.nashorn.internal.codegen.FunctionSignature; |
|
11 import jdk.nashorn.internal.codegen.types.Type; |
|
12 import jdk.nashorn.internal.ir.FunctionNode; |
|
13 import jdk.nashorn.internal.runtime.CodeInstaller; |
|
14 import jdk.nashorn.internal.runtime.Context; |
|
15 import jdk.nashorn.internal.runtime.ScriptEnvironment; |
|
16 import jdk.nashorn.internal.runtime.ScriptFunction; |
|
17 import jdk.nashorn.internal.runtime.ScriptFunctionData; |
|
18 import jdk.nashorn.internal.runtime.ScriptObject; |
|
19 |
|
20 /** |
|
21 * A trampoline is a promise to compile a {@link ScriptFunction} later. It just looks like |
|
22 * the call to the script function, but when invoked it will compile the script function |
|
23 * (in a new compile unit) and invoke it |
|
24 */ |
|
25 public final class ScriptFunctionTrampolineImpl extends ScriptFunctionImpl { |
|
26 |
|
27 private CodeInstaller<ScriptEnvironment> installer; |
|
28 |
|
29 /** Function node to lazily recompile when trampoline is hit */ |
|
30 private FunctionNode functionNode; |
|
31 |
|
32 /** |
|
33 * Constructor |
|
34 * |
|
35 * @param installer opaque code installer from context |
|
36 * @param functionNode function node to lazily compile when trampoline is hit |
|
37 * @param data {@link ScriptFunctionData} for function |
|
38 * @param scope scope |
|
39 * @param allocator allocator |
|
40 */ |
|
41 public ScriptFunctionTrampolineImpl(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) { |
|
42 super(null, data, scope, allocator); |
|
43 |
|
44 this.installer = installer; |
|
45 this.functionNode = functionNode; |
|
46 |
|
47 data.setMethodHandles(makeTrampoline(), allocator); |
|
48 } |
|
49 |
|
50 private final MethodHandle makeTrampoline() { |
|
51 final MethodType mt = |
|
52 new FunctionSignature( |
|
53 true, |
|
54 functionNode.needsCallee(), |
|
55 Type.OBJECT, |
|
56 functionNode.getParameters().size()). |
|
57 getMethodType(); |
|
58 |
|
59 return |
|
60 MH.bindTo( |
|
61 MH.asCollector( |
|
62 findOwnMH( |
|
63 "trampoline", |
|
64 Object.class, |
|
65 Object[].class), |
|
66 Object[].class, |
|
67 mt.parameterCount()), |
|
68 this); |
|
69 } |
|
70 |
|
71 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { |
|
72 return MH.findVirtual(MethodHandles.lookup(), ScriptFunctionTrampolineImpl.class, name, MH.type(rtype, types)); |
|
73 } |
|
74 |
|
75 @Override |
|
76 protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) { |
|
77 //prevent trampoline recompilation cycle if a function is bound before use |
|
78 compile(); |
|
79 return super.makeBoundFunction(data); |
|
80 } |
|
81 |
|
82 private MethodHandle compile() throws CompilationException { |
|
83 final Compiler compiler = new Compiler(installer, functionNode); |
|
84 |
|
85 compiler.compile(); |
|
86 |
|
87 final Class<?> clazz = compiler.install(); |
|
88 /* compute function signature for lazy method. this can be done first after compilation, as only then do we know |
|
89 * the final state about callees, scopes and specialized parameter types */ |
|
90 final FunctionSignature signature = new FunctionSignature(true, functionNode.needsCallee(), Type.OBJECT, functionNode.getParameters().size()); |
|
91 final MethodType mt = signature.getMethodType(); |
|
92 |
|
93 MethodHandle mh = MH.findStatic(MethodHandles.publicLookup(), clazz, functionNode.getName(), mt); |
|
94 if (functionNode.needsCallee()) { |
|
95 mh = MH.bindTo(mh, this); |
|
96 } |
|
97 |
|
98 // now the invoker method looks like the one our superclass is expecting |
|
99 resetInvoker(mh); |
|
100 |
|
101 return mh; |
|
102 } |
|
103 |
|
104 @SuppressWarnings("unused") |
|
105 private Object trampoline(final Object... args) throws CompilationException { |
|
106 Compiler.LOG.info(">>> TRAMPOLINE: Hitting trampoline for '" + functionNode.getName() + "'"); |
|
107 MethodHandle mh = compile(); |
|
108 |
|
109 Compiler.LOG.info("<<< COMPILED TO: " + mh); |
|
110 // spread the array to invididual args of the correct type |
|
111 mh = MH.asSpreader(mh, Object[].class, mh.type().parameterCount()); |
|
112 |
|
113 try { |
|
114 //invoke the real method the trampoline points to. this only happens once |
|
115 return mh.invoke(args); |
|
116 } catch (final RuntimeException | Error e) { |
|
117 throw e; |
|
118 } catch (final Throwable t) { |
|
119 throw new RuntimeException(t); |
|
120 } |
|
121 } |
|
122 } |
|