blob: 19fe6bd758c49cd2420adc0a983804d56b78925f [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime.linker;
import java.lang.invoke.MethodHandle;
/**
* A tuple of method handles, one for dynamically getting function as a property of an object, and another for invoking
* a function with a given signature. A typical use for this class is to create method handles that can be used to
* efficiently recreate dynamic invocation of a method on an object from Java code. E.g. if you would have a call site
* in JavaScript that says
* <pre>
* value = obj.toJSON(key)
* </pre>
* then the efficient way to code an exact equivalent of this in Java would be:
* <pre>
* private static final InvokeByName TO_JSON = new InvokeByName("toJSON", Object.class, Object.class, Object.class);
* ...
* final Object toJSONFn = TO_JSON.getGetter().invokeExact(obj);
* value = TO_JSON.getInvoker().invokeExact(toJSONFn, obj, key);
* </pre>
* In practice, you can have stronger type assumptions if it makes sense for your code, just remember that you must use
* the same parameter types as the formal types of the arguments for {@code invokeExact} to work:
* <pre>
* private static final InvokeByName TO_JSON = new InvokeByName("toJSON", ScriptObject.class, Object.class, Object.class);
* ...
* final ScriptObject sobj = (ScriptObject)obj;
* final Object toJSONFn = TO_JSON.getGetter().invokeExact(sobj);
* if(toJSONFn instanceof ScriptFunction) {
* value = TO_JSON.getInvoker().invokeExact(toJSONFn, sobj, key);
* }
* </pre>
* Note that in general you will not want to reuse a single instance of this class for implementing more than one call
* site - that would increase the risk of them becoming megamorphic or otherwise hard to optimize by the JVM. Even if
* you dynamically invoke a function with the same name from multiple places in your code, it is advisable to create a
* separate instance of this class for every place.
*/
public final class InvokeByName {
private final String name;
private final MethodHandle getter;
private final MethodHandle invoker;
/**
* Creates a getter and invoker for a function of the given name that takes no arguments and has a return type of
* {@code Object}.
* @param name the name of the function
* @param targetClass the target class it is invoked on; e.g. {@code Object} or {@code ScriptObject}.
*/
public InvokeByName(final String name, final Class<?> targetClass) {
this(name, targetClass, Object.class);
}
/**
* Creates a getter and invoker for a function of the given name with given parameter types and a given return type
* of {@code Object}.
* @param name the name of the function
* @param targetClass the target class it is invoked on; e.g. {@code Object} or {@code ScriptObject}.
* @param rtype the return type of the function
* @param ptypes the parameter types of the function.
*/
public InvokeByName(final String name, final Class<?> targetClass, final Class<?> rtype, final Class<?>... ptypes) {
this.name = name;
getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, Object.class, targetClass);
final Class<?>[] finalPtypes;
final int plength = ptypes.length;
if(plength == 0) {
finalPtypes = new Class<?>[] { Object.class, targetClass };
} else {
finalPtypes = new Class<?>[plength + 2];
finalPtypes[0] = Object.class;
finalPtypes[1] = targetClass;
System.arraycopy(ptypes, 0, finalPtypes, 2, plength);
}
invoker = Bootstrap.createDynamicInvoker("dyn:call", rtype, finalPtypes);
}
/**
* Returns the name of the function retrieved through this invoker.
* @return the name of the function retrieved through this invoker.
*/
public String getName() {
return name;
}
/**
* Returns the property getter that can be invoked on an object to retrieve the function object that will be
* subsequently invoked by the invoker returned by {@link #getInvoker()}.
* @return the property getter method handle for the function.
*/
public MethodHandle getGetter() {
return getter;
}
/**
* Returns the function invoker that can be used to invoke a function object previously retrieved by invoking
* the getter retrieved with {@link #getGetter()} on the target object.
* @return the invoker method handle for the function.
*/
public MethodHandle getInvoker() {
return invoker;
}
}