/* * 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(name, NashornCallSiteDescriptor.GET_METHOD_PROPERTY, 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.createDynamicCallInvoker(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; } }