src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java

changeset 150
999cc1bf5520
parent 133
5759f600fcf7
equal deleted inserted replaced
135:fbbdef940138 150:999cc1bf5520
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 }

mercurial