blob: 4d3df5093ed59136cc67ea259ef03d75f8521c51 [file] [log] [blame]
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.nashorn.internal.codegen.CompilationException;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.FunctionSignature;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
* A trampoline is a promise to compile a {@link ScriptFunction} later. It just looks like
* the call to the script function, but when invoked it will compile the script function
* (in a new compile unit) and invoke it
*/
public final class ScriptFunctionTrampolineImpl extends ScriptFunctionImpl {
private CodeInstaller<ScriptEnvironment> installer;
/** Function node to lazily recompile when trampoline is hit */
private FunctionNode functionNode;
/**
* Constructor
*
* @param installer opaque code installer from context
* @param functionNode function node to lazily compile when trampoline is hit
* @param data {@link ScriptFunctionData} for function
* @param scope scope
* @param allocator allocator
*/
public ScriptFunctionTrampolineImpl(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) {
super(null, data, scope, allocator);
this.installer = installer;
this.functionNode = functionNode;
data.setMethodHandles(makeTrampoline(), allocator);
}
private final MethodHandle makeTrampoline() {
final MethodType mt =
new FunctionSignature(
true,
functionNode.needsCallee(),
Type.OBJECT,
functionNode.getParameters().size()).
getMethodType();
return
MH.bindTo(
MH.asCollector(
findOwnMH(
"trampoline",
Object.class,
Object[].class),
Object[].class,
mt.parameterCount()),
this);
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findVirtual(MethodHandles.lookup(), ScriptFunctionTrampolineImpl.class, name, MH.type(rtype, types));
}
@Override
protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) {
//prevent trampoline recompilation cycle if a function is bound before use
compile();
return super.makeBoundFunction(data);
}
private MethodHandle compile() throws CompilationException {
final Compiler compiler = new Compiler(installer, functionNode);
compiler.compile();
final Class<?> clazz = compiler.install();
/* compute function signature for lazy method. this can be done first after compilation, as only then do we know
* the final state about callees, scopes and specialized parameter types */
final FunctionSignature signature = new FunctionSignature(true, functionNode.needsCallee(), Type.OBJECT, functionNode.getParameters().size());
final MethodType mt = signature.getMethodType();
MethodHandle mh = MH.findStatic(MethodHandles.publicLookup(), clazz, functionNode.getName(), mt);
if (functionNode.needsCallee()) {
mh = MH.bindTo(mh, this);
}
// now the invoker method looks like the one our superclass is expecting
resetInvoker(mh);
return mh;
}
@SuppressWarnings("unused")
private Object trampoline(final Object... args) throws CompilationException {
Compiler.LOG.info(">>> TRAMPOLINE: Hitting trampoline for '" + functionNode.getName() + "'");
MethodHandle mh = compile();
Compiler.LOG.info("<<< COMPILED TO: " + mh);
// spread the array to invididual args of the correct type
mh = MH.asSpreader(mh, Object[].class, mh.type().parameterCount());
try {
//invoke the real method the trampoline points to. this only happens once
return mh.invoke(args);
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
throw new RuntimeException(t);
}
}
}