nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java
changeset 16535 791a15cd7701
parent 16416 bcebd3fdefc9
parent 16534 6993b3541b28
child 16536 c3a8125548f0
equal deleted inserted replaced
16416:bcebd3fdefc9 16535:791a15cd7701
     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 }