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 
  26 package jdk.nashorn.internal.runtime.linker;
  27 
  28 import java.lang.invoke.MethodHandle;
  29 
  30 /**
  31  * A tuple of method handles, one for dynamically getting function as a property of an object, and another for invoking
  32  * a function with a given signature. A typical use for this class is to create method handles that can be used to
  33  * efficiently recreate dynamic invocation of a method on an object from Java code. E.g. if you would have a call site
  34  * in JavaScript that says
  35  * <pre>
  36  *     value = obj.toJSON(key)
  37  * </pre>
  38  * then the efficient way to code an exact equivalent of this in Java would be:
  39  * <pre>
  40  *     private static final InvokeByName TO_JSON = new InvokeByName("toJSON", Object.class, Object.class, Object.class);
  41  *     ...
  42  *     final Object toJSONFn = TO_JSON.getGetter().invokeExact(obj);
  43  *     value = TO_JSON.getInvoker().invokeExact(toJSONFn, obj, key);
  44  * </pre>
  45  * In practice, you can have stronger type assumptions if it makes sense for your code, just remember that you must use
  46  * the same parameter types as the formal types of the arguments for {@code invokeExact} to work:
  47  * <pre>
  48  *     private static final InvokeByName TO_JSON = new InvokeByName("toJSON", ScriptObject.class, Object.class, Object.class);
  49  *     ...
  50  *     final ScriptObject sobj = (ScriptObject)obj;
  51  *     final Object toJSONFn = TO_JSON.getGetter().invokeExact(sobj);
  52  *     if(toJSONFn instanceof ScriptFunction) {
  53  *         value = TO_JSON.getInvoker().invokeExact(toJSONFn, sobj, key);
  54  *     }
  55  * </pre>
  56  * Note that in general you will not want to reuse a single instance of this class for implementing more than one call
  57  * site - that would increase the risk of them becoming megamorphic or otherwise hard to optimize by the JVM. Even if
  58  * you dynamically invoke a function with the same name from multiple places in your code, it is advisable to create a
  59  * separate instance of this class for every place.
  60  */
  61 public final class InvokeByName {
  62     private final String name;
  63     private final MethodHandle getter;
  64     private final MethodHandle invoker;
  65 
  66     /**
  67      * Creates a getter and invoker for a function of the given name that takes no arguments and has a return type of
  68      * {@code Object}.
  69      * @param name the name of the function
  70      * @param targetClass the target class it is invoked on; e.g. {@code Object} or {@code ScriptObject}.
  71      */
  72     public InvokeByName(final String name, final Class<?> targetClass) {
  73         this(name, targetClass, Object.class);
  74     }
  75 
  76     /**
  77      * Creates a getter and invoker for a function of the given name with given parameter types and a given return type
  78      * of {@code Object}.
  79      * @param name the name of the function
  80      * @param targetClass the target class it is invoked on; e.g. {@code Object} or {@code ScriptObject}.
  81      * @param rtype the return type of the function
  82      * @param ptypes the parameter types of the function.
  83      */
  84     public InvokeByName(final String name, final Class<?> targetClass, final Class<?> rtype, final Class<?>... ptypes) {
  85         this.name = name;
  86         getter  = Bootstrap.createDynamicInvoker(name, NashornCallSiteDescriptor.GET_METHOD_PROPERTY, Object.class, targetClass);
  87 
  88         final Class<?>[] finalPtypes;
  89         final int plength = ptypes.length;
  90         if(plength == 0) {
  91             finalPtypes = new Class<?>[] { Object.class, targetClass };
  92         } else {
  93             finalPtypes = new Class<?>[plength + 2];
  94             finalPtypes[0] = Object.class;
  95             finalPtypes[1] = targetClass;
  96             System.arraycopy(ptypes, 0, finalPtypes, 2, plength);
  97         }
  98         invoker = Bootstrap.createDynamicCallInvoker(rtype, finalPtypes);
  99     }
 100 
 101     /**
 102      * Returns the name of the function retrieved through this invoker.
 103      * @return the name of the function retrieved through this invoker.
 104      */
 105     public String getName() {
 106         return name;
 107     }
 108 
 109     /**
 110      * Returns the property getter that can be invoked on an object to retrieve the function object that will be
 111      * subsequently invoked by the invoker returned by {@link #getInvoker()}.
 112      * @return the property getter method handle for the function.
 113      */
 114     public MethodHandle getGetter() {
 115         return getter;
 116     }
 117 
 118     /**
 119      * Returns the function invoker that can be used to invoke a function object previously retrieved by invoking
 120      * the getter retrieved with {@link #getGetter()} on the target object.
 121      * @return the invoker method handle for the function.
 122      */
 123     public MethodHandle getInvoker() {
 124         return invoker;
 125     }
 126 }