();
+
+ /**
+ * Invalidate all invokedynamic
call sites everywhere.
+ *
+ * When this method returns, every invokedynamic
instruction
+ * will invoke its bootstrap method on next call.
+ *
+ * It is unspecified whether call sites already known to the Java
+ * code will continue to be associated with invokedynamic
+ * instructions. If any call site is still so associated, its
+ * {@link CallSite#getTarget()} method is guaranteed to return null
+ * the invalidation operation completes.
+ *
+ * Invalidation operations are likely to be slow. Use them sparingly.
+ */
+ public static
+ Object invalidateAll() {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkPermission(new LinkagePermission("invalidateAll"));
+ }
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /**
+ * Invalidate all invokedynamic
call sites associated
+ * with the given class.
+ * (These are exactly those sites which report the given class
+ * via the {@link CallSite#callerClass()} method.)
+ *
+ * When this method returns, every matching invokedynamic
+ * instruction will invoke its bootstrap method on next call.
+ *
+ * For additional semantics of call site invalidation,
+ * see {@link #invalidateAll()}.
+ */
+ public static
+ Object invalidateCallerClass(Class> callerClass) {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkPermission(new LinkagePermission("invalidateAll", callerClass));
+ }
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ private static Object doNotBootstrap(CallSite site, Object... arguments) {
+ throw new UnsupportedOperationException("call site must not have null target: "+site);
+ }
+
+ private static final MethodHandle DO_NOT_BOOTSTRAP =
+ MethodHandles.Lookup.IMPL_LOOKUP.findStatic(Linkage.class, "doNotBootstrap",
+ OLD_BOOTSTRAP_METHOD_TYPE);
+
+ // Up-call from the JVM. Obsolete. FIXME: Delete from VM then from here.
+ static
+ MethodHandle findBootstrapMethod(Class callerClass, Class searchBootstrapClass) {
+ return DO_NOT_BOOTSTRAP;
+ }
+}
diff --git a/src/share/classes/java/dyn/LinkagePermission.java b/src/share/classes/java/dyn/LinkagePermission.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ea86f8b55539d2277038bd366891f4fda54d917
--- /dev/null
+++ b/src/share/classes/java/dyn/LinkagePermission.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+import java.security.*;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+/**
+ * This class is for runtime permissions. A RuntimePermission
+ * contains a name (also referred to as a "target name") but
+ * no actions list; you either have the named permission
+ * or you don't.
+ *
+ *
+ * The target name is the name of the runtime permission (see below). The
+ * naming convention follows the hierarchical property naming convention.
+ * Also, an asterisk
+ * may appear at the end of the name, following a ".", or by itself, to
+ * signify a wildcard match. For example: "loadLibrary.*" or "*" is valid,
+ * "*loadLibrary" or "a*b" is not valid.
+ *
+ * The following table lists all the possible RuntimePermission target names,
+ * and for each provides a description of what the permission allows
+ * and a discussion of the risks of granting code the permission.
+ *
+ *
+ *
+ *
+ * Permission Target Name
+ * What the Permission Allows
+ * Risks of Allowing this Permission
+ *
+ *
+ *
+ * registerBootstrapMethod.{class name}
+ * Specifying a bootstrap method for invokedynamic, within a class of the given name
+ * An attacker could attempt to attach a bootstrap method to a class which
+ * has just been loaded, thus gaining control of its invokedynamic calls.
+ *
+ *
+ *
+ * invalidateAll
+ * Force the relinking of invokedynamic call sites everywhere.
+ * This could allow an attacker to slow down the system, or perhaps surface timing bugs in a dynamic language implementations, by forcing redundant relinking operations.
+ *
+ *
+ *
+ *
+ * invalidateCallerClass.{class name}
+ * Force the relinking of invokedynamic call sites in the given class.
+ * See {@code invalidateAll}.
+ *
+ *
+ *
+ * @see java.security.BasicPermission
+ * @see java.lang.SecurityManager
+ *
+ * @author John Rose, JSR 292 EG
+ */
+
+public final class LinkagePermission extends BasicPermission {
+ /**
+ * Create a new LinkagePermission with the given name.
+ * The name is the symbolic name of the LinkagePermission, such as
+ * "registerBootstrapMethod", "invalidateClass.*", etc. An asterisk
+ * may appear at the end of the name, following a ".", or by itself, to
+ * signify a wildcard match.
+ *
+ * @param name the name of the LinkagePermission
+ */
+ public LinkagePermission(String name) {
+ super(name);
+ }
+
+ /**
+ * Create a new LinkagePermission with the given name on the given class.
+ * Equivalent to {@code LinkagePermission(name+"."+clazz.getName())}.
+ *
+ * @param name the name of the LinkagePermission
+ * @param clazz the class affected by the permission
+ */
+ public LinkagePermission(String name, Class> clazz) {
+ super(name + "." + clazz.getName());
+ }
+}
diff --git a/src/share/classes/java/dyn/MethodHandle.java b/src/share/classes/java/dyn/MethodHandle.java
new file mode 100644
index 0000000000000000000000000000000000000000..688a9d3fd706d955827e4002f7563589ae4480df
--- /dev/null
+++ b/src/share/classes/java/dyn/MethodHandle.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+//import sun.dyn.*;
+
+import sun.dyn.Access;
+import sun.dyn.MethodHandleImpl;
+
+/**
+ * A method handle is a typed reference to the entry point of a method.
+ *
+ * Method handles are strongly typed according to signature.
+ * They are not distinguished by method name or enclosing class.
+ * A method handle must be invoked under a signature which exactly matches
+ * the method handle's own type.
+ *
+ * Every method handle confesses its type via the type
accessor.
+ * The structure of this type is a series of classes, one of which is
+ * the return type of the method (or void.class
if none).
+ *
+ * Every method handle appears as an object containing a method named
+ * invoke
, whose signature exactly matches
+ * the method handle's type.
+ * A normal Java method call (using the invokevirtual
instruction)
+ * can invoke this method from Java source code (if language support is present).
+ *
+ * Every call to a method handle specifies an intended method type,
+ * which must exactly match the type of the method handle.
+ * (The type is specified in the invokevirtual
instruction,
+ * via a {@code CONSTANT_NameAndType} constant pool entry.)
+ * The call looks within the receiver object for a method
+ * named invoke
of the intended method type.
+ * The call fails with a {@link WrongMethodTypeException}
+ * if the method does not exist, even if there is an invoke
+ * method of a closely similar signature.
+ *
+ * A method handle is an unrestricted capability to call a method.
+ * A method handle can be formed on a non-public method by a class
+ * that has access to that method; the resulting handle can be used
+ * in any place by any caller who receives a reference to it. Thus, access
+ * checking is performed when the method handle is created, not
+ * (as in reflection) every time it is called. Handles to non-public
+ * methods, or in non-public classes, should generally be kept secret.
+ * They should not be passed to untrusted code.
+ *
+ * Bytecode in an extended JVM can directly call a method handle's
+ * invoke
from an invokevirtual
instruction.
+ * The receiver class type must be MethodHandle
and the method name
+ * must be invoke
. The signature of the invocation
+ * (after resolving symbolic type names) must exactly match the method type
+ * of the target method.
+ *
+ * Bytecode in an extended JVM can directly obtain a method handle
+ * for any accessible method from a ldc
instruction
+ * which refers to a CONSTANT_Methodref
or
+ * CONSTANT_InterfaceMethodref
constant pool entry.
+ *
+ * All JVMs can also use a reflective API called MethodHandles
+ * for creating and calling method handles.
+ *
+ * A method reference may refer either to a static or non-static method.
+ * In the non-static case, the method handle type includes an explicit
+ * receiver argument, prepended before any other arguments.
+ * In the method handle's type, the initial receiver argument is typed
+ * according to the class under which the method was initially requested.
+ * (E.g., if a non-static method handle is obtained via ldc
,
+ * the type of the receiver is the class named in the constant pool entry.)
+ *
+ * When a method handle to a virtual method is invoked, the method is
+ * always looked up in the receiver (that is, the first argument).
+ *
+ * A non-virtual method handles to a specific virtual method implementation
+ * can also be created. These do not perform virtual lookup based on
+ * receiver type. Such a method handle simulates the effect of
+ * an invokespecial
instruction to the same method.
+ *
+ * @see MethodType
+ * @see MethodHandles
+ * @author John Rose, JSR 292 EG
+ */
+public abstract class MethodHandle
+ // Note: This is an implementation inheritance hack, and will be removed
+ // with a JVM change which moves the required hidden state onto this class.
+ extends MethodHandleImpl
+{
+ // interface MethodHandle>
+ // { T type(); public R invoke(A...); }
+
+ final private MethodType type;
+
+ /**
+ * Report the type of this method handle.
+ * Every invocation of this method handle must exactly match this type.
+ * @return the method handle type
+ */
+ public MethodType type() {
+ return type;
+ }
+
+ /**
+ * The constructor for MethodHandle may only be called by privileged code.
+ * Subclasses may be in other packages, but must possess
+ * a token which they obtained from MH with a security check.
+ * @param token non-null object which proves access permission
+ * @param type type (permanently assigned) of the new method handle
+ */
+ protected MethodHandle(Access token, MethodType type) {
+ super(token);
+ this.type = type;
+ }
+}
diff --git a/src/share/classes/java/dyn/MethodHandles.java b/src/share/classes/java/dyn/MethodHandles.java
new file mode 100644
index 0000000000000000000000000000000000000000..e7a6febb2c7e84fec94823e1d4dbc1514a6e6230
--- /dev/null
+++ b/src/share/classes/java/dyn/MethodHandles.java
@@ -0,0 +1,1092 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+import java.lang.reflect.Constructor;
+import sun.dyn.Access;
+import sun.dyn.MemberName;
+import sun.dyn.MethodHandleImpl;
+import sun.dyn.util.VerifyAccess;
+import sun.dyn.util.Wrapper;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import sun.dyn.Invokers;
+import sun.dyn.MethodTypeImpl;
+import sun.reflect.Reflection;
+import static sun.dyn.MemberName.newIllegalArgumentException;
+import static sun.dyn.MemberName.newNoAccessException;
+
+/**
+ * Fundamental operations and utilities for MethodHandle.
+ *
+ * API Note: The matching of method types in this API cannot
+ * be completely checked by Java's generic type system for three reasons:
+ *
+ * Method types range over all possible arities,
+ * from no arguments to an arbitrary number of arguments.
+ * Generics are not variadic, and so cannot represent this.
+ * Method types can specify arguments of primitive types,
+ * which Java generic types cannot range over.
+ * Method types can optionally specify varargs (ellipsis).
+ *
+ * @author John Rose, JSR 292 EG
+ */
+public class MethodHandles {
+
+ private MethodHandles() { } // do not instantiate
+
+ private static final Access IMPL_TOKEN = Access.getToken();
+ private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
+ static { MethodHandleImpl.initStatics(); }
+ // See IMPL_LOOKUP below.
+
+ //// Method handle creation from ordinary methods.
+
+ public static Lookup lookup() {
+ return new Lookup();
+ }
+
+ /**
+ * A factory object for creating method handles, when the creation
+ * requires access checking. Method handles do not perform
+ * access checks when they are called; this is a major difference
+ * from reflective {@link Method}, which performs access checking
+ * against every caller, on every call. Method handle access
+ * restrictions are enforced when a method handle is created.
+ * The caller class against which those restrictions are enforced
+ * is known as the "lookup class". {@link Lookup} embodies an
+ * authenticated lookup class, and can be used to create any number
+ * of access-checked method handles, all checked against a single
+ * lookup class.
+ *
+ * A class which needs to create method handles will call
+ * {@code MethodHandles.lookup()} to create a factory for itself.
+ * It may then use this factory to create method handles on
+ * all of its methods, including private ones.
+ * It may also delegate the lookup (e.g., to a metaobject protocol)
+ * by passing the {@code Lookup} object to other code.
+ * If this other code creates method handles, they will be access
+ * checked against the original lookup class, and not with any higher
+ * privileges.
+ *
+ * Note that access checks only apply to named and reflected methods.
+ * Other method handle creation methods, such as {@link #convertArguments},
+ * do not require any access checks, and can be done independently
+ * of any lookup class.
+ *
+ * A note about error conditions: A lookup can fail, because
+ * the containing class is not accessible to the lookup class, or
+ * because the desired class member is missing, or because the
+ * desired class member is not accessible to the lookup class.
+ * It can also fail if a security manager is installed and refuses
+ * access. In any of these cases, an exception will be
+ * thrown from the attempted lookup.
+ * In general, the conditions under which a method handle may be
+ * created for a method M are exactly as restrictive as the conditions
+ * under which the lookup class could have compiled a call to M.
+ */
+ public static final
+ class Lookup {
+ private final Class> lookupClass;
+
+ /** Which class is performing the lookup? It is this class against
+ * which checks are performed for visibility and access permissions.
+ *
+ * This value is null if and only if this lookup is {@link #PUBLIC_LOOKUP}.
+ */
+ public Class> lookupClass() {
+ return lookupClass;
+ }
+
+ /** Embody the current class (the lookupClass) as a lookup class
+ * for method handle creation.
+ * Must be called by from a method in this package,
+ * which in turn is called by a method not in this package.
+ * Also, don't make it private, lest javac interpose
+ * an access$N method.
+ */
+ Lookup() {
+ Class caller = getCallerClassAtEntryPoint();
+ // make sure we haven't accidentally picked up this class:
+ checkUnprivilegedlookupClass(caller);
+ this.lookupClass = caller;
+ }
+
+ private Lookup(Class> lookupClass) {
+ this.lookupClass = lookupClass;
+ }
+
+ /** Version of lookup which is trusted minimally.
+ * It can only be used to create method handles to
+ * publicly accessible members.
+ */
+ public static final Lookup PUBLIC_LOOKUP = new Lookup(null);
+
+ /** Package-private version of lookup which is trusted. */
+ static final Lookup IMPL_LOOKUP = new Lookup(Access.class);
+ static { MethodHandleImpl.initLookup(IMPL_TOKEN, IMPL_LOOKUP); }
+
+ private static void checkUnprivilegedlookupClass(Class> lookupClass) {
+ if (lookupClass == null ||
+ lookupClass == Access.class ||
+ lookupClass.getName().startsWith("java.dyn."))
+ throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
+ }
+
+ @Override
+ public String toString() {
+ if (lookupClass == null)
+ return "public";
+ return lookupClass.getName();
+ }
+
+ // call this from an entry point method in Lookup with extraFrames=0.
+ private static Class> getCallerClassAtEntryPoint() {
+ final int CALLER_DEPTH = 4;
+ // 0: Reflection.getCC, 1: getCallerClassAtEntryPoint,
+ // 2: Lookup., 3: MethodHandles.*, 4: caller
+ // Note: This should be the only use of getCallerClass in this file.
+ return Reflection.getCallerClass(CALLER_DEPTH);
+ }
+
+ /**
+ * Produce a method handle for a static method.
+ * The type of the method handle will be that of the method.
+ * The method and all its argument types must be accessible to the lookup class.
+ * If the method's class has not yet been initialized, that is done
+ * immediately, before the method handle is returned.
+ * @param defc the class from which the method is accessed
+ * @param name the name of the method
+ * @param type the type of the method
+ * @return the desired method handle
+ * @exception SecurityException TBD
+ * @exception NoAccessException if the method does not exist or access checking fails
+ */
+ public
+ MethodHandle findStatic(Class> defc, String name, MethodType type) throws NoAccessException {
+ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
+ checkStatic(true, method, lookupClass);
+ //throw NoSuchMethodException
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
+ }
+
+ /**
+ * Produce a method handle for a virtual method.
+ * The type of the method handle will be that of the method,
+ * with the receiver type ({@code defc}) prepended.
+ * The method and all its argument types must be accessible to the lookup class.
+ *
+ * When called, the handle will treat the first argument as a receiver
+ * and dispatch on the receiver's type to determine which method
+ * implementation to enter.
+ * (The dispatching action is identical with that performed by an
+ * {@code invokevirtual} or {@code invokeinterface} instruction.)
+ * @param defc the class or interface from which the method is accessed
+ * @param name the name of the method
+ * @param type the type of the method, with the receiver argument omitted
+ * @return the desired method handle
+ * @exception SecurityException TBD
+ * @exception NoAccessException if the method does not exist or access checking fails
+ */
+ public MethodHandle findVirtual(Class> defc, String name, MethodType type) throws NoAccessException {
+ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), true, lookupClass);
+ checkStatic(false, method, lookupClass);
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
+ }
+
+ /**
+ * Produce an early-bound method handle for a virtual method,
+ * or a handle for a constructor, as if called from an {@code invokespecial}
+ * instruction from {@code caller}.
+ * The type of the method handle will be that of the method or constructor,
+ * with a suitably restricted receiver type (such as {@code caller}) prepended.
+ * The method or constructor and all its argument types must be accessible
+ * to the caller.
+ *
+ * When called, the handle will treat the first argument as a receiver,
+ * but will not dispatch on the receiver's type.
+ * (This direct invocation action is identical with that performed by an
+ * {@code invokespecial} instruction.)
+ *
+ * If the explicitly specified caller class is not identical with the
+ * lookup class, a security check TBD is performed.
+ * @param defc the class or interface from which the method is accessed
+ * @param name the name of the method, or "" for a constructor
+ * @param type the type of the method, with the receiver argument omitted
+ * @param specialCaller the proposed calling class to perform the {@code invokespecial}
+ * @return the desired method handle
+ * @exception SecurityException TBD
+ * @exception NoAccessException if the method does not exist or access checking fails
+ */
+ public MethodHandle findSpecial(Class> defc, String name, MethodType type,
+ Class> specialCaller) throws NoAccessException {
+ checkSpecialCaller(specialCaller, lookupClass);
+ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), false, specialCaller);
+ checkStatic(false, method, lookupClass);
+ if (name.equals("")) {
+ if (defc != specialCaller)
+ throw newNoAccessException("constructor must be local to lookup class", method, lookupClass);
+ } else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) {
+ throw newNoAccessException("method must be in a superclass of lookup class", method, lookupClass);
+ }
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, specialCaller);
+ }
+
+ /**
+ * Produce an early-bound method handle for a non-static method.
+ * The receiver must have a supertype {@code defc} in which a method
+ * of the given name and type is accessible to the lookup class.
+ * The method and all its argument types must be accessible to the lookup class.
+ * The type of the method handle will be that of the method.
+ * The given receiver will be bound into the method handle.
+ *
+ * Equivalent to the following expression:
+ *
+ * {@link #insertArgument}({@link #findVirtual}(defc, name, type), receiver)
+ *
+ * @param receiver the object from which the method is accessed
+ * @param name the name of the method
+ * @param type the type of the method, with the receiver argument omitted
+ * @return the desired method handle
+ * @exception SecurityException TBD
+ * @exception NoAccessException if the method does not exist or access checking fails
+ */
+ public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException {
+ Class extends Object> rcvc = receiver.getClass(); // may get NPE
+ MemberName reference = new MemberName(rcvc, name, type);
+ MemberName method = IMPL_NAMES.resolveOrFail(reference, true, lookupClass);
+ checkStatic(false, method, lookupClass);
+ MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
+ MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
+ if (bmh == null)
+ throw newNoAccessException(method, lookupClass);
+ return bmh;
+ }
+
+ /**
+ * Make a direct method handle to m , if the lookup class has permission.
+ * If m is non-static, the receiver argument is treated as an initial argument.
+ * If m is virtual, overriding is respected on every call.
+ * Unlike the Core Reflection API, exceptions are not wrapped.
+ * The type of the method handle will be that of the method,
+ * with the receiver type prepended (but only if it is non-static).
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * If m is not public, do not share the resulting handle with untrusted parties.
+ * @param m the reflected method
+ * @return a method handle which can invoke the reflected method
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflect(Method m) throws NoAccessException {
+ return unreflectImpl(new MemberName(m), m.isAccessible(), true, lookupClass);
+ }
+
+ /**
+ * Produce a method handle for a reflected method.
+ * It will bypass checks for overriding methods on the receiver,
+ * as if by the {@code invokespecial} instruction.
+ * The type of the method handle will be that of the method,
+ * with the receiver type prepended.
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class,
+ * as if {@code invokespecial} instruction were being linked.
+ * @param m the reflected method
+ * @return a method handle which can invoke the reflected method
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflectSpecial(Method m, Class> specialCaller) throws NoAccessException {
+ checkSpecialCaller(specialCaller, lookupClass);
+ MemberName mname = new MemberName(m);
+ checkStatic(false, mname, lookupClass);
+ return unreflectImpl(mname, m.isAccessible(), false, specialCaller);
+ }
+
+ /**
+ * Produce a method handle for a reflected constructor.
+ * The type of the method handle will be that of the constructor.
+ * The method handle will perform a {@code newInstance} operation,
+ * creating a new instance of the constructor's class on the
+ * arguments passed to the method handle.
+ *
+ * If the constructor's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class,
+ * as if {@code invokespecial} instruction were being linked.
+ * @param ctor the reflected constructor
+ * @return a method handle which can invoke the reflected constructor
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflectConstructor(Constructor ctor) throws NoAccessException {
+ MemberName m = new MemberName(ctor);
+ return unreflectImpl(m, ctor.isAccessible(), false, lookupClass);
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle giving read access to a reflected field.
+ * The type of the method handle will have a return type of the field's
+ * value type. Its sole argument will be the field's containing class
+ * (but only if it is non-static).
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * @param f the reflected field
+ * @return a method handle which can load values from the reflected field
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflectGetter(Field f) throws NoAccessException {
+ return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), false, lookupClass);
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle giving write access to a reflected field.
+ * The type of the method handle will have a void return type.
+ * Its last argument will be the field's value type.
+ * Its other argument will be the field's containing class
+ * (but only if it is non-static).
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * @param f the reflected field
+ * @return a method handle which can store values into the reflected field
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflectSetter(Field f) throws NoAccessException {
+ return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), true, lookupClass);
+ }
+
+ }
+
+ static /*must not be public*/
+ MethodHandle findStaticFrom(Class> lookupClass,
+ Class> defc, String name, MethodType type) throws NoAccessException {
+ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
+ checkStatic(true, method, lookupClass);
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
+ }
+
+ static void checkStatic(boolean wantStatic, MemberName m, Class> lookupClass) {
+ if (wantStatic != m.isStatic()) {
+ String message = wantStatic ? "expected a static method" : "expected a non-static method";
+ throw newNoAccessException(message, m, lookupClass);
+ }
+ }
+
+ static void checkSpecialCaller(Class> specialCaller, Class> lookupClass) {
+ if (lookupClass == Lookup.IMPL_LOOKUP.lookupClass())
+ return; // privileged action
+ if (lookupClass == null || // public-only access
+ !VerifyAccess.isSamePackageMember(specialCaller, lookupClass))
+ throw newNoAccessException("no private access", new MemberName(specialCaller), lookupClass);
+ }
+
+ // Helper for creating handles on reflected methods and constructors.
+ static MethodHandle unreflectImpl(MemberName m, boolean isAccessible,
+ boolean doDispatch, Class> lookupClass) {
+ MethodType mtype = m.getInvocationType();
+ Class> defc = m.getDeclaringClass();
+ int mods = m.getModifiers();
+ if (m.isStatic()) {
+ if (!isAccessible &&
+ VerifyAccess.isAccessible(defc, mods, false, lookupClass) == null)
+ throw newNoAccessException(m, lookupClass);
+ } else {
+ Class> constraint;
+ if (isAccessible) {
+ // abbreviated access check for "unlocked" method
+ constraint = doDispatch ? defc : lookupClass;
+ } else {
+ constraint = VerifyAccess.isAccessible(defc, mods, doDispatch, lookupClass);
+ }
+ if (constraint != defc && !constraint.isAssignableFrom(defc)) {
+ if (!defc.isAssignableFrom(constraint))
+ throw newNoAccessException("receiver must be in caller class", m, lookupClass);
+ mtype = mtype.changeParameterType(0, constraint);
+ }
+ }
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, m, doDispatch, lookupClass);
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle giving read access to elements of an array.
+ * The type of the method handle will have a return type of the array's
+ * element type. Its first argument will be the array type,
+ * and the second will be {@code int}.
+ * @param arrayClass an array type
+ * @return a method handle which can load values from the given array type
+ * @throws IllegalArgumentException if arrayClass is not an array type
+ */
+ public static
+ MethodHandle arrayElementGetter(Class> arrayClass) throws IllegalArgumentException {
+ return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, false);
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle giving write access to elements of an array.
+ * The type of the method handle will have a void return type.
+ * Its last argument will be the array's element type.
+ * The first and second arguments will be the array type and int.
+ * @return a method handle which can store values into the array type
+ * @throws IllegalArgumentException if arrayClass is not an array type
+ */
+ public static
+ MethodHandle arrayElementSetter(Class> arrayClass) throws IllegalArgumentException {
+ return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, true);
+ }
+
+
+ /// method handle invocation (reflective style)
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Call the {@code invoke} method of a given method handle,
+ * with arguments that exactly match the parameter types of the method handle.
+ * The length of the arguments array must equal the parameter count
+ * of the target's type.
+ * The arguments array is spread into separate arguments, and
+ * basic reference and unboxing conversions are applied.
+ *
+ * In order to match the type of the target, the following argument
+ * conversions are applied as necessary:
+ *
+ * reference casting
+ * unboxing
+ *
+ * The following conversions are not applied:
+ *
+ * primitive conversions (e.g., {@code byte} to {@code int}
+ * varargs conversions other than the initial spread
+ * any application-specific conversions (e.g., string to number)
+ *
+ * The result returned by the call is boxed if it is a primitive,
+ * or forced to null if the return type is void.
+ *
+ * This call is a convenience method for the following code:
+ *
+ * MethodHandle invoker = MethodHandles.genericInvoker(target.type(), 0, true);
+ * Object result = invoker.invoke(arguments);
+ *
+ * @param target the method handle to invoke
+ * @param arguments the arguments to pass to the target
+ * @return the result returned by the target
+ */
+ public static
+ Object invoke(MethodHandle target, Object... arguments) {
+ int argc = arguments == null ? 0 : arguments.length;
+ MethodType type = target.type();
+ if (argc <= 4) {
+ MethodHandle invoker = invokers(type).genericInvoker();
+ switch (argc) {
+ case 0: return invoker.invoke(target);
+ case 1: return invoker.invoke(target,
+ arguments[0]);
+ case 2: return invoker.invoke(target,
+ arguments[0], arguments[1]);
+ case 3: return invoker.invoke(target,
+ arguments[0], arguments[1], arguments[2]);
+ case 4: return invoker.invoke(target,
+ arguments[0], arguments[1], arguments[2], arguments[3]);
+ }
+ }
+ MethodHandle invoker = invokers(type).varargsInvoker();
+ return invoker.invoke(target, arguments);
+ }
+
+ public static
+ Object invoke_0(MethodHandle target) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.invoke(target);
+ }
+ public static
+ Object invoke_1(MethodHandle target, Object a0) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.invoke(target, a0);
+ }
+ public static
+ Object invoke_2(MethodHandle target, Object a0, Object a1) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.invoke(target, a0, a1);
+ }
+ public static
+ Object invoke_3(MethodHandle target, Object a0, Object a1, Object a2) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.invoke(target, a0, a1, a2);
+ }
+ public static
+ Object invoke_4(MethodHandle target, Object a0, Object a1, Object a2, Object a3) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.invoke(target, a0, a1, a2, a3);
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Give a method handle which will invoke any method handle of the
+ * given type on a standard set of {@code Object} type arguments.
+ * The the resulting invoker will be a method handle with the following
+ * arguments:
+ *
+ * a single {@code MethodHandle} target
+ * zero or more {@code Object} values
+ * an optional {@code Object[]} array containing more arguments
+ *
+ * The invoker will spread the varargs array (if present), apply
+ * reference casts as necessary, and unbox primitive arguments.
+ * The return value of the invoker will be an {@code Object} reference,
+ * boxing a primitive value if the original type returns a primitive,
+ * and always null if the original type returns void.
+ *
+ * This is a convenience method equivalent to the following code:
+ *
+ * MethodHandle invoker = exactInvoker(type);
+ * MethodType genericType = MethodType.makeGeneric(objectArgCount, varargs);
+ * genericType = genericType.insertParameterType(0, MethodHandle.class);
+ * if (!varargs)
+ * return convertArguments(invoker, genericType);
+ * else
+ * return spreadArguments(invoker, genericType);
+ *
+ * @param type the desired target type
+ * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments
+ * @param varargs if true, the invoker will accept a final {@code Object[]} argument
+ * @return a method handle suitable for invoking any method handle of the given type
+ */
+ static public
+ MethodHandle genericInvoker(MethodType type, int objectArgCount, boolean varargs) {
+ return invokers(type).genericInvoker();
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Give a method handle which will take a invoke any method handle of the
+ * given type. The resulting invoker will have a type which is
+ * exactly equal to the desired type, except that it will accept
+ * an additional leading argument of type {@code MethodHandle}.
+ *
+ * This is a convenience method equivalent to the following code:
+ *
+ * MethodHandles.lookup().findVirtual(MethodHandle.class, "invoke", type);
+ *
+ * @param type the desired target type
+ * @return a method handle suitable for invoking any method handle of the given type
+ */
+ static public
+ MethodHandle exactInvoker(MethodType type) {
+ return invokers(type).exactInvoker();
+ }
+
+ static private Invokers invokers(MethodType type) {
+ return MethodTypeImpl.invokers(IMPL_TOKEN, type);
+ }
+
+ /**
+ * WORK IN PROGRESS:
+ * Perform value checking, exactly as if for an adapted method handle.
+ * It is assumed that the given value is either null, of type T0,
+ * or (if T0 is primitive) of the wrapper type corresponding to T0.
+ * The following checks and conversions are made:
+ *
+ * If T0 and T1 are references, then a cast to T1 is applied.
+ * (The types do not need to be related in any particular way.)
+ * If T0 and T1 are primitives, then a widening or narrowing
+ * conversion is applied, if one exists.
+ * If T0 is a primitive and T1 a reference, and
+ * T0 has a wrapper type TW, a boxing conversion to TW is applied,
+ * possibly followed by a reference conversion.
+ * T1 must be TW or a supertype.
+ * If T0 is a reference and T1 a primitive, and
+ * T1 has a wrapper type TW, an unboxing conversion is applied,
+ * possibly preceded by a reference conversion.
+ * T0 must be TW or a supertype.
+ * If T1 is void, the return value is discarded
+ * If T0 is void and T1 a reference, a null value is introduced.
+ * If T0 is void and T1 a primitive, a zero value is introduced.
+ *
+ * If the value is discarded, null will be returned.
+ * @param valueType
+ * @param value
+ * @return the value, converted if necessary
+ * @throws java.lang.ClassCastException if a cast fails
+ */
+ static
+ T1 checkValue(Class t0, Class t1, Object value)
+ throws ClassCastException
+ {
+ if (t0 == t1) {
+ // no conversion needed; just reassert the same type
+ if (t0.isPrimitive())
+ return Wrapper.asPrimitiveType(t1).cast(value);
+ else
+ return Wrapper.OBJECT.cast(value, t1);
+ }
+ boolean prim0 = t0.isPrimitive(), prim1 = t1.isPrimitive();
+ if (!prim0) {
+ // check contract with caller
+ Wrapper.OBJECT.cast(value, t0);
+ if (!prim1) {
+ return Wrapper.OBJECT.cast(value, t1);
+ }
+ // convert reference to primitive by unboxing
+ Wrapper w1 = Wrapper.forPrimitiveType(t1);
+ return w1.cast(value, t1);
+ }
+ // check contract with caller:
+ Wrapper.asWrapperType(t0).cast(value);
+ Wrapper w1 = Wrapper.forPrimitiveType(t1);
+ return w1.cast(value, t1);
+ }
+
+ static
+ Object checkValue(Class> T1, Object value)
+ throws ClassCastException
+ {
+ Class> T0;
+ if (value == null)
+ T0 = Object.class;
+ else
+ T0 = value.getClass();
+ return checkValue(T0, T1, value);
+ }
+
+ /// method handle modification (creation from other method handles)
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle which adapts the type of the
+ * given method handle to a new type, by pairwise argument conversion,
+ * and/or varargs conversion.
+ * The original type and new type must have the same number of
+ * arguments, or else one or both them the must be varargs types.
+ * The resulting method handle is guaranteed to confess a type
+ * which is equal to the desired new type, with any varargs property erased.
+ *
+ * If the original type and new type are equal, returns target.
+ *
+ * The following conversions are applied as needed both to
+ * arguments and return types. Let T0 and T1 be the differing
+ * new and old parameter types (or old and new return types)
+ * for corresponding values passed by the new and old method types.
+ *
+ * If an ordinary (non-varargs) parameter of the new type is
+ * to be boxed in a varargs parameter of the old type of type T1[],
+ * then T1 is the element type of the varargs array.
+ * Otherwise, if a varargs parameter of the new type of type T0[]
+ * is to be spread into one or more outgoing old type parameters,
+ * then T0 is the element type of the
+ * If the new type is varargs and the old type is not, the varargs
+ * argument will be checked and must be a non-null array of exactly
+ * the right length. If there are no parameters in the old type
+ * corresponding to the new varargs parameter, the varargs argument
+ * is also allowed to be null.
+ *
+ * Given those types T0, T1, one of the following conversions is applied
+ * if possible:
+ *
+ * If T0 and T1 are references, then a cast to T2 is applied,
+ * where T2 is Object if T1 is an interface, else T1.
+ * (The types do not need to be related in any particular way.
+ * The treatment of interfaces follows the usage of the bytecode verifier.)
+ * If T0 and T1 are primitives, then a Java casting
+ * conversion (JLS 5.5) is applied, if one exists.
+ * If T0 and T1 are primitives and one is boolean,
+ * the boolean is treated as a one-bit unsigned integer.
+ * (This treatment follows the usage of the bytecode verifier.)
+ * A conversion from another primitive type behaves as if
+ * it first converts to byte, and then masks all but the low bit.
+ * If T0 is a primitive and T1 a reference, a boxing
+ * conversion is applied if one exists, possibly followed by
+ * an reference conversion to a superclass.
+ * T1 must be a wrapper class or a supertype of one.
+ * If T1 is a wrapper class, T0 is converted if necessary
+ * to T1's primitive type by one of the preceding conversions.
+ * Otherwise, T0 is boxed, and its wrapper converted to T1.
+ * If T0 is a reference and T1 a primitive, an unboxing
+ * conversion is applied if one exists, possibly preceded by
+ * a reference conversion to a wrapper class.
+ * T0 must be a wrapper class or a supertype of one.
+ * If T0 is a wrapper class, its primitive value is converted
+ * if necessary to T1 by one of the preceding conversions.
+ * Otherwise, T0 is converted directly to the wrapper type for T1,
+ * which is then unboxed.
+ * If T1 is void, any returned value is discarded
+ * If T0 is void and T1 a reference, a null value is introduced.
+ * If T0 is void and T1 a primitive, a zero value is introduced.
+ *
+ * @param target the method handle to invoke after arguments are retyped
+ * @param newType the expected type of the new method handle
+ * @return a method handle which delegates to {@code target} after performing
+ * any necessary argument conversions, and arranges for any
+ * necessary return value conversions
+ * @throws WrongMethodTypeException if the conversion cannot be made
+ */
+ public static
+ MethodHandle convertArguments(MethodHandle target, MethodType newType) {
+ MethodType oldType = target.type();
+ if (oldType.equals(newType))
+ return target;
+ MethodHandle res = MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
+ newType, oldType, null);
+ if (res == null)
+ throw newIllegalArgumentException("cannot convert to "+newType+": "+target);
+ return res;
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle which adapts the calling sequence of the
+ * given method handle to a new type, by reordering the arguments.
+ * The resulting method handle is guaranteed to confess a type
+ * which is equal to the desired new type.
+ *
+ * The given array controls the reordering.
+ * Call {@code #I} the number of incoming parameters (the value
+ * {@code newType.parameterCount()}, and call {@code #O} the number
+ * of outgoing parameters (the value {@code target.type().parameterCount()}).
+ * Then the length of the reordering array must be {@code #O},
+ * and each element must be a non-negative number less than {@code #I}.
+ * For every {@code N} less than {@code #O}, the {@code N}-th
+ * outgoing argument will be taken from the {@code I}-th incoming
+ * argument, where {@code I} is {@code reorder[N]}.
+ *
+ * The reordering array need not specify an actual permutation.
+ * An incoming argument will be duplicated if its index appears
+ * more than once in the array, and an incoming argument will be dropped
+ * if its index does not appear in the array.
+ *
+ * Pairwise conversions are applied as needed to arguments and return
+ * values, as with {@link #convertArguments}.
+ * @param target the method handle to invoke after arguments are reordered
+ * @param newType the expected type of the new method handle
+ * @param reorder a string which controls the reordering
+ * @return a method handle which delegates to {@code target} after performing
+ * any necessary argument motion and conversions, and arranges for any
+ * necessary return value conversions
+ */
+ public static
+ MethodHandle permuteArguments(MethodHandle target, MethodType newType, int[] reorder) {
+ MethodType oldType = target.type();
+ checkReorder(reorder, newType, oldType);
+ return MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
+ newType, oldType,
+ reorder);
+ }
+
+ private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
+ if (reorder.length == oldType.parameterCount()) {
+ int limit = newType.parameterCount();
+ boolean bad = false;
+ for (int i : reorder) {
+ if (i < 0 || i >= limit) {
+ bad = true; break;
+ }
+ }
+ if (!bad) return;
+ }
+ throw newIllegalArgumentException("bad reorder array");
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle which adapts the type of the
+ * given method handle to a new type, by spreading the final argument.
+ * The resulting method handle is guaranteed to confess a type
+ * which is equal to the desired new type.
+ *
+ * The final parameter type of the new type must be an array type T[].
+ * This is the type of what is called the spread argument.
+ * All other arguments of the new type are called ordinary arguments.
+ *
+ * The ordinary arguments of the new type are pairwise converted
+ * to the initial parameter types of the old type, according to the
+ * rules in {@link #convertArguments}.
+ * Any additional arguments in the old type
+ * are converted from the array element type T,
+ * again according to the rules in {@link #convertArguments}.
+ * The return value is converted according likewise.
+ *
+ * The call verifies that the spread argument is in fact an array
+ * of exactly the type length, i.e., the excess number of
+ * arguments in the old type over the ordinary arguments in the new type.
+ * If there are no excess arguments, the spread argument is also
+ * allowed to be null.
+ * @param target the method handle to invoke after the argument is prepended
+ * @param newType the expected type of the new method handle
+ * @return a new method handle which spreads its final argument,
+ * before calling the original method handle
+ */
+ public static
+ MethodHandle spreadArguments(MethodHandle target, MethodType newType) {
+ MethodType oldType = target.type();
+ int inargs = newType.parameterCount();
+ int outargs = oldType.parameterCount();
+ int spreadPos = inargs - 1;
+ int numSpread = (outargs - spreadPos);
+ MethodHandle res = null;
+ if (spreadPos >= 0 && numSpread >= 0) {
+ res = MethodHandleImpl.spreadArguments(IMPL_TOKEN, target, newType, spreadPos);
+ }
+ if (res == null) {
+ throw newIllegalArgumentException("cannot spread "+newType+" to " +oldType);
+ }
+ return res;
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle which adapts the type of the
+ * given method handle to a new type, by collecting a series of
+ * trailing arguments into an array.
+ * The resulting method handle is guaranteed to confess a type
+ * which is equal to the desired new type.
+ *
+ * This method is inverse to {@link #spreadArguments}.
+ * The final parameter type of the old type must be an array type T[],
+ * which is the type of what is called the spread argument.
+ * The trailing arguments of the new type which correspond to
+ * the spread argument are all converted to type T and collected
+ * into an array before the original method is called.
+ *
+ * ISSUE: Unify this with combineArguments. CollectArguments
+ * is combineArguments with (a) new Object[]{...} as a combiner,
+ * and (b) the combined arguments dropped, in favor of the combined result.
+ * @param target the method handle to invoke after the argument is prepended
+ * @param newType the expected type of the new method handle
+ * @return a new method handle which collects some trailings argument
+ * into an array, before calling the original method handle
+ */
+ public static
+ MethodHandle collectArguments(MethodHandle target, MethodType newType) {
+ MethodType oldType = target.type();
+ int inargs = newType.parameterCount();
+ int outargs = oldType.parameterCount();
+ int collectPos = outargs - 1;
+ int numCollect = (inargs - collectPos);
+ if (collectPos < 0 || numCollect < 0)
+ throw newIllegalArgumentException("wrong number of arguments");
+ return MethodHandleImpl.collectArguments(IMPL_TOKEN, target, newType, collectPos);
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle which calls the original method handle,
+ * after inserting the given argument at the given position.
+ * The type of the new method handle will drop the corresponding argument
+ * type from the original handle's type.
+ *
+ * The given argument object must match the dropped argument type.
+ * If the dropped argument type is a primitive, the argument object
+ * must be a wrapper, and is unboxed to produce the primitive.
+ *
+ * The pos may range between zero and N (inclusively),
+ * where N is the number of argument types in target ,
+ * meaning to insert the new argument as the first or last (respectively),
+ * or somewhere in between.
+ * @param target the method handle to invoke after the argument is inserted
+ * @param pos where to insert the argument (zero for the first)
+ * @param value the argument to insert
+ * @return a new method handle which inserts an additional argument,
+ * before calling the original method handle
+ */
+ public static
+ MethodHandle insertArgument(MethodHandle target, int pos, Object value) {
+ MethodType oldType = target.type();
+ ArrayList> ptypes =
+ new ArrayList>(oldType.parameterList());
+ int outargs = oldType.parameterCount();
+ int inargs = outargs - 1;
+ if (pos < 0 || pos >= outargs)
+ throw newIllegalArgumentException("no argument type to append");
+ Class> valueType = ptypes.remove(pos);
+ value = checkValue(valueType, value);
+ if (pos == 0 && !valueType.isPrimitive()) {
+ // At least for now, make bound method handles a special case.
+ // This lets us get by with minimal JVM support, at the expense
+ // of generating signature-specific adapters as Java bytecodes.
+ MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, target, value);
+ if (bmh != null) return bmh;
+ // else fall through to general adapter machinery
+ }
+ return MethodHandleImpl.bindArgument(IMPL_TOKEN, target, pos, value);
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Produce a method handle which calls the original method handle,
+ * after dropping the given argument(s) at the given position.
+ * The type of the new method handle will insert the given argument
+ * type(s), at that position, into the original handle's type.
+ *
+ * The pos may range between zero and N-1 ,
+ * where N is the number of argument types in target ,
+ * meaning to drop the first or last argument (respectively),
+ * or an argument somewhere in between.
+ * @param target the method handle to invoke after the argument is dropped
+ * @param valueTypes the type(s) of the argument to drop
+ * @param pos which argument to drop (zero for the first)
+ * @return a new method handle which drops an argument of the given type,
+ * before calling the original method handle
+ */
+ public static
+ MethodHandle dropArguments(MethodHandle target, int pos, Class>... valueTypes) {
+ if (valueTypes.length == 0) return target;
+ MethodType oldType = target.type();
+ int outargs = oldType.parameterCount();
+ int inargs = outargs + valueTypes.length;
+ if (pos < 0 || pos >= inargs)
+ throw newIllegalArgumentException("no argument type to remove");
+ ArrayList> ptypes =
+ new ArrayList>(oldType.parameterList());
+ ptypes.addAll(pos, Arrays.asList(valueTypes));
+ MethodType newType = MethodType.make(oldType.returnType(), ptypes);
+ return MethodHandleImpl.dropArguments(IMPL_TOKEN, target, newType, pos);
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Make a method handle which adapts a target method handle,
+ * by guarding it with a test, a boolean-valued method handle.
+ * If the guard fails, a fallback handle is called instead.
+ * All three method handles must have the same corresponding
+ * argument and return types, except that the return type
+ * of the test must be boolean.
+ * Here is pseudocode for the resulting adapter:
+ *
+ * signature T(A...);
+ * boolean test(A...);
+ * T target(A...);
+ * T fallback(A...);
+ * T adapter(A... a) {
+ * if (test(a...))
+ * return target(a...);
+ * else
+ * return fallback(a...);
+ * }
+ *
+ * @param test method handle used for test, must return boolean
+ * @param target method handle to call if test passes
+ * @param fallback method handle to call if test fails
+ * @return method handle which incorporates the specified if/then/else logic
+ * @throws IllegalArgumentException if {@code test} does not return boolean,
+ * or if all three method types do not match (with the return
+ * type of {@code test} changed to match that of {@code target}).
+ */
+ public static
+ MethodHandle guardWithTest(MethodHandle test,
+ MethodHandle target,
+ MethodHandle fallback) {
+ if (target.type() != fallback.type())
+ throw newIllegalArgumentException("target and fallback types do not match");
+ if (target.type().changeReturnType(boolean.class) != test.type())
+ throw newIllegalArgumentException("target and test types do not match");
+ /* {
+ MethodHandle invoke = findVirtual(MethodHandle.class, "invoke", target.type());
+ static MethodHandle choose(boolean z, MethodHandle t, MethodHandle f) {
+ return z ? t : f;
+ }
+ static MethodHandle compose(MethodHandle f, MethodHandle g) {
+ Class> initargs = g.type().parameterArray();
+ f = dropArguments(f, 1, initargs); // ignore 2nd copy of args
+ return combineArguments(f, g);
+ }
+ // choose = \z.(z ? target : fallback)
+ MethodHandle choose = findVirtual(MethodHandles.class, "choose",
+ MethodType.make(boolean.class, MethodHandle.class, MethodHandle.class));
+ choose = appendArgument(choose, target);
+ choose = appendArgument(choose, fallback);
+ MethodHandle dispatch = compose(choose, test);
+ // dispatch = \(a...).(test(a...) ? target : fallback)
+ return combineArguments(invoke, dispatch, 0);
+ // return \(a...).((test(a...) ? target : fallback).invoke(a...))
+ } */
+ return MethodHandleImpl.makeGuardWithTest(IMPL_TOKEN, test, target, fallback);
+ }
+
+ /**
+ * PROVISIONAL API, WORK IN PROGRESS:
+ * Adapt a target method handle {@code target} by first processing
+ * its arguments, and then calling the target.
+ * The initial processing is performed by a second method handle, the {@code combiner}.
+ * After this, control passes to the {@code target}, with the same arguments.
+ *
+ * The return value of the {@code combiner} is inserted into the argument list
+ * for the {@code target} at the indicated position {@code pos}, if it is non-negative.
+ * Except for this inserted argument (if any), the argument types of
+ * the target {@code target} and the {@code combiner} must be identical.
+ *
+ * (Note that {@link #dropArguments} can be used to remove any arguments
+ * that either the {@code combiner} or {@code target} does not wish to receive.)
+ *
+ * The combiner handle must have the same argument types as the
+ * target handle, but must return {@link MethodHandle} instead of
+ * the ultimate return type. The returned method handle, in turn,
+ * is required to have exactly the given final method type.
+ *
Here is pseudocode for the resulting adapter:
+ *
+ * signature V(A[pos]..., B...);
+ * signature T(A[pos]..., V, B...);
+ * T target(A... a, V v, B... b);
+ * V combiner(A..., B...);
+ * T adapter(A... a, B... b) {
+ * V v = combiner(a..., b...);
+ * return target(a..., v, b...);
+ * }
+ *
+ * @param target the method handle to invoke after arguments are combined
+ * @param pos where the return value of {@code combiner} is to
+ * be inserted as an argument to {@code target}
+ * @param combiner method handle to call initially on the incoming arguments
+ * @return method handle which incorporates the specified dispatch logic
+ * @throws IllegalArgumentException if {@code combiner} does not itself
+ * return either void or the {@code pos}-th argument of {@code target},
+ * or does not have the same argument types as {@code target}
+ * (minus the inserted argument)
+ */
+ public static
+ MethodHandle combineArguments(MethodHandle target, int pos, MethodHandle combiner) {
+ MethodType mhType = target.type();
+ Class> combineType = combiner.type().returnType();
+ MethodType incomingArgs;
+ if (pos < 0) {
+ // No inserted argument; target & combiner must have same argument types.
+ incomingArgs = mhType;
+ if (!incomingArgs.changeReturnType(combineType).equals(combiner.type()))
+ throw newIllegalArgumentException("target and combiner types do not match");
+ } else {
+ // Inserted argument.
+ if (pos >= mhType.parameterCount()
+ || mhType.parameterType(pos) != combineType)
+ throw newIllegalArgumentException("inserted combiner argument does not match target");
+ incomingArgs = mhType.dropParameterType(pos);
+ }
+ if (!incomingArgs.changeReturnType(combineType).equals(combiner.type())) {
+ throw newIllegalArgumentException("target and combiner types do not match");
+ }
+ return MethodHandleImpl.combineArguments(IMPL_TOKEN, target, combiner, pos);
+ }
+
+}
diff --git a/src/share/classes/java/dyn/MethodType.java b/src/share/classes/java/dyn/MethodType.java
new file mode 100644
index 0000000000000000000000000000000000000000..e39f5b56e634016ce43f938ce88850bb65f48fa1
--- /dev/null
+++ b/src/share/classes/java/dyn/MethodType.java
@@ -0,0 +1,575 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import sun.dyn.Access;
+import sun.dyn.Invokers;
+import sun.dyn.MethodTypeImpl;
+import sun.dyn.util.BytecodeSignature;
+import static sun.dyn.MemberName.newIllegalArgumentException;
+
+/**
+ * Run-time token used to match call sites with method handles.
+ * The structure is a return type accompanied by any number of parameter types.
+ * The types (primitive, void, and reference) are represented by Class objects.
+ * All instances of MethodType
are immutable.
+ * Two instances are completely interchangeable if they compare equal.
+ * Equality depends exactly on the return and parameter types.
+ *
+ * This type can be created only by factory methods, which manage interning.
+ *
+ * @author John Rose, JSR 292 EG
+ */
+public final
+class MethodType {
+ private final Class> rtype;
+ private final Class>[] ptypes;
+ private MethodTypeForm form; // erased form, plus cached data about primitives
+ private MethodType wrapAlt; // alternative wrapped/unwrapped version
+ private Invokers invokers; // cache of handy higher-order adapters
+
+ private static final Access IMPL_TOKEN = Access.getToken();
+
+ // share a cache with a friend in this package
+ Invokers getInvokers() { return invokers; }
+ void setInvokers(Invokers inv) { invokers = inv; }
+
+ static {
+ // This hack allows the implementation package special access to
+ // the internals of MethodType. In particular, the Form has all sorts
+ // of cached information useful to the implementation code.
+ MethodTypeImpl.setMethodTypeFriend(IMPL_TOKEN, new MethodTypeImpl.MethodTypeFriend() {
+ public Class>[] ptypes(MethodType mt) { return mt.ptypes; }
+ public MethodTypeImpl form(MethodType mt) { return mt.form; }
+ public void setForm(MethodType mt, MethodTypeImpl form) {
+ assert(mt.form == null);
+ mt.form = (MethodTypeForm) form;
+ }
+ public MethodType makeImpl(Class> rtype, Class>[] ptypes, boolean trusted) {
+ return MethodType.makeImpl(rtype, ptypes, trusted);
+ }
+ public MethodTypeImpl newMethodTypeForm(MethodType mt) {
+ return new MethodTypeForm(mt);
+ }
+ public Invokers getInvokers(MethodType mt) { return mt.invokers; }
+ public void setInvokers(MethodType mt, Invokers inv) { mt.invokers = inv; }
+ });
+ }
+
+ private MethodType(Class> rtype, Class>[] ptypes) {
+ checkRtype(rtype);
+ checkPtypes(ptypes);
+ this.rtype = rtype;
+ this.ptypes = ptypes;
+ }
+
+ private void checkRtype(Class> rtype) {
+ rtype.equals(rtype); // null check
+ }
+ private void checkPtypes(Class>[] ptypes) {
+ for (Class> ptype : ptypes) {
+ ptype.equals(ptype); // null check
+ if (ptype == void.class)
+ throw newIllegalArgumentException("void parameter: "+this);
+ }
+ }
+
+ static final HashMap internTable
+ = new HashMap();
+
+ static final Class>[] NO_PTYPES = {};
+
+ /** Find or create an instance of the given method type.
+ * @param rtype the return type
+ * @param ptypes the parameter types
+ * @return the interned method type with the given parts
+ * @throws NullPointerException if rtype or any ptype is null
+ * @throws IllegalArgumentException if any of the ptypes is void
+ */
+ public static
+ MethodType make(Class> rtype, Class>[] ptypes) {
+ return makeImpl(rtype, ptypes, false);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. */
+ public static
+ MethodType make(Class> rtype, List extends Class>> ptypes) {
+ return makeImpl(rtype, ptypes.toArray(NO_PTYPES), true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * The leading parameter type is prepended to the remaining array.
+ */
+ public static
+ MethodType make(Class> rtype, Class> ptype0, Class>... ptypes) {
+ Class>[] ptypes1 = new Class>[1+ptypes.length];
+ ptypes1[0] = ptype0;
+ System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length);
+ return makeImpl(rtype, ptypes1, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * The resulting method has no parameter types.
+ */
+ public static
+ MethodType make(Class> rtype) {
+ return makeImpl(rtype, NO_PTYPES, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * The resulting method has the single given parameter type.
+ */
+ public static
+ MethodType make(Class> rtype, Class> ptype0) {
+ return makeImpl(rtype, new Class>[]{ ptype0 }, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * The resulting method has the same parameter types as {@code ptypes},
+ * and the specified return type.
+ */
+ public static
+ MethodType make(Class> rtype, MethodType ptypes) {
+ return makeImpl(rtype, ptypes.ptypes, true);
+ }
+
+ /**
+ * Sole factory method to find or create an interned method type.
+ * @param rtype desired return type
+ * @param ptypes desired parameter types
+ * @param trusted whether the ptypes can be used without cloning
+ * @return the unique method type of the desired structure
+ */
+ private static
+ MethodType makeImpl(Class> rtype, Class>[] ptypes, boolean trusted) {
+ if (ptypes == null || ptypes.length == 0) {
+ ptypes = NO_PTYPES; trusted = true;
+ }
+ MethodType mt1 = new MethodType(rtype, ptypes);
+ MethodType mt0;
+ synchronized (internTable) {
+ mt0 = internTable.get(mt1);
+ if (mt0 != null)
+ return mt0;
+ }
+ if (!trusted)
+ // defensively copy the array passed in by the user
+ mt1 = new MethodType(rtype, ptypes.clone());
+ // promote the object to the Real Thing, and reprobe
+ MethodTypeImpl.initForm(IMPL_TOKEN, mt1);
+ synchronized (internTable) {
+ mt0 = internTable.get(mt1);
+ if (mt0 != null)
+ return mt0;
+ internTable.put(mt1, mt1);
+ }
+ return mt1;
+ }
+
+ // Entry point from JVM. TODO: Change the name & signature.
+ private static MethodType makeImpl(Class> rtype, Class>[] ptypes,
+ boolean ignore1, boolean ignore2) {
+ return makeImpl(rtype, ptypes, true);
+ }
+
+ private static final MethodType[] objectOnlyTypes = new MethodType[20];
+
+ /**
+ * Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * All parameters and the return type will be Object, except the final varargs parameter if any.
+ * @param objectArgCount number of parameters (excluding the varargs parameter if any)
+ * @param varargs whether there will be a varargs parameter, of type Object[]
+ * @return a totally generic method type, given only its count of parameters and varargs
+ * @see #makeGeneric(int)
+ */
+ public static
+ MethodType makeGeneric(int objectArgCount, boolean varargs) {
+ MethodType mt;
+ int ivarargs = (!varargs ? 0 : 1);
+ int ootIndex = objectArgCount*2 + ivarargs;
+ if (ootIndex < objectOnlyTypes.length) {
+ mt = objectOnlyTypes[ootIndex];
+ if (mt != null) return mt;
+ }
+ Class>[] ptypes = new Class>[objectArgCount + ivarargs];
+ Arrays.fill(ptypes, Object.class);
+ if (ivarargs != 0) ptypes[objectArgCount] = Object[].class;
+ mt = makeImpl(Object.class, ptypes, true);
+ if (ootIndex < objectOnlyTypes.length) {
+ objectOnlyTypes[ootIndex] = mt; // cache it here also!
+ }
+ return mt;
+ }
+
+ /**
+ * All parameters and the return type will be Object.
+ * @param objectArgCount number of parameters
+ * @return a totally generic method type, given only its count of parameters
+ * @see #makeGeneric(int, boolean)
+ */
+ public static
+ MethodType makeGeneric(int objectArgCount) {
+ return makeGeneric(objectArgCount, false);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * @param num the index (zero-based) of the parameter type to change
+ * @param nptype a new parameter type to replace the old one with
+ * @return the same type, except with the selected parameter changed
+ */
+ public MethodType changeParameterType(int num, Class> nptype) {
+ if (parameterType(num) == nptype) return this;
+ Class>[] nptypes = ptypes.clone();
+ nptypes[num] = nptype;
+ return makeImpl(rtype, nptypes, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * @param num the position (zero-based) of the inserted parameter type
+ * @param nptype a new parameter type to insert into the parameter list
+ * @return the same type, except with the selected parameter inserted
+ */
+ public MethodType insertParameterType(int num, Class> nptype) {
+ int len = ptypes.length;
+ Class>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+1);
+ System.arraycopy(nptypes, num, nptypes, num+1, len-num);
+ nptypes[num] = nptype;
+ return makeImpl(rtype, nptypes, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * @param num the index (zero-based) of the parameter type to remove
+ * @return the same type, except with the selected parameter removed
+ */
+ public MethodType dropParameterType(int num) {
+ int len = ptypes.length;
+ Class>[] nptypes;
+ if (num == 0) {
+ nptypes = Arrays.copyOfRange(ptypes, 1, len);
+ } else {
+ nptypes = Arrays.copyOfRange(ptypes, 0, len-1);
+ System.arraycopy(ptypes, num+1, nptypes, num, (len-1)-num);
+ }
+ return makeImpl(rtype, nptypes, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * @param nrtype a return parameter type to replace the old one with
+ * @return the same type, except with the return type change
+ */
+ public MethodType changeReturnType(Class> nrtype) {
+ if (returnType() == nrtype) return this;
+ return makeImpl(nrtype, ptypes, true);
+ }
+
+ /** Convenience method.
+ * Report if this type contains a primitive argument or return value.
+ * @return true if any of the types are primitives
+ */
+ public boolean hasPrimitives() {
+ return form.hasPrimitives();
+ }
+
+ /** Convenience method.
+ * Report if this type contains a wrapper argument or return value.
+ * Wrappers are types which box primitive values, such as {@link Integer}.
+ * @return true if any of the types are wrappers
+ */
+ public boolean hasWrappers() {
+ return unwrap() != this;
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Erase all reference types to Object.
+ * @return a version of the original type with all reference types replaced
+ */
+ public MethodType erase() {
+ return form.erasedType();
+ }
+
+ /** Convenience method for {@link #makeGeneric(int)}.
+ * Convert all types, both reference and primitive, to Object.
+ * @return a version of the original type with all types replaced
+ */
+ public MethodType generic() {
+ return makeGeneric(parameterCount());
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Convert all primitive types to their corresponding wrapper types.
+ * A {@code void} return type is changed to the type {@code java.lang.Void}.
+ * @return a version of the original type with all primitive types replaced
+ */
+ public MethodType wrap() {
+ return hasPrimitives() ? wrapWithPrims(this) : this;
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Convert all wrapper types to their corresponding primitive types.
+ * A return type of {@java.lang.Void} is changed to {@code void}.
+ * @return a version of the original type with all wrapper types replaced
+ */
+ public MethodType unwrap() {
+ MethodType noprims = !hasPrimitives() ? this : wrapWithPrims(this);
+ return unwrapWithNoPrims(noprims);
+ }
+
+ private static MethodType wrapWithPrims(MethodType pt) {
+ assert(pt.hasPrimitives());
+ MethodType wt = pt.wrapAlt;
+ if (wt == null) {
+ // fill in lazily
+ wt = MethodTypeImpl.canonicalize(pt, MethodTypeImpl.WRAP, MethodTypeImpl.WRAP);
+ assert(wt != null);
+ pt.wrapAlt = wt;
+ }
+ return wt;
+ }
+
+ private static MethodType unwrapWithNoPrims(MethodType wt) {
+ assert(!wt.hasPrimitives());
+ MethodType uwt = wt.wrapAlt;
+ if (uwt == null) {
+ // fill in lazily
+ uwt = MethodTypeImpl.canonicalize(wt, MethodTypeImpl.UNWRAP, MethodTypeImpl.UNWRAP);
+ if (uwt == null)
+ uwt = wt; // type has no wrappers or prims at all
+ wt.wrapAlt = uwt;
+ }
+ return uwt;
+ }
+
+ /** @param num the index (zero-based) of the desired parameter type
+ * @return the selected parameter type
+ */
+ public Class> parameterType(int num) {
+ return ptypes[num];
+ }
+ /** @return the number of parameter types */
+ public int parameterCount() {
+ return ptypes.length;
+ }
+ /** @return the return type */
+ public Class> returnType() {
+ return rtype;
+ }
+
+ /**
+ * Convenience method to present the arguments as a list.
+ * @return the parameter types (as an immutable list)
+ */
+ public List> parameterList() {
+ return Collections.unmodifiableList(Arrays.asList(ptypes));
+ }
+
+ /**
+ * Convenience method to present the arguments as an array.
+ * @return the parameter types (as a fresh copy if necessary)
+ */
+ public Class>[] parameterArray() {
+ return ptypes.clone();
+ }
+
+ /**
+ * Compares the specified object with this type for equality.
+ * That is, it returns true if and only if the specified object
+ * is also a method type with exactly the same parameters and return type.
+ * @param x object to compare
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object x) {
+ return this == x || x instanceof MethodType && equals((MethodType)x);
+ }
+
+ private boolean equals(MethodType that) {
+ return this.rtype == that.rtype
+ && Arrays.equals(this.ptypes, that.ptypes);
+ }
+
+ /**
+ * Returns the hash code value for this method type.
+ * It is defined to be the same as the hashcode of a List
+ * whose elements are the return type followed by the
+ * parameter types.
+ * @return the hash code value for this method type
+ * @see Object#hashCode()
+ * @see #equals(Object)
+ * @see List#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ int hashCode = 31 + rtype.hashCode();
+ for (Class> ptype : ptypes)
+ hashCode = 31*hashCode + ptype.hashCode();
+ return hashCode;
+ }
+
+ /**
+ * The string representation of a method type is a
+ * parenthesis enclosed, comma separated list of type names,
+ * followed immediately by the return type.
+ *
+ * If a type name is array, it the base type followed
+ * by [], rather than the Class.getName of the array type.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ for (int i = 0; i < ptypes.length; i++) {
+ if (i > 0) sb.append(",");
+ putName(sb, ptypes[i]);
+ }
+ sb.append(")");
+ putName(sb, rtype);
+ return sb.toString();
+ }
+
+ static void putName(StringBuilder sb, Class> cls) {
+ int brackets = 0;
+ while (cls.isArray()) {
+ cls = cls.getComponentType();
+ brackets++;
+ }
+ String n = cls.getName();
+ /*
+ if (n.startsWith("java.lang.")) {
+ String nb = n.substring("java.lang.".length());
+ if (nb.indexOf('.') < 0) n = nb;
+ } else if (n.indexOf('.') < 0) {
+ n = "."+n; // anonymous package
+ }
+ */
+ sb.append(n);
+ while (brackets > 0) {
+ sb.append("[]");
+ brackets--;
+ }
+ }
+
+ /// Queries which have to do with the bytecode architecture
+
+ /** The number of JVM stack slots required to invoke a method
+ * of this type. Note that (for historic reasons) the JVM requires
+ * a second stack slot to pass long and double arguments.
+ * So this method returns {@link #parameterCount()} plus the
+ * number of long and double parameters (if any).
+ *
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * @return the number of JVM stack slots for this type's parameters
+ */
+ public int parameterSlotCount() {
+ return form.parameterSlotCount();
+ }
+
+ /** Number of JVM stack slots which carry all parameters after
+ * the given position, which must be in the range of 0 to
+ * {@code parameterCount} inclusive. Successive parameters are
+ * more shallowly stacked, and parameters are indexed in the bytecodes
+ * according to their trailing edge. Thus, to obtain the depth
+ * in the outgoing call stack of parameter {@code N}, obtain
+ * the {@code parameterSlotDepth} of its trailing edge
+ * at position {@code N+1}.
+ *
+ * Parameters of type {@code long} and {@code double} occupy
+ * two stack slots (for historical reasons) and all others occupy one.
+ * Therefore, the number returned is the number of arguments
+ * including and after the given parameter,
+ * plus the number of long or double arguments
+ * at or after after the argument for the given parameter.
+ *
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * @param num an index (zero-based, inclusive) within the parameter types
+ * @return the index of the (shallowest) JVM stack slot transmitting the
+ * given parameter
+ */
+ public int parameterSlotDepth(int num) {
+ if (num < 0 || num > ptypes.length)
+ parameterType(num); // force a range check
+ return form.parameterToArgSlot(num-1);
+ }
+
+ /** The number of JVM stack slots required to receive a return value
+ * from a method of this type.
+ * If the {@link #returnType() return type} is void, it will be zero,
+ * else if the return type is long or double, it will be two, else one.
+ *
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * @return the number of JVM stack slots (0, 1, or 2) for this type's return value
+ */
+ public int returnSlotCount() {
+ return form.returnSlotCount();
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Find or create an instance (interned) of the given method type.
+ * Any class or interface name embedded in the signature string
+ * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
+ * on the given loader (or if it is null, on the system class loader).
+ *
+ * Note that it is possible to build method types which cannot be
+ * constructed by this method, because their component types are
+ * not all reachable from a common class loader.
+ *
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * @param bytecodeSignature a bytecode-level signature string "(T...)T"
+ * @param loader the class loader in which to look up the types
+ * @return a method type matching the bytecode-level signature
+ * @throws IllegalArgumentException if the string is not well-formed
+ * @throws TypeNotPresentException if a named type cannot be found
+ */
+ public static MethodType fromBytecodeString(String bytecodeSignature, ClassLoader loader)
+ throws IllegalArgumentException, TypeNotPresentException
+ {
+ List> types = BytecodeSignature.parseMethod(bytecodeSignature, loader);
+ Class> rtype = types.remove(types.size() - 1);
+ Class>[] ptypes = types.toArray(NO_PTYPES);
+ return makeImpl(rtype, ptypes, true);
+ }
+
+ /**
+ * Create a bytecode signature representation of the type.
+ * Note that this is not a strict inverse of
+ *
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * {@link #fromBytecodeString(java.lang.String, java.lang.ClassLoader)},
+ * because the latter requires a suitable class loader argument.
+ * @return the bytecode signature representation
+ */
+ public String toBytecodeString() {
+ return BytecodeSignature.unparse(this);
+ }
+}
diff --git a/src/share/classes/java/dyn/MethodTypeForm.java b/src/share/classes/java/dyn/MethodTypeForm.java
new file mode 100644
index 0000000000000000000000000000000000000000..d37ba91f7a8dfd2cc70a275c09c0dffc30eef40c
--- /dev/null
+++ b/src/share/classes/java/dyn/MethodTypeForm.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+/**
+ * TO DO: Temporary shim; remove after refactoring effects are complete in JVM.
+ * @author John Rose
+ */
+import sun.dyn.MethodTypeImpl;
+
+class MethodTypeForm extends MethodTypeImpl {
+
+ MethodTypeForm(MethodType erasedType) {
+ super(erasedType);
+ }
+}
diff --git a/src/share/classes/java/dyn/NoAccessException.java b/src/share/classes/java/dyn/NoAccessException.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb83b401cce6726dad45105ec94bec690f531e45
--- /dev/null
+++ b/src/share/classes/java/dyn/NoAccessException.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+/**
+ * Thrown to indicate that a caller has attempted to create a method handle
+ * which calls a method to which the caller does not have access.
+ * This unchecked exception is analogous to {@link IllegalAccessException},
+ * which is a checked exception thrown when reflective invocation fails
+ * because of an access check. With method handles, this same access
+ * checking is performed on behalf of the method handle creator,
+ * at the time of creation.
+ * @author John Rose, JSR 292 EG
+ */
+public class NoAccessException extends RuntimeException {
+ /**
+ * Constructs a {@code NoAccessException} with no detail message.
+ */
+ public NoAccessException() {
+ super();
+ }
+
+ /**
+ * Constructs a {@code NoAccessException} with the specified
+ * detail message.
+ *
+ * @param s the detail message
+ */
+ public NoAccessException(String s) {
+ super(s);
+ }
+
+ /**
+ * Constructs a {@code NoAccessException} with the specified cause.
+ *
+ * @param cause the underlying cause of the exception
+ */
+ public NoAccessException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs a {@code NoAccessException} with the specified
+ * detail message and cause.
+ *
+ * @param s the detail message
+ * @param cause the underlying cause of the exception
+ */
+ public NoAccessException(String s, Throwable cause) {
+ super(s, cause);
+ }
+}
diff --git a/src/share/classes/java/dyn/WrongMethodTypeException.java b/src/share/classes/java/dyn/WrongMethodTypeException.java
new file mode 100644
index 0000000000000000000000000000000000000000..08745f6711cf578040d845ac6571687eb3c382bd
--- /dev/null
+++ b/src/share/classes/java/dyn/WrongMethodTypeException.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+/**
+ * Thrown to indicate that code has attempted to call a method handle
+ * via the wrong method type. As with the bytecode representation of
+ * normal Java method calls, method handle calls are strongly typed
+ * to a specific signature associated with a call site.
+ *
+ * This exception may also be thrown when two method handles are
+ * composed, and the system detects that their types cannot be
+ * matched up correctly. This amounts to an early evaluation
+ * of the type mismatch, at method handle construction time,
+ * instead of when the mismatched method handle is called.
+ *
+ * @author John Rose, JSR 292 EG
+ */
+public class WrongMethodTypeException extends RuntimeException {
+ /**
+ * Constructs a {@code WrongMethodTypeException} with no detail message.
+ */
+ public WrongMethodTypeException() {
+ super();
+ }
+
+ /**
+ * Constructs a {@code WrongMethodTypeException} with the specified
+ * detail message.
+ *
+ * @param s the detail message.
+ */
+ public WrongMethodTypeException(String s) {
+ super(s);
+ }
+}
diff --git a/src/share/classes/java/dyn/package-info.java b/src/share/classes/java/dyn/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..858d0e9bcbfe35bafc35bae2a98ec7c8e7cb7c14
--- /dev/null
+++ b/src/share/classes/java/dyn/package-info.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * This package contains dynamic language support provided directly by
+ * the Java core class libraries and virtual machine.
+ * @author John Rose, JSR 292 EG
+ */
+
+package java.dyn;
diff --git a/src/share/classes/sun/dyn/Access.java b/src/share/classes/sun/dyn/Access.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf137c32d1031fc6da0a2df624f3ea66032103fe
--- /dev/null
+++ b/src/share/classes/sun/dyn/Access.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import sun.reflect.Reflection;
+
+/**
+ * Access control to this package.
+ * Classes in other packages can attempt to acquire the access token,
+ * but will fail if they are not recognized as friends.
+ * Certain methods in this package, although public, require a non-null
+ * access token in order to proceed; they act like package-private methods.
+ * @author jrose
+ */
+
+public class Access {
+
+ private Access() { }
+
+ /**
+ * The heart of this pattern: The list of classes which are
+ * permitted to acquire the access token, and become honorary
+ * members of this package.
+ */
+ private static final String[] FRIENDS = {
+ "java.dyn.", "sun.dyn."
+ };
+
+ /**
+ * The following object is NOT public. That's the point of the pattern.
+ * It is package-private, so that any member of this package
+ * can acquire the access token, and give it away to trusted friends.
+ */
+ static final Access TOKEN = new Access();
+
+ /**
+ * @return Access.TOKEN, if the caller is a friend of this package
+ */
+ public static Access getToken() {
+ Class> callc = Reflection.getCallerClass(2);
+ if (isFriend(callc))
+ return TOKEN;
+ else
+ throw new IllegalAccessError("bad caller: " + callc);
+ }
+
+ /** Is the given name the name of a class which could be our friend? */
+ public static boolean isFriendName(String name) {
+ for (String friend : FRIENDS) {
+ if (name.startsWith(friend))
+ return true;
+ }
+ return false;
+ }
+
+ /** Is the given class a friend? True if {@link #isFriendName},
+ * and the given class also shares a class loader with us.
+ */
+ public static boolean isFriend(Class> c) {
+ return isFriendName(c.getName()) && c.getClassLoader() == CLASS_LOADER;
+ }
+
+ private static final ClassLoader CLASS_LOADER = Access.class.getClassLoader();
+
+ /**
+ * Throw an IllegalAccessError if the caller does not possess
+ * the Access.TOKEN.
+ * @param must be Access.TOKEN
+ */
+ public static void check(Access token) {
+ if (token == null)
+ fail();
+ // else it must be the unique Access.TOKEN
+ assert(token == Access.TOKEN);
+ }
+ private static void fail() {
+ final int CALLER_DEPTH = 3;
+ // 0: Reflection.getCC, 1: this.fail, 2: Access.*, 3: caller
+ Class> callc = Reflection.getCallerClass(CALLER_DEPTH);
+ throw new IllegalAccessError("bad caller: " + callc);
+ }
+
+ static {
+ //sun.reflect.Reflection.registerMethodsToFilter(MH.class, "getToken");
+ }
+}
diff --git a/src/share/classes/sun/dyn/AdapterMethodHandle.java b/src/share/classes/sun/dyn/AdapterMethodHandle.java
new file mode 100644
index 0000000000000000000000000000000000000000..334e0a563a71d8bea30ae9d02fd350fb7ff48cca
--- /dev/null
+++ b/src/share/classes/sun/dyn/AdapterMethodHandle.java
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import sun.dyn.util.VerifyType;
+import sun.dyn.util.Wrapper;
+import java.dyn.*;
+import java.util.Arrays;
+import static sun.dyn.MethodHandleNatives.Constants.*;
+import static sun.dyn.MethodHandleImpl.newIllegalArgumentException;
+
+/**
+ * This method handle performs simple conversion or checking of a single argument.
+ * @author jrose
+ */
+public class AdapterMethodHandle extends BoundMethodHandle {
+
+ //MethodHandle vmtarget; // next AMH or BMH in chain or final DMH
+ //Object argument; // parameter to the conversion if needed
+ //int vmargslot; // which argument slot is affected
+ private final int conversion; // the type of conversion: RETYPE_ONLY, etc.
+
+ // Constructors in this class *must* be package scoped or private.
+ private AdapterMethodHandle(MethodHandle target, MethodType newType,
+ long conv, Object convArg) {
+ super(newType, convArg, newType.parameterSlotDepth(1+convArgPos(conv)));
+ this.conversion = convCode(conv);
+ if (MethodHandleNatives.JVM_SUPPORT) {
+ // JVM might update VM-specific bits of conversion (ignore)
+ MethodHandleNatives.init(this, target, convArgPos(conv));
+ }
+ }
+ private AdapterMethodHandle(MethodHandle target, MethodType newType,
+ long conv) {
+ this(target, newType, conv, null);
+ }
+
+ private static final Access IMPL_TOKEN = Access.getToken();
+
+ // TO DO: When adapting another MH with a null conversion, clone
+ // the target and change its type, instead of adding another layer.
+
+ /** Can a JVM-level adapter directly implement the proposed
+ * argument conversions, as if by MethodHandles.convertArguments?
+ */
+ public static boolean canPairwiseConvert(MethodType newType, MethodType oldType) {
+ // same number of args, of course
+ int len = newType.parameterCount();
+ if (len != oldType.parameterCount())
+ return false;
+
+ // Check return type. (Not much can be done with it.)
+ Class> exp = newType.returnType();
+ Class> ret = oldType.returnType();
+ if (!VerifyType.isNullConversion(ret, exp))
+ return false;
+
+ // Check args pairwise.
+ for (int i = 0; i < len; i++) {
+ Class> src = newType.parameterType(i); // source type
+ Class> dst = oldType.parameterType(i); // destination type
+ if (!canConvertArgument(src, dst))
+ return false;
+ }
+
+ return true;
+ }
+
+ /** Can a JVM-level adapter directly implement the proposed
+ * argument conversion, as if by MethodHandles.convertArguments?
+ */
+ public static boolean canConvertArgument(Class> src, Class> dst) {
+ // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
+ // so we don't need to repeat so much decision making.
+ if (VerifyType.isNullConversion(src, dst)) {
+ return true;
+ } else if (src.isPrimitive()) {
+ if (dst.isPrimitive())
+ return canPrimCast(src, dst);
+ else
+ return canBoxArgument(src, dst);
+ } else {
+ if (dst.isPrimitive())
+ return canUnboxArgument(src, dst);
+ else
+ return true; // any two refs can be interconverted
+ }
+ }
+
+ /**
+ * Create a JVM-level adapter method handle to conform the given method
+ * handle to the similar newType, using only pairwise argument conversions.
+ * For each argument, convert incoming argument to the exact type needed.
+ * Only null conversions are allowed on the return value (until
+ * the JVM supports ricochet adapters).
+ * The argument conversions allowed are casting, unboxing,
+ * integral widening or narrowing, and floating point widening or narrowing.
+ * @param token access check
+ * @param newType required call type
+ * @param target original method handle
+ * @return an adapter to the original handle with the desired new type,
+ * or the original target if the types are already identical
+ * or null if the adaptation cannot be made
+ */
+ public static MethodHandle makePairwiseConvert(Access token,
+ MethodType newType, MethodHandle target) {
+ Access.check(token);
+ MethodType oldType = target.type();
+ if (newType == oldType) return target;
+
+ if (!canPairwiseConvert(newType, oldType))
+ return null;
+ // (after this point, it is an assertion error to fail to convert)
+
+ // Find last non-trivial conversion (if any).
+ int lastConv = newType.parameterCount()-1;
+ while (lastConv >= 0) {
+ Class> src = newType.parameterType(lastConv); // source type
+ Class> dst = oldType.parameterType(lastConv); // destination type
+ if (VerifyType.isNullConversion(src, dst)) {
+ --lastConv;
+ } else {
+ break;
+ }
+ }
+ // Now build a chain of one or more adapters.
+ MethodHandle adapter = target;
+ MethodType midType = oldType.changeReturnType(newType.returnType());
+ for (int i = 0; i <= lastConv; i++) {
+ Class> src = newType.parameterType(i); // source type
+ Class> dst = midType.parameterType(i); // destination type
+ if (VerifyType.isNullConversion(src, dst)) {
+ // do nothing: difference is trivial
+ continue;
+ }
+ // Work the current type backward toward the desired caller type:
+ if (i != lastConv) {
+ midType = midType.changeParameterType(i, src);
+ } else {
+ // When doing the last (or only) real conversion,
+ // force all remaining null conversions to happen also.
+ assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src)));
+ midType = newType;
+ }
+
+ // Tricky case analysis follows.
+ // It parallels canConvertArgument() above.
+ if (src.isPrimitive()) {
+ if (dst.isPrimitive()) {
+ adapter = makePrimCast(token, midType, adapter, i, dst);
+ } else {
+ adapter = makeBoxArgument(token, midType, adapter, i, dst);
+ }
+ } else {
+ if (dst.isPrimitive()) {
+ // Caller has boxed a primitive. Unbox it for the target.
+ // The box type must correspond exactly to the primitive type.
+ // This is simpler than the powerful set of widening
+ // conversions supported by reflect.Method.invoke.
+ // Those conversions require a big nest of if/then/else logic,
+ // which we prefer to make a user responsibility.
+ adapter = makeUnboxArgument(token, midType, adapter, i, dst);
+ } else {
+ // Simple reference conversion.
+ // Note: Do not check for a class hierarchy relation
+ // between src and dst. In all cases a 'null' argument
+ // will pass the cast conversion.
+ adapter = makeCheckCast(token, midType, adapter, i, dst);
+ }
+ }
+ assert(adapter != null);
+ assert(adapter.type() == midType);
+ }
+ if (adapter.type() != newType) {
+ // Only trivial conversions remain.
+ adapter = makeRetypeOnly(IMPL_TOKEN, newType, adapter);
+ assert(adapter != null);
+ // Actually, that's because there were no non-trivial ones:
+ assert(lastConv == -1);
+ }
+ assert(adapter.type() == newType);
+ return adapter;
+ }
+
+ /**
+ * Create a JVM-level adapter method handle to permute the arguments
+ * of the given method.
+ * @param token access check
+ * @param newType required call type
+ * @param target original method handle
+ * @param argumentMap for each target argument, position of its source in newType
+ * @return an adapter to the original handle with the desired new type,
+ * or the original target if the types are already identical
+ * and the permutation is null
+ * @throws IllegalArgumentException if the adaptation cannot be made
+ * directly by a JVM-level adapter, without help from Java code
+ */
+ public static MethodHandle makePermutation(Access token,
+ MethodType newType, MethodHandle target,
+ int[] argumentMap) {
+ MethodType oldType = target.type();
+ boolean nullPermutation = true;
+ for (int i = 0; i < argumentMap.length; i++) {
+ int pos = argumentMap[i];
+ if (pos != i)
+ nullPermutation = false;
+ if (pos < 0 || pos >= newType.parameterCount()) {
+ argumentMap = new int[0]; break;
+ }
+ }
+ if (argumentMap.length != oldType.parameterCount())
+ throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
+ if (nullPermutation) {
+ MethodHandle res = makePairwiseConvert(token, newType, target);
+ // well, that was easy
+ if (res == null)
+ throw newIllegalArgumentException("cannot convert pairwise: "+newType);
+ return res;
+ }
+
+ // Check return type. (Not much can be done with it.)
+ Class> exp = newType.returnType();
+ Class> ret = oldType.returnType();
+ if (!VerifyType.isNullConversion(ret, exp))
+ throw newIllegalArgumentException("bad return conversion for "+newType);
+
+ // See if the argument types match up.
+ for (int i = 0; i < argumentMap.length; i++) {
+ int j = argumentMap[i];
+ Class> src = newType.parameterType(j);
+ Class> dst = oldType.parameterType(i);
+ if (!VerifyType.isNullConversion(src, dst))
+ throw newIllegalArgumentException("bad argument #"+j+" conversion for "+newType);
+ }
+
+ // Now figure out a nice mix of SWAP, ROT, DUP, and DROP adapters.
+ // A workable greedy algorithm is as follows:
+ // Drop unused outgoing arguments (right to left: shallowest first).
+ // Duplicate doubly-used outgoing arguments (left to right: deepest first).
+ // Then the remaining problem is a true argument permutation.
+ // Marshal the outgoing arguments as required from left to right.
+ // That is, find the deepest outgoing stack position that does not yet
+ // have the correct argument value, and correct at least that position
+ // by swapping or rotating in the misplaced value (from a shallower place).
+ // If the misplaced value is followed by one or more consecutive values
+ // (also misplaced) issue a rotation which brings as many as possible
+ // into position. Otherwise make progress with either a swap or a
+ // rotation. Prefer the swap as cheaper, but do not use it if it
+ // breaks a slot pair. Prefer the rotation over the swap if it would
+ // preserve more consecutive values shallower than the target position.
+ // When more than one rotation will work (because the required value
+ // is already adjacent to the target position), then use a rotation
+ // which moves the old value in the target position adjacent to
+ // one of its consecutive values. Also, prefer shorter rotation
+ // spans, since they use fewer memory cycles for shuffling.
+
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ private static byte basicType(Class> type) {
+ if (type == null) return T_VOID;
+ switch (Wrapper.forBasicType(type)) {
+ case BOOLEAN: return T_BOOLEAN;
+ case CHAR: return T_CHAR;
+ case FLOAT: return T_FLOAT;
+ case DOUBLE: return T_DOUBLE;
+ case BYTE: return T_BYTE;
+ case SHORT: return T_SHORT;
+ case INT: return T_INT;
+ case LONG: return T_LONG;
+ case OBJECT: return T_OBJECT;
+ case VOID: return T_VOID;
+ }
+ return 99; // T_ILLEGAL or some such
+ }
+
+ /** Number of stack slots for the given type.
+ * Two for T_DOUBLE and T_FLOAT, one for the rest.
+ */
+ private static int type2size(int type) {
+ assert(type >= T_BOOLEAN && type <= T_OBJECT);
+ return (type == T_FLOAT || type == T_DOUBLE) ? 2 : 1;
+ }
+
+ /** Construct an adapter conversion descriptor for a single-argument conversion. */
+ private static long makeConv(int convOp, int argnum, int src, int dest) {
+ assert(src == (src & 0xF));
+ assert(dest == (dest & 0xF));
+ assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF);
+ long stackMove = type2size(dest) - type2size(src);
+ return ((long) argnum << 32 |
+ (long) convOp << CONV_OP_SHIFT |
+ (int) src << CONV_SRC_TYPE_SHIFT |
+ (int) dest << CONV_DEST_TYPE_SHIFT |
+ stackMove << CONV_STACK_MOVE_SHIFT
+ );
+ }
+ private static long makeConv(int convOp, int argnum, int stackMove) {
+ assert(convOp >= OP_SWAP_ARGS && convOp <= OP_SPREAD_ARGS);
+ byte src = 0, dest = 0;
+ if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS)
+ src = dest = T_OBJECT;
+ return ((long) argnum << 32 |
+ (long) convOp << CONV_OP_SHIFT |
+ (int) src << CONV_SRC_TYPE_SHIFT |
+ (int) dest << CONV_DEST_TYPE_SHIFT |
+ stackMove << CONV_STACK_MOVE_SHIFT
+ );
+ }
+ private static long makeConv(int convOp) {
+ assert(convOp == OP_RETYPE_ONLY);
+ return (long) convOp << CONV_OP_SHIFT; // stackMove, src, dst, argnum all zero
+ }
+ private static int convCode(long conv) {
+ return (int)conv;
+ }
+ private static int convArgPos(long conv) {
+ return (int)(conv >>> 32);
+ }
+ private static boolean convOpSupported(int convOp) {
+ assert(convOp >= 0 && convOp <= CONV_OP_LIMIT);
+ return ((1<> CONV_OP_SHIFT; }
+
+ @Override
+ public String toString() {
+ return addTypeString(this, "Adapted[" + basicToString(nonAdapter((MethodHandle)vmtarget)) + "]");
+ }
+
+ private static MethodHandle nonAdapter(MethodHandle mh) {
+ return (MethodHandle)
+ MethodHandleNatives.getTarget(mh, ETF_DIRECT_HANDLE);
+ }
+
+ /* Return one plus the position of the first non-trivial difference
+ * between the given types. This is not a symmetric operation;
+ * we are considering adapting the targetType to adapterType.
+ * Trivial differences are those which could be ignored by the JVM
+ * without subverting the verifier. Otherwise, adaptable differences
+ * are ones for which we could create an adapter to make the type change.
+ * Return zero if there are no differences (other than trivial ones).
+ * Return 1+N if N is the only adaptable argument difference.
+ * Return the -2-N where N is the first of several adaptable
+ * argument differences.
+ * Return -1 if there there are differences which are not adaptable.
+ */
+ private static int diffTypes(MethodType adapterType,
+ MethodType targetType,
+ boolean raw) {
+ int diff;
+ diff = diffReturnTypes(adapterType, targetType, raw);
+ if (diff != 0) return diff;
+ int nargs = adapterType.parameterCount();
+ if (nargs != targetType.parameterCount())
+ return -1;
+ diff = diffParamTypes(adapterType, 0, targetType, 0, nargs, raw);
+ //System.out.println("diff "+adapterType);
+ //System.out.println(" "+diff+" "+targetType);
+ return diff;
+ }
+ private static int diffReturnTypes(MethodType adapterType,
+ MethodType targetType,
+ boolean raw) {
+ Class> src = targetType.returnType();
+ Class> dst = adapterType.returnType();
+ if ((!raw
+ ? VerifyType.canPassUnchecked(src, dst)
+ : VerifyType.canPassRaw(src, dst)
+ ) > 0)
+ return 0; // no significant difference
+ if (raw && !src.isPrimitive() && !dst.isPrimitive())
+ return 0; // can force a reference return (very carefully!)
+ //if (false) return 1; // never adaptable!
+ return -1; // some significant difference
+ }
+ private static int diffParamTypes(MethodType adapterType, int tstart,
+ MethodType targetType, int astart,
+ int nargs, boolean raw) {
+ assert(nargs >= 0);
+ int res = 0;
+ for (int i = 0; i < nargs; i++) {
+ Class> src = adapterType.parameterType(tstart+i);
+ Class> dest = targetType.parameterType(astart+i);
+ if ((!raw
+ ? VerifyType.canPassUnchecked(src, dest)
+ : VerifyType.canPassRaw(src, dest)
+ ) <= 0) {
+ // found a difference; is it the only one so far?
+ if (res != 0)
+ return -1-res; // return -2-i for prev. i
+ res = 1+i;
+ }
+ }
+ return res;
+ }
+
+ /** Can a retyping adapter (alone) validly convert the target to newType? */
+ public static boolean canRetypeOnly(MethodType newType, MethodType targetType) {
+ return canRetypeOnly(newType, targetType, false);
+ }
+ /** Can a retyping adapter (alone) convert the target to newType?
+ * It is allowed to widen subword types and void to int, to make bitwise
+ * conversions between float/int and double/long, and to perform unchecked
+ * reference conversions on return. This last feature requires that the
+ * caller be trusted, and perform explicit cast conversions on return values.
+ */
+ static boolean canRawRetypeOnly(MethodType newType, MethodType targetType) {
+ return canRetypeOnly(newType, targetType, true);
+ }
+ static boolean canRetypeOnly(MethodType newType, MethodType targetType, boolean raw) {
+ if (!convOpSupported(OP_RETYPE_ONLY)) return false;
+ int diff = diffTypes(newType, targetType, raw);
+ // %%% This assert is too strong. Factor diff into VerifyType and reconcile.
+ assert((diff == 0) == VerifyType.isNullConversion(newType, targetType));
+ return diff == 0;
+ }
+
+ /** Factory method: Performs no conversions; simply retypes the adapter.
+ * Allows unchecked argument conversions pairwise, if they are safe.
+ * Returns null if not possible.
+ */
+ public static MethodHandle makeRetypeOnly(Access token,
+ MethodType newType, MethodHandle target) {
+ return makeRetypeOnly(token, newType, target, false);
+ }
+ public static MethodHandle makeRawRetypeOnly(Access token,
+ MethodType newType, MethodHandle target) {
+ return makeRetypeOnly(token, newType, target, true);
+ }
+ static MethodHandle makeRetypeOnly(Access token,
+ MethodType newType, MethodHandle target, boolean raw) {
+ Access.check(token);
+ if (!canRetypeOnly(newType, target.type(), raw))
+ return null;
+ // TO DO: clone the target guy, whatever he is, with new type.
+ return new AdapterMethodHandle(target, newType, makeConv(OP_RETYPE_ONLY));
+ }
+
+ /** Can a checkcast adapter validly convert the target to newType?
+ * The JVM supports all kind of reference casts, even silly ones.
+ */
+ public static boolean canCheckCast(MethodType newType, MethodType targetType,
+ int arg, Class> castType) {
+ if (!convOpSupported(OP_CHECK_CAST)) return false;
+ Class> src = newType.parameterType(arg);
+ Class> dst = targetType.parameterType(arg);
+ if (!canCheckCast(src, castType)
+ || !VerifyType.isNullConversion(castType, dst))
+ return false;
+ int diff = diffTypes(newType, targetType, false);
+ return (diff == arg+1); // arg is sole non-trivial diff
+ }
+ /** Can an primitive conversion adapter validly convert src to dst? */
+ public static boolean canCheckCast(Class> src, Class> dst) {
+ return (!src.isPrimitive() && !dst.isPrimitive());
+ }
+
+ /** Factory method: Forces a cast at the given argument.
+ * The castType is the target of the cast, and can be any type
+ * with a null conversion to the corresponding target parameter.
+ * Return null if this cannot be done.
+ */
+ public static MethodHandle makeCheckCast(Access token,
+ MethodType newType, MethodHandle target,
+ int arg, Class> castType) {
+ Access.check(token);
+ if (!canCheckCast(newType, target.type(), arg, castType))
+ return null;
+ long conv = makeConv(OP_CHECK_CAST, arg, 0);
+ return new AdapterMethodHandle(target, newType, conv, castType);
+ }
+
+ /** Can an primitive conversion adapter validly convert the target to newType?
+ * The JVM currently supports all conversions except those between
+ * floating and integral types.
+ */
+ public static boolean canPrimCast(MethodType newType, MethodType targetType,
+ int arg, Class> convType) {
+ if (!convOpSupported(OP_PRIM_TO_PRIM)) return false;
+ Class> src = newType.parameterType(arg);
+ Class> dst = targetType.parameterType(arg);
+ if (!canPrimCast(src, convType)
+ || !VerifyType.isNullConversion(convType, dst))
+ return false;
+ int diff = diffTypes(newType, targetType, false);
+ return (diff == arg+1); // arg is sole non-trivial diff
+ }
+ /** Can an primitive conversion adapter validly convert src to dst? */
+ public static boolean canPrimCast(Class> src, Class> dst) {
+ if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
+ return false;
+ } else if (Wrapper.forPrimitiveType(dst).isFloating()) {
+ // both must be floating types
+ return Wrapper.forPrimitiveType(src).isFloating();
+ } else {
+ // both are integral, and all combinations work fine
+ assert(Wrapper.forPrimitiveType(src).isIntegral() &&
+ Wrapper.forPrimitiveType(dst).isIntegral());
+ return true;
+ }
+ }
+
+ /** Factory method: Truncate the given argument with zero or sign extension,
+ * and/or convert between single and doubleword versions of integer or float.
+ * The convType is the target of the conversion, and can be any type
+ * with a null conversion to the corresponding target parameter.
+ * Return null if this cannot be done.
+ */
+ public static MethodHandle makePrimCast(Access token,
+ MethodType newType, MethodHandle target,
+ int arg, Class> convType) {
+ Access.check(token);
+ MethodType oldType = target.type();
+ Class> src = newType.parameterType(arg);
+ Class> dst = oldType.parameterType(arg);
+ if (!canPrimCast(newType, oldType, arg, convType))
+ return null;
+ long conv = makeConv(OP_PRIM_TO_PRIM, arg, basicType(src), basicType(convType));
+ return new AdapterMethodHandle(target, newType, conv);
+ }
+
+ /** Can an unboxing conversion validly convert src to dst?
+ * The JVM currently supports all kinds of casting and unboxing.
+ * The convType is the unboxed type; it can be either a primitive or wrapper.
+ */
+ public static boolean canUnboxArgument(MethodType newType, MethodType targetType,
+ int arg, Class> convType) {
+ if (!convOpSupported(OP_REF_TO_PRIM)) return false;
+ Class> src = newType.parameterType(arg);
+ Class> dst = targetType.parameterType(arg);
+ Class> boxType = Wrapper.asWrapperType(convType);
+ convType = Wrapper.asPrimitiveType(convType);
+ if (!canCheckCast(src, boxType)
+ || boxType == convType
+ || !VerifyType.isNullConversion(convType, dst))
+ return false;
+ int diff = diffTypes(newType, targetType, false);
+ return (diff == arg+1); // arg is sole non-trivial diff
+ }
+ /** Can an primitive unboxing adapter validly convert src to dst? */
+ public static boolean canUnboxArgument(Class> src, Class> dst) {
+ return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive());
+ }
+
+ /** Factory method: Unbox the given argument.
+ * Return null if this cannot be done.
+ */
+ public static MethodHandle makeUnboxArgument(Access token,
+ MethodType newType, MethodHandle target,
+ int arg, Class> convType) {
+ MethodType oldType = target.type();
+ Class> src = newType.parameterType(arg);
+ Class> dst = oldType.parameterType(arg);
+ Class> boxType = Wrapper.asWrapperType(convType);
+ Class> primType = Wrapper.asPrimitiveType(convType);
+ if (!canUnboxArgument(newType, oldType, arg, convType))
+ return null;
+ MethodType castDone = newType;
+ if (!VerifyType.isNullConversion(src, boxType))
+ castDone = newType.changeParameterType(arg, boxType);
+ long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType));
+ MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType);
+ if (castDone == newType)
+ return adapter;
+ return makeCheckCast(token, newType, adapter, arg, boxType);
+ }
+
+ /** Can an primitive boxing adapter validly convert src to dst? */
+ public static boolean canBoxArgument(Class> src, Class> dst) {
+ if (!convOpSupported(OP_PRIM_TO_REF)) return false;
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /** Factory method: Unbox the given argument.
+ * Return null if this cannot be done.
+ */
+ public static MethodHandle makeBoxArgument(Access token,
+ MethodType newType, MethodHandle target,
+ int arg, Class> convType) {
+ // this is difficult to do in the JVM because it must GC
+ return null;
+ }
+
+ // TO DO: makeSwapArguments, makeRotateArguments, makeDuplicateArguments
+
+ /** Can an adapter simply drop arguments to convert the target to newType? */
+ public static boolean canDropArguments(MethodType newType, MethodType targetType,
+ int dropArgPos, int dropArgCount) {
+ if (dropArgCount == 0)
+ return canRetypeOnly(newType, targetType);
+ if (!convOpSupported(OP_DROP_ARGS)) return false;
+ if (diffReturnTypes(newType, targetType, false) != 0)
+ return false;
+ int nptypes = newType.parameterCount();
+ // parameter types must be the same up to the drop point
+ if (dropArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, dropArgPos, false) != 0)
+ return false;
+ int afterPos = dropArgPos + dropArgCount;
+ int afterCount = nptypes - afterPos;
+ if (dropArgPos < 0 || dropArgPos >= nptypes ||
+ dropArgCount < 1 || afterPos > nptypes ||
+ targetType.parameterCount() != nptypes - dropArgCount)
+ return false;
+ // parameter types after the drop point must also be the same
+ if (afterCount != 0 && diffParamTypes(newType, afterPos, targetType, dropArgPos, afterCount, false) != 0)
+ return false;
+ return true;
+ }
+
+ /** Factory method: Drop selected arguments.
+ * Allow unchecked retyping of remaining arguments, pairwise.
+ * Return null if this is not possible.
+ */
+ public static MethodHandle makeDropArguments(Access token,
+ MethodType newType, MethodHandle target,
+ int dropArgPos, int dropArgCount) {
+ Access.check(token);
+ if (dropArgCount == 0)
+ return makeRetypeOnly(IMPL_TOKEN, newType, target);
+ MethodType mt = target.type();
+ int argCount = mt.parameterCount();
+ if (!canDropArguments(newType, mt, dropArgPos, dropArgCount))
+ return null;
+ int dropSlotCount, dropSlotPos;
+ if (dropArgCount >= argCount) {
+ assert(dropArgPos == argCount-1);
+ dropSlotPos = 0;
+ dropSlotCount = mt.parameterSlotCount();
+ } else {
+ // arglist: [0: keep... | dpos: drop... | dpos+dcount: keep... ]
+ int lastDroppedArg = dropArgPos + dropArgCount - 1;
+ int lastKeptArg = dropArgPos - 1; // might be -1, which is OK
+ dropSlotPos = mt.parameterSlotDepth(1+lastDroppedArg);
+ int lastKeptSlot = mt.parameterSlotDepth(1+lastKeptArg);
+ dropSlotCount = lastKeptSlot - dropSlotPos;
+ assert(dropSlotCount >= dropArgCount);
+ }
+ long conv = makeConv(OP_DROP_ARGS, dropArgPos, +dropSlotCount);
+ return new AdapterMethodHandle(target, newType, dropSlotCount, conv);
+ }
+
+ /** Can an adapter spread an argument to convert the target to newType? */
+ public static boolean canSpreadArguments(MethodType newType, MethodType targetType,
+ Class> spreadArgType, int spreadArgPos, int spreadArgCount) {
+ if (!convOpSupported(OP_SPREAD_ARGS)) return false;
+ if (diffReturnTypes(newType, targetType, false) != 0)
+ return false;
+ int nptypes = newType.parameterCount();
+ // parameter types must be the same up to the spread point
+ if (spreadArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, spreadArgPos, false) != 0)
+ return false;
+ int afterPos = spreadArgPos + spreadArgCount;
+ int afterCount = nptypes - afterPos;
+ if (spreadArgPos < 0 || spreadArgPos >= nptypes ||
+ spreadArgCount < 0 ||
+ targetType.parameterCount() != nptypes - 1 + spreadArgCount)
+ return false;
+ // parameter types after the spread point must also be the same
+ if (afterCount != 0 && diffParamTypes(newType, spreadArgPos+1, targetType, afterPos, afterCount, false) != 0)
+ return false;
+ // match the array element type to the spread arg types
+ Class> rawSpreadArgType = newType.parameterType(spreadArgPos);
+ if (rawSpreadArgType != spreadArgType && !canCheckCast(rawSpreadArgType, spreadArgType))
+ return false;
+ for (int i = 0; i < spreadArgCount; i++) {
+ Class> src = VerifyType.spreadArgElementType(spreadArgType, i);
+ Class> dst = targetType.parameterType(spreadArgPos + i);
+ if (src == null || !VerifyType.isNullConversion(src, dst))
+ return false;
+ }
+ return true;
+ }
+
+ /** Factory method: Spread selected argument. */
+ public static MethodHandle makeSpreadArguments(Access token,
+ MethodType newType, MethodHandle target,
+ Class> spreadArgType, int spreadArgPos, int spreadArgCount) {
+ Access.check(token);
+ MethodType mt = target.type();
+ int argCount = mt.parameterCount();
+ if (!canSpreadArguments(newType, mt, spreadArgType, spreadArgPos, spreadArgCount))
+ return null;
+ int spreadSlotCount, spreadSlotPos;
+ if (spreadArgCount >= argCount) {
+ assert(spreadArgPos == argCount-1);
+ spreadSlotPos = 0;
+ spreadSlotCount = mt.parameterSlotCount();
+ } else {
+ // arglist: [0: keep... | dpos: spread... | dpos+dcount: keep... ]
+ int lastSpreadArg = spreadArgPos + spreadArgCount - 1;
+ int lastKeptArg = spreadArgPos - 1; // might be -1, which is OK
+ spreadSlotPos = mt.parameterSlotDepth(1+lastSpreadArg);
+ int lastKeptSlot = mt.parameterSlotDepth(1+lastKeptArg);
+ spreadSlotCount = lastKeptSlot - spreadSlotPos;
+ assert(spreadSlotCount >= spreadArgCount);
+ }
+ long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, spreadSlotCount);
+ return new AdapterMethodHandle(target, newType, conv, spreadArgType);
+ }
+
+ // TO DO: makeCollectArguments, makeFlyby, makeRicochet
+}
diff --git a/src/share/classes/sun/dyn/BoundMethodHandle.java b/src/share/classes/sun/dyn/BoundMethodHandle.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0c511c2171693085cb4fe092416590e29421960
--- /dev/null
+++ b/src/share/classes/sun/dyn/BoundMethodHandle.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import sun.dyn.util.VerifyType;
+import sun.dyn.util.Wrapper;
+import java.dyn.*;
+
+/**
+ * The flavor of method handle which emulates an invoke instruction
+ * on a predetermined argument. The JVM dispatches to the correct method
+ * when the handle is created, not when it is invoked.
+ * @author jrose
+ */
+public class BoundMethodHandle extends MethodHandle {
+ //MethodHandle vmtarget; // next BMH or final DMH or methodOop
+ private final Object argument; // argument to insert
+ private final int vmargslot; // position at which it is inserted
+
+ // Constructors in this class *must* be package scoped or private.
+
+ /** Bind a direct MH to its receiver (or first ref. argument).
+ * The JVM will pre-dispatch the MH if it is not already static.
+ */
+ BoundMethodHandle(DirectMethodHandle mh, Object argument) {
+ super(Access.TOKEN, mh.type().dropParameterType(0));
+ // check the type now, once for all:
+ this.argument = checkReferenceArgument(argument, mh, 0);
+ this.vmargslot = this.type().parameterSlotCount();
+ if (MethodHandleNatives.JVM_SUPPORT) {
+ this.vmtarget = null; // maybe updated by JVM
+ MethodHandleNatives.init(this, mh, 0);
+ } else {
+ this.vmtarget = mh;
+ }
+ }
+
+ private static final int REF_ARG = 0, PRIM_ARG = 1, SELF_ARG = 2;
+
+ /** Insert an argument into an arbitrary method handle.
+ * If argnum is zero, inserts the first argument, etc.
+ * The argument type must be a reference.
+ */
+ BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
+ this(mh, argument, argnum, mh.type().parameterType(argnum).isPrimitive() ? PRIM_ARG : REF_ARG);
+ }
+
+ /** Insert an argument into an arbitrary method handle.
+ * If argnum is zero, inserts the first argument, etc.
+ */
+ BoundMethodHandle(MethodHandle mh, Object argument, int argnum, int whichArg) {
+ super(Access.TOKEN, mh.type().dropParameterType(argnum));
+ if (whichArg == PRIM_ARG)
+ this.argument = bindPrimitiveArgument(argument, mh, argnum);
+ else {
+ if (whichArg == SELF_ARG) argument = this;
+ this.argument = checkReferenceArgument(argument, mh, argnum);
+ }
+ this.vmargslot = this.type().parameterSlotDepth(argnum);
+ if (MethodHandleNatives.JVM_SUPPORT) {
+ this.vmtarget = null; // maybe updated by JVM
+ MethodHandleNatives.init(this, mh, argnum);
+ } else {
+ this.vmtarget = mh;
+ }
+ }
+
+ /** For the AdapterMethodHandle subclass.
+ */
+ BoundMethodHandle(MethodType type, Object argument, int vmargslot) {
+ super(Access.TOKEN, type);
+ this.argument = argument;
+ this.vmargslot = vmargslot;
+ assert(this.getClass() == AdapterMethodHandle.class);
+ }
+
+ /** Initialize the current object as a method handle, binding it
+ * as the {@code argnum}th argument of the method handle {@code entryPoint}.
+ * The invocation type of the resulting method handle will be the
+ * same as {@code entryPoint}, except that the {@code argnum}th argument
+ * type will be dropped.
+ */
+ public BoundMethodHandle(MethodHandle entryPoint, int argnum) {
+ this(entryPoint, null, argnum, SELF_ARG);
+
+ // Note: If the conversion fails, perhaps because of a bad entryPoint,
+ // the MethodHandle.type field will not be filled in, and therefore
+ // no MH.invoke call will ever succeed. The caller may retain a pointer
+ // to the broken method handle, but no harm can be done with it.
+ }
+
+ /** Initialize the current object as a method handle, binding it
+ * as the first argument of the method handle {@code entryPoint}.
+ * The invocation type of the resulting method handle will be the
+ * same as {@code entryPoint}, except that the first argument
+ * type will be dropped.
+ */
+ public BoundMethodHandle(MethodHandle entryPoint) {
+ this(entryPoint, null, 0, SELF_ARG);
+ }
+
+ /** Make sure the given {@code argument} can be used as {@code argnum}-th
+ * parameter of the given method handle {@code mh}, which must be a reference.
+ *
+ * If this fails, throw a suitable {@code WrongMethodTypeException},
+ * which will prevent the creation of an illegally typed bound
+ * method handle.
+ */
+ final static Object checkReferenceArgument(Object argument, MethodHandle mh, int argnum) {
+ Class> ptype = mh.type().parameterType(argnum);
+ if (ptype.isPrimitive()) {
+ // fail
+ } else if (argument == null) {
+ return null;
+ } else if (VerifyType.isNullReferenceConversion(argument.getClass(), ptype)) {
+ return argument;
+ }
+ throw badBoundArgumentException(argument, mh, argnum);
+ }
+
+ /** Make sure the given {@code argument} can be used as {@code argnum}-th
+ * parameter of the given method handle {@code mh}, which must be a primitive.
+ *
+ * If this fails, throw a suitable {@code WrongMethodTypeException},
+ * which will prevent the creation of an illegally typed bound
+ * method handle.
+ */
+ final static Object bindPrimitiveArgument(Object argument, MethodHandle mh, int argnum) {
+ Class> ptype = mh.type().parameterType(argnum);
+ Wrapper wrap = Wrapper.forPrimitiveType(ptype);
+ Object zero = wrap.zero();
+ if (zero == null) {
+ // fail
+ } else if (argument == null) {
+ if (ptype != int.class && wrap.isSubwordOrInt())
+ return Integer.valueOf(0);
+ else
+ return zero;
+ } else if (VerifyType.isNullReferenceConversion(argument.getClass(), zero.getClass())) {
+ if (ptype != int.class && wrap.isSubwordOrInt())
+ return Wrapper.INT.wrap(argument);
+ else
+ return argument;
+ }
+ throw badBoundArgumentException(argument, mh, argnum);
+ }
+
+ final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) {
+ String atype = (argument == null) ? "null" : argument.getClass().toString();
+ return new WrongMethodTypeException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
+ }
+
+ @Override
+ public String toString() {
+ return "Bound[" + super.toString() + "]";
+ }
+}
diff --git a/src/share/classes/sun/dyn/CallSiteImpl.java b/src/share/classes/sun/dyn/CallSiteImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5ac600eacd416f7e6d923755beb0a4e49c30415
--- /dev/null
+++ b/src/share/classes/sun/dyn/CallSiteImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.*;
+
+/**
+ * The CallSite privately created by the JVM at every invokedynamic instruction.
+ * @author jrose
+ */
+class CallSiteImpl extends CallSite {
+ // Fields used only by the JVM. Do not use or change.
+ private Object vmmethod;
+
+ // Values supplied by the JVM:
+ int callerMID, callerBCI;
+
+ private CallSiteImpl(Class> caller, String name, MethodType type) {
+ super(caller, name, type);
+ }
+
+ @Override
+ public void setTarget(MethodHandle mh) {
+ checkTarget(mh);
+ if (MethodHandleNatives.JVM_SUPPORT)
+ MethodHandleNatives.linkCallSite(this, (MethodHandle) mh);
+ else
+ super.setTarget(mh);
+ }
+
+ private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE =
+ MethodHandleImpl.IMPL_LOOKUP.findStatic(CallSite.class, "privateInitializeCallSite",
+ MethodType.make(void.class, CallSite.class, int.class, int.class));
+
+ // this is the up-call from the JVM:
+ static CallSite makeSite(Class> caller, String name, MethodType type,
+ int callerMID, int callerBCI) {
+ MethodHandle bsm = Linkage.getBootstrapMethod(caller);
+ if (bsm == null)
+ throw new InvokeDynamicBootstrapError("class has no bootstrap method: "+caller);
+ CallSite site = bsm.invoke(caller, name, type);
+ if (site == null)
+ throw new InvokeDynamicBootstrapError("class bootstrap method failed to create a call site: "+caller);
+ PRIVATE_INITIALIZE_CALL_SITE.invoke(site, callerMID, callerBCI);
+ return site;
+ }
+}
diff --git a/src/share/classes/sun/dyn/DirectMethodHandle.java b/src/share/classes/sun/dyn/DirectMethodHandle.java
new file mode 100644
index 0000000000000000000000000000000000000000..d86b64a61de2c371dd24fe8d3d34b6835688b25c
--- /dev/null
+++ b/src/share/classes/sun/dyn/DirectMethodHandle.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.*;
+import static sun.dyn.MethodHandleNatives.Constants.*;
+
+/**
+ * The flavor of method handle which emulates invokespecial or invokestatic.
+ * @author jrose
+ */
+class DirectMethodHandle extends MethodHandle {
+ //inherited oop vmtarget; // methodOop or virtual class/interface oop
+ private final int vmindex; // method index within class or interface
+ { vmindex = VM_INDEX_UNINITIALIZED; } // JVM may change this
+
+ // Constructors in this class *must* be package scoped or private.
+ DirectMethodHandle(MethodType mtype, MemberName m, boolean doDispatch, Class> lookupClass) {
+ super(Access.TOKEN, mtype);
+
+ assert(m.isMethod() || !doDispatch && m.isConstructor());
+ if (!m.isResolved())
+ throw new InternalError();
+
+ // Null check and replace privilege token (as passed to JVM) with null.
+ if (lookupClass.equals(Access.class)) lookupClass = null;
+ MethodHandleNatives.init(this, (Object) m, doDispatch, lookupClass);
+ }
+
+ boolean isValid() {
+ return (vmindex != VM_INDEX_UNINITIALIZED);
+ }
+}
diff --git a/src/share/classes/sun/dyn/FilterGeneric.java b/src/share/classes/sun/dyn/FilterGeneric.java
new file mode 100644
index 0000000000000000000000000000000000000000..a99eb26c9eef9d1d8c7479069bc3479390129c26
--- /dev/null
+++ b/src/share/classes/sun/dyn/FilterGeneric.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sf, tifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.JavaMethodHandle;
+import java.dyn.MethodHandle;
+import java.dyn.MethodType;
+import java.dyn.NoAccessException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * "Flyby adapters" which apply arbitrary conversions to arguments
+ * on the way to a ultimate target.
+ * For simplicity, these are all generically typed.
+ * @author jrose
+ */
+class FilterGeneric {
+ // type for the outgoing call (will be generic)
+ private final MethodType targetType;
+ // position of (first) argument to participate in filtering
+ private final short argumentPosition;
+ // number of arguments to participate in filtering
+ private final short argumentCount;
+ // how the result interacts with the filtered arguments: Prepend, Append, Replace, Discard
+ private final char replaceMode;
+ // prototype adapter (clone and customize for each new target & conversion!)
+ private final Adapter adapter;
+ // entry point for adapter (Adapter mh, a...) => ...
+ private final MethodHandle entryPoint;
+ // more of them (loosely cached)
+ private FilterGeneric variations;
+
+ /** Compute and cache information common to all unboxing adapters
+ * that can call out to targets of the erasure-family of the given erased type.
+ */
+ // TO DO: Make this private.
+ FilterGeneric(MethodType targetType, short argumentPosition, short argumentCount, char replaceMode) {
+ if (argumentCount == 0) {
+ if (replaceMode == 'P' || replaceMode == 'A') replaceMode = 'R';
+ if (replaceMode == 'I') argumentPosition = 0;
+ }
+ this.targetType = targetType;
+ this.argumentPosition = argumentPosition;
+ this.argumentCount = argumentCount;
+ this.replaceMode = replaceMode;
+ validate(targetType, argumentPosition, argumentCount, replaceMode);
+ Adapter ad = findAdapter(targetType, argumentPosition, argumentCount, replaceMode, filterType());
+ if (ad == null)
+ ad = buildAdapterFromBytecodes(targetType, argumentPosition, argumentCount, replaceMode, filterType());
+ this.adapter = ad;
+ this.entryPoint = ad.prototypeEntryPoint();
+ }
+
+ Adapter makeInstance(MethodHandle filter, MethodHandle target) {
+ return adapter.makeInstance(entryPoint, filter, target);
+ }
+
+ /** Build an adapter of the given generic type, which invokes typedTarget
+ * on the incoming arguments, after unboxing as necessary.
+ * The return value is boxed if necessary.
+ * @param genericType the required type of the result
+ * @param typedTarget the target
+ * @return an adapter method handle
+ */
+ public static MethodHandle make(MethodHandle target, int pos, MethodHandle filter) {
+ return FilterGeneric.of(target.type(), (short)pos, (short)1, 'R').makeInstance(filter, target);
+ }
+
+ /** Return the adapter information for this type's erasure. */
+ static FilterGeneric of(MethodType type, short ap, short ac, char mode) {
+ if (type.generic() != type)
+ throw new IllegalArgumentException("must be generic: "+type);
+ validate(type, ap, ac, mode);
+ MethodTypeImpl form = MethodTypeImpl.of(type);
+ FilterGeneric filterGen = form.filterGeneric;
+ if (filterGen == null)
+ form.filterGeneric = filterGen = new FilterGeneric(type, (short)0, (short)1, 'R');
+ return find(filterGen, ap, ac, mode);
+ }
+
+ static FilterGeneric find(FilterGeneric gen, short ap, short ac, char mode) {
+ for (;;) {
+ if (gen.argumentPosition == ap &&
+ gen.argumentCount == ac &&
+ gen.replaceMode == mode) {
+ return gen;
+ }
+ FilterGeneric gen2 = gen.variations;
+ if (gen2 == null) break;
+ gen = gen2;
+ }
+ FilterGeneric gen2 = new FilterGeneric(gen.targetType, ap, ac, mode);
+ gen.variations = gen2; // OK if this smashes another cached chain
+ return gen2;
+ }
+
+ private static void validate(MethodType type, short ap, short ac, char mode) {
+ int endpos = ap + ac;
+ switch (mode) {
+ case 'P': case 'A': case 'R': case 'D':
+ if (ap >= 0 && ac >= 0 &&
+ endpos >= 0 && endpos <= type.parameterCount())
+ return;
+ default:
+ throw new InternalError("configuration "+patternName(ap, ac, mode));
+ }
+ }
+
+ public String toString() {
+ return "FilterGeneric/"+patternName()+targetType;
+ }
+
+ String patternName() {
+ return patternName(argumentPosition, argumentCount, replaceMode);
+ }
+
+ static String patternName(short ap, short ac, char mode) {
+ return ""+mode+ap+(ac>1?"_"+ac:"");
+ }
+
+ Class> filterType() {
+ return Object.class; // result of filter operation; an uninteresting type
+ }
+
+ static MethodType targetType(MethodType entryType, short ap, short ac, char mode,
+ Class> arg) {
+ MethodType type = entryType;
+ int pos = ap;
+ switch (mode) {
+ case 'A':
+ pos += ac;
+ case 'P':
+ type = type.insertParameterType(pos, arg);
+ break;
+ case 'I':
+ for (int i = 1; i < ac; i++)
+ type = type.dropParameterType(pos);
+ assert(type.parameterType(pos) == arg);
+ break;
+ case 'D':
+ break;
+ }
+ return type;
+ }
+
+ static MethodType entryType(MethodType targetType, short ap, short ac, char mode,
+ Class> arg) {
+ MethodType type = targetType;
+ int pos = ap;
+ switch (mode) {
+ case 'A':
+ pos += ac;
+ case 'P':
+ type = type.dropParameterType(pos);
+ break;
+ case 'I':
+ for (int i = 1; i < ac; i++)
+ type = type.insertParameterType(pos, arg);
+ assert(type.parameterType(pos) == arg);
+ break;
+ case 'D':
+ break;
+ }
+ return type;
+ }
+
+ /* Create an adapter that handles spreading calls for the given type. */
+ static Adapter findAdapter(MethodType targetType, short ap, short ac, char mode, Class> arg) {
+ MethodType entryType = entryType(targetType, ap, ac, mode, arg);
+ int argc = targetType.parameterCount();
+ String pname = patternName(ap, ac, mode);
+ String cname0 = "F"+argc;
+ String cname1 = "F"+argc+mode;
+ String cname2 = "F"+argc+pname;
+ String[] cnames = { cname0, cname1, cname1+"X", cname2 };
+ String iname = "invoke_"+pname;
+ // e.g., F5R; invoke_R3
+ for (String cname : cnames) {
+ Class extends Adapter> acls = Adapter.findSubClass(cname);
+ if (acls == null) continue;
+ // see if it has the required invoke method
+ MethodHandle entryPoint = null;
+ try {
+ entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
+ } catch (NoAccessException ex) {
+ }
+ if (entryPoint == null) continue;
+ Constructor extends Adapter> ctor = null;
+ try {
+ ctor = acls.getDeclaredConstructor(MethodHandle.class);
+ } catch (NoSuchMethodException ex) {
+ } catch (SecurityException ex) {
+ }
+ if (ctor == null) continue;
+ try {
+ // Produce an instance configured as a prototype.
+ return ctor.newInstance(entryPoint);
+ } catch (IllegalArgumentException ex) {
+ } catch (InvocationTargetException ex) {
+ } catch (InstantiationException ex) {
+ } catch (IllegalAccessException ex) {
+ }
+ }
+ return null;
+ }
+
+ static Adapter buildAdapterFromBytecodes(MethodType targetType, short ap, short ac, char mode, Class> arg) {
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /**
+ * This adapter takes some untyped arguments, and returns an untyped result.
+ * Internally, it applies the invoker to the target, which causes the
+ * objects to be unboxed; the result is a raw type in L/I/J/F/D.
+ * This result is passed to convert, which is responsible for
+ * converting the raw result into a boxed object.
+ * The invoker is kept separate from the target because it can be
+ * generated once per type erasure family, and reused across adapters.
+ */
+ static abstract class Adapter extends JavaMethodHandle {
+ protected final MethodHandle filter;
+ protected final MethodHandle target;
+
+ protected boolean isPrototype() { return target == null; }
+ protected Adapter(MethodHandle entryPoint) {
+ this(entryPoint, entryPoint, null);
+ assert(isPrototype());
+ }
+ protected MethodHandle prototypeEntryPoint() {
+ if (!isPrototype()) throw new InternalError();
+ return filter;
+ }
+
+ protected Adapter(MethodHandle entryPoint,
+ MethodHandle filter, MethodHandle target) {
+ super(entryPoint);
+ this.filter = filter;
+ this.target = target;
+ }
+
+ /** Make a copy of self, with new fields. */
+ protected abstract Adapter makeInstance(MethodHandle entryPoint,
+ MethodHandle filter, MethodHandle target);
+ // { return new ThisType(entryPoint, filter, target); }
+
+ static private final String CLASS_PREFIX; // "sun.dyn.FilterGeneric$"
+ static {
+ String aname = Adapter.class.getName();
+ String sname = Adapter.class.getSimpleName();
+ if (!aname.endsWith(sname)) throw new InternalError();
+ CLASS_PREFIX = aname.substring(0, aname.length() - sname.length());
+ }
+ /** Find a sibing class of Adapter. */
+ static Class extends Adapter> findSubClass(String name) {
+ String cname = Adapter.CLASS_PREFIX + name;
+ try {
+ return Class.forName(cname).asSubclass(Adapter.class);
+ } catch (ClassNotFoundException ex) {
+ return null;
+ } catch (ClassCastException ex) {
+ return null;
+ }
+ }
+ }
+
+ //* generated classes follow this pattern:
+ static class F1RX extends Adapter {
+ protected F1RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected F1RX(MethodHandle e, MethodHandle f, MethodHandle t)
+ { super(e, f, t); }
+ protected F1RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t)
+ { return new F1RX(e, f, t); }
+ protected Object filter(Object a0) { return filter.invoke(a0); }
+ protected Object target(Object a0) { return target.invoke(a0); }
+ protected Object invoke_R0(Object a0) { return target(filter(a0)); }
+ }
+ static class F2RX extends Adapter {
+ protected F2RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected F2RX(MethodHandle e, MethodHandle f, MethodHandle t)
+ { super(e, f, t); }
+ protected F2RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t)
+ { return new F2RX(e, f, t); }
+ protected Object filter(Object a0) { return filter.invoke(a0); }
+ protected Object target(Object a0, Object a1) { return target.invoke(a0, a1); }
+ protected Object invoke_R0(Object a0, Object a1) { return target(filter(a0), a1); }
+ protected Object invoke_R1(Object a0, Object a1) { return target(a0, filter(a1)); }
+ }
+ static class F3RX extends Adapter {
+ protected F3RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected F3RX(MethodHandle e, MethodHandle f, MethodHandle t)
+ { super(e, f, t); }
+ protected F3RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t)
+ { return new F3RX(e, f, t); }
+ protected Object filter(Object a0) { return filter.invoke(a0); }
+ protected Object target(Object a0, Object a1, Object a2) { return target.invoke(a0, a1, a2); }
+ protected Object invoke_R0(Object a0, Object a1, Object a2) { return target(filter(a0), a1, a2); }
+ protected Object invoke_R1(Object a0, Object a1, Object a2) { return target(a0, filter(a1), a2); }
+ protected Object invoke_R2(Object a0, Object a1, Object a2) { return target(a0, a1, filter(a2)); }
+ }
+ static class F4RX extends Adapter {
+ protected F4RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected F4RX(MethodHandle e, MethodHandle f, MethodHandle t)
+ { super(e, f, t); }
+ protected F4RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t)
+ { return new F4RX(e, f, t); }
+ protected Object filter(Object a0) { return filter.invoke(a0); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3) { return target.invoke(a0, a1, a2, a3); }
+ protected Object invoke_R0(Object a0, Object a1, Object a2, Object a3) { return target(filter(a0), a1, a2, a3); }
+ protected Object invoke_R1(Object a0, Object a1, Object a2, Object a3) { return target(a0, filter(a1), a2, a3); }
+ protected Object invoke_R2(Object a0, Object a1, Object a2, Object a3) { return target(a0, a1, filter(a2), a3); }
+ protected Object invoke_R3(Object a0, Object a1, Object a2, Object a3) { return target(a0, a1, a2, filter(a3)); }
+ }
+ // */
+}
diff --git a/src/share/classes/sun/dyn/FilterOneArgument.java b/src/share/classes/sun/dyn/FilterOneArgument.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2fd98e0c09dfe890e3d86ea09d61286cb1bf484
--- /dev/null
+++ b/src/share/classes/sun/dyn/FilterOneArgument.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.JavaMethodHandle;
+import java.dyn.MethodHandle;
+import java.dyn.MethodHandles;
+import java.dyn.MethodType;
+
+/**
+ * Unary function composition, useful for many small plumbing jobs.
+ * The invoke method takes a single reference argument, and returns a reference
+ * Internally, it first calls the {@code filter} method on the argument,
+ * Making up the difference between the raw method type and the
+ * final method type is the responsibility of a JVM-level adapter.
+ * @author jrose
+ */
+public class FilterOneArgument extends JavaMethodHandle {
+ protected final MethodHandle filter; // Object -> Object
+ protected final MethodHandle target; // Object -> Object
+
+ protected Object entryPoint(Object argument) {
+ Object filteredArgument = filter.invoke(argument);
+ return target.invoke(filteredArgument);
+ }
+
+ private static final MethodHandle entryPoint =
+ MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "entryPoint", MethodType.makeGeneric(1));
+
+ protected FilterOneArgument(MethodHandle filter, MethodHandle target) {
+ super(entryPoint);
+ this.filter = filter;
+ this.target = target;
+ }
+
+ public static MethodHandle make(MethodHandle filter, MethodHandle target) {
+ if (filter == null) return target;
+ if (target == null) return filter;
+ return new FilterOneArgument(filter, target);
+ }
+
+ public String toString() {
+ return filter + "|>" + target;
+ }
+
+// MethodHandle make(MethodHandle filter1, MethodHandle filter2, MethodHandle target) {
+// MethodHandle filter = make(filter1, filter2);
+// return make(filter, target);
+// }
+}
diff --git a/src/share/classes/sun/dyn/FromGeneric.java b/src/share/classes/sun/dyn/FromGeneric.java
new file mode 100644
index 0000000000000000000000000000000000000000..85f35d6557755ff2dec53fe3c2ac18e691b926b3
--- /dev/null
+++ b/src/share/classes/sun/dyn/FromGeneric.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.JavaMethodHandle;
+import java.dyn.MethodHandle;
+import java.dyn.MethodHandles;
+import java.dyn.MethodType;
+import java.dyn.NoAccessException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import sun.dyn.util.ValueConversions;
+import sun.dyn.util.Wrapper;
+
+/**
+ * Adapters which mediate between incoming calls which are not generic
+ * and outgoing calls which are. Any call can be represented generically
+ * boxing up its arguments, and (on return) unboxing the return value.
+ *
+ * A call is "generic" (in MethodHandle terms) if its MethodType features
+ * only Object arguments. A non-generic call therefore features
+ * primitives and/or reference types other than Object.
+ * An adapter has types for its incoming and outgoing calls.
+ * The incoming call type is simply determined by the adapter's type
+ * (the MethodType it presents to callers). The outgoing call type
+ * is determined by the adapter's target (a MethodHandle that the adapter
+ * either binds internally or else takes as a leading argument).
+ * (To stretch the term, adapter-like method handles may have multiple
+ * targets or be polymorphic across multiple call types.)
+ *
+ * This adapter can sometimes be more directly implemented
+ * by the JVM's built-in OP_SPREAD_ARGS adapter.
+ * @author jrose
+ */
+class FromGeneric {
+ // type for the outgoing call (may have primitives, etc.)
+ private final MethodType targetType;
+ // type of the outgoing call internal to the adapter
+ private final MethodType internalType;
+ // prototype adapter (clone and customize for each new target!)
+ private final Adapter adapter;
+ // entry point for adapter (Adapter mh, a...) => ...
+ private final MethodHandle entryPoint;
+ // unboxing invoker of type (MH, Object**N) => raw return value
+ // it makes up the difference of internalType => targetType
+ private final MethodHandle unboxingInvoker;
+ // conversion which boxes a the target's raw return value
+ private final MethodHandle returnConversion;
+
+ /** Compute and cache information common to all unboxing adapters
+ * that can call out to targets of the erasure-family of the given erased type.
+ */
+ private FromGeneric(MethodType targetType) {
+ this.targetType = targetType;
+ MethodType internalType0;
+ // the target invoker will generally need casts on reference arguments
+ Adapter ad = findAdapter(internalType0 = targetType.erase());
+ if (ad != null) {
+ // Immediate hit to exactly the adapter we want,
+ // with no monkeying around with primitive types.
+ this.internalType = internalType0;
+ this.adapter = ad;
+ this.entryPoint = ad.prototypeEntryPoint();
+ this.returnConversion = computeReturnConversion(targetType, internalType0);
+ this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
+ return;
+ }
+
+ // outgoing primitive arguments will be wrapped; unwrap them
+ MethodType primsAsObj = MethodTypeImpl.of(targetType).primArgsAsBoxes();
+ MethodType objArgsRawRet = MethodTypeImpl.of(primsAsObj).primsAsInts();
+ if (objArgsRawRet != targetType)
+ ad = findAdapter(internalType0 = objArgsRawRet);
+ if (ad == null) {
+ ad = buildAdapterFromBytecodes(internalType0 = targetType);
+ }
+ this.internalType = internalType0;
+ this.adapter = ad;
+ MethodType tepType = targetType.insertParameterType(0, adapter.getClass());
+ this.entryPoint = ad.prototypeEntryPoint();
+ this.returnConversion = computeReturnConversion(targetType, internalType0);
+ this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
+ }
+
+ /**
+ * The typed target will be called according to targetType.
+ * The adapter code will in fact see the raw result from internalType,
+ * and must box it into an object. Produce a converter for this.
+ */
+ private static MethodHandle computeReturnConversion(
+ MethodType targetType, MethodType internalType) {
+ Class> tret = targetType.returnType();
+ Class> iret = internalType.returnType();
+ Wrapper wrap = Wrapper.forBasicType(tret);
+ if (!iret.isPrimitive()) {
+ assert(iret == Object.class);
+ return ValueConversions.identity();
+ } else if (wrap.primitiveType() == iret) {
+ return ValueConversions.box(wrap, false);
+ } else {
+ assert(tret == double.class ? iret == long.class : iret == int.class);
+ return ValueConversions.boxRaw(wrap, false);
+ }
+ }
+
+ /**
+ * The typed target will need an exact invocation point; provide it here.
+ * The adapter will possibly need to make a slightly different call,
+ * so adapt the invoker. This way, the logic for making up the
+ * difference between what the adapter can call and what the target
+ * needs can be cached once per type.
+ */
+ private static MethodHandle computeUnboxingInvoker(
+ MethodType targetType, MethodType internalType) {
+ // All the adapters we have here have reference-untyped internal calls.
+ assert(internalType == internalType.erase());
+ MethodHandle invoker = MethodHandles.exactInvoker(targetType);
+ // cast all narrow reference types, unbox all primitive arguments:
+ MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
+ MethodHandle fixArgs = AdapterMethodHandle.convertArguments(Access.TOKEN,
+ invoker, Invokers.invokerType(fixArgsType),
+ invoker.type(), null);
+ if (fixArgs == null)
+ throw new InternalError("bad fixArgs");
+ // reinterpret the calling sequence as raw:
+ MethodHandle retyper = AdapterMethodHandle.makeRawRetypeOnly(Access.TOKEN,
+ Invokers.invokerType(internalType), fixArgs);
+ if (retyper == null)
+ throw new InternalError("bad retyper");
+ return retyper;
+ }
+
+ Adapter makeInstance(MethodHandle typedTarget) {
+ MethodType type = typedTarget.type();
+ if (type == targetType) {
+ return adapter.makeInstance(entryPoint, unboxingInvoker, returnConversion, typedTarget);
+ }
+ // my erased-type is not exactly the same as the desired type
+ assert(type.erase() == targetType); // else we are busted
+ MethodHandle invoker = computeUnboxingInvoker(type, internalType);
+ return adapter.makeInstance(entryPoint, invoker, returnConversion, typedTarget);
+ }
+
+ /** Build an adapter of the given generic type, which invokes typedTarget
+ * on the incoming arguments, after unboxing as necessary.
+ * The return value is boxed if necessary.
+ * @param genericType the required type of the result
+ * @param typedTarget the target
+ * @return an adapter method handle
+ */
+ public static MethodHandle make(MethodHandle typedTarget) {
+ MethodType type = typedTarget.type();
+ if (type == type.generic()) return typedTarget;
+ return FromGeneric.of(type).makeInstance(typedTarget);
+ }
+
+ /** Return the adapter information for this type's erasure. */
+ static FromGeneric of(MethodType type) {
+ MethodTypeImpl form = MethodTypeImpl.of(type);
+ FromGeneric fromGen = form.fromGeneric;
+ if (fromGen == null)
+ form.fromGeneric = fromGen = new FromGeneric(form.erasedType());
+ return fromGen;
+ }
+
+ public String toString() {
+ return "FromGeneric"+targetType;
+ }
+
+ /* Create an adapter that handles spreading calls for the given type. */
+ static Adapter findAdapter(MethodType internalType) {
+ MethodType entryType = internalType.generic();
+ MethodTypeImpl form = MethodTypeImpl.of(internalType);
+ Class> rtype = internalType.returnType();
+ int argc = form.parameterCount();
+ int lac = form.longPrimitiveParameterCount();
+ int iac = form.primitiveParameterCount() - lac;
+ String intsAndLongs = (iac > 0 ? "I"+iac : "")+(lac > 0 ? "J"+lac : "");
+ String rawReturn = String.valueOf(Wrapper.forPrimitiveType(rtype).basicTypeChar());
+ String cname0 = rawReturn + argc;
+ String cname1 = "A" + argc;
+ String[] cnames = { cname0+intsAndLongs, cname0, cname1+intsAndLongs, cname1 };
+ String iname = "invoke_"+cname0+intsAndLongs;
+ // e.g., D5I2, D5, L5I2, L5; invoke_D5
+ for (String cname : cnames) {
+ Class extends Adapter> acls = Adapter.findSubClass(cname);
+ if (acls == null) continue;
+ // see if it has the required invoke method
+ MethodHandle entryPoint = null;
+ try {
+ entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
+ } catch (NoAccessException ex) {
+ }
+ if (entryPoint == null) continue;
+ Constructor extends Adapter> ctor = null;
+ try {
+ ctor = acls.getDeclaredConstructor(MethodHandle.class);
+ } catch (NoSuchMethodException ex) {
+ } catch (SecurityException ex) {
+ }
+ if (ctor == null) continue;
+ try {
+ // Produce an instance configured as a prototype.
+ return ctor.newInstance(entryPoint);
+ } catch (IllegalArgumentException ex) {
+ } catch (InvocationTargetException ex) {
+ } catch (InstantiationException ex) {
+ } catch (IllegalAccessException ex) {
+ }
+ }
+ return null;
+ }
+
+ static Adapter buildAdapterFromBytecodes(MethodType internalType) {
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /**
+ * This adapter takes some untyped arguments, and returns an untyped result.
+ * Internally, it applies the invoker to the target, which causes the
+ * objects to be unboxed; the result is a raw type in L/I/J/F/D.
+ * This result is passed to convert, which is responsible for
+ * converting the raw result into a boxed object.
+ * The invoker is kept separate from the target because it can be
+ * generated once per type erasure family, and reused across adapters.
+ */
+ static abstract class Adapter extends JavaMethodHandle {
+ /*
+ * class X<> extends Adapter {
+ * (MH, Object**N)=>raw(R) invoker;
+ * (any**N)=>R target;
+ * raw(R)=>Object convert;
+ * Object invoke(Object**N a) = convert(invoker(target, a...))
+ * }
+ */
+ protected final MethodHandle invoker; // (MH, Object**N) => raw(R)
+ protected final MethodHandle convert; // raw(R) => Object
+ protected final MethodHandle target; // (any**N) => R
+
+ protected boolean isPrototype() { return target == null; }
+ protected Adapter(MethodHandle entryPoint) {
+ this(entryPoint, null, entryPoint, null);
+ assert(isPrototype());
+ }
+ protected MethodHandle prototypeEntryPoint() {
+ if (!isPrototype()) throw new InternalError();
+ return convert;
+ }
+
+ protected Adapter(MethodHandle entryPoint,
+ MethodHandle invoker, MethodHandle convert, MethodHandle target) {
+ super(entryPoint);
+ this.invoker = invoker;
+ this.convert = convert;
+ this.target = target;
+ }
+
+ /** Make a copy of self, with new fields. */
+ protected abstract Adapter makeInstance(MethodHandle entryPoint,
+ MethodHandle invoker, MethodHandle convert, MethodHandle target);
+ // { return new ThisType(entryPoint, convert, target); }
+
+ /// Conversions on the value returned from the target.
+ protected Object convert_L(Object result) { return convert.invoke(result); }
+ protected Object convert_I(int result) { return convert.invoke(result); }
+ protected Object convert_J(long result) { return convert.invoke(result); }
+ protected Object convert_F(float result) { return convert.invoke(result); }
+ protected Object convert_D(double result) { return convert.invoke(result); }
+
+ static private final String CLASS_PREFIX; // "sun.dyn.FromGeneric$"
+ static {
+ String aname = Adapter.class.getName();
+ String sname = Adapter.class.getSimpleName();
+ if (!aname.endsWith(sname)) throw new InternalError();
+ CLASS_PREFIX = aname.substring(0, aname.length() - sname.length());
+ }
+ /** Find a sibing class of Adapter. */
+ static Class extends Adapter> findSubClass(String name) {
+ String cname = Adapter.CLASS_PREFIX + name;
+ try {
+ return Class.forName(cname).asSubclass(Adapter.class);
+ } catch (ClassNotFoundException ex) {
+ return null;
+ } catch (ClassCastException ex) {
+ return null;
+ }
+ }
+ }
+
+ /* generated classes follow this pattern:
+ static class xA2 extends Adapter {
+ protected xA2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected xA2(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected xA2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new xA2(e, i, c, t); }
+ protected Object invoke_L2(Object a0, Object a1) { return convert_L(invoker.invoke(target, a0, a1)); }
+ protected Object invoke_I2(Object a0, Object a1) { return convert_I(invoker.invoke(target, a0, a1)); }
+ protected Object invoke_J2(Object a0, Object a1) { return convert_J(invoker.invoke(target, a0, a1)); }
+ protected Object invoke_F2(Object a0, Object a1) { return convert_F(invoker.invoke(target, a0, a1)); }
+ protected Object invoke_D2(Object a0, Object a1) { return convert_D(invoker.invoke(target, a0, a1)); }
+ }
+ // */
+
+/*
+: SHELL; n=FromGeneric; cp -p $n.java $n.java-; sed < $n.java- > $n.java+ -e '/{{*{{/,/}}*}}/w /tmp/genclasses.java' -e '/}}*}}/q'; (cd /tmp; javac -d . genclasses.java; java -cp . genclasses) >> $n.java+; echo '}' >> $n.java+; mv $n.java+ $n.java; mv $n.java- $n.java~
+//{{{
+import java.util.*;
+class genclasses {
+ static String[] TYPES = { "Object", "int ", "long ", "float ", "double" };
+ static String[] TCHARS = { "L", "I", "J", "F", "D", "A" };
+ static String[][] TEMPLATES = { {
+ "@for@ arity=0..10 rcat<=4 nrefs<=99 nints=0 nlongs=0",
+ " //@each-cat@",
+ " static class @cat@ extends Adapter {",
+ " protected @cat@(MethodHandle entryPoint) { super(entryPoint); } // to build prototype",
+ " protected @cat@(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)",
+ " { super(e, i, c, t); }",
+ " protected @cat@ makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)",
+ " { return new @cat@(e, i, c, t); }",
+ " //@each-R@",
+ " protected Object invoke_@catN@(@Tvav@) { return convert_@Rc@(invoker.<@R@>invoke(target@av@)); }",
+ " //@end-R@",
+ " }",
+ } };
+ static final String NEWLINE_INDENT = "\n ";
+ enum VAR {
+ cat, catN, R, Rc, av, Tvav, Ovav;
+ public final String pattern = "@"+toString().replace('_','.')+"@";
+ public String binding;
+ static void makeBindings(boolean topLevel, int rcat, int nrefs, int nints, int nlongs) {
+ int nargs = nrefs + nints + nlongs;
+ if (topLevel)
+ VAR.cat.binding = catstr(ALL_RETURN_TYPES ? TYPES.length : rcat, nrefs, nints, nlongs);
+ VAR.catN.binding = catstr(rcat, nrefs, nints, nlongs);
+ VAR.R.binding = TYPES[rcat];
+ VAR.Rc.binding = TCHARS[rcat];
+ String[] Tv = new String[nargs];
+ String[] av = new String[nargs];
+ String[] Tvav = new String[nargs];
+ String[] Ovav = new String[nargs];
+ for (int i = 0; i < nargs; i++) {
+ int tcat = (i < nrefs) ? 0 : (i < nrefs + nints) ? 1 : 2;
+ Tv[i] = TYPES[tcat];
+ av[i] = arg(i);
+ Tvav[i] = param(Tv[i], av[i]);
+ Ovav[i] = param("Object", av[i]);
+ }
+ VAR.av.binding = comma(", ", av);
+ VAR.Tvav.binding = comma(Tvav);
+ VAR.Ovav.binding = comma(Ovav);
+ }
+ static String arg(int i) { return "a"+i; }
+ static String param(String t, String a) { return t+" "+a; }
+ static String comma(String[] v) { return comma("", v); }
+ static String comma(String sep, String[] v) {
+ if (v.length == 0) return "";
+ String res = sep+v[0];
+ for (int i = 1; i < v.length; i++) res += ", "+v[i];
+ return res;
+ }
+ static String transform(String string) {
+ for (VAR var : values())
+ string = string.replaceAll(var.pattern, var.binding);
+ return string;
+ }
+ }
+ static String[] stringsIn(String[] strings, int beg, int end) {
+ return Arrays.copyOfRange(strings, beg, Math.min(end, strings.length));
+ }
+ static String[] stringsBefore(String[] strings, int pos) {
+ return stringsIn(strings, 0, pos);
+ }
+ static String[] stringsAfter(String[] strings, int pos) {
+ return stringsIn(strings, pos, strings.length);
+ }
+ static int indexAfter(String[] strings, int pos, String tag) {
+ return Math.min(indexBefore(strings, pos, tag) + 1, strings.length);
+ }
+ static int indexBefore(String[] strings, int pos, String tag) {
+ for (int i = pos, end = strings.length; ; i++) {
+ if (i == end || strings[i].endsWith(tag)) return i;
+ }
+ }
+ static int MIN_ARITY, MAX_ARITY, MAX_RCAT, MAX_REFS, MAX_INTS, MAX_LONGS;
+ static boolean ALL_ARG_TYPES, ALL_RETURN_TYPES;
+ static HashSet done = new HashSet();
+ public static void main(String... av) {
+ for (String[] template : TEMPLATES) {
+ int forLinesLimit = indexBefore(template, 0, "@each-cat@");
+ String[] forLines = stringsBefore(template, forLinesLimit);
+ template = stringsAfter(template, forLinesLimit);
+ for (String forLine : forLines)
+ expandTemplate(forLine, template);
+ }
+ }
+ static void expandTemplate(String forLine, String[] template) {
+ String[] params = forLine.split("[^0-9]+");
+ if (params[0].length() == 0) params = stringsAfter(params, 1);
+ System.out.println("//params="+Arrays.asList(params));
+ int pcur = 0;
+ MIN_ARITY = Integer.valueOf(params[pcur++]);
+ MAX_ARITY = Integer.valueOf(params[pcur++]);
+ MAX_RCAT = Integer.valueOf(params[pcur++]);
+ MAX_REFS = Integer.valueOf(params[pcur++]);
+ MAX_INTS = Integer.valueOf(params[pcur++]);
+ MAX_LONGS = Integer.valueOf(params[pcur++]);
+ if (pcur != params.length) throw new RuntimeException("bad extra param: "+forLine);
+ if (MAX_RCAT >= TYPES.length) MAX_RCAT = TYPES.length - 1;
+ ALL_ARG_TYPES = (indexBefore(template, 0, "@each-Tv@") < template.length);
+ ALL_RETURN_TYPES = (indexBefore(template, 0, "@each-R@") < template.length);
+ for (int nargs = MIN_ARITY; nargs <= MAX_ARITY; nargs++) {
+ for (int rcat = 0; rcat <= MAX_RCAT; rcat++) {
+ expandTemplate(template, true, rcat, nargs, 0, 0);
+ if (ALL_ARG_TYPES) break;
+ expandTemplateForPrims(template, true, rcat, nargs, 1, 1);
+ if (ALL_RETURN_TYPES) break;
+ }
+ }
+ }
+ static String catstr(int rcat, int nrefs, int nints, int nlongs) {
+ int nargs = nrefs + nints + nlongs;
+ String cat = TCHARS[rcat] + nargs;
+ if (!ALL_ARG_TYPES) cat += (nints==0?"":"I"+nints)+(nlongs==0?"":"J"+nlongs);
+ return cat;
+ }
+ static void expandTemplateForPrims(String[] template, boolean topLevel, int rcat, int nargs, int minints, int minlongs) {
+ for (int isLong = 0; isLong <= 1; isLong++) {
+ for (int nprims = 1; nprims <= nargs; nprims++) {
+ int nrefs = nargs - nprims;
+ int nints = ((1-isLong) * nprims);
+ int nlongs = (isLong * nprims);
+ expandTemplate(template, topLevel, rcat, nrefs, nints, nlongs);
+ }
+ }
+ }
+ static void expandTemplate(String[] template, boolean topLevel,
+ int rcat, int nrefs, int nints, int nlongs) {
+ int nargs = nrefs + nints + nlongs;
+ if (nrefs > MAX_REFS || nints > MAX_INTS || nlongs > MAX_LONGS) return;
+ VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs);
+ if (topLevel && !done.add(VAR.cat.binding)) {
+ System.out.println(" //repeat "+VAR.cat.binding);
+ return;
+ }
+ for (int i = 0; i < template.length; i++) {
+ String line = template[i];
+ if (line.endsWith("@each-cat@")) {
+ // ignore
+ } else if (line.endsWith("@each-R@")) {
+ int blockEnd = indexAfter(template, i, "@end-R@");
+ String[] block = stringsIn(template, i+1, blockEnd-1);
+ for (int rcat1 = rcat; rcat1 <= MAX_RCAT; rcat1++)
+ expandTemplate(block, false, rcat1, nrefs, nints, nlongs);
+ VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs);
+ i = blockEnd-1; continue;
+ } else if (line.endsWith("@each-Tv@")) {
+ int blockEnd = indexAfter(template, i, "@end-Tv@");
+ String[] block = stringsIn(template, i+1, blockEnd-1);
+ expandTemplate(block, false, rcat, nrefs, nints, nlongs);
+ expandTemplateForPrims(block, false, rcat, nargs, nints+1, nlongs+1);
+ VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs);
+ i = blockEnd-1; continue;
+ } else {
+ System.out.println(VAR.transform(line));
+ }
+ }
+ }
+}
+//}}} */
+//params=[0, 10, 4, 99, 0, 0]
+ static class A0 extends Adapter {
+ protected A0(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A0(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A0 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A0(e, i, c, t); }
+ protected Object invoke_L0() { return convert_L(invoker.invoke(target)); }
+ protected Object invoke_I0() { return convert_I(invoker.invoke(target)); }
+ protected Object invoke_J0() { return convert_J(invoker.invoke(target)); }
+ protected Object invoke_F0() { return convert_F(invoker.invoke(target)); }
+ protected Object invoke_D0() { return convert_D(invoker.invoke(target)); }
+ }
+ static class A1 extends Adapter {
+ protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A1(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A1(e, i, c, t); }
+ protected Object invoke_L1(Object a0) { return convert_L(invoker.invoke(target, a0)); }
+ protected Object invoke_I1(Object a0) { return convert_I(invoker.invoke(target, a0)); }
+ protected Object invoke_J1(Object a0) { return convert_J(invoker.invoke(target, a0)); }
+ protected Object invoke_F1(Object a0) { return convert_F(invoker.invoke(target, a0)); }
+ protected Object invoke_D1(Object a0) { return convert_D(invoker.invoke(target, a0)); }
+ }
+ static class A2 extends Adapter {
+ protected A2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A2(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A2(e, i, c, t); }
+ protected Object invoke_L2(Object a0, Object a1) { return convert_L(invoker.invoke(target, a0, a1)); }
+ protected Object invoke_I2(Object a0, Object a1) { return convert_I(invoker.invoke(target, a0, a1)); }
+ protected Object invoke_J2(Object a0, Object a1) { return convert_J(invoker.invoke(target, a0, a1)); }
+ protected Object invoke_F2(Object a0, Object a1) { return convert_F(invoker.invoke(target, a0, a1)); }
+ protected Object invoke_D2(Object a0, Object a1) { return convert_D(invoker.invoke(target, a0, a1)); }
+ }
+ static class A3 extends Adapter {
+ protected A3(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A3(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A3 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A3(e, i, c, t); }
+ protected Object invoke_L3(Object a0, Object a1, Object a2) { return convert_L(invoker.invoke(target, a0, a1, a2)); }
+ protected Object invoke_I3(Object a0, Object a1, Object a2) { return convert_I(invoker.invoke(target, a0, a1, a2)); }
+ protected Object invoke_J3(Object a0, Object a1, Object a2) { return convert_J(invoker.invoke(target, a0, a1, a2)); }
+ protected Object invoke_F3(Object a0, Object a1, Object a2) { return convert_F(invoker.invoke(target, a0, a1, a2)); }
+ protected Object invoke_D3(Object a0, Object a1, Object a2) { return convert_D(invoker.invoke(target, a0, a1, a2)); }
+ }
+ static class A4 extends Adapter {
+ protected A4(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A4(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A4 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A4(e, i, c, t); }
+ protected Object invoke_L4(Object a0, Object a1, Object a2, Object a3) { return convert_L(invoker.invoke(target, a0, a1, a2, a3)); }
+ protected Object invoke_I4(Object a0, Object a1, Object a2, Object a3) { return convert_I(invoker.invoke(target, a0, a1, a2, a3)); }
+ protected Object invoke_J4(Object a0, Object a1, Object a2, Object a3) { return convert_J(invoker.invoke(target, a0, a1, a2, a3)); }
+ protected Object invoke_F4(Object a0, Object a1, Object a2, Object a3) { return convert_F(invoker.invoke(target, a0, a1, a2, a3)); }
+ protected Object invoke_D4(Object a0, Object a1, Object a2, Object a3) { return convert_D(invoker.invoke(target, a0, a1, a2, a3)); }
+ }
+ static class A5 extends Adapter {
+ protected A5(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A5(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A5 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A5(e, i, c, t); }
+ protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_L(invoker.invoke(target, a0, a1, a2, a3, a4)); }
+ protected Object invoke_I5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_I(invoker.invoke(target, a0, a1, a2, a3, a4)); }
+ protected Object invoke_J5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_J(invoker.invoke(target, a0, a1, a2, a3, a4)); }
+ protected Object invoke_F5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_F(invoker.invoke(target, a0, a1, a2, a3, a4)); }
+ protected Object invoke_D5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_D(invoker.invoke(target, a0, a1, a2, a3, a4)); }
+ }
+ static class A6 extends Adapter {
+ protected A6(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A6(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A6 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A6(e, i, c, t); }
+ protected Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_L(invoker.invoke(target, a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_I6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_I(invoker.invoke(target, a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_J6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_J(invoker.invoke(target, a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_F6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_F(invoker.invoke(target, a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_D6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_D(invoker.invoke(target, a0, a1, a2, a3, a4, a5)); }
+ }
+ static class A7 extends Adapter {
+ protected A7(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A7(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A7 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A7(e, i, c, t); }
+ protected Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_L(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_I7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_I(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_J7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_J(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_F7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_F(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_D7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_D(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
+ }
+ static class A8 extends Adapter {
+ protected A8(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A8(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A8 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A8(e, i, c, t); }
+ protected Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_L(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_I8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_I(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_J8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_J(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_F8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_F(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_D8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_D(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
+ }
+ static class A9 extends Adapter {
+ protected A9(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A9(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A9 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A9(e, i, c, t); }
+ protected Object invoke_L9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_L(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_I9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_I(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_J9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_J(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_F9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_F(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_D9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_D(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ }
+ static class A10 extends Adapter {
+ protected A10(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A10(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected A10 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new A10(e, i, c, t); }
+ protected Object invoke_L10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_L(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_I10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_I(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_J10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_J(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_F10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_F(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_D10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_D(invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ }
+}
diff --git a/src/share/classes/sun/dyn/Invokers.java b/src/share/classes/sun/dyn/Invokers.java
new file mode 100644
index 0000000000000000000000000000000000000000..eddd437a5d54a30179ac31267db271949346f827
--- /dev/null
+++ b/src/share/classes/sun/dyn/Invokers.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.MethodHandle;
+import java.dyn.MethodHandles;
+import java.dyn.MethodType;
+
+
+/**
+ * Construction and caching of often-used invokers.
+ * @author jrose
+ */
+public class Invokers {
+ // exact type (sans leading taget MH) for the outgoing call
+ private final MethodType targetType;
+
+ // exact invoker for the outgoing call
+ private /*lazy*/ MethodHandle exactInvoker;
+
+ // generic (untyped) invoker for the outgoing call
+ private /*lazy*/ MethodHandle genericInvoker;
+
+ /** Compute and cache information common to all collecting adapters
+ * that implement members of the erasure-family of the given erased type.
+ */
+ public Invokers(Access token, MethodType targetType) {
+ Access.check(token);
+ this.targetType = targetType;
+ }
+
+ public static MethodType invokerType(MethodType targetType) {
+ return targetType.insertParameterType(0, MethodHandle.class);
+ }
+
+ public MethodHandle exactInvoker() {
+ MethodHandle invoker = exactInvoker;
+ if (invoker != null) return invoker;
+ invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType);
+ if (invoker == null) throw new InternalError("JVM cannot find invoker for "+targetType);
+ assert(invokerType(targetType) == invoker.type());
+ exactInvoker = invoker;
+ return invoker;
+ }
+
+ public MethodHandle genericInvoker() {
+ MethodHandle invoker1 = exactInvoker();
+ MethodHandle invoker = genericInvoker;
+ if (invoker != null) return invoker;
+ MethodType genericType = targetType.generic();
+ invoker = MethodHandles.convertArguments(invoker1, invokerType(genericType));
+ genericInvoker = invoker;
+ return invoker;
+ }
+
+ public MethodHandle varargsInvoker() {
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ public String toString() {
+ return "Invokers"+targetType;
+ }
+}
diff --git a/src/share/classes/sun/dyn/MemberName.java b/src/share/classes/sun/dyn/MemberName.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5ea25e46fd5f51b146935283bbc80bc75a76c67
--- /dev/null
+++ b/src/share/classes/sun/dyn/MemberName.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import sun.dyn.util.BytecodeSignature;
+import java.dyn.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Member;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import static sun.dyn.MethodHandleNatives.Constants.*;
+
+/**
+ * Compact information which fully characterizes a method or field reference.
+ * When resolved, it includes a direct pointer to JVM metadata.
+ * This representation is stateless and only decriptive.
+ * It provides no private information and no capability to use the member.
+ *
+ * By contrast, a java.lang.reflect.Method contains fuller information
+ * about the internals of a method (except its bytecodes) and also
+ * allows invocation. A MemberName is much lighter than a reflect.Method,
+ * since it contains about 7 fields to Method's 16 (plus its sub-arrays),
+ * and those seven fields omit much of the information in Method.
+ * @author jrose
+ */
+public final class MemberName implements Member, Cloneable {
+ private Class> clazz; // class in which the method is defined
+ private String name; // may be null if not yet materialized
+ private Object type; // may be null if not yet materialized
+ private int flags; // modifier bits; see reflect.Modifier
+
+ private Object vmtarget; // VM-specific target value
+ private int vmindex; // method index within class or interface
+
+ { vmindex = VM_INDEX_UNINITIALIZED; }
+
+ public Class> getDeclaringClass() {
+ if (clazz == null && isResolved()) {
+ expandFromVM();
+ }
+ return clazz;
+ }
+
+ public ClassLoader getClassLoader() {
+ return clazz.getClassLoader();
+ }
+
+ public String getName() {
+ if (name == null) {
+ expandFromVM();
+ if (name == null) return null;
+ }
+ return name;
+ }
+
+ public MethodType getMethodType() {
+ if (type == null) {
+ expandFromVM();
+ if (type == null) return null;
+ }
+ if (!isInvocable())
+ throw newIllegalArgumentException("not invocable, no method type");
+ if (type instanceof MethodType) {
+ return (MethodType) type;
+ }
+ if (type instanceof String) {
+ String sig = (String) type;
+ MethodType res = MethodType.fromBytecodeString(sig, getClassLoader());
+ this.type = res;
+ return res;
+ }
+ if (type instanceof Object[]) {
+ Object[] typeInfo = (Object[]) type;
+ Class>[] ptypes = (Class>[]) typeInfo[1];
+ Class> rtype = (Class>) typeInfo[0];
+ MethodType res = MethodType.make(rtype, ptypes);
+ this.type = res;
+ return res;
+ }
+ throw new InternalError("bad method type "+type);
+ }
+
+ public MethodType getInvocationType() {
+ MethodType itype = getMethodType();
+ if (!isStatic())
+ itype = itype.insertParameterType(0, clazz);
+ return itype;
+ }
+
+ public Class>[] getParameterTypes() {
+ return getMethodType().parameterArray();
+ }
+
+ public Class> getReturnType() {
+ return getMethodType().returnType();
+ }
+
+ public Class> getFieldType() {
+ if (type == null) {
+ expandFromVM();
+ if (type == null) return null;
+ }
+ if (isInvocable())
+ throw newIllegalArgumentException("not a field or nested class, no simple type");
+ if (type instanceof Class>) {
+ return (Class>) type;
+ }
+ if (type instanceof String) {
+ String sig = (String) type;
+ MethodType mtype = MethodType.fromBytecodeString("()"+sig, getClassLoader());
+ Class> res = mtype.returnType();
+ this.type = res;
+ return res;
+ }
+ throw new InternalError("bad field type "+type);
+ }
+
+ public Object getType() {
+ return (isInvocable() ? getMethodType() : getFieldType());
+ }
+
+ public String getSignature() {
+ if (type == null) {
+ expandFromVM();
+ if (type == null) return null;
+ }
+ if (type instanceof String)
+ return (String) type;
+ if (isInvocable())
+ return BytecodeSignature.unparse(getMethodType());
+ else
+ return BytecodeSignature.unparse(getFieldType());
+ }
+
+ public int getModifiers() {
+ return (flags & RECOGNIZED_MODIFIERS);
+ }
+
+ private void setFlags(int flags) {
+ this.flags = flags;
+ assert(testAnyFlags(ALL_KINDS));
+ }
+
+ private boolean testFlags(int mask, int value) {
+ return (flags & mask) == value;
+ }
+ private boolean testAllFlags(int mask) {
+ return testFlags(mask, mask);
+ }
+ private boolean testAnyFlags(int mask) {
+ return !testFlags(mask, 0);
+ }
+
+ public boolean isStatic() {
+ return Modifier.isStatic(flags);
+ }
+ public boolean isPublic() {
+ return Modifier.isPublic(flags);
+ }
+ public boolean isPrivate() {
+ return Modifier.isPrivate(flags);
+ }
+ public boolean isProtected() {
+ return Modifier.isProtected(flags);
+ }
+ public boolean isFinal() {
+ return Modifier.isFinal(flags);
+ }
+ public boolean isAbstract() {
+ return Modifier.isAbstract(flags);
+ }
+ // let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
+
+ // unofficial modifier flags, used by HotSpot:
+ static final int BRIDGE = 0x00000040;
+ static final int VARARGS = 0x00000080;
+ static final int SYNTHETIC = 0x00001000;
+ static final int ANNOTATION= 0x00002000;
+ static final int ENUM = 0x00004000;
+ public boolean isBridge() {
+ return testAllFlags(IS_METHOD | BRIDGE);
+ }
+ public boolean isVarargs() {
+ return testAllFlags(VARARGS) && isInvocable();
+ }
+ public boolean isSynthetic() {
+ return testAllFlags(SYNTHETIC);
+ }
+
+ static final String CONSTRUCTOR_NAME = ""; // the ever-popular
+
+ // modifiers exported by the JVM:
+ static final int RECOGNIZED_MODIFIERS = 0xFFFF;
+
+ // private flags, not part of RECOGNIZED_MODIFIERS:
+ static final int
+ IS_METHOD = MN_IS_METHOD, // method (not constructor)
+ IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
+ IS_FIELD = MN_IS_FIELD, // field
+ IS_TYPE = MN_IS_TYPE; // nested type
+ static final int // for MethodHandleNatives.getMembers
+ SEARCH_SUPERCLASSES = MN_SEARCH_SUPERCLASSES,
+ SEARCH_INTERFACES = MN_SEARCH_INTERFACES;
+
+ static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
+ static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
+ static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR;
+ static final int IS_FIELD_OR_METHOD = IS_METHOD | IS_FIELD;
+ static final int SEARCH_ALL_SUPERS = SEARCH_SUPERCLASSES | SEARCH_INTERFACES;
+
+ public boolean isInvocable() {
+ return testAnyFlags(IS_INVOCABLE);
+ }
+ public boolean isFieldOrMethod() {
+ return testAnyFlags(IS_FIELD_OR_METHOD);
+ }
+ public boolean isMethod() {
+ return testAllFlags(IS_METHOD);
+ }
+ public boolean isConstructor() {
+ return testAllFlags(IS_CONSTRUCTOR);
+ }
+ public boolean isField() {
+ return testAllFlags(IS_FIELD);
+ }
+ public boolean isType() {
+ return testAllFlags(IS_TYPE);
+ }
+ public boolean isPackage() {
+ return !testAnyFlags(ALL_ACCESS);
+ }
+
+ /** Initialize a query. It is not resolved. */
+ private void init(Class> defClass, String name, Object type, int flags) {
+ // defining class is allowed to be null (for a naked name/type pair)
+ name.toString(); // null check
+ type.equals(type); // null check
+ // fill in fields:
+ this.clazz = defClass;
+ this.name = name;
+ this.type = type;
+ setFlags(flags);
+ assert(!isResolved());
+ }
+
+ private void expandFromVM() {
+ if (!isResolved()) return;
+ if (type instanceof Object[])
+ type = null; // don't saddle JVM w/ typeInfo
+ MethodHandleNatives.expand(this);
+ }
+
+ // Capturing information from the Core Reflection API:
+ private static int flagsMods(int flags, int mods) {
+ assert((flags & RECOGNIZED_MODIFIERS) == 0);
+ assert((mods & ~RECOGNIZED_MODIFIERS) == 0);
+ return flags | mods;
+ }
+ public MemberName(Method m) {
+ Object[] typeInfo = { m.getReturnType(), m.getParameterTypes() };
+ init(m.getDeclaringClass(), m.getName(), typeInfo, flagsMods(IS_METHOD, m.getModifiers()));
+ // fill in vmtarget, vmindex while we have m in hand:
+ MethodHandleNatives.init(this, m);
+ assert(isResolved());
+ }
+ public MemberName(Constructor ctor) {
+ Object[] typeInfo = { void.class, ctor.getParameterTypes() };
+ init(ctor.getDeclaringClass(), CONSTRUCTOR_NAME, typeInfo, flagsMods(IS_CONSTRUCTOR, ctor.getModifiers()));
+ // fill in vmtarget, vmindex while we have ctor in hand:
+ MethodHandleNatives.init(this, ctor);
+ assert(isResolved());
+ }
+ public MemberName(Field fld) {
+ init(fld.getDeclaringClass(), fld.getName(), fld.getType(), flagsMods(IS_FIELD, fld.getModifiers()));
+ // fill in vmtarget, vmindex while we have fld in hand:
+ MethodHandleNatives.init(this, fld);
+ assert(isResolved());
+ }
+ public MemberName(Class> type) {
+ init(type.getDeclaringClass(), type.getSimpleName(), type, flagsMods(IS_TYPE, type.getModifiers()));
+ vmindex = 0; // isResolved
+ assert(isResolved());
+ }
+
+ // bare-bones constructor; the JVM will fill it in
+ MemberName() { }
+
+ // locally useful cloner
+ @Override protected MemberName clone() {
+ try {
+ return (MemberName) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new InternalError();
+ }
+ }
+
+ // %%% define equals/hashcode?
+
+ // Construction from symbolic parts, for queries:
+ public MemberName(Class> defClass, String name, Class> type, int modifiers) {
+ init(defClass, name, type, IS_FIELD | (modifiers & RECOGNIZED_MODIFIERS));
+ }
+ public MemberName(Class> defClass, String name, Class> type) {
+ this(defClass, name, type, 0);
+ }
+ public MemberName(Class> defClass, String name, MethodType type, int modifiers) {
+ int flagBit = (name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
+ init(defClass, name, type, flagBit | (modifiers & RECOGNIZED_MODIFIERS));
+ }
+ public MemberName(Class> defClass, String name, MethodType type) {
+ this(defClass, name, type, 0);
+ }
+
+ boolean isResolved() {
+ return (vmindex != VM_INDEX_UNINITIALIZED);
+ }
+
+ public boolean hasReceiverTypeDispatch() {
+ return (isMethod() && getVMIndex(Access.TOKEN) >= 0);
+ }
+
+ @Override
+ public String toString() {
+ if (isType())
+ return type.toString(); // class java.lang.String
+ // else it is a field, method, or constructor
+ StringBuilder buf = new StringBuilder();
+ if (getDeclaringClass() != null) {
+ buf.append(getName(clazz));
+ buf.append('.');
+ }
+ buf.append(getName());
+ if (!isInvocable()) buf.append('/');
+ buf.append(getName(getType()));
+ /*
+ buf.append('/');
+ // key: Public, private, pRotected, sTatic, Final, sYnchronized,
+ // transient/Varargs, native, (interface), abstract, sTrict, sYnthetic,
+ // (annotation), Enum, (unused)
+ final String FIELD_MOD_CHARS = "PprTF?vt????Y?E?";
+ final String METHOD_MOD_CHARS = "PprTFybVn?atY???";
+ String modChars = (isInvocable() ? METHOD_MOD_CHARS : FIELD_MOD_CHARS);
+ for (int i = 0; i < modChars.length(); i++) {
+ if ((flags & (1 << i)) != 0) {
+ char mc = modChars.charAt(i);
+ if (mc != '.')
+ buf.append(mc);
+ }
+ }
+ */
+ return buf.toString();
+ }
+ private static String getName(Object obj) {
+ if (obj instanceof Class>)
+ return ((Class>)obj).getName();
+ return obj.toString();
+ }
+
+ // Queries to the JVM:
+ public int getVMIndex(Access token) {
+ Access.check(token);
+ if (!isResolved())
+ throw newIllegalStateException("not resolved");
+ return vmindex;
+ }
+// public Object getVMTarget(Access token) {
+// Access.check(token);
+// if (!isResolved())
+// throw newIllegalStateException("not resolved");
+// return vmtarget;
+// }
+ private RuntimeException newIllegalStateException(String message) {
+ return new IllegalStateException(message+": "+this);
+ }
+
+ // handy shared exception makers (they simplify the common case code)
+ public static RuntimeException newIllegalArgumentException(String message) {
+ return new IllegalArgumentException(message);
+ }
+ public static NoAccessException newNoAccessException(MemberName name, Class> lookupClass) {
+ return newNoAccessException("cannot access", name, lookupClass);
+ }
+ public static NoAccessException newNoAccessException(String message,
+ MemberName name, Class> lookupClass) {
+ message += ": " + name;
+ if (lookupClass != null) message += ", from " + lookupClass.getName();
+ return new NoAccessException(message);
+ }
+
+ /** Actually making a query requires an access check. */
+ public static Factory getFactory(Access token) {
+ Access.check(token);
+ return Factory.INSTANCE;
+ }
+ public static Factory getFactory() {
+ return getFactory(Access.getToken());
+ }
+ public static class Factory {
+ private Factory() { } // singleton pattern
+ static Factory INSTANCE = new Factory();
+
+ private static int ALLOWED_FLAGS = SEARCH_ALL_SUPERS | ALL_KINDS;
+
+ /// Queries
+ List getMembers(Class> defc,
+ String matchName, Object matchType,
+ int matchFlags, Class> lookupClass) {
+ matchFlags &= ALLOWED_FLAGS;
+ String matchSig = null;
+ if (matchType != null) {
+ matchSig = BytecodeSignature.unparse(matchType);
+ if (matchSig.startsWith("("))
+ matchFlags &= ~(ALL_KINDS & ~IS_INVOCABLE);
+ else
+ matchFlags &= ~(ALL_KINDS & ~IS_FIELD);
+ }
+ final int BUF_MAX = 0x2000;
+ int len1 = matchName == null ? 10 : matchType == null ? 4 : 1;
+ MemberName[] buf = newMemberBuffer(len1);
+ int totalCount = 0;
+ ArrayList bufs = null;
+ for (;;) {
+ int bufCount = MethodHandleNatives.getMembers(defc,
+ matchName, matchSig, matchFlags,
+ MethodHandleNatives.asNativeCaller(lookupClass),
+ totalCount, buf);
+ if (bufCount <= buf.length) {
+ if (bufCount >= 0)
+ totalCount += bufCount;
+ break;
+ }
+ // JVM returned tp us with an intentional overflow!
+ totalCount += buf.length;
+ int excess = bufCount - buf.length;
+ if (bufs == null) bufs = new ArrayList(1);
+ bufs.add(buf);
+ int len2 = buf.length;
+ len2 = Math.max(len2, excess);
+ len2 = Math.max(len2, totalCount / 4);
+ buf = newMemberBuffer(Math.min(BUF_MAX, len2));
+ }
+ ArrayList result = new ArrayList(totalCount);
+ if (bufs != null) {
+ for (MemberName[] buf0 : bufs) {
+ Collections.addAll(result, buf0);
+ }
+ }
+ Collections.addAll(result, buf);
+ // Signature matching is not the same as type matching, since
+ // one signature might correspond to several types.
+ // So if matchType is a Class or MethodType, refilter the results.
+ if (matchType != null && matchType != matchSig) {
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ MemberName m = it.next();
+ if (!matchType.equals(m.getType()))
+ it.remove();
+ }
+ }
+ return result;
+ }
+ boolean resolveInPlace(MemberName m, boolean searchSupers, Class> lookupClass) {
+ Class> caller = MethodHandleNatives.asNativeCaller(lookupClass);
+ MethodHandleNatives.resolve(m, caller);
+ if (m.isResolved()) return true;
+ int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0);
+ String matchSig = m.getSignature();
+ MemberName[] buf = { m };
+ int n = MethodHandleNatives.getMembers(m.getDeclaringClass(),
+ m.getName(), matchSig, matchFlags, caller, 0, buf);
+ if (n != 1) return false;
+ return m.isResolved();
+ }
+ public MemberName resolveOrNull(MemberName m, boolean searchSupers, Class> lookupClass) {
+ MemberName result = m.clone();
+ if (resolveInPlace(result, searchSupers, lookupClass))
+ return result;
+ return null;
+ }
+ public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class> lookupClass) {
+ MemberName result = resolveOrNull(m, searchSupers, lookupClass);
+ if (result != null)
+ return result;
+ throw newNoAccessException(m, lookupClass);
+ }
+ public List getMethods(Class> defc, boolean searchSupers,
+ Class> lookupClass) {
+ return getMethods(defc, searchSupers, null, null, lookupClass);
+ }
+ public List getMethods(Class> defc, boolean searchSupers,
+ String name, MethodType type, Class> lookupClass) {
+ int matchFlags = IS_METHOD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
+ return getMembers(defc, name, type, matchFlags, lookupClass);
+ }
+ public List getConstructors(Class> defc, Class> lookupClass) {
+ return getMembers(defc, null, null, IS_CONSTRUCTOR, lookupClass);
+ }
+ public List getFields(Class> defc, boolean searchSupers,
+ Class> lookupClass) {
+ return getFields(defc, searchSupers, null, null, lookupClass);
+ }
+ public List getFields(Class> defc, boolean searchSupers,
+ String name, Class> type, Class> lookupClass) {
+ int matchFlags = IS_FIELD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
+ return getMembers(defc, name, type, matchFlags, lookupClass);
+ }
+ public List getNestedTypes(Class> defc, boolean searchSupers,
+ Class> lookupClass) {
+ int matchFlags = IS_TYPE | (searchSupers ? SEARCH_ALL_SUPERS : 0);
+ return getMembers(defc, null, null, matchFlags, lookupClass);
+ }
+ private static MemberName[] newMemberBuffer(int length) {
+ MemberName[] buf = new MemberName[length];
+ // fill the buffer with dummy structs for the JVM to fill in
+ for (int i = 0; i < length; i++)
+ buf[i] = new MemberName();
+ return buf;
+ }
+ }
+
+// static {
+// System.out.println("Hello world! My methods are:");
+// System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null));
+// }
+}
diff --git a/src/share/classes/sun/dyn/MethodHandleImpl.java b/src/share/classes/sun/dyn/MethodHandleImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..a063b6fc6d558be9df9f56ae07e03e5de4b4d83c
--- /dev/null
+++ b/src/share/classes/sun/dyn/MethodHandleImpl.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.MethodHandle;
+import java.dyn.MethodHandles;
+import java.dyn.MethodHandles.Lookup;
+import java.dyn.MethodType;
+import sun.dyn.util.VerifyType;
+import java.dyn.NoAccessException;
+import static sun.dyn.MemberName.newIllegalArgumentException;
+import static sun.dyn.MemberName.newNoAccessException;
+
+/**
+ * Base class for method handles, containing JVM-specific fields and logic.
+ * TO DO: It should not be a base class.
+ * @author jrose
+ */
+public abstract class MethodHandleImpl {
+
+ // Fields which really belong in MethodHandle:
+ private byte vmentry; // adapter stub or method entry point
+ //private int vmslots; // optionally, hoist type.form.vmslots
+ protected Object vmtarget; // VM-specific, class-specific target value
+ //MethodType type; // defined in MethodHandle
+
+ // TO DO: vmtarget should be invisible to Java, since the JVM puts internal
+ // managed pointers into it. Making it visible exposes it to debuggers,
+ // which can cause errors when they treat the pointer as an Object.
+
+ // These two dummy fields are present to force 'I' and 'J' signatures
+ // into this class's constant pool, so they can be transferred
+ // to vmentry when this class is loaded.
+ static final int INT_FIELD = 0;
+ static final long LONG_FIELD = 0;
+
+ // type is defined in java.dyn.MethodHandle, which is platform-independent
+
+ // vmentry (a void* field) is used *only* by by the JVM.
+ // The JVM adjusts its type to int or long depending on system wordsize.
+ // Since it is statically typed as neither int nor long, it is impossible
+ // to use this field from Java bytecode. (Please don't try to, either.)
+
+ // The vmentry is an assembly-language stub which is jumped to
+ // immediately after the method type is verified.
+ // For a direct MH, this stub loads the vmtarget's entry point
+ // and jumps to it.
+
+ /**
+ * VM-based method handles must have a security token.
+ * This security token can only be obtained by trusted code.
+ * Do not create method handles directly; use factory methods.
+ */
+ public MethodHandleImpl(Access token) {
+ Access.check(token);
+ }
+
+ /** Initialize the method type form to participate in JVM calls.
+ * This is done once for each erased type.
+ */
+ public static void init(Access token, MethodType self) {
+ Access.check(token);
+ if (MethodHandleNatives.JVM_SUPPORT)
+ MethodHandleNatives.init(self);
+ }
+
+ /// Factory methods to create method handles:
+
+ private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;
+
+ static private Lookup IMPL_LOOKUP_INIT;
+
+ public static void initLookup(Access token, Lookup lookup) {
+ Access.check(token);
+ if (IMPL_LOOKUP_INIT != null || lookup.lookupClass() != Access.class)
+ throw new InternalError();
+ IMPL_LOOKUP_INIT = lookup;
+ }
+
+ public static Lookup getLookup(Access token) {
+ Access.check(token);
+ return IMPL_LOOKUP;
+ }
+
+ static {
+ // Force initialization:
+ Lookup.PUBLIC_LOOKUP.lookupClass();
+ if (IMPL_LOOKUP_INIT == null)
+ throw new InternalError();
+ }
+
+ public static void initStatics() {
+ // Trigger preceding sequence.
+ }
+
+ /** Shared secret with MethodHandles.Lookup, a copy of Lookup.IMPL_LOOKUP. */
+ static final Lookup IMPL_LOOKUP = IMPL_LOOKUP_INIT;
+
+
+ /** Look up a given method.
+ * Callable only from java.dyn and related packages.
+ *
+ * The resulting method handle type will be of the given type,
+ * with a receiver type {@code rcvc} prepended if the member is not static.
+ *
+ * Access checks are made as of the given lookup class.
+ * In particular, if the method is protected and {@code defc} is in a
+ * different package from the lookup class, then {@code rcvc} must be
+ * the lookup class or a subclass.
+ * @param token Proof that the lookup class has access to this package.
+ * @param member Resolved method or constructor to call.
+ * @param name Name of the desired method.
+ * @param rcvc Receiver type of desired non-static method (else null)
+ * @param doDispatch whether the method handle will test the receiver type
+ * @param lookupClass access-check relative to this class
+ * @return a direct handle to the matching method
+ * @throws NoAccessException if the given method cannot be accessed by the lookup class
+ */
+ public static
+ MethodHandle findMethod(Access token, MemberName method,
+ boolean doDispatch, Class> lookupClass) {
+ Access.check(token); // only trusted calls
+ MethodType mtype = method.getMethodType();
+ if (method.isStatic()) {
+ doDispatch = false;
+ } else {
+ // adjust the advertised receiver type to be exactly the one requested
+ // (in the case of invokespecial, this will be the calling class)
+ mtype = mtype.insertParameterType(0, method.getDeclaringClass());
+ if (method.isConstructor())
+ doDispatch = true;
+ }
+ DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
+ if (!mh.isValid())
+ throw newNoAccessException(method, lookupClass);
+ return mh;
+ }
+
+ public static
+ MethodHandle accessField(Access token,
+ MemberName member, boolean isSetter,
+ Class> lookupClass) {
+ Access.check(token);
+ // FIXME: Use sun.misc.Unsafe to dig up the dirt on the field.
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ public static
+ MethodHandle accessArrayElement(Access token,
+ Class> arrayClass, boolean isSetter) {
+ Access.check(token);
+ if (!arrayClass.isArray())
+ throw newIllegalArgumentException("not an array: "+arrayClass);
+ // FIXME: Use sun.misc.Unsafe to dig up the dirt on the array.
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ /** Bind a predetermined first argument to the given direct method handle.
+ * Callable only from MethodHandles.
+ * @param token Proof that the caller has access to this package.
+ * @param target Any direct method handle.
+ * @param receiver Receiver (or first static method argument) to pre-bind.
+ * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist
+ */
+ public static
+ MethodHandle bindReceiver(Access token,
+ MethodHandle target, Object receiver) {
+ Access.check(token);
+ if (target instanceof DirectMethodHandle)
+ return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
+ return null; // let caller try something else
+ }
+
+ /** Bind a predetermined argument to the given arbitrary method handle.
+ * Callable only from MethodHandles.
+ * @param token Proof that the caller has access to this package.
+ * @param target Any method handle.
+ * @param receiver Argument (which can be a boxed primitive) to pre-bind.
+ * @return a suitable BoundMethodHandle
+ */
+ public static
+ MethodHandle bindArgument(Access token,
+ MethodHandle target, int argnum, Object receiver) {
+ Access.check(token);
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ public static MethodHandle convertArguments(Access token,
+ MethodHandle target,
+ MethodType newType,
+ MethodType oldType,
+ int[] permutationOrNull) {
+ Access.check(token);
+ MethodHandle res = AdapterMethodHandle.makePairwiseConvert(token, newType, target);
+ if (res != null)
+ return res;
+ int argc = oldType.parameterCount();
+ // The JVM can't do it directly, so fill in the gap with a Java adapter.
+ // TO DO: figure out what to put here from case-by-case experience
+ // Use a heavier method: Convert all the arguments to Object,
+ // then back to the desired types. We might have to use Java-based
+ // method handles to do this.
+ MethodType objType = MethodType.makeGeneric(argc);
+ MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(token, objType, target);
+ if (objTarget == null)
+ objTarget = FromGeneric.make(target);
+ res = AdapterMethodHandle.makePairwiseConvert(token, newType, objTarget);
+ if (res != null)
+ return res;
+ return ToGeneric.make(newType, objTarget);
+ }
+
+ public static MethodHandle spreadArguments(Access token,
+ MethodHandle target,
+ MethodType newType,
+ int spreadArg) {
+ Access.check(token);
+ // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
+ MethodType oldType = target.type();
+ // spread the last argument of newType to oldType
+ int spreadCount = oldType.parameterCount() - spreadArg;
+ Class spreadArgType = Object[].class;
+ MethodHandle res = AdapterMethodHandle.makeSpreadArguments(token, newType, target, spreadArgType, spreadArg, spreadCount);
+ if (res != null)
+ return res;
+ // try an intermediate adapter
+ Class> spreadType = null;
+ if (spreadArg < 0 || spreadArg >= newType.parameterCount()
+ || !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg)))
+ throw newIllegalArgumentException("no restarg in "+newType);
+ Class>[] ptypes = oldType.parameterArray();
+ for (int i = 0; i < spreadCount; i++)
+ ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i);
+ MethodType midType = MethodType.make(newType.returnType(), ptypes);
+ // after spreading, some arguments may need further conversion
+ target = convertArguments(token, target, midType, oldType, null);
+ if (target == null)
+ throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType);
+ res = AdapterMethodHandle.makeSpreadArguments(token, newType, target, spreadArgType, spreadArg, spreadCount);
+ return res;
+ }
+
+ public static MethodHandle collectArguments(Access token,
+ MethodHandle target,
+ MethodType newType,
+ int collectArg) {
+ if (collectArg > 0)
+ throw new UnsupportedOperationException("NYI");
+ throw new UnsupportedOperationException("NYI");
+ }
+ public static
+ MethodHandle dropArguments(Access token, MethodHandle target,
+ MethodType newType, int argnum) {
+ Access.check(token);
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ public static
+ MethodHandle makeGuardWithTest(Access token,
+ final MethodHandle test,
+ final MethodHandle target,
+ final MethodHandle fallback) {
+ Access.check(token);
+ // %%% This is just a sketch. It needs to be de-boxed.
+ // Adjust the handles to accept varargs lists.
+ MethodType type = target.type();
+ Class> rtype = type.returnType();
+ if (type.parameterCount() != 1 || type.parameterType(0).isPrimitive()) {
+ MethodType vatestType = MethodType.make(boolean.class, Object[].class);
+ MethodType vatargetType = MethodType.make(rtype, Object[].class);
+ MethodHandle vaguard = makeGuardWithTest(token,
+ MethodHandles.spreadArguments(test, vatestType),
+ MethodHandles.spreadArguments(target, vatargetType),
+ MethodHandles.spreadArguments(fallback, vatargetType));
+ return MethodHandles.collectArguments(vaguard, type);
+ }
+ if (rtype.isPrimitive()) {
+ MethodType boxtype = type.changeReturnType(Object.class);
+ MethodHandle boxguard = makeGuardWithTest(token,
+ test,
+ MethodHandles.convertArguments(target, boxtype),
+ MethodHandles.convertArguments(fallback, boxtype));
+ return MethodHandles.convertArguments(boxguard, type);
+ }
+ // Got here? Reduced calling sequence to Object(Object).
+ class Guarder {
+ Object invoke(Object x) {
+ // If javac supports MethodHandle.invoke directly:
+ //z = vatest.invoke(arguments);
+ // If javac does not support direct MH.invoke calls:
+ boolean z = (Boolean) MethodHandles.invoke_1(test, x);
+ MethodHandle mh = (z ? target : fallback);
+ return MethodHandles.invoke_1(mh, x);
+ }
+ MethodHandle handle() {
+ MethodType invokeType = MethodType.makeGeneric(0, true);
+ MethodHandle vh = IMPL_LOOKUP.bind(this, "invoke", invokeType);
+ return MethodHandles.collectArguments(vh, target.type());
+ }
+ }
+ return new Guarder().handle();
+ }
+
+ public static
+ MethodHandle combineArguments(Access token, MethodHandle target, MethodHandle checker, int pos) {
+ Access.check(token);
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ protected static String basicToString(MethodHandle target) {
+ MemberName name = null;
+ if (target != null)
+ name = MethodHandleNatives.getMethodName(target);
+ if (name == null)
+ return "";
+ return name.getName();
+ }
+
+ protected static String addTypeString(MethodHandle target, String name) {
+ if (target == null) return name;
+ return name+target.type();
+ }
+ static RuntimeException newIllegalArgumentException(String string) {
+ return new IllegalArgumentException(string);
+ }
+
+ @Override
+ public String toString() {
+ MethodHandle self = (MethodHandle) this;
+ return addTypeString(self, basicToString(self));
+ }
+}
diff --git a/src/share/classes/sun/dyn/MethodHandleNatives.java b/src/share/classes/sun/dyn/MethodHandleNatives.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2ed41eff8ce8fd9161ade2b37006522bf921b81
--- /dev/null
+++ b/src/share/classes/sun/dyn/MethodHandleNatives.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.MethodHandle;
+import java.dyn.MethodType;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import static sun.dyn.MethodHandleNatives.Constants.*;
+
+/**
+ * The JVM interface for the method handles package is all here.
+ * @author jrose
+ */
+class MethodHandleNatives {
+
+ private MethodHandleNatives() { } // static only
+
+ /// MethodName support
+
+ static native void init(MemberName self, Object ref);
+ static native void expand(MemberName self);
+ static native void resolve(MemberName self, Class> caller);
+ static native int getMembers(Class> defc, String matchName, String matchSig,
+ int matchFlags, Class> caller, int skip, MemberName[] results);
+
+ static Class> asNativeCaller(Class> lookupClass) {
+ if (lookupClass == null) // means "public only, non-privileged"
+ return sun.dyn.empty.Empty.class;
+ if (lookupClass == Access.class) // means "internal, privileged"
+ return null; // to the JVM, null means completely privileged
+ return lookupClass;
+ }
+
+ /// MethodHandle support
+
+ /** Initialize the method handle to adapt the call. */
+ static native void init(AdapterMethodHandle self, MethodHandle target, int argnum);
+ /** Initialize the method handle to call the correct method, directly. */
+ static native void init(BoundMethodHandle self, Object target, int argnum);
+ /** Initialize the method handle to call as if by an invoke* instruction. */
+ static native void init(DirectMethodHandle self, Object ref, boolean doDispatch, Class> caller);
+
+ /** Initialize a method type, once per form. */
+ static native void init(MethodType self);
+
+ /** Tell the JVM that we need to change the target of an invokedynamic. */
+ static native void linkCallSite(CallSiteImpl site, MethodHandle target);
+
+ /** Fetch the vmtarget field.
+ * It will be sanitized as necessary to avoid exposing non-Java references.
+ * This routine is for debugging and reflection.
+ */
+ static native Object getTarget(MethodHandle self, int format);
+
+ /** Fetch the name of the handled method, if available.
+ * This routine is for debugging and reflection.
+ */
+ static MemberName getMethodName(MethodHandle self) {
+ if (!JVM_SUPPORT) return null;
+ return (MemberName) getTarget(self, ETF_METHOD_NAME);
+ }
+
+ /** Fetch the reflective version of the handled method, if available.
+ */
+ static AccessibleObject getTargetMethod(MethodHandle self) {
+ if (!JVM_SUPPORT) return null;
+ return (AccessibleObject) getTarget(self, ETF_REFLECT_METHOD);
+ }
+
+ /** Fetch the target of this method handle.
+ * If it directly targets a method, return a tuple of method info.
+ * The info is of the form new Object[]{defclass, name, sig, refclass}.
+ * If it is chained to another method handle, return that handle.
+ */
+ static Object getTargetInfo(MethodHandle self) {
+ if (!JVM_SUPPORT) return null;
+ return getTarget(self, ETF_HANDLE_OR_METHOD_NAME);
+ }
+
+ static Object[] makeTarget(Class> defc, String name, String sig, int mods, Class> refc) {
+ return new Object[] { defc, name, sig, mods, refc };
+ }
+
+ /** Fetch MH-related JVM parameter.
+ * which=0 retrieves MethodHandlePushLimit
+ * which=1 retrieves stack slot push size (in address units)
+ */
+ static native int getConstant(int which);
+
+ /** True iff this HotSpot JVM has built-in support for method handles.
+ * If false, some test cases might run, but functionality will be missing.
+ */
+ public static final boolean JVM_SUPPORT;
+
+ /** Java copy of MethodHandlePushLimit in range 2..255. */
+ static final int JVM_PUSH_LIMIT;
+ /** JVM stack motion (in words) after one slot is pushed, usually -1.
+ */
+ static final int JVM_STACK_MOVE_UNIT;
+
+ private static native void registerNatives();
+ static {
+ boolean JVM_SUPPORT_;
+ int JVM_PUSH_LIMIT_;
+ int JVM_STACK_MOVE_UNIT_;
+ try {
+ registerNatives();
+ JVM_SUPPORT_ = true;
+ JVM_PUSH_LIMIT_ = getConstant(Constants.GC_JVM_PUSH_LIMIT);
+ JVM_STACK_MOVE_UNIT_ = getConstant(Constants.GC_JVM_STACK_MOVE_LIMIT);
+ //sun.reflect.Reflection.registerMethodsToFilter(MethodHandleImpl.class, "init");
+ } catch (UnsatisfiedLinkError ee) {
+ // ignore; if we use init() methods later we'll see linkage errors
+ JVM_SUPPORT_ = false;
+ JVM_PUSH_LIMIT_ = 3; // arbitrary
+ JVM_STACK_MOVE_UNIT_ = -1; // arbitrary
+ //System.out.println("Warning: Running with JVM_SUPPORT=false");
+ //System.out.println(ee);
+ JVM_SUPPORT = JVM_SUPPORT_;
+ JVM_PUSH_LIMIT = JVM_PUSH_LIMIT_;
+ JVM_STACK_MOVE_UNIT = JVM_STACK_MOVE_UNIT_;
+ throw ee; // just die; hopeless to try to run with an older JVM
+ }
+ JVM_SUPPORT = JVM_SUPPORT_;
+ JVM_PUSH_LIMIT = JVM_PUSH_LIMIT_;
+ JVM_STACK_MOVE_UNIT = JVM_STACK_MOVE_UNIT_;
+ }
+
+ // All compile-time constants go here.
+ // There is an opportunity to check them against the JVM's idea of them.
+ static class Constants {
+ Constants() { } // static only
+ // MethodHandleImpl
+ static final int // for getConstant
+ GC_JVM_PUSH_LIMIT = 0,
+ GC_JVM_STACK_MOVE_LIMIT = 1;
+ static final int
+ ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method)
+ ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self)
+ ETF_METHOD_NAME = 2, // ultimate method as MemberName
+ ETF_REFLECT_METHOD = 3; // ultimate method as java.lang.reflect object (sans refClass)
+
+ // MemberName
+ // The JVM uses values of -2 and above for vtable indexes.
+ // Field values are simple positive offsets.
+ // Ref: src/share/vm/oops/methodOop.hpp
+ // This value is negative enough to avoid such numbers,
+ // but not too negative.
+ static final int
+ MN_IS_METHOD = 0x00010000, // method (not constructor)
+ MN_IS_CONSTRUCTOR = 0x00020000, // constructor
+ MN_IS_FIELD = 0x00040000, // field
+ MN_IS_TYPE = 0x00080000, // nested type
+ MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers
+ MN_SEARCH_INTERFACES = 0x00200000, // for MHN.getMembers
+ VM_INDEX_UNINITIALIZED = -99;
+
+ // AdapterMethodHandle
+ /** Conversions recognized by the JVM.
+ * They must align with the constants in sun.dyn_AdapterMethodHandle,
+ * in the JVM file hotspot/src/share/vm/classfile/javaClasses.hpp.
+ */
+ static final int
+ OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
+ OP_CHECK_CAST = 0x1, // ref-to-ref conversion; requires a Class argument
+ OP_PRIM_TO_PRIM = 0x2, // converts from one primitive to another
+ OP_REF_TO_PRIM = 0x3, // unboxes a wrapper to produce a primitive
+ OP_PRIM_TO_REF = 0x4, // boxes a primitive into a wrapper (NYI)
+ OP_SWAP_ARGS = 0x5, // swap arguments (vminfo is 2nd arg)
+ OP_ROT_ARGS = 0x6, // rotate arguments (vminfo is displaced arg)
+ OP_DUP_ARGS = 0x7, // duplicates one or more arguments (at TOS)
+ OP_DROP_ARGS = 0x8, // remove one or more argument slots
+ OP_COLLECT_ARGS = 0x9, // combine one or more arguments into a varargs (NYI)
+ OP_SPREAD_ARGS = 0xA, // expand in place a varargs array (of known size)
+ OP_FLYBY = 0xB, // operate first on reified argument list (NYI)
+ OP_RICOCHET = 0xC, // run an adapter chain on the return value (NYI)
+ CONV_OP_LIMIT = 0xD; // limit of CONV_OP enumeration
+ /** Shift and mask values for decoding the AMH.conversion field.
+ * These numbers are shared with the JVM for creating AMHs.
+ */
+ static final int
+ CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
+ CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
+ CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
+ CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
+ CONV_DEST_TYPE_SHIFT = 12, // byte 2 has the adapter BasicType (if needed)
+ CONV_SRC_TYPE_SHIFT = 16, // byte 2 has the source BasicType (if needed)
+ CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change
+ CONV_STACK_MOVE_MASK = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1;
+
+ /** Which conv-ops are implemented by the JVM? */
+ static final int CONV_OP_IMPLEMENTED_MASK =
+ // TODO: The following expression should be replaced by
+ // a JVM query.
+ ((1<
+ * For an empirical discussion of the structure of method types,
+ * see
+ * the thread "Avoiding Boxing" on jvm-languages .
+ * There are approximately 2000 distinct erased method types in the JDK.
+ * There are a little over 10 times that number of unerased types.
+ * No more than half of these are likely to be loaded at once.
+ * @author John Rose
+ */
+public class MethodTypeImpl {
+ final int[] argToSlotTable, slotToArgTable;
+ final long argCounts; // packed slot & value counts
+ final long primCounts; // packed prim & double counts
+ final int vmslots; // total number of parameter slots
+ final MethodType erasedType; // the canonical erasure
+ /*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers
+ /*lazy*/ MethodType primArgsAsBoxes; // wrap args only; make raw return
+ /*lazy*/ MethodType primsAsInts; // replace prims by int/long
+ /*lazy*/ MethodType primsAsLongs; // replace prims by long
+ /*lazy*/ MethodType primsAtEnd; // reorder primitives to the end
+
+ // Cached adapter information:
+ /*lazy*/ ToGeneric toGeneric; // convert cs. with prims to w/o
+ /*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with
+ /*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly
+ ///*lazy*/ Invokers invokers; // cache of handy higher-order adapters
+
+ public MethodType erasedType() {
+ return erasedType;
+ }
+
+ public static MethodTypeImpl of(MethodType type) {
+ return METHOD_TYPE_FRIEND.form(type);
+ }
+
+ /** Access methods for the internals of MethodType, supplied to
+ * MethodTypeForm as a trusted agent.
+ */
+ static public interface MethodTypeFriend {
+ Class>[] ptypes(MethodType mt);
+ MethodTypeImpl form(MethodType mt);
+ void setForm(MethodType mt, MethodTypeImpl form);
+ MethodType makeImpl(Class> rtype, Class>[] ptypes, boolean trusted);
+ MethodTypeImpl newMethodTypeForm(MethodType mt);
+ Invokers getInvokers(MethodType mt);
+ void setInvokers(MethodType mt, Invokers inv);
+ }
+ public static void setMethodTypeFriend(Access token, MethodTypeFriend am) {
+ Access.check(token);
+ if (METHOD_TYPE_FRIEND != null)
+ throw new InternalError(); // just once
+ METHOD_TYPE_FRIEND = am;
+ }
+ static private MethodTypeFriend METHOD_TYPE_FRIEND;
+
+ protected MethodTypeImpl(MethodType erasedType) {
+ this.erasedType = erasedType;
+
+ Class>[] ptypes = METHOD_TYPE_FRIEND.ptypes(erasedType);
+ int ptypeCount = ptypes.length;
+ int pslotCount = ptypeCount; // temp. estimate
+ int rtypeCount = 1; // temp. estimate
+ int rslotCount = 1; // temp. estimate
+
+ int[] argToSlotTab = null, slotToArgTab = null;
+
+ // Walk the argument types, looking for primitives.
+ int pac = 0, lac = 0, prc = 0, lrc = 0;
+ Class> epts[] = ptypes;
+ for (int i = 0; i < epts.length; i++) {
+ Class> pt = epts[i];
+ if (pt != Object.class) {
+ assert(pt.isPrimitive());
+ ++pac;
+ if (hasTwoArgSlots(pt)) ++lac;
+ }
+ }
+ pslotCount += lac; // #slots = #args + #longs
+ Class> rt = erasedType.returnType();
+ if (rt != Object.class) {
+ ++prc; // even void.class counts as a prim here
+ if (hasTwoArgSlots(rt)) ++lrc;
+ // adjust #slots, #args
+ if (rt == void.class)
+ rtypeCount = rslotCount = 0;
+ else
+ rslotCount += lrc;
+ }
+ if (lac != 0) {
+ int slot = ptypeCount + lac;
+ slotToArgTab = new int[slot+1];
+ argToSlotTab = new int[1+ptypeCount];
+ argToSlotTab[0] = slot; // argument "-1" is past end of slots
+ for (int i = 0; i < epts.length; i++) {
+ Class> pt = epts[i];
+ if (hasTwoArgSlots(pt)) --slot;
+ --slot;
+ slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
+ argToSlotTab[1+i] = slot;
+ }
+ assert(slot == 0); // filled the table
+ }
+ this.primCounts = pack(lrc, prc, lac, pac);
+ this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
+ if (slotToArgTab == null) {
+ int slot = ptypeCount; // first arg is deepest in stack
+ slotToArgTab = new int[slot+1];
+ argToSlotTab = new int[1+ptypeCount];
+ argToSlotTab[0] = slot; // argument "-1" is past end of slots
+ for (int i = 0; i < ptypeCount; i++) {
+ --slot;
+ slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
+ argToSlotTab[1+i] = slot;
+ }
+ }
+ this.argToSlotTable = argToSlotTab;
+ this.slotToArgTable = slotToArgTab;
+
+ if (pslotCount >= 256) throw new IllegalArgumentException("too many arguments");
+
+ // send a few bits down to the JVM:
+ this.vmslots = parameterSlotCount();
+
+ // short circuit some no-op canonicalizations:
+ if (!hasPrimitives()) {
+ primsAsBoxes = erasedType;
+ primArgsAsBoxes = erasedType;
+ primsAsInts = erasedType;
+ primsAsLongs = erasedType;
+ primsAtEnd = erasedType;
+ }
+ }
+
+ /** Turn all primitive types to corresponding wrapper types.
+ */
+ public MethodType primsAsBoxes() {
+ MethodType ct = primsAsBoxes;
+ if (ct != null) return ct;
+ MethodType t = erasedType;
+ ct = canonicalize(erasedType, WRAP, WRAP);
+ if (ct == null) ct = t; // no prims to box
+ return primsAsBoxes = ct;
+ }
+
+ /** Turn all primitive argument types to corresponding wrapper types.
+ * Subword and void return types are promoted to int.
+ */
+ public MethodType primArgsAsBoxes() {
+ MethodType ct = primArgsAsBoxes;
+ if (ct != null) return ct;
+ MethodType t = erasedType;
+ ct = canonicalize(erasedType, RAW_RETURN, WRAP);
+ if (ct == null) ct = t; // no prims to box
+ return primArgsAsBoxes = ct;
+ }
+
+ /** Turn all primitive types to either int or long.
+ * Floating point return types are not changed, because
+ * they may require special calling sequences.
+ * A void return value is turned to int.
+ */
+ public MethodType primsAsInts() {
+ MethodType ct = primsAsInts;
+ if (ct != null) return ct;
+ MethodType t = erasedType;
+ ct = canonicalize(t, RAW_RETURN, INTS);
+ if (ct == null) ct = t; // no prims to int-ify
+ return primsAsInts = ct;
+ }
+
+ /** Turn all primitive types to either int or long.
+ * Floating point return types are not changed, because
+ * they may require special calling sequences.
+ * A void return value is turned to int.
+ */
+ public MethodType primsAsLongs() {
+ MethodType ct = primsAsLongs;
+ if (ct != null) return ct;
+ MethodType t = erasedType;
+ ct = canonicalize(t, RAW_RETURN, LONGS);
+ if (ct == null) ct = t; // no prims to int-ify
+ return primsAsLongs = ct;
+ }
+
+ /** Stably sort parameters into 3 buckets: ref, int, long. */
+ public MethodType primsAtEnd() {
+ MethodType ct = primsAtEnd;
+ if (ct != null) return ct;
+ MethodType t = erasedType;
+
+ int pac = primitiveParameterCount();
+ if (pac == 0)
+ return primsAtEnd = t;
+
+ int argc = parameterCount();
+ int lac = longPrimitiveParameterCount();
+ if (pac == argc && (lac == 0 || lac == argc))
+ return primsAtEnd = t;
+
+ // known to have a mix of 2 or 3 of ref, int, long
+ return primsAtEnd = reorderParameters(t, primsAtEndOrder(t), null);
+
+ }
+
+ /** Compute a new ordering of parameters so that all references
+ * are before all ints or longs, and all ints are before all longs.
+ * For this ordering, doubles count as longs, and all other primitive
+ * values count as ints.
+ * As a special case, if the parameters are already in the specified
+ * order, this method returns a null reference, rather than an array
+ * specifying a null permutation.
+ *
+ * For example, the type {@code (int,boolean,int,Object,String)void}
+ * produces the order {@code {3,4,0,1,2}}, the type
+ * {@code (long,int,String)void} produces {@code {2,1,2}}, and
+ * the type {@code (Object,int)Object} produces {@code null}.
+ */
+ public static int[] primsAtEndOrder(MethodType mt) {
+ MethodTypeImpl form = METHOD_TYPE_FRIEND.form(mt);
+ if (form.primsAtEnd == form.erasedType)
+ // quick check shows no reordering is necessary
+ return null;
+
+ int argc = form.parameterCount();
+ int[] paramOrder = new int[argc];
+
+ // 3-way bucket sort:
+ int pac = form.primitiveParameterCount();
+ int lac = form.longPrimitiveParameterCount();
+ int rfill = 0, ifill = argc - pac, lfill = argc - lac;
+
+ Class>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt);
+ boolean changed = false;
+ for (int i = 0; i < ptypes.length; i++) {
+ Class> pt = ptypes[i];
+ int ord;
+ if (!pt.isPrimitive()) ord = rfill++;
+ else if (!hasTwoArgSlots(pt)) ord = ifill++;
+ else ord = lfill++;
+ if (ord != i) changed = true;
+ paramOrder[i] = ord;
+ }
+ assert(rfill == argc - pac && ifill == argc - lac && lfill == argc);
+ if (!changed) {
+ form.primsAtEnd = form.erasedType;
+ return null;
+ }
+ return paramOrder;
+ }
+
+ /** Put the existing parameters of mt into a new order, given by newParamOrder.
+ * The third argument is logically appended to mt.parameterArray,
+ * so that elements of newParamOrder can index either pre-existing or
+ * new parameter types.
+ */
+ public static MethodType reorderParameters(MethodType mt, int[] newParamOrder, Class>[] moreParams) {
+ if (newParamOrder == null) return mt; // no-op reordering
+ Class>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt);
+ Class>[] ntypes = new Class>[newParamOrder.length];
+ int ordMax = ptypes.length + (moreParams == null ? 0 : moreParams.length);
+ boolean changed = (ntypes.length != ptypes.length);
+ for (int i = 0; i < newParamOrder.length; i++) {
+ int ord = newParamOrder[i];
+ if (ord != i) changed = true;
+ Class> nt;
+ if (ord < ptypes.length) nt = ptypes[ord];
+ else if (ord == ordMax) nt = mt.returnType();
+ else nt = moreParams[ord - ptypes.length];
+ ntypes[i] = nt;
+ }
+ if (!changed) return mt;
+ return METHOD_TYPE_FRIEND.makeImpl(mt.returnType(), ntypes, true);
+ }
+
+ private static boolean hasTwoArgSlots(Class> type) {
+ return type == long.class || type == double.class;
+ }
+
+ private static long pack(int a, int b, int c, int d) {
+ assert(((a|b|c|d) & ~0xFFFF) == 0);
+ long hw = ((a << 16) | b), lw = ((c << 16) | d);
+ return (hw << 32) | lw;
+ }
+ private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d
+ assert(word <= 3);
+ return (char)(packed >> ((3-word) * 16));
+ }
+
+ public int parameterCount() { // # outgoing values
+ return unpack(argCounts, 3);
+ }
+ public int parameterSlotCount() { // # outgoing interpreter slots
+ return unpack(argCounts, 2);
+ }
+ public int returnCount() { // = 0 (V), or 1
+ return unpack(argCounts, 1);
+ }
+ public int returnSlotCount() { // = 0 (V), 2 (J/D), or 1
+ return unpack(argCounts, 0);
+ }
+ public int primitiveParameterCount() {
+ return unpack(primCounts, 3);
+ }
+ public int longPrimitiveParameterCount() {
+ return unpack(primCounts, 2);
+ }
+ public int primitiveReturnCount() { // = 0 (obj), or 1
+ return unpack(primCounts, 1);
+ }
+ public int longPrimitiveReturnCount() { // = 1 (J/D), or 0
+ return unpack(primCounts, 0);
+ }
+ public boolean hasPrimitives() {
+ return primCounts != 0;
+ }
+// public boolean hasNonVoidPrimitives() {
+// if (primCounts == 0) return false;
+// if (primitiveParameterCount() != 0) return true;
+// return (primitiveReturnCount() != 0 && returnCount() != 0);
+// }
+ public boolean hasLongPrimitives() {
+ return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
+ }
+ public int parameterToArgSlot(int i) {
+ return argToSlotTable[1+i];
+ }
+ public int argSlotToParameter(int argSlot) {
+ // Note: Empty slots are represented by zero in this table.
+ // Valid arguments slots contain incremented entries, so as to be non-zero.
+ // We return -1 the caller to mean an empty slot.
+ return slotToArgTable[argSlot] - 1;
+ }
+
+ public static void initForm(Access token, MethodType mt) {
+ Access.check(token);
+ MethodTypeImpl form = findForm(mt);
+ METHOD_TYPE_FRIEND.setForm(mt, form);
+ if (form.erasedType == mt) {
+ // This is a principal (erased) type; show it to the JVM.
+ MethodHandleImpl.init(token, mt);
+ }
+ }
+
+ static MethodTypeImpl findForm(MethodType mt) {
+ MethodType erased = canonicalize(mt, ERASE, ERASE);
+ if (erased == null) {
+ // It is already erased. Make a new MethodTypeForm.
+ return METHOD_TYPE_FRIEND.newMethodTypeForm(mt);
+ } else {
+ // Share the MethodTypeForm with the erased version.
+ return METHOD_TYPE_FRIEND.form(erased);
+ }
+ }
+
+ /** Codes for {@link #canonicalize(java.lang.Class, int).
+ * ERASE means change every reference to {@code Object}.
+ * WRAP means convert primitives (including {@code void} to their
+ * corresponding wrapper types. UNWRAP means the reverse of WRAP.
+ * INTS means convert all non-void primitive types to int or long,
+ * according to size. LONGS means convert all non-void primitives
+ * to long, regardless of size. RAW_RETURN means convert a type
+ * (assumed to be a return type) to int if it is smaller than an int,
+ * or if it is void.
+ */
+ public static final int NO_CHANGE = 0, ERASE = 1, WRAP = 2, UNWRAP = 3, INTS = 4, LONGS = 5, RAW_RETURN = 6;
+
+ /** Canonicalize the types in the given method type.
+ * If any types change, intern the new type, and return it.
+ * Otherwise return null.
+ */
+ public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
+ Class>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt);
+ Class>[] ptc = MethodTypeImpl.canonicalizes(ptypes, howArgs);
+ Class> rtype = mt.returnType();
+ Class> rtc = MethodTypeImpl.canonicalize(rtype, howRet);
+ if (ptc == null && rtc == null) {
+ // It is already canonical.
+ return null;
+ }
+ // Find the erased version of the method type:
+ if (rtc == null) rtc = rtype;
+ if (ptc == null) ptc = ptypes;
+ return METHOD_TYPE_FRIEND.makeImpl(rtc, ptc, true);
+ }
+
+ /** Canonicalize the given return or param type.
+ * Return null if the type is already canonicalized.
+ */
+ static Class> canonicalize(Class> t, int how) {
+ Class> ct;
+ if (t == Object.class) {
+ // no change, ever
+ } else if (!t.isPrimitive()) {
+ switch (how) {
+ case UNWRAP:
+ ct = Wrapper.asPrimitiveType(t);
+ if (ct != t) return ct;
+ break;
+ case RAW_RETURN:
+ case ERASE:
+ return Object.class;
+ }
+ } else if (t == void.class) {
+ // no change, usually
+ switch (how) {
+ case RAW_RETURN:
+ return int.class;
+ case WRAP:
+ return Void.class;
+ }
+ } else {
+ // non-void primitive
+ switch (how) {
+ case WRAP:
+ return Wrapper.asWrapperType(t);
+ case INTS:
+ if (t == int.class || t == long.class)
+ return null; // no change
+ if (t == double.class)
+ return long.class;
+ return int.class;
+ case LONGS:
+ if (t == long.class)
+ return null; // no change
+ return long.class;
+ case RAW_RETURN:
+ if (t == int.class || t == long.class ||
+ t == float.class || t == double.class)
+ return null; // no change
+ // everything else returns as an int
+ return int.class;
+ }
+ }
+ // no change; return null to signify
+ return null;
+ }
+
+ /** Canonicalize each param type in the given array.
+ * Return null if all types are already canonicalized.
+ */
+ static Class>[] canonicalizes(Class>[] ts, int how) {
+ Class>[] cs = null;
+ for (int imax = ts.length, i = 0; i < imax; i++) {
+ Class> c = canonicalize(ts[i], how);
+ if (c != null) {
+ if (cs == null)
+ cs = ts.clone();
+ cs[i] = c;
+ }
+ }
+ return cs;
+ }
+
+ public static Invokers invokers(Access token, MethodType type) {
+ Access.check(token);
+ Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type);
+ if (inv != null) return inv;
+ inv = new Invokers(token, type);
+ METHOD_TYPE_FRIEND.setInvokers(type, inv);
+ return inv;
+ }
+
+ @Override
+ public String toString() {
+ return "Form"+erasedType;
+ }
+
+}
diff --git a/src/share/classes/sun/dyn/ToGeneric.java b/src/share/classes/sun/dyn/ToGeneric.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd04d753e7447a6b733cb3ba373ae50f6e9a637d
--- /dev/null
+++ b/src/share/classes/sun/dyn/ToGeneric.java
@@ -0,0 +1,1018 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.JavaMethodHandle;
+import java.dyn.MethodHandle;
+import java.dyn.MethodHandles;
+import java.dyn.MethodType;
+import java.dyn.NoAccessException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import sun.dyn.util.ValueConversions;
+import sun.dyn.util.Wrapper;
+
+/**
+ * Adapters which mediate between incoming calls which are not generic
+ * and outgoing calls which are. Any call can be represented generically
+ * boxing up its arguments, and (on return) unboxing the return value.
+ *
+ * A call is "generic" (in MethodHandle terms) if its MethodType features
+ * only Object arguments. A non-generic call therefore features
+ * primitives and/or reference types other than Object.
+ * An adapter has types for its incoming and outgoing calls.
+ * The incoming call type is simply determined by the adapter's type
+ * (the MethodType it presents to callers). The outgoing call type
+ * is determined by the adapter's target (a MethodHandle that the adapter
+ * either binds internally or else takes as a leading argument).
+ * (To stretch the term, adapter-like method handles may have multiple
+ * targets or be polymorphic across multiple call types.)
+ * @author jrose
+ */
+class ToGeneric {
+ // type for the incoming call (may be erased)
+ private final MethodType entryType;
+ // incoming type with primitives moved to the end and turned to int/long
+ private final MethodType rawEntryType;
+ // adapter for the erased type
+ private final Adapter adapter;
+ // entry point for adapter (Adapter mh, a...) => ...
+ private final MethodHandle entryPoint;
+ // permutation of arguments for primsAtEndType
+ private final int[] primsAtEndOrder;
+ // optional final argument list conversions (at least, invokes the target)
+ private final MethodHandle invoker;
+ // conversion which unboxes a primitive return value
+ private final MethodHandle returnConversion;
+
+ /** Compute and cache information common to all collecting adapters
+ * that implement members of the erasure-family of the given erased type.
+ */
+ private ToGeneric(MethodType entryType) {
+ assert(entryType.erase() == entryType); // for now
+ // incoming call will first "forget" all reference types except Object
+ this.entryType = entryType;
+ MethodHandle invoker0 = MethodHandles.exactInvoker(entryType.generic());
+ MethodType rawEntryTypeInit;
+ Adapter ad = findAdapter(rawEntryTypeInit = entryType);
+ if (ad != null) {
+ // Immediate hit to exactly the adapter we want,
+ // with no monkeying around with primitive types.
+ this.returnConversion = computeReturnConversion(entryType, rawEntryTypeInit, false);
+ this.rawEntryType = rawEntryTypeInit;
+ this.adapter = ad;
+ this.entryPoint = ad.prototypeEntryPoint();
+ this.primsAtEndOrder = null;
+ this.invoker = invoker0;
+ return;
+ }
+
+ // next, it will reorder primitives after references
+ MethodType primsAtEnd = MethodTypeImpl.of(entryType).primsAtEnd();
+ // at the same time, it will "forget" all primitive types except int/long
+ this.primsAtEndOrder = MethodTypeImpl.primsAtEndOrder(entryType);
+ if (primsAtEndOrder != null) {
+ // reordering is required; build on top of a simpler ToGeneric
+ ToGeneric va2 = ToGeneric.of(primsAtEnd);
+ this.adapter = va2.adapter;
+ this.entryPoint = MethodHandleImpl.convertArguments(Access.TOKEN,
+ va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder);
+ // example: for entryType of (int,Object,Object), the reordered
+ // type is (Object,Object,int) and the order is {1,2,0},
+ // and putPAE is (mh,int0,obj1,obj2) => mh.invoke(obj1,obj2,int0)
+ if (true) throw new UnsupportedOperationException("NYI");
+ return;
+ }
+
+ // after any needed argument reordering, it will reinterpret
+ // primitive arguments according to their "raw" types int/long
+ MethodType intsAtEnd = MethodTypeImpl.of(primsAtEnd).primsAsInts();
+ ad = findAdapter(rawEntryTypeInit = intsAtEnd);
+ if (ad == null) {
+ // Perhaps the adapter is available only for longs.
+ // If so, we can use it, but there will have to be a little
+ // more stack motion on each call.
+ MethodType longsAtEnd = MethodTypeImpl.of(primsAtEnd).primsAsLongs();
+ ad = findAdapter(rawEntryTypeInit = longsAtEnd);
+ if (ad == null) {
+ // If there is no statically compiled adapter,
+ // build one by means of dynamic bytecode generation.
+ ad = buildAdapterFromBytecodes(rawEntryTypeInit = intsAtEnd);
+ }
+ }
+ MethodHandle rawEntryPoint = ad.prototypeEntryPoint();
+ MethodType tepType = entryType.insertParameterType(0, ad.getClass());
+ this.entryPoint =
+ AdapterMethodHandle.makeRawRetypeOnly(Access.TOKEN, tepType, rawEntryPoint);
+ if (this.entryPoint == null)
+ throw new UnsupportedOperationException("cannot retype to "+entryType
+ +" from "+rawEntryPoint.type().dropParameterType(0));
+ this.returnConversion = computeReturnConversion(entryType, rawEntryTypeInit, false);
+ this.rawEntryType = rawEntryTypeInit;
+ this.adapter = ad;
+ this.invoker = makeRawArgumentFilter(invoker0,
+ rawEntryPoint.type().dropParameterType(0), entryType);
+ }
+
+ /** A generic argument list will be created by a call of type 'raw'.
+ * The values need to be reboxed for to match 'cooked'.
+ * Do this on the fly.
+ */
+ // TO DO: Use a generic argument converter in a different file
+ static MethodHandle makeRawArgumentFilter(MethodHandle invoker,
+ MethodType raw, MethodType cooked) {
+ MethodHandle filteredInvoker = null;
+ for (int i = 0, nargs = raw.parameterCount(); i < nargs; i++) {
+ Class> src = raw.parameterType(i);
+ Class> dst = cooked.parameterType(i);
+ if (src == dst) continue;
+ assert(src.isPrimitive() && dst.isPrimitive());
+ if (filteredInvoker == null) {
+ filteredInvoker =
+ AdapterMethodHandle.makeCheckCast(Access.TOKEN,
+ invoker.type().generic(), invoker, 0, MethodHandle.class);
+ if (filteredInvoker == null) throw new UnsupportedOperationException("NYI");
+ }
+ MethodHandle reboxer = ValueConversions.rebox(dst, false);
+ FilterGeneric gen = new FilterGeneric(filteredInvoker.type(), (short)(1+i), (short)1, 'R');
+ filteredInvoker = gen.makeInstance(reboxer, filteredInvoker);
+ }
+ if (filteredInvoker == null) return invoker;
+ return AdapterMethodHandle.makeRetypeOnly(Access.TOKEN, invoker.type(), filteredInvoker);
+ }
+
+ /**
+ * Caller will be expecting a result from a call to {@code type},
+ * while the internal adapter entry point is rawEntryType.
+ * Also, the internal target method will be returning a boxed value,
+ * as an untyped object.
+ *
+ * Produce a value converter which will be typed to convert from
+ * {@code Object} to the return value of {@code rawEntryType}, and will
+ * in fact ensure that the value is compatible with the return type of
+ * {@code type}.
+ */
+ private static MethodHandle computeReturnConversion(
+ MethodType type, MethodType rawEntryType, boolean mustCast) {
+ Class> tret = type.returnType();
+ Class> rret = rawEntryType.returnType();
+ if (mustCast || !tret.isPrimitive()) {
+ assert(!tret.isPrimitive());
+ assert(!rret.isPrimitive());
+ if (rret == Object.class && !mustCast)
+ return null;
+ return ValueConversions.cast(tret, false);
+ } else if (tret == rret) {
+ return ValueConversions.unbox(tret, false);
+ } else {
+ assert(rret.isPrimitive());
+ assert(tret == double.class ? rret == long.class : rret == int.class);
+ return ValueConversions.unboxRaw(tret, false);
+ }
+ }
+
+ Adapter makeInstance(MethodType type, MethodHandle genericTarget) {
+ genericTarget.getClass(); // check for NPE
+ MethodHandle convert = returnConversion;
+ if (primsAtEndOrder != null)
+ // reorder arguments passed to genericTarget, if primsAtEndOrder
+ throw new UnsupportedOperationException("NYI");
+ if (type == entryType) {
+ if (convert == null) convert = ValueConversions.identity();
+ return adapter.makeInstance(entryPoint, invoker, convert, genericTarget);
+ }
+ // my erased-type is not exactly the same as the desired type
+ assert(type.erase() == entryType); // else we are busted
+ if (convert == null)
+ convert = computeReturnConversion(type, rawEntryType, true);
+ // retype erased reference arguments (the cast makes it safe to do this)
+ MethodType tepType = type.insertParameterType(0, adapter.getClass());
+ MethodHandle typedEntryPoint =
+ AdapterMethodHandle.makeRawRetypeOnly(Access.TOKEN, tepType, entryPoint);
+ return adapter.makeInstance(typedEntryPoint, invoker, convert, genericTarget);
+ }
+
+ /** Build an adapter of the given type, which invokes genericTarget
+ * on the incoming arguments, after boxing as necessary.
+ * The return value is unboxed if necessary.
+ * @param type the required type of the
+ * @param genericTarget the target, which must accept and return only Object values
+ * @return an adapter method handle
+ */
+ public static MethodHandle make(MethodType type, MethodHandle genericTarget) {
+ MethodType gtype = genericTarget.type();
+ if (type.generic() != gtype)
+ throw new IllegalArgumentException();
+ if (type == gtype) return genericTarget;
+ return ToGeneric.of(type).makeInstance(type, genericTarget);
+ }
+
+ /** Return the adapter information for this type's erasure. */
+ static ToGeneric of(MethodType type) {
+ MethodTypeImpl form = MethodTypeImpl.of(type);
+ ToGeneric toGen = form.toGeneric;
+ if (toGen == null)
+ form.toGeneric = toGen = new ToGeneric(form.erasedType());
+ return toGen;
+ }
+
+ public String toString() {
+ return "ToGeneric"+entryType
+ +(primsAtEndOrder!=null?"[reorder]":"");
+ }
+
+ /* Create an adapter for the given incoming call type. */
+ static Adapter findAdapter(MethodType entryPointType) {
+ MethodTypeImpl form = MethodTypeImpl.of(entryPointType);
+ Class> rtype = entryPointType.returnType();
+ int argc = form.parameterCount();
+ int lac = form.longPrimitiveParameterCount();
+ int iac = form.primitiveParameterCount() - lac;
+ String intsAndLongs = (iac > 0 ? "I"+iac : "")+(lac > 0 ? "J"+lac : "");
+ String rawReturn = String.valueOf(Wrapper.forPrimitiveType(rtype).basicTypeChar());
+ String iname0 = "invoke_"+rawReturn;
+ String iname1 = "invoke";
+ String[] inames = { iname0, iname1 };
+ String cname0 = rawReturn + argc;
+ String cname1 = "A" + argc;
+ String[] cnames = { cname1, cname1+intsAndLongs, cname0, cname0+intsAndLongs };
+ // e.g., D5I2, D5, L5I2, L5
+ for (String cname : cnames) {
+ Class extends Adapter> acls = Adapter.findSubClass(cname);
+ if (acls == null) continue;
+ // see if it has the required invoke method
+ for (String iname : inames) {
+ MethodHandle entryPoint = null;
+ try {
+ entryPoint = MethodHandleImpl.IMPL_LOOKUP.
+ findSpecial(acls, iname, entryPointType, acls);
+ } catch (NoAccessException ex) {
+ }
+ if (entryPoint == null) continue;
+ Constructor extends Adapter> ctor = null;
+ try {
+ // Prototype builder:
+ ctor = acls.getDeclaredConstructor(MethodHandle.class);
+ } catch (NoSuchMethodException ex) {
+ } catch (SecurityException ex) {
+ }
+ if (ctor == null) continue;
+ try {
+ return ctor.newInstance(entryPoint);
+ } catch (IllegalArgumentException ex) {
+ } catch (InvocationTargetException ex) {
+ } catch (InstantiationException ex) {
+ } catch (IllegalAccessException ex) {
+ }
+ }
+ }
+ return null;
+ }
+
+ static Adapter buildAdapterFromBytecodes(MethodType entryPointType) {
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /**
+ * The invoke method takes some particular but unconstrained spread
+ * of raw argument types, and returns a raw return type (in L/I/J/F/D).
+ * Internally, it converts the incoming arguments uniformly into objects.
+ * This series of objects is then passed to the {@code target} method,
+ * which returns a result object. This result is finally converted,
+ * via another method handle {@code convert}, which is responsible for
+ * converting the object result into the raw return value.
+ */
+ static abstract class Adapter extends JavaMethodHandle {
+ /*
+ * class X<> extends Adapter {
+ * Object...=>Object target;
+ * Object=>R convert;
+ * R invoke(A... a...) = convert(invoker(target, a...)))
+ * }
+ */
+ protected final MethodHandle invoker; // (MH, Object...) -> Object
+ protected final MethodHandle target; // Object... -> Object
+ protected final MethodHandle convert; // Object -> R
+
+ protected boolean isPrototype() { return target == null; }
+ /* Prototype constructor. */
+ protected Adapter(MethodHandle entryPoint) {
+ super(entryPoint);
+ this.invoker = null;
+ this.convert = entryPoint;
+ this.target = null;
+ assert(isPrototype());
+ }
+ protected MethodHandle prototypeEntryPoint() {
+ if (!isPrototype()) throw new InternalError();
+ return convert;
+ }
+
+ protected Adapter(MethodHandle entryPoint, MethodHandle invoker, MethodHandle convert, MethodHandle target) {
+ super(entryPoint);
+ this.invoker = invoker;
+ this.convert = convert;
+ this.target = target;
+ }
+
+ /** Make a copy of self, with new fields. */
+ protected abstract Adapter makeInstance(MethodHandle entryPoint,
+ MethodHandle invoker, MethodHandle convert, MethodHandle target);
+ // { return new ThisType(entryPoint, convert, target); }
+
+ // Code to run when the arguments (<= 4) have all been boxed.
+ protected Object target() { return invoker.invoke(target); }
+ protected Object target(Object a0) { return invoker.invoke(target, a0); }
+ protected Object target(Object a0, Object a1)
+ { return invoker.invoke(target, a0, a1); }
+ protected Object target(Object a0, Object a1, Object a2)
+ { return invoker.invoke(target, a0, a1, a2); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3)
+ { return invoker.invoke(target, a0, a1, a2, a3); }
+ /*
+ protected Object target_0(Object... av) { return invoker.invoke(target, av); }
+ protected Object target_1(Object a0, Object... av)
+ { return invoker.invoke(target, a0, (Object)av); }
+ protected Object target_2(Object a0, Object a1, Object... av)
+ { return invoker.invoke(target, a0, a1, (Object)av); }
+ protected Object target_3(Object a0, Object a1, Object a2, Object... av)
+ { return invoker.invoke(target, a0, a1, a2, (Object)av); }
+ protected Object target_4(Object a0, Object a1, Object a2, Object a3, Object... av)
+ { return invoker.invoke(target, a0, a1, a2, a3, (Object)av); }
+ // */
+ // (For more than 4 arguments, generate the code in the adapter itself.)
+
+ // Code to run when the generic target has finished and produced a value.
+ protected Object return_L(Object res) { return convert.invoke(res); }
+ protected int return_I(Object res) { return convert.invoke(res); }
+ protected long return_J(Object res) { return convert.invoke(res); }
+ protected float return_F(Object res) { return convert.invoke(res); }
+ protected double return_D(Object res) { return convert.invoke(res); }
+
+ static private final String CLASS_PREFIX; // "sun.dyn.ToGeneric$"
+ static {
+ String aname = Adapter.class.getName();
+ String sname = Adapter.class.getSimpleName();
+ if (!aname.endsWith(sname)) throw new InternalError();
+ CLASS_PREFIX = aname.substring(0, aname.length() - sname.length());
+ }
+ /** Find a sibing class of Adapter. */
+ static Class extends Adapter> findSubClass(String name) {
+ String cname = Adapter.CLASS_PREFIX + name;
+ try {
+ return Class.forName(cname).asSubclass(Adapter.class);
+ } catch (ClassNotFoundException ex) {
+ return null;
+ } catch (ClassCastException ex) {
+ return null;
+ }
+ }
+ }
+
+ /* generated classes follow this pattern:
+ static class A1 extends Adapter {
+ protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A1(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A1(e, i, c, t); }
+ protected Object target(Object a0) { return invoker.invoke(target, a0); }
+ protected Object targetA1(Object a0) { return target(a0); }
+ protected Object targetA1(int a0) { return target(a0); }
+ protected Object targetA1(long a0) { return target(a0); }
+ protected Object invoke_L(Object a0) { return return_L(targetA1(a0)); }
+ protected int invoke_I(Object a0) { return return_I(targetA1(a0)); }
+ protected long invoke_J(Object a0) { return return_J(targetA1(a0)); }
+ protected float invoke_F(Object a0) { return return_F(targetA1(a0)); }
+ protected double invoke_D(Object a0) { return return_D(targetA1(a0)); }
+ protected Object invoke_L(int a0) { return return_L(targetA1(a0)); }
+ protected int invoke_I(int a0) { return return_I(targetA1(a0)); }
+ protected long invoke_J(int a0) { return return_J(targetA1(a0)); }
+ protected float invoke_F(int a0) { return return_F(targetA1(a0)); }
+ protected double invoke_D(int a0) { return return_D(targetA1(a0)); }
+ protected Object invoke_L(long a0) { return return_L(targetA1(a0)); }
+ protected int invoke_I(long a0) { return return_I(targetA1(a0)); }
+ protected long invoke_J(long a0) { return return_J(targetA1(a0)); }
+ protected float invoke_F(long a0) { return return_F(targetA1(a0)); }
+ protected double invoke_D(long a0) { return return_D(targetA1(a0)); }
+ }
+ // */
+
+/*
+: SHELL; n=ToGeneric; cp -p $n.java $n.java-; sed < $n.java- > $n.java+ -e '/{{*{{/,/}}*}}/w /tmp/genclasses.java' -e '/}}*}}/q'; (cd /tmp; javac -d . genclasses.java; java -cp . genclasses) >> $n.java+; echo '}' >> $n.java+; mv $n.java+ $n.java; mv $n.java- $n.java~
+//{{{
+import java.util.*;
+class genclasses {
+ static String[] TYPES = { "Object", "int ", "long ", "float ", "double" };
+ static String[] TCHARS = { "L", "I", "J", "F", "D", "A" };
+ static String[][] TEMPLATES = { {
+ "@for@ arity=0..3 rcat<=4 nrefs<=99 nints<=99 nlongs<=99",
+ "@for@ arity=4..5 rcat<=2 nrefs<=99 nints<=99 nlongs<=99",
+ "@for@ arity=6..10 rcat<=2 nrefs<=99 nints=0 nlongs<=99",
+ " //@each-cat@",
+ " static class @cat@ extends Adapter {",
+ " protected @cat@(MethodHandle entryPoint) { super(entryPoint); } // to build prototype",
+ " protected @cat@(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }",
+ " protected @cat@ makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new @cat@(e, i, c, t); }",
+ " protected Object target(@Ovav@) { return invoker.invoke(target, @av@); }",
+ " //@each-Tv@",
+ " protected Object target@cat@(@Tvav@) { return target(@av@); }",
+ " //@end-Tv@",
+ " //@each-Tv@",
+ " //@each-R@",
+ " protected @R@ invoke_@Rc@(@Tvav@) { return return_@Rc@(target@cat@(@av@)); }",
+ " //@end-R@",
+ " //@end-Tv@",
+ " }",
+ } };
+ enum VAR {
+ cat, R, Rc, Tv, av, Tvav, Ovav;
+ public final String pattern = "@"+toString().replace('_','.')+"@";
+ public String binding;
+ static void makeBindings(boolean topLevel, int rcat, int nrefs, int nints, int nlongs) {
+ int nargs = nrefs + nints + nlongs;
+ if (topLevel)
+ VAR.cat.binding = catstr(ALL_RETURN_TYPES ? TYPES.length : rcat, nrefs, nints, nlongs);
+ VAR.R.binding = TYPES[rcat];
+ VAR.Rc.binding = TCHARS[rcat];
+ String[] Tv = new String[nargs];
+ String[] av = new String[nargs];
+ String[] Tvav = new String[nargs];
+ String[] Ovav = new String[nargs];
+ for (int i = 0; i < nargs; i++) {
+ int tcat = (i < nrefs) ? 0 : (i < nrefs + nints) ? 1 : 2;
+ Tv[i] = TYPES[tcat];
+ av[i] = arg(i);
+ Tvav[i] = param(Tv[i], av[i]);
+ Ovav[i] = param("Object", av[i]);
+ }
+ VAR.Tv.binding = comma(Tv);
+ VAR.av.binding = comma(av);
+ VAR.Tvav.binding = comma(Tvav);
+ VAR.Ovav.binding = comma(Ovav);
+ }
+ static String arg(int i) { return "a"+i; }
+ static String param(String t, String a) { return t+" "+a; }
+ static String comma(String[] v) { return comma(v, ""); }
+ static String comma(String sep, String[] v) {
+ if (v.length == 0) return "";
+ String res = sep+v[0];
+ for (int i = 1; i < v.length; i++) res += ", "+v[i];
+ return res;
+ }
+ static String transform(String string) {
+ for (VAR var : values())
+ string = string.replaceAll(var.pattern, var.binding);
+ return string;
+ }
+ }
+ static String[] stringsIn(String[] strings, int beg, int end) {
+ return Arrays.copyOfRange(strings, beg, Math.min(end, strings.length));
+ }
+ static String[] stringsBefore(String[] strings, int pos) {
+ return stringsIn(strings, 0, pos);
+ }
+ static String[] stringsAfter(String[] strings, int pos) {
+ return stringsIn(strings, pos, strings.length);
+ }
+ static int indexAfter(String[] strings, int pos, String tag) {
+ return Math.min(indexBefore(strings, pos, tag) + 1, strings.length);
+ }
+ static int indexBefore(String[] strings, int pos, String tag) {
+ for (int i = pos, end = strings.length; ; i++) {
+ if (i == end || strings[i].endsWith(tag)) return i;
+ }
+ }
+ static int MIN_ARITY, MAX_ARITY, MAX_RCAT, MAX_REFS, MAX_INTS, MAX_LONGS;
+ static boolean ALL_ARG_TYPES, ALL_RETURN_TYPES;
+ static HashSet done = new HashSet();
+ public static void main(String... av) {
+ for (String[] template : TEMPLATES) {
+ int forLinesLimit = indexBefore(template, 0, "@each-cat@");
+ String[] forLines = stringsBefore(template, forLinesLimit);
+ template = stringsAfter(template, forLinesLimit);
+ for (String forLine : forLines)
+ expandTemplate(forLine, template);
+ }
+ }
+ static void expandTemplate(String forLine, String[] template) {
+ String[] params = forLine.split("[^0-9]+");
+ if (params[0].length() == 0) params = stringsAfter(params, 1);
+ System.out.println("//params="+Arrays.asList(params));
+ int pcur = 0;
+ MIN_ARITY = Integer.valueOf(params[pcur++]);
+ MAX_ARITY = Integer.valueOf(params[pcur++]);
+ MAX_RCAT = Integer.valueOf(params[pcur++]);
+ MAX_REFS = Integer.valueOf(params[pcur++]);
+ MAX_INTS = Integer.valueOf(params[pcur++]);
+ MAX_LONGS = Integer.valueOf(params[pcur++]);
+ if (pcur != params.length) throw new RuntimeException("bad extra param: "+forLine);
+ if (MAX_RCAT >= TYPES.length) MAX_RCAT = TYPES.length - 1;
+ ALL_ARG_TYPES = (indexBefore(template, 0, "@each-Tv@") < template.length);
+ ALL_RETURN_TYPES = (indexBefore(template, 0, "@each-R@") < template.length);
+ for (int nargs = MIN_ARITY; nargs <= MAX_ARITY; nargs++) {
+ for (int rcat = 0; rcat <= MAX_RCAT; rcat++) {
+ expandTemplate(template, true, rcat, nargs, 0, 0);
+ if (ALL_ARG_TYPES) break;
+ expandTemplateForPrims(template, true, rcat, nargs, 1, 1);
+ if (ALL_RETURN_TYPES) break;
+ }
+ }
+ }
+ static String catstr(int rcat, int nrefs, int nints, int nlongs) {
+ int nargs = nrefs + nints + nlongs;
+ String cat = TCHARS[rcat] + nargs;
+ if (!ALL_ARG_TYPES) cat += (nints==0?"":"I"+nints)+(nlongs==0?"":"J"+nlongs);
+ return cat;
+ }
+ static void expandTemplateForPrims(String[] template, boolean topLevel, int rcat, int nargs, int minints, int minlongs) {
+ for (int isLong = 0; isLong <= 1; isLong++) {
+ for (int nprims = 1; nprims <= nargs; nprims++) {
+ int nrefs = nargs - nprims;
+ int nints = ((1-isLong) * nprims);
+ int nlongs = (isLong * nprims);
+ expandTemplate(template, topLevel, rcat, nrefs, nints, nlongs);
+ }
+ }
+ }
+ static void expandTemplate(String[] template, boolean topLevel,
+ int rcat, int nrefs, int nints, int nlongs) {
+ int nargs = nrefs + nints + nlongs;
+ if (nrefs > MAX_REFS || nints > MAX_INTS || nlongs > MAX_LONGS) return;
+ VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs);
+ if (topLevel && !done.add(VAR.cat.binding)) {
+ System.out.println(" //repeat "+VAR.cat.binding);
+ return;
+ }
+ for (int i = 0; i < template.length; i++) {
+ String line = template[i];
+ if (line.endsWith("@each-cat@")) {
+ // ignore
+ } else if (line.endsWith("@each-R@")) {
+ int blockEnd = indexAfter(template, i, "@end-R@");
+ String[] block = stringsIn(template, i+1, blockEnd-1);
+ for (int rcat1 = rcat; rcat1 <= MAX_RCAT; rcat1++)
+ expandTemplate(block, false, rcat1, nrefs, nints, nlongs);
+ VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs);
+ i = blockEnd-1; continue;
+ } else if (line.endsWith("@each-Tv@")) {
+ int blockEnd = indexAfter(template, i, "@end-Tv@");
+ String[] block = stringsIn(template, i+1, blockEnd-1);
+ expandTemplate(block, false, rcat, nrefs, nints, nlongs);
+ expandTemplateForPrims(block, false, rcat, nargs, nints+1, nlongs+1);
+ VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs);
+ i = blockEnd-1; continue;
+ } else {
+ System.out.println(VAR.transform(line));
+ }
+ }
+ }
+}
+//}}} */
+//params=[0, 3, 4, 99, 99, 99]
+ static class A0 extends Adapter {
+ protected A0(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A0(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A0 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A0(e, i, c, t); }
+ protected Object target() { return invoker.invoke(target); }
+ protected Object targetA0() { return target(); }
+ protected Object invoke_L() { return return_L(targetA0()); }
+ protected int invoke_I() { return return_I(targetA0()); }
+ protected long invoke_J() { return return_J(targetA0()); }
+ protected float invoke_F() { return return_F(targetA0()); }
+ protected double invoke_D() { return return_D(targetA0()); }
+ }
+ static class A1 extends Adapter {
+ protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A1(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A1(e, i, c, t); }
+ protected Object target(Object a0) { return invoker.invoke(target, a0); }
+ protected Object targetA1(Object a0) { return target(a0); }
+ protected Object targetA1(int a0) { return target(a0); }
+ protected Object targetA1(long a0) { return target(a0); }
+ protected Object invoke_L(Object a0) { return return_L(targetA1(a0)); }
+ protected int invoke_I(Object a0) { return return_I(targetA1(a0)); }
+ protected long invoke_J(Object a0) { return return_J(targetA1(a0)); }
+ protected float invoke_F(Object a0) { return return_F(targetA1(a0)); }
+ protected double invoke_D(Object a0) { return return_D(targetA1(a0)); }
+ protected Object invoke_L(int a0) { return return_L(targetA1(a0)); }
+ protected int invoke_I(int a0) { return return_I(targetA1(a0)); }
+ protected long invoke_J(int a0) { return return_J(targetA1(a0)); }
+ protected float invoke_F(int a0) { return return_F(targetA1(a0)); }
+ protected double invoke_D(int a0) { return return_D(targetA1(a0)); }
+ protected Object invoke_L(long a0) { return return_L(targetA1(a0)); }
+ protected int invoke_I(long a0) { return return_I(targetA1(a0)); }
+ protected long invoke_J(long a0) { return return_J(targetA1(a0)); }
+ protected float invoke_F(long a0) { return return_F(targetA1(a0)); }
+ protected double invoke_D(long a0) { return return_D(targetA1(a0)); }
+ }
+ static class A2 extends Adapter {
+ protected A2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A2(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A2(e, i, c, t); }
+ protected Object target(Object a0, Object a1) { return invoker.invoke(target, a0, a1); }
+ protected Object targetA2(Object a0, Object a1) { return target(a0, a1); }
+ protected Object targetA2(Object a0, int a1) { return target(a0, a1); }
+ protected Object targetA2(int a0, int a1) { return target(a0, a1); }
+ protected Object targetA2(Object a0, long a1) { return target(a0, a1); }
+ protected Object targetA2(long a0, long a1) { return target(a0, a1); }
+ protected Object invoke_L(Object a0, Object a1) { return return_L(targetA2(a0, a1)); }
+ protected int invoke_I(Object a0, Object a1) { return return_I(targetA2(a0, a1)); }
+ protected long invoke_J(Object a0, Object a1) { return return_J(targetA2(a0, a1)); }
+ protected float invoke_F(Object a0, Object a1) { return return_F(targetA2(a0, a1)); }
+ protected double invoke_D(Object a0, Object a1) { return return_D(targetA2(a0, a1)); }
+ protected Object invoke_L(Object a0, int a1) { return return_L(targetA2(a0, a1)); }
+ protected int invoke_I(Object a0, int a1) { return return_I(targetA2(a0, a1)); }
+ protected long invoke_J(Object a0, int a1) { return return_J(targetA2(a0, a1)); }
+ protected float invoke_F(Object a0, int a1) { return return_F(targetA2(a0, a1)); }
+ protected double invoke_D(Object a0, int a1) { return return_D(targetA2(a0, a1)); }
+ protected Object invoke_L(int a0, int a1) { return return_L(targetA2(a0, a1)); }
+ protected int invoke_I(int a0, int a1) { return return_I(targetA2(a0, a1)); }
+ protected long invoke_J(int a0, int a1) { return return_J(targetA2(a0, a1)); }
+ protected float invoke_F(int a0, int a1) { return return_F(targetA2(a0, a1)); }
+ protected double invoke_D(int a0, int a1) { return return_D(targetA2(a0, a1)); }
+ protected Object invoke_L(Object a0, long a1) { return return_L(targetA2(a0, a1)); }
+ protected int invoke_I(Object a0, long a1) { return return_I(targetA2(a0, a1)); }
+ protected long invoke_J(Object a0, long a1) { return return_J(targetA2(a0, a1)); }
+ protected float invoke_F(Object a0, long a1) { return return_F(targetA2(a0, a1)); }
+ protected double invoke_D(Object a0, long a1) { return return_D(targetA2(a0, a1)); }
+ protected Object invoke_L(long a0, long a1) { return return_L(targetA2(a0, a1)); }
+ protected int invoke_I(long a0, long a1) { return return_I(targetA2(a0, a1)); }
+ protected long invoke_J(long a0, long a1) { return return_J(targetA2(a0, a1)); }
+ protected float invoke_F(long a0, long a1) { return return_F(targetA2(a0, a1)); }
+ protected double invoke_D(long a0, long a1) { return return_D(targetA2(a0, a1)); }
+ }
+ static class A3 extends Adapter {
+ protected A3(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A3(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A3 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A3(e, i, c, t); }
+ protected Object target(Object a0, Object a1, Object a2) { return invoker.invoke(target, a0, a1, a2); }
+ protected Object targetA3(Object a0, Object a1, Object a2) { return target(a0, a1, a2); }
+ protected Object targetA3(Object a0, Object a1, int a2) { return target(a0, a1, a2); }
+ protected Object targetA3(Object a0, int a1, int a2) { return target(a0, a1, a2); }
+ protected Object targetA3(int a0, int a1, int a2) { return target(a0, a1, a2); }
+ protected Object targetA3(Object a0, Object a1, long a2) { return target(a0, a1, a2); }
+ protected Object targetA3(Object a0, long a1, long a2) { return target(a0, a1, a2); }
+ protected Object targetA3(long a0, long a1, long a2) { return target(a0, a1, a2); }
+ protected Object invoke_L(Object a0, Object a1, Object a2) { return return_L(targetA3(a0, a1, a2)); }
+ protected int invoke_I(Object a0, Object a1, Object a2) { return return_I(targetA3(a0, a1, a2)); }
+ protected long invoke_J(Object a0, Object a1, Object a2) { return return_J(targetA3(a0, a1, a2)); }
+ protected float invoke_F(Object a0, Object a1, Object a2) { return return_F(targetA3(a0, a1, a2)); }
+ protected double invoke_D(Object a0, Object a1, Object a2) { return return_D(targetA3(a0, a1, a2)); }
+ protected Object invoke_L(Object a0, Object a1, int a2) { return return_L(targetA3(a0, a1, a2)); }
+ protected int invoke_I(Object a0, Object a1, int a2) { return return_I(targetA3(a0, a1, a2)); }
+ protected long invoke_J(Object a0, Object a1, int a2) { return return_J(targetA3(a0, a1, a2)); }
+ protected float invoke_F(Object a0, Object a1, int a2) { return return_F(targetA3(a0, a1, a2)); }
+ protected double invoke_D(Object a0, Object a1, int a2) { return return_D(targetA3(a0, a1, a2)); }
+ protected Object invoke_L(Object a0, int a1, int a2) { return return_L(targetA3(a0, a1, a2)); }
+ protected int invoke_I(Object a0, int a1, int a2) { return return_I(targetA3(a0, a1, a2)); }
+ protected long invoke_J(Object a0, int a1, int a2) { return return_J(targetA3(a0, a1, a2)); }
+ protected float invoke_F(Object a0, int a1, int a2) { return return_F(targetA3(a0, a1, a2)); }
+ protected double invoke_D(Object a0, int a1, int a2) { return return_D(targetA3(a0, a1, a2)); }
+ protected Object invoke_L(int a0, int a1, int a2) { return return_L(targetA3(a0, a1, a2)); }
+ protected int invoke_I(int a0, int a1, int a2) { return return_I(targetA3(a0, a1, a2)); }
+ protected long invoke_J(int a0, int a1, int a2) { return return_J(targetA3(a0, a1, a2)); }
+ protected float invoke_F(int a0, int a1, int a2) { return return_F(targetA3(a0, a1, a2)); }
+ protected double invoke_D(int a0, int a1, int a2) { return return_D(targetA3(a0, a1, a2)); }
+ protected Object invoke_L(Object a0, Object a1, long a2) { return return_L(targetA3(a0, a1, a2)); }
+ protected int invoke_I(Object a0, Object a1, long a2) { return return_I(targetA3(a0, a1, a2)); }
+ protected long invoke_J(Object a0, Object a1, long a2) { return return_J(targetA3(a0, a1, a2)); }
+ protected float invoke_F(Object a0, Object a1, long a2) { return return_F(targetA3(a0, a1, a2)); }
+ protected double invoke_D(Object a0, Object a1, long a2) { return return_D(targetA3(a0, a1, a2)); }
+ protected Object invoke_L(Object a0, long a1, long a2) { return return_L(targetA3(a0, a1, a2)); }
+ protected int invoke_I(Object a0, long a1, long a2) { return return_I(targetA3(a0, a1, a2)); }
+ protected long invoke_J(Object a0, long a1, long a2) { return return_J(targetA3(a0, a1, a2)); }
+ protected float invoke_F(Object a0, long a1, long a2) { return return_F(targetA3(a0, a1, a2)); }
+ protected double invoke_D(Object a0, long a1, long a2) { return return_D(targetA3(a0, a1, a2)); }
+ protected Object invoke_L(long a0, long a1, long a2) { return return_L(targetA3(a0, a1, a2)); }
+ protected int invoke_I(long a0, long a1, long a2) { return return_I(targetA3(a0, a1, a2)); }
+ protected long invoke_J(long a0, long a1, long a2) { return return_J(targetA3(a0, a1, a2)); }
+ protected float invoke_F(long a0, long a1, long a2) { return return_F(targetA3(a0, a1, a2)); }
+ protected double invoke_D(long a0, long a1, long a2) { return return_D(targetA3(a0, a1, a2)); }
+ }
+//params=[4, 5, 2, 99, 99, 99]
+ static class A4 extends Adapter {
+ protected A4(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A4(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A4 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A4(e, i, c, t); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3) { return invoker.invoke(target, a0, a1, a2, a3); }
+ protected Object targetA4(Object a0, Object a1, Object a2, Object a3) { return target(a0, a1, a2, a3); }
+ protected Object targetA4(Object a0, Object a1, Object a2, int a3) { return target(a0, a1, a2, a3); }
+ protected Object targetA4(Object a0, Object a1, int a2, int a3) { return target(a0, a1, a2, a3); }
+ protected Object targetA4(Object a0, int a1, int a2, int a3) { return target(a0, a1, a2, a3); }
+ protected Object targetA4(int a0, int a1, int a2, int a3) { return target(a0, a1, a2, a3); }
+ protected Object targetA4(Object a0, Object a1, Object a2, long a3) { return target(a0, a1, a2, a3); }
+ protected Object targetA4(Object a0, Object a1, long a2, long a3) { return target(a0, a1, a2, a3); }
+ protected Object targetA4(Object a0, long a1, long a2, long a3) { return target(a0, a1, a2, a3); }
+ protected Object targetA4(long a0, long a1, long a2, long a3) { return target(a0, a1, a2, a3); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3) { return return_L(targetA4(a0, a1, a2, a3)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3) { return return_I(targetA4(a0, a1, a2, a3)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3) { return return_J(targetA4(a0, a1, a2, a3)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, int a3) { return return_L(targetA4(a0, a1, a2, a3)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, int a3) { return return_I(targetA4(a0, a1, a2, a3)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, int a3) { return return_J(targetA4(a0, a1, a2, a3)); }
+ protected Object invoke_L(Object a0, Object a1, int a2, int a3) { return return_L(targetA4(a0, a1, a2, a3)); }
+ protected int invoke_I(Object a0, Object a1, int a2, int a3) { return return_I(targetA4(a0, a1, a2, a3)); }
+ protected long invoke_J(Object a0, Object a1, int a2, int a3) { return return_J(targetA4(a0, a1, a2, a3)); }
+ protected Object invoke_L(Object a0, int a1, int a2, int a3) { return return_L(targetA4(a0, a1, a2, a3)); }
+ protected int invoke_I(Object a0, int a1, int a2, int a3) { return return_I(targetA4(a0, a1, a2, a3)); }
+ protected long invoke_J(Object a0, int a1, int a2, int a3) { return return_J(targetA4(a0, a1, a2, a3)); }
+ protected Object invoke_L(int a0, int a1, int a2, int a3) { return return_L(targetA4(a0, a1, a2, a3)); }
+ protected int invoke_I(int a0, int a1, int a2, int a3) { return return_I(targetA4(a0, a1, a2, a3)); }
+ protected long invoke_J(int a0, int a1, int a2, int a3) { return return_J(targetA4(a0, a1, a2, a3)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, long a3) { return return_L(targetA4(a0, a1, a2, a3)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, long a3) { return return_I(targetA4(a0, a1, a2, a3)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, long a3) { return return_J(targetA4(a0, a1, a2, a3)); }
+ protected Object invoke_L(Object a0, Object a1, long a2, long a3) { return return_L(targetA4(a0, a1, a2, a3)); }
+ protected int invoke_I(Object a0, Object a1, long a2, long a3) { return return_I(targetA4(a0, a1, a2, a3)); }
+ protected long invoke_J(Object a0, Object a1, long a2, long a3) { return return_J(targetA4(a0, a1, a2, a3)); }
+ protected Object invoke_L(Object a0, long a1, long a2, long a3) { return return_L(targetA4(a0, a1, a2, a3)); }
+ protected int invoke_I(Object a0, long a1, long a2, long a3) { return return_I(targetA4(a0, a1, a2, a3)); }
+ protected long invoke_J(Object a0, long a1, long a2, long a3) { return return_J(targetA4(a0, a1, a2, a3)); }
+ protected Object invoke_L(long a0, long a1, long a2, long a3) { return return_L(targetA4(a0, a1, a2, a3)); }
+ protected int invoke_I(long a0, long a1, long a2, long a3) { return return_I(targetA4(a0, a1, a2, a3)); }
+ protected long invoke_J(long a0, long a1, long a2, long a3) { return return_J(targetA4(a0, a1, a2, a3)); }
+ }
+ static class A5 extends Adapter {
+ protected A5(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A5(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A5 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A5(e, i, c, t); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4) { return invoker.invoke(target, a0, a1, a2, a3, a4); }
+ protected Object targetA5(Object a0, Object a1, Object a2, Object a3, Object a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(Object a0, Object a1, Object a2, Object a3, int a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(Object a0, Object a1, Object a2, int a3, int a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(Object a0, Object a1, int a2, int a3, int a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(Object a0, int a1, int a2, int a3, int a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(int a0, int a1, int a2, int a3, int a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(Object a0, Object a1, Object a2, Object a3, long a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(Object a0, Object a1, Object a2, long a3, long a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(Object a0, Object a1, long a2, long a3, long a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(Object a0, long a1, long a2, long a3, long a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object targetA5(long a0, long a1, long a2, long a3, long a4) { return target(a0, a1, a2, a3, a4); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, int a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, int a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, int a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(Object a0, Object a1, int a2, int a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(Object a0, Object a1, int a2, int a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(Object a0, Object a1, int a2, int a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(Object a0, int a1, int a2, int a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(Object a0, int a1, int a2, int a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(Object a0, int a1, int a2, int a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(int a0, int a1, int a2, int a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(int a0, int a1, int a2, int a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(int a0, int a1, int a2, int a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(Object a0, long a1, long a2, long a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(Object a0, long a1, long a2, long a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ protected Object invoke_L(long a0, long a1, long a2, long a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); }
+ protected int invoke_I(long a0, long a1, long a2, long a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); }
+ protected long invoke_J(long a0, long a1, long a2, long a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); }
+ }
+//params=[6, 10, 2, 99, 0, 99]
+ static class A6 extends Adapter {
+ protected A6(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A6(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A6 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A6(e, i, c, t); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return invoker.invoke(target, a0, a1, a2, a3, a4, a5); }
+ protected Object targetA6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return target(a0, a1, a2, a3, a4, a5); }
+ protected Object targetA6(Object a0, Object a1, Object a2, Object a3, Object a4, long a5) { return target(a0, a1, a2, a3, a4, a5); }
+ protected Object targetA6(Object a0, Object a1, Object a2, Object a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); }
+ protected Object targetA6(Object a0, Object a1, Object a2, long a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); }
+ protected Object targetA6(Object a0, Object a1, long a2, long a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); }
+ protected Object targetA6(Object a0, long a1, long a2, long a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); }
+ protected Object targetA6(long a0, long a1, long a2, long a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); }
+ protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); }
+ }
+ static class A7 extends Adapter {
+ protected A7(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A7(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A7 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A7(e, i, c, t); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6); }
+ protected Object targetA7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return target(a0, a1, a2, a3, a4, a5, a6); }
+ protected Object targetA7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); }
+ protected Object targetA7(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); }
+ protected Object targetA7(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); }
+ protected Object targetA7(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); }
+ protected Object targetA7(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); }
+ protected Object targetA7(Object a0, long a1, long a2, long a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); }
+ protected Object targetA7(long a0, long a1, long a2, long a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); }
+ }
+ static class A8 extends Adapter {
+ protected A8(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A8(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A8 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A8(e, i, c, t); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object targetA8(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object targetA8(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object targetA8(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object targetA8(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object targetA8(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); }
+ }
+ static class A9 extends Adapter {
+ protected A9(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A9(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A9 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A9(e, i, c, t); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object targetA9(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
+ }
+ static class A10 extends Adapter {
+ protected A10(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected A10(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }
+ protected A10 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A10(e, i, c, t); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return invoker.invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object targetA10(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
+ }
+}
diff --git a/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java b/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..a182161271b52b66c57a050e3f177213456a287b
--- /dev/null
+++ b/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.anon;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Anonymous class loader. Will load any valid classfile, producing
+ * a {@link Class} metaobject, without installing that class in the
+ * system dictionary. Therefore, {@link Class#forName(String)} will never
+ * produce a reference to an anonymous class.
+ *
+ * The access permissions of the anonymous class are borrowed from
+ * a host class . The new class behaves as if it were an
+ * inner class of the host class. It can access the host's private
+ * members, if the creator of the class loader has permission to
+ * do so (or to create accessible reflective objects).
+ *
+ * When the anonymous class is loaded, elements of its constant pool
+ * can be patched to new values. This provides a hook to pre-resolve
+ * named classes in the constant pool to other classes, including
+ * anonymous ones. Also, string constants can be pre-resolved to
+ * any reference. (The verifier treats non-string, non-class reference
+ * constants as plain objects.)
+ *
+ * Why include the patching function? It makes some use cases much easier.
+ * Second, the constant pool needed some internal patching anyway,
+ * to anonymize the loaded class itself. Finally, if you are going
+ * to use this seriously, you'll want to build anonymous classes
+ * on top of pre-existing anonymous classes, and that requires patching.
+ *
+ *
%%% TO-DO:
+ *
+ * needs better documentation
+ * needs more security work (for safe delegation)
+ * needs a clearer story about error processing
+ * patch member references also (use ';' as delimiter char)
+ * patch method references to (conforming) method handles
+ *
+ *
+ * @author jrose
+ * @author Remi Forax
+ * @see
+ * http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm
+ */
+
+public class AnonymousClassLoader {
+ final Class> hostClass;
+
+ // Note: Do not refactor the calls to checkHostClass unless you
+ // also adjust this constant:
+ private static int CHC_CALLERS = 3;
+
+ public AnonymousClassLoader() {
+ this.hostClass = checkHostClass(null);
+ }
+ public AnonymousClassLoader(Class> hostClass) {
+ this.hostClass = checkHostClass(hostClass);
+ }
+
+ private static Class> getTopLevelClass(Class> clazz) {
+ for(Class> outer = clazz.getDeclaringClass(); outer != null;
+ outer = outer.getDeclaringClass()) {
+ clazz = outer;
+ }
+ return clazz;
+ }
+
+ private static Class> checkHostClass(Class> hostClass) {
+ // called only from the constructor
+ // does a context-sensitive check on caller class
+ // CC[0..3] = {Reflection, this.checkHostClass, this., caller}
+ Class> caller = sun.reflect.Reflection.getCallerClass(CHC_CALLERS);
+
+ if (caller == null) {
+ // called from the JVM directly
+ if (hostClass == null)
+ return AnonymousClassLoader.class; // anything central will do
+ return hostClass;
+ }
+
+ if (hostClass == null)
+ hostClass = caller; // default value is caller itself
+
+ // anonymous class will access hostClass on behalf of caller
+ Class> callee = hostClass;
+
+ if (caller == callee)
+ // caller can always nominate itself to grant caller's own access rights
+ return hostClass;
+
+ // normalize caller and callee to their top-level classes:
+ caller = getTopLevelClass(caller);
+ callee = getTopLevelClass(callee);
+ if (caller == callee)
+ return caller;
+
+ ClassLoader callerCL = caller.getClassLoader();
+ if (callerCL == null) {
+ // caller is trusted code, so accept the proposed hostClass
+ return hostClass;
+ }
+
+ // %%% should do something with doPrivileged, because trusted
+ // code should have a way to execute on behalf of
+ // partially-trusted clients
+
+ // Does the caller have the right to access the private
+ // members of the callee? If not, raise an error.
+ final int ACC_PRIVATE = 2;
+ try {
+ sun.reflect.Reflection.ensureMemberAccess(caller, callee, null, ACC_PRIVATE);
+ } catch (IllegalAccessException ee) {
+ throw new IllegalArgumentException(ee);
+ }
+
+ return hostClass;
+ }
+
+ public Class> loadClass(byte[] classFile) {
+ if (defineAnonymousClass == null) {
+ // no JVM support; try to fake an approximation
+ try {
+ return fakeLoadClass(new ConstantPoolParser(classFile).createPatch());
+ } catch (InvalidConstantPoolFormatException ee) {
+ throw new IllegalArgumentException(ee);
+ }
+ }
+ return loadClass(classFile, null);
+ }
+
+ public Class> loadClass(ConstantPoolPatch classPatch) {
+ if (defineAnonymousClass == null) {
+ // no JVM support; try to fake an approximation
+ return fakeLoadClass(classPatch);
+ }
+ Object[] patches = classPatch.patchArray;
+ // Convert class names (this late in the game)
+ // to use slash '/' instead of dot '.'.
+ // Java likes dots, but the JVM likes slashes.
+ for (int i = 0; i < patches.length; i++) {
+ Object value = patches[i];
+ if (value != null) {
+ byte tag = classPatch.getTag(i);
+ switch (tag) {
+ case ConstantPoolVisitor.CONSTANT_Class:
+ if (value instanceof String) {
+ if (patches == classPatch.patchArray)
+ patches = patches.clone();
+ patches[i] = ((String)value).replace('.', '/');
+ }
+ break;
+ case ConstantPoolVisitor.CONSTANT_Fieldref:
+ case ConstantPoolVisitor.CONSTANT_Methodref:
+ case ConstantPoolVisitor.CONSTANT_InterfaceMethodref:
+ case ConstantPoolVisitor.CONSTANT_NameAndType:
+ // When/if the JVM supports these patches,
+ // we'll probably need to reformat them also.
+ // Meanwhile, let the class loader create the error.
+ break;
+ }
+ }
+ }
+ return loadClass(classPatch.outer.classFile, classPatch.patchArray);
+ }
+
+ private Class> loadClass(byte[] classFile, Object[] patchArray) {
+ try {
+ return (Class>)
+ defineAnonymousClass.invoke(unsafe,
+ hostClass, classFile, patchArray);
+ } catch (Exception ex) {
+ throwReflectedException(ex);
+ throw new RuntimeException("error loading into "+hostClass, ex);
+ }
+ }
+
+ private static void throwReflectedException(Exception ex) {
+ if (ex instanceof InvocationTargetException) {
+ Throwable tex = ((InvocationTargetException)ex).getTargetException();
+ if (tex instanceof Error)
+ throw (Error) tex;
+ ex = (Exception) tex;
+ }
+ if (ex instanceof RuntimeException) {
+ throw (RuntimeException) ex;
+ }
+ }
+
+ private Class> fakeLoadClass(ConstantPoolPatch classPatch) {
+ // Implementation:
+ // 1. Make up a new name nobody has used yet.
+ // 2. Inspect the tail-header of the class to find the this_class index.
+ // 3. Patch the CONSTANT_Class for this_class to the new name.
+ // 4. Add other CP entries required by (e.g.) string patches.
+ // 5. Flatten Class constants down to their names, making sure that
+ // the host class loader can pick them up again accurately.
+ // 6. Generate the edited class file bytes.
+ //
+ // Potential limitations:
+ // * The class won't be truly anonymous, and may interfere with others.
+ // * Flattened class constants might not work, because of loader issues.
+ // * Pseudo-string constants will not flatten down to real strings.
+ // * Method handles will (of course) fail to flatten to linkage strings.
+ if (true) throw new UnsupportedOperationException("NYI");
+ Object[] cpArray;
+ try {
+ cpArray = classPatch.getOriginalCP();
+ } catch (InvalidConstantPoolFormatException ex) {
+ throw new RuntimeException(ex);
+ }
+ int thisClassIndex = classPatch.getParser().getThisClassIndex();
+ String thisClassName = (String) cpArray[thisClassIndex];
+ synchronized (AnonymousClassLoader.class) {
+ thisClassName = thisClassName+"\\|"+(++fakeNameCounter);
+ }
+ classPatch.putUTF8(thisClassIndex, thisClassName);
+ byte[] classFile = null;
+ return unsafe.defineClass(null, classFile, 0, classFile.length,
+ hostClass.getClassLoader(),
+ hostClass.getProtectionDomain());
+ }
+ private static int fakeNameCounter = 99999;
+
+ // ignore two warnings on this line:
+ static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+ // preceding line requires that this class be on the boot class path
+
+ static private final Method defineAnonymousClass;
+ static {
+ Method dac = null;
+ Class extends sun.misc.Unsafe> unsafeClass = unsafe.getClass();
+ try {
+ dac = unsafeClass.getMethod("defineAnonymousClass",
+ Class.class,
+ byte[].class,
+ Object[].class);
+ } catch (Exception ee) {
+ dac = null;
+ }
+ defineAnonymousClass = dac;
+ }
+
+ private static void noJVMSupport() {
+ throw new UnsupportedOperationException("no JVM support for anonymous classes");
+ }
+
+
+ private static native Class> loadClassInternal(Class> hostClass,
+ byte[] classFile,
+ Object[] patchArray);
+
+ public static byte[] readClassFile(Class> templateClass) throws IOException {
+ String templateName = templateClass.getName();
+ int lastDot = templateName.lastIndexOf('.');
+ java.net.URL url = templateClass.getResource(templateName.substring(lastDot+1)+".class");
+ java.net.URLConnection connection = url.openConnection();
+ int contentLength = connection.getContentLength();
+ if (contentLength < 0)
+ throw new IOException("invalid content length "+contentLength);
+
+ byte[] classFile = new byte[contentLength];
+ InputStream tcs = connection.getInputStream();
+ for (int fill = 0, nr; fill < classFile.length; fill += nr) {
+ nr = tcs.read(classFile, fill, classFile.length - fill);
+ if (nr < 0)
+ throw new IOException("premature end of file");
+ }
+ return classFile;
+ }
+}
diff --git a/src/share/classes/sun/dyn/anon/ConstantPoolParser.java b/src/share/classes/sun/dyn/anon/ConstantPoolParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..db12e950355e2406a1f0aa6d5fa085dc8034d6ac
--- /dev/null
+++ b/src/share/classes/sun/dyn/anon/ConstantPoolParser.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.anon;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+import static sun.dyn.anon.ConstantPoolVisitor.*;
+
+/** A constant pool parser.
+ */
+public class ConstantPoolParser {
+ final byte[] classFile;
+ final byte[] tags;
+ final char[] firstHeader; // maghi, maglo, minor, major, cplen
+
+ // these are filled in on first parse:
+ int endOffset;
+ char[] secondHeader; // flags, this_class, super_class, intlen
+
+ // used to decode UTF8 array
+ private char[] charArray = new char[80];
+
+ /** Creates a constant pool parser.
+ * @param classFile an array of bytes containing a class.
+ * @throws InvalidConstantPoolFormatException if the header of the class has errors.
+ */
+ public ConstantPoolParser(byte[] classFile) throws InvalidConstantPoolFormatException {
+ this.classFile = classFile;
+ this.firstHeader = parseHeader(classFile);
+ this.tags = new byte[firstHeader[4]];
+ }
+
+ /** Create a constant pool parser by loading the bytecodes of the
+ * class taken as argument.
+ *
+ * @param templateClass the class to parse.
+ *
+ * @throws IOException raised if an I/O occurs when loading
+ * the bytecode of the template class.
+ * @throws InvalidConstantPoolFormatException if the header of the class has errors.
+ *
+ * @see #ConstantPoolParser(byte[])
+ * @see AnonymousClassLoader#readClassFile(Class)
+ */
+ public ConstantPoolParser(Class> templateClass) throws IOException, InvalidConstantPoolFormatException {
+ this(AnonymousClassLoader.readClassFile(templateClass));
+ }
+
+ /** Creates an empty patch to patch the class file
+ * used by the current parser.
+ * @return a new class patch.
+ */
+ public ConstantPoolPatch createPatch() {
+ return new ConstantPoolPatch(this);
+ }
+
+ /** Report the tag of the indicated CP entry.
+ * @param index
+ * @return one of {@link ConstantPoolVisitor#CONSTANT_Utf8}, etc.
+ */
+ public byte getTag(int index) {
+ getEndOffset(); // trigger an exception if we haven't parsed yet
+ return tags[index];
+ }
+
+ /** Report the length of the constant pool. */
+ public int getLength() {
+ return firstHeader[4];
+ }
+
+ /** Report the offset, within the class file, of the start of the constant pool. */
+ public int getStartOffset() {
+ return firstHeader.length * 2;
+ }
+
+ /** Report the offset, within the class file, of the end of the constant pool. */
+ public int getEndOffset() {
+ if (endOffset == 0)
+ throw new IllegalStateException("class file has not yet been parsed");
+ return endOffset;
+ }
+
+ /** Report the CP index of this class's own name. */
+ public int getThisClassIndex() {
+ getEndOffset(); // provoke exception if not yet parsed
+ return secondHeader[1];
+ }
+
+ /** Report the total size of the class file. */
+ public int getTailLength() {
+ return classFile.length - getEndOffset();
+ }
+
+ /** Write the head (header plus constant pool)
+ * of the class file to the indicated stream.
+ */
+ public void writeHead(OutputStream out) throws IOException {
+ out.write(classFile, 0, getEndOffset());
+ }
+
+ /** Write the head (header plus constant pool)
+ * of the class file to the indicated stream,
+ * incorporating the non-null entries of the given array
+ * as patches.
+ */
+ void writePatchedHead(OutputStream out, Object[] patchArray) {
+ // this will be useful to partially emulate the class loader on old JVMs
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ /** Write the tail (everything after the constant pool)
+ * of the class file to the indicated stream.
+ */
+ public void writeTail(OutputStream out) throws IOException {
+ out.write(classFile, getEndOffset(), getTailLength());
+ }
+
+ private static char[] parseHeader(byte[] classFile) throws InvalidConstantPoolFormatException {
+ char[] result = new char[5];
+ ByteBuffer buffer = ByteBuffer.wrap(classFile);
+ for (int i = 0; i < result.length; i++)
+ result[i] = (char) getUnsignedShort(buffer);
+ int magic = result[0] << 16 | result[1] << 0;
+ if (magic != 0xCAFEBABE)
+ throw new InvalidConstantPoolFormatException("invalid magic number "+magic);
+ // skip major, minor version
+ int len = result[4];
+ if (len < 1)
+ throw new InvalidConstantPoolFormatException("constant pool length < 1");
+ return result;
+ }
+
+ /** Parse the constant pool of the class
+ * calling a method visit* each time a constant pool entry is parsed.
+ *
+ * The order of the calls to visit* is not guaranteed to be the same
+ * than the order of the constant pool entry in the bytecode array.
+ *
+ * @param visitor
+ * @throws InvalidConstantPoolFormatException
+ */
+ public void parse(ConstantPoolVisitor visitor) throws InvalidConstantPoolFormatException {
+ ByteBuffer buffer = ByteBuffer.wrap(classFile);
+ buffer.position(getStartOffset()); //skip header
+
+ Object[] values = new Object[getLength()];
+ try {
+ parseConstantPool(buffer, values, visitor);
+ } catch(BufferUnderflowException e) {
+ throw new InvalidConstantPoolFormatException(e);
+ }
+ if (endOffset == 0) {
+ endOffset = buffer.position();
+ secondHeader = new char[4];
+ for (int i = 0; i < secondHeader.length; i++) {
+ secondHeader[i] = (char) getUnsignedShort(buffer);
+ }
+ }
+ resolveConstantPool(values, visitor);
+ }
+
+ private char[] getCharArray(int utfLength) {
+ if (utfLength <= charArray.length)
+ return charArray;
+ return charArray = new char[utfLength];
+ }
+
+ private void parseConstantPool(ByteBuffer buffer, Object[] values, ConstantPoolVisitor visitor) throws InvalidConstantPoolFormatException {
+ for (int i = 1; i < tags.length; ) {
+ byte tag = (byte) getUnsignedByte(buffer);
+ assert(tags[i] == 0 || tags[i] == tag);
+ tags[i] = tag;
+ switch (tag) {
+ case CONSTANT_Utf8:
+ int utfLen = getUnsignedShort(buffer);
+ String value = getUTF8(buffer, utfLen, getCharArray(utfLen));
+ visitor.visitUTF8(i, CONSTANT_Utf8, value);
+ tags[i] = tag;
+ values[i++] = value;
+ break;
+ case CONSTANT_Integer:
+ visitor.visitConstantValue(i, tag, buffer.getInt());
+ i++;
+ break;
+ case CONSTANT_Float:
+ visitor.visitConstantValue(i, tag, buffer.getFloat());
+ i++;
+ break;
+ case CONSTANT_Long:
+ visitor.visitConstantValue(i, tag, buffer.getLong());
+ i+=2;
+ break;
+ case CONSTANT_Double:
+ visitor.visitConstantValue(i, tag, buffer.getDouble());
+ i+=2;
+ break;
+
+ case CONSTANT_Class: // fall through:
+ case CONSTANT_String:
+ tags[i] = tag;
+ values[i++] = new int[] { getUnsignedShort(buffer) };
+ break;
+
+ case CONSTANT_Fieldref: // fall through:
+ case CONSTANT_Methodref: // fall through:
+ case CONSTANT_InterfaceMethodref: // fall through:
+ case CONSTANT_NameAndType:
+ tags[i] = tag;
+ values[i++] = new int[] { getUnsignedShort(buffer), getUnsignedShort(buffer) };
+ break;
+ default:
+ throw new AssertionError("invalid constant "+tag);
+ }
+ }
+ }
+
+ private void resolveConstantPool(Object[] values, ConstantPoolVisitor visitor) {
+ // clean out the int[] values, which are temporary
+ for (int beg = 1, end = values.length-1, beg2, end2;
+ beg <= end;
+ beg = beg2, end = end2) {
+ beg2 = end; end2 = beg-1;
+ //System.out.println("CP resolve pass: "+beg+".."+end);
+ for (int i = beg; i <= end; i++) {
+ Object value = values[i];
+ if (!(value instanceof int[]))
+ continue;
+ int[] array = (int[]) value;
+ byte tag = tags[i];
+ switch (tag) {
+ case CONSTANT_String:
+ String stringBody = (String) values[array[0]];
+ visitor.visitConstantString(i, tag, stringBody, array[0]);
+ values[i] = null;
+ break;
+ case CONSTANT_Class: {
+ String className = (String) values[array[0]];
+ // use the external form favored by Class.forName:
+ className = className.replace('/', '.');
+ visitor.visitConstantString(i, tag, className, array[0]);
+ values[i] = className;
+ break;
+ }
+ case CONSTANT_NameAndType: {
+ String memberName = (String) values[array[0]];
+ String signature = (String) values[array[1]];
+ visitor.visitDescriptor(i, tag, memberName, signature,
+ array[0], array[1]);
+ values[i] = new String[] {memberName, signature};
+ break;
+ }
+ case CONSTANT_Fieldref: // fall through:
+ case CONSTANT_Methodref: // fall through:
+ case CONSTANT_InterfaceMethodref: {
+ Object className = values[array[0]];
+ Object nameAndType = values[array[1]];
+ if (!(className instanceof String) ||
+ !(nameAndType instanceof String[])) {
+ // one more pass is needed
+ if (beg2 > i) beg2 = i;
+ if (end2 < i) end2 = i;
+ continue;
+ }
+ String[] nameAndTypeArray = (String[]) nameAndType;
+ visitor.visitMemberRef(i, tag,
+ (String)className,
+ nameAndTypeArray[0],
+ nameAndTypeArray[1],
+ array[0], array[1]);
+ values[i] = null;
+ }
+ break;
+ default:
+ continue;
+ }
+ }
+ }
+ }
+
+ private static int getUnsignedByte(ByteBuffer buffer) {
+ return buffer.get() & 0xFF;
+ }
+
+ private static int getUnsignedShort(ByteBuffer buffer) {
+ int b1 = getUnsignedByte(buffer);
+ int b2 = getUnsignedByte(buffer);
+ return (b1 << 8) + (b2 << 0);
+ }
+
+ private static String getUTF8(ByteBuffer buffer, int utfLen, char[] charArray) throws InvalidConstantPoolFormatException {
+ int utfLimit = buffer.position() + utfLen;
+ int index = 0;
+ while (buffer.position() < utfLimit) {
+ int c = buffer.get() & 0xff;
+ if (c > 127) {
+ buffer.position(buffer.position() - 1);
+ return getUTF8Extended(buffer, utfLimit, charArray, index);
+ }
+ charArray[index++] = (char)c;
+ }
+ return new String(charArray, 0, index);
+ }
+
+ private static String getUTF8Extended(ByteBuffer buffer, int utfLimit, char[] charArray, int index) throws InvalidConstantPoolFormatException {
+ int c, c2, c3;
+ while (buffer.position() < utfLimit) {
+ c = buffer.get() & 0xff;
+ switch (c >> 4) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ /* 0xxxxxxx*/
+ charArray[index++] = (char)c;
+ break;
+ case 12: case 13:
+ /* 110x xxxx 10xx xxxx*/
+ c2 = buffer.get();
+ if ((c2 & 0xC0) != 0x80)
+ throw new InvalidConstantPoolFormatException(
+ "malformed input around byte " + buffer.position());
+ charArray[index++] = (char)(((c & 0x1F) << 6) |
+ (c2 & 0x3F));
+ break;
+ case 14:
+ /* 1110 xxxx 10xx xxxx 10xx xxxx */
+ c2 = buffer.get();
+ c3 = buffer.get();
+ if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80))
+ throw new InvalidConstantPoolFormatException(
+ "malformed input around byte " + (buffer.position()));
+ charArray[index++] = (char)(((c & 0x0F) << 12) |
+ ((c2 & 0x3F) << 6) |
+ ((c3 & 0x3F) << 0));
+ break;
+ default:
+ /* 10xx xxxx, 1111 xxxx */
+ throw new InvalidConstantPoolFormatException(
+ "malformed input around byte " + buffer.position());
+ }
+ }
+ // The number of chars produced may be less than utflen
+ return new String(charArray, 0, index);
+ }
+}
diff --git a/src/share/classes/sun/dyn/anon/ConstantPoolPatch.java b/src/share/classes/sun/dyn/anon/ConstantPoolPatch.java
new file mode 100644
index 0000000000000000000000000000000000000000..2775ad2f67daca908e0082495e4b0baaf01aa170
--- /dev/null
+++ b/src/share/classes/sun/dyn/anon/ConstantPoolPatch.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.anon;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import static sun.dyn.anon.ConstantPoolVisitor.*;
+
+/** A class and its patched constant pool.
+ *
+ * This class allow to modify (patch) a constant pool
+ * by changing the value of its entry.
+ * Entry are referenced using index that can be get
+ * by parsing the constant pool using
+ * {@link ConstantPoolParser#parse(ConstantPoolVisitor)}.
+ *
+ * @see ConstantPoolVisitor
+ * @see ConstantPoolParser#createPatch()
+ */
+public class ConstantPoolPatch {
+ final ConstantPoolParser outer;
+ final Object[] patchArray;
+
+ ConstantPoolPatch(ConstantPoolParser outer) {
+ this.outer = outer;
+ this.patchArray = new Object[outer.getLength()];
+ }
+
+ /** Create a {@link ConstantPoolParser} and
+ * a {@link ConstantPoolPatch} in one step.
+ * Equivalent to {@code new ConstantPoolParser(classFile).createPatch()}.
+ *
+ * @param classFile an array of bytes containing a class.
+ * @see #ConstantPoolParser(Class)
+ */
+ public ConstantPoolPatch(byte[] classFile) throws InvalidConstantPoolFormatException {
+ this(new ConstantPoolParser(classFile));
+ }
+
+ /** Create a {@link ConstantPoolParser} and
+ * a {@link ConstantPoolPatch} in one step.
+ * Equivalent to {@code new ConstantPoolParser(templateClass).createPatch()}.
+ *
+ * @param templateClass the class to parse.
+ * @see #ConstantPoolParser(Class)
+ */
+ public ConstantPoolPatch(Class> templateClass) throws IOException, InvalidConstantPoolFormatException {
+ this(new ConstantPoolParser(templateClass));
+ }
+
+
+ /** Creates a patch from an existing patch.
+ * All changes are copied from that patch.
+ * @param patch a patch
+ *
+ * @see ConstantPoolParser#createPatch()
+ */
+ public ConstantPoolPatch(ConstantPoolPatch patch) {
+ outer = patch.outer;
+ patchArray = patch.patchArray.clone();
+ }
+
+ /** Which parser built this patch? */
+ public ConstantPoolParser getParser() {
+ return outer;
+ }
+
+ /** Report the tag at the given index in the constant pool. */
+ public byte getTag(int index) {
+ return outer.getTag(index);
+ }
+
+ /** Report the current patch at the given index of the constant pool.
+ * Null means no patch will be made.
+ * To observe the unpatched entry at the given index, use
+ * {@link #getParser()}{@code .}@link ConstantPoolParser#parse(ConstantPoolVisitor)}
+ */
+ public Object getPatch(int index) {
+ Object value = patchArray[index];
+ if (value == null) return null;
+ switch (getTag(index)) {
+ case CONSTANT_Fieldref:
+ case CONSTANT_Methodref:
+ case CONSTANT_InterfaceMethodref:
+ if (value instanceof String)
+ value = stripSemis(2, (String) value);
+ break;
+ case CONSTANT_NameAndType:
+ if (value instanceof String)
+ value = stripSemis(1, (String) value);
+ break;
+ }
+ return value;
+ }
+
+ /** Clear all patches. */
+ public void clear() {
+ Arrays.fill(patchArray, null);
+ }
+
+ /** Clear one patch. */
+ public void clear(int index) {
+ patchArray[index] = null;
+ }
+
+ /** Produce the patches as an array. */
+ public Object[] getPatches() {
+ return patchArray.clone();
+ }
+
+ /** Produce the original constant pool as an array. */
+ public Object[] getOriginalCP() throws InvalidConstantPoolFormatException {
+ return getOriginalCP(0, patchArray.length, -1);
+ }
+
+ /** Walk the constant pool, applying patches using the given map.
+ *
+ * @param utf8Map Utf8 strings to modify, if encountered
+ * @param classMap Classes (or their names) to modify, if encountered
+ * @param valueMap Constant values to modify, if encountered
+ * @param deleteUsedEntries if true, delete map entries that are used
+ */
+ public void putPatches(final Map utf8Map,
+ final Map classMap,
+ final Map valueMap,
+ boolean deleteUsedEntries) throws InvalidConstantPoolFormatException {
+ final HashSet usedUtf8Keys;
+ final HashSet usedClassKeys;
+ final HashSet usedValueKeys;
+ if (deleteUsedEntries) {
+ usedUtf8Keys = (utf8Map == null) ? null : new HashSet();
+ usedClassKeys = (classMap == null) ? null : new HashSet();
+ usedValueKeys = (valueMap == null) ? null : new HashSet();
+ } else {
+ usedUtf8Keys = null;
+ usedClassKeys = null;
+ usedValueKeys = null;
+ }
+
+ outer.parse(new ConstantPoolVisitor() {
+
+ @Override
+ public void visitUTF8(int index, byte tag, String utf8) {
+ putUTF8(index, utf8Map.get(utf8));
+ if (usedUtf8Keys != null) usedUtf8Keys.add(utf8);
+ }
+
+ @Override
+ public void visitConstantValue(int index, byte tag, Object value) {
+ putConstantValue(index, tag, valueMap.get(value));
+ if (usedValueKeys != null) usedValueKeys.add(value);
+ }
+
+ @Override
+ public void visitConstantString(int index, byte tag, String name, int nameIndex) {
+ if (tag == CONSTANT_Class) {
+ putConstantValue(index, tag, classMap.get(name));
+ if (usedClassKeys != null) usedClassKeys.add(name);
+ } else {
+ assert(tag == CONSTANT_String);
+ visitConstantValue(index, tag, name);
+ }
+ }
+ });
+ if (usedUtf8Keys != null) utf8Map.keySet().removeAll(usedUtf8Keys);
+ if (usedClassKeys != null) classMap.keySet().removeAll(usedClassKeys);
+ if (usedValueKeys != null) valueMap.keySet().removeAll(usedValueKeys);
+ }
+
+ Object[] getOriginalCP(final int startIndex,
+ final int endIndex,
+ final int tagMask) throws InvalidConstantPoolFormatException {
+ final Object[] cpArray = new Object[endIndex - startIndex];
+ outer.parse(new ConstantPoolVisitor() {
+
+ void show(int index, byte tag, Object value) {
+ if (index < startIndex || index >= endIndex) return;
+ if (((1 << tag) & tagMask) == 0) return;
+ cpArray[index - startIndex] = value;
+ }
+
+ @Override
+ public void visitUTF8(int index, byte tag, String utf8) {
+ show(index, tag, utf8);
+ }
+
+ @Override
+ public void visitConstantValue(int index, byte tag, Object value) {
+ assert(tag != CONSTANT_String);
+ show(index, tag, value);
+ }
+
+ @Override
+ public void visitConstantString(int index, byte tag,
+ String value, int j) {
+ show(index, tag, value);
+ }
+
+ @Override
+ public void visitMemberRef(int index, byte tag,
+ String className, String memberName,
+ String signature,
+ int j, int k) {
+ show(index, tag, new String[]{ className, memberName, signature });
+ }
+
+ @Override
+ public void visitDescriptor(int index, byte tag,
+ String memberName, String signature,
+ int j, int k) {
+ show(index, tag, new String[]{ memberName, signature });
+ }
+ });
+ return cpArray;
+ }
+
+ /** Write the head (header plus constant pool)
+ * of the patched class file to the indicated stream.
+ */
+ void writeHead(OutputStream out) throws IOException {
+ outer.writePatchedHead(out, patchArray);
+ }
+
+ /** Write the tail (everything after the constant pool)
+ * of the patched class file to the indicated stream.
+ */
+ void writeTail(OutputStream out) throws IOException {
+ outer.writeTail(out);
+ }
+
+ private void checkConstantTag(byte tag, Object value) {
+ if (value == null)
+ throw new IllegalArgumentException(
+ "invalid null constant value");
+ if (classForTag(tag) != value.getClass())
+ throw new IllegalArgumentException(
+ "invalid constant value"
+ + (tag == CONSTANT_None ? ""
+ : " for tag "+tagName(tag))
+ + " of class "+value.getClass());
+ }
+
+ private void checkTag(int index, byte putTag) {
+ byte tag = outer.tags[index];
+ if (tag != putTag)
+ throw new IllegalArgumentException(
+ "invalid put operation"
+ + " for " + tagName(putTag)
+ + " at index " + index + " found " + tagName(tag));
+ }
+
+ private void checkTagMask(int index, int tagBitMask) {
+ byte tag = outer.tags[index];
+ int tagBit = ((tag & 0x1F) == tag) ? (1 << tag) : 0;
+ if ((tagBit & tagBitMask) == 0)
+ throw new IllegalArgumentException(
+ "invalid put operation"
+ + " at index " + index + " found " + tagName(tag));
+ }
+
+ private static void checkMemberName(String memberName) {
+ if (memberName.indexOf(';') >= 0)
+ throw new IllegalArgumentException("memberName " + memberName + " contains a ';'");
+ }
+
+ /** Set the entry of the constant pool indexed by index to
+ * a new string.
+ *
+ * @param index an index to a constant pool entry containing a
+ * {@link ConstantPoolVisitor#CONSTANT_Utf8} value.
+ * @param utf8 a string
+ *
+ * @see ConstantPoolVisitor#visitUTF8(int, byte, String)
+ */
+ public void putUTF8(int index, String utf8) {
+ if (utf8 == null) { clear(index); return; }
+ checkTag(index, CONSTANT_Utf8);
+ patchArray[index] = utf8;
+ }
+
+ /** Set the entry of the constant pool indexed by index to
+ * a new value, depending on its dynamic type.
+ *
+ * @param index an index to a constant pool entry containing a
+ * one of the following structures:
+ * {@link ConstantPoolVisitor#CONSTANT_Integer},
+ * {@link ConstantPoolVisitor#CONSTANT_Float},
+ * {@link ConstantPoolVisitor#CONSTANT_Long},
+ * {@link ConstantPoolVisitor#CONSTANT_Double},
+ * {@link ConstantPoolVisitor#CONSTANT_String}, or
+ * {@link ConstantPoolVisitor#CONSTANT_Class}
+ * @param value a boxed int, float, long or double; or a string or class object
+ * @throws IllegalArgumentException if the type of the constant does not
+ * match the constant pool entry type,
+ * as reported by {@link #getTag(int)}
+ *
+ * @see #putConstantValue(int, byte, Object)
+ * @see ConstantPoolVisitor#visitConstantValue(int, byte, Object)
+ * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int)
+ */
+ public void putConstantValue(int index, Object value) {
+ if (value == null) { clear(index); return; }
+ byte tag = tagForConstant(value.getClass());
+ checkConstantTag(tag, value);
+ checkTag(index, tag);
+ patchArray[index] = value;
+ }
+
+ /** Set the entry of the constant pool indexed by index to
+ * a new value.
+ *
+ * @param index an index to a constant pool entry matching the given tag
+ * @param tag one of the following values:
+ * {@link ConstantPoolVisitor#CONSTANT_Integer},
+ * {@link ConstantPoolVisitor#CONSTANT_Float},
+ * {@link ConstantPoolVisitor#CONSTANT_Long},
+ * {@link ConstantPoolVisitor#CONSTANT_Double},
+ * {@link ConstantPoolVisitor#CONSTANT_String}, or
+ * {@link ConstantPoolVisitor#CONSTANT_Class}
+ * @param value a boxed number, string, or class object
+ * @throws IllegalArgumentException if the type of the constant does not
+ * match the constant pool entry type, or if a class name contains
+ * '/' or ';'
+ *
+ * @see #putConstantValue(int, Object)
+ * @see ConstantPoolVisitor#visitConstantValue(int, byte, Object)
+ * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int)
+ */
+ public void putConstantValue(int index, byte tag, Object value) {
+ if (value == null) { clear(index); return; }
+ checkTag(index, tag);
+ if (tag == CONSTANT_Class && value instanceof String) {
+ checkClassName((String) value);
+ } else if (tag == CONSTANT_String) {
+ // the JVM accepts any object as a patch for a string
+ } else {
+ // make sure the incoming value is the right type
+ checkConstantTag(tag, value);
+ }
+ checkTag(index, tag);
+ patchArray[index] = value;
+ }
+
+ /** Set the entry of the constant pool indexed by index to
+ * a new {@link ConstantPoolVisitor#CONSTANT_NameAndType} value.
+ *
+ * @param index an index to a constant pool entry containing a
+ * {@link ConstantPoolVisitor#CONSTANT_NameAndType} value.
+ * @param memberName a memberName
+ * @param signature a signature
+ * @throws IllegalArgumentException if memberName contains the character ';'
+ *
+ * @see ConstantPoolVisitor#visitDescriptor(int, byte, String, String, int, int)
+ */
+ public void putDescriptor(int index, String memberName, String signature) {
+ checkTag(index, CONSTANT_NameAndType);
+ checkMemberName(memberName);
+ patchArray[index] = addSemis(memberName, signature);
+ }
+
+ /** Set the entry of the constant pool indexed by index to
+ * a new {@link ConstantPoolVisitor#CONSTANT_Fieldref},
+ * {@link ConstantPoolVisitor#CONSTANT_Methodref}, or
+ * {@link ConstantPoolVisitor#CONSTANT_InterfaceMethodref} value.
+ *
+ * @param index an index to a constant pool entry containing a member reference
+ * @param className a class name
+ * @param memberName a field or method name
+ * @param signature a field or method signature
+ * @throws IllegalArgumentException if memberName contains the character ';'
+ * or signature is not a correct signature
+ *
+ * @see ConstantPoolVisitor#visitMemberRef(int, byte, String, String, String, int, int)
+ */
+ public void putMemberRef(int index, byte tag,
+ String className, String memberName, String signature) {
+ checkTagMask(tag, CONSTANT_MemberRef_MASK);
+ checkTag(index, tag);
+ checkClassName(className);
+ checkMemberName(memberName);
+ if (signature.startsWith("(") == (tag == CONSTANT_Fieldref))
+ throw new IllegalArgumentException("bad signature: "+signature);
+ patchArray[index] = addSemis(className, memberName, signature);
+ }
+
+ static private final int CONSTANT_MemberRef_MASK =
+ CONSTANT_Fieldref
+ | CONSTANT_Methodref
+ | CONSTANT_InterfaceMethodref;
+
+ private static final Map, Byte> CONSTANT_VALUE_CLASS_TAG
+ = new IdentityHashMap, Byte>();
+ private static final Class[] CONSTANT_VALUE_CLASS = new Class[16];
+ static {
+ Object[][] values = {
+ {Integer.class, CONSTANT_Integer},
+ {Long.class, CONSTANT_Long},
+ {Float.class, CONSTANT_Float},
+ {Double.class, CONSTANT_Double},
+ {String.class, CONSTANT_String},
+ {Class.class, CONSTANT_Class}
+ };
+ for (Object[] value : values) {
+ Class> cls = (Class>)value[0];
+ Byte tag = (Byte) value[1];
+ CONSTANT_VALUE_CLASS_TAG.put(cls, tag);
+ CONSTANT_VALUE_CLASS[(byte)tag] = cls;
+ }
+ }
+
+ static Class> classForTag(byte tag) {
+ if ((tag & 0xFF) >= CONSTANT_VALUE_CLASS.length)
+ return null;
+ return CONSTANT_VALUE_CLASS[tag];
+ }
+
+ static byte tagForConstant(Class> cls) {
+ Byte tag = CONSTANT_VALUE_CLASS_TAG.get(cls);
+ return (tag == null) ? CONSTANT_None : (byte)tag;
+ }
+
+ private static void checkClassName(String className) {
+ if (className.indexOf('/') >= 0 || className.indexOf(';') >= 0)
+ throw new IllegalArgumentException("invalid class name " + className);
+ }
+
+ static String addSemis(String name, String... names) {
+ StringBuilder buf = new StringBuilder(name.length() * 5);
+ buf.append(name);
+ for (String name2 : names) {
+ buf.append(';').append(name2);
+ }
+ String res = buf.toString();
+ assert(stripSemis(names.length, res)[0].equals(name));
+ assert(stripSemis(names.length, res)[1].equals(names[0]));
+ assert(names.length == 1 ||
+ stripSemis(names.length, res)[2].equals(names[1]));
+ return res;
+ }
+
+ static String[] stripSemis(int count, String string) {
+ String[] res = new String[count+1];
+ int pos = 0;
+ for (int i = 0; i < count; i++) {
+ int pos2 = string.indexOf(';', pos);
+ if (pos2 < 0) pos2 = string.length(); // yuck
+ res[i] = string.substring(pos, pos2);
+ pos = pos2;
+ }
+ res[count] = string.substring(pos);
+ return res;
+ }
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder(this.getClass().getName());
+ buf.append("{");
+ Object[] origCP = null;
+ for (int i = 0; i < patchArray.length; i++) {
+ if (patchArray[i] == null) continue;
+ if (origCP != null) {
+ buf.append(", ");
+ } else {
+ try {
+ origCP = getOriginalCP();
+ } catch (InvalidConstantPoolFormatException ee) {
+ origCP = new Object[0];
+ }
+ }
+ Object orig = (i < origCP.length) ? origCP[i] : "?";
+ buf.append(orig).append("=").append(patchArray[i]);
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+}
diff --git a/src/share/classes/sun/dyn/anon/ConstantPoolVisitor.java b/src/share/classes/sun/dyn/anon/ConstantPoolVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..4157f13e77f29777ce04a5b3f5b92e19650f2d77
--- /dev/null
+++ b/src/share/classes/sun/dyn/anon/ConstantPoolVisitor.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.anon;
+
+/**
+ * A visitor called by {@link ConstantPoolParser#parse(ConstantPoolVisitor)}
+ * when a constant pool entry is parsed.
+ *
+ * A visit* method is called when a constant pool entry is parsed.
+ * The first argument is always the constant pool index.
+ * The second argument is always the constant pool tag,
+ * even for methods like {@link #visitUTF8(int, byte, String)} which only apply to one tag.
+ * String arguments refer to Utf8 or NameAndType entries declared elsewhere,
+ * and are always accompanied by the indexes of those entries.
+ *
+ * The order of the calls to the visit* methods is not necessarily related
+ * to the order of the entries in the constant pool.
+ * If one entry has a reference to another entry, the latter (lower-level)
+ * entry will be visited first.
+ *
+ * The following table shows the relation between constant pool entry
+ * types and the corresponding visit* methods:
+ *
+ *
+ * Tag(s) Method
+ *
+ * {@link #CONSTANT_Utf8}
+ * {@link #visitUTF8(int, byte, String)}
+ *
+ * {@link #CONSTANT_Integer}, {@link #CONSTANT_Float},
+ * {@link #CONSTANT_Long}, {@link #CONSTANT_Double}
+ * {@link #visitConstantValue(int, byte, Object)}
+ *
+ * {@link #CONSTANT_String}, {@link #CONSTANT_Class}
+ * {@link #visitConstantString(int, byte, String, int)}
+ *
+ * {@link #CONSTANT_NameAndType}
+ * {@link #visitDescriptor(int, byte, String, String, int, int)}
+ *
+ * {@link #CONSTANT_Fieldref},
+ * {@link #CONSTANT_Methodref},
+ * {@link #CONSTANT_InterfaceMethodref}
+ * {@link #visitMemberRef(int, byte, String, String, String, int, int)}
+ *
+ *
+ *
+ * @see ConstantPoolPatch
+ * @author Remi Forax
+ * @author jrose
+ */
+public class ConstantPoolVisitor {
+ /** Called each time an UTF8 constant pool entry is found.
+ * @param index the constant pool index
+ * @param tag always {@link #CONSTANT_Utf8}
+ * @param utf8 string encoded in modified UTF-8 format passed as a {@code String}
+ *
+ * @see ConstantPoolPatch#putUTF8(int, String)
+ */
+ public void visitUTF8(int index, byte tag, String utf8) {
+ // do nothing
+ }
+
+ /** Called for each constant pool entry that encodes an integer,
+ * a float, a long, or a double.
+ * Constant strings and classes are not managed by this method but
+ * by {@link #visitConstantString(int, byte, String, int)}.
+ *
+ * @param index the constant pool index
+ * @param tag one of {@link #CONSTANT_Integer},
+ * {@link #CONSTANT_Float},
+ * {@link #CONSTANT_Long},
+ * or {@link #CONSTANT_Double}
+ * @param value encoded value
+ *
+ * @see ConstantPoolPatch#putConstantValue(int, Object)
+ */
+ public void visitConstantValue(int index, byte tag, Object value) {
+ // do nothing
+ }
+
+ /** Called for each constant pool entry that encodes a string or a class.
+ * @param index the constant pool index
+ * @param tag one of {@link #CONSTANT_String},
+ * {@link #CONSTANT_Class},
+ * @param name string body or class name (using dot separator)
+ * @param nameIndex the index of the Utf8 string for the name
+ *
+ * @see ConstantPoolPatch#putConstantValue(int, byte, Object)
+ */
+ public void visitConstantString(int index, byte tag,
+ String name, int nameIndex) {
+ // do nothing
+ }
+
+ /** Called for each constant pool entry that encodes a name and type.
+ * @param index the constant pool index
+ * @param tag always {@link #CONSTANT_NameAndType}
+ * @param memberName a field or method name
+ * @param signature the member signature
+ * @param memberNameIndex index of the Utf8 string for the member name
+ * @param signatureIndex index of the Utf8 string for the signature
+ *
+ * @see ConstantPoolPatch#putDescriptor(int, String, String)
+ */
+ public void visitDescriptor(int index, byte tag,
+ String memberName, String signature,
+ int memberNameIndex, int signatureIndex) {
+ // do nothing
+ }
+
+ /** Called for each constant pool entry that encodes a field or method.
+ * @param index the constant pool index
+ * @param tag one of {@link #CONSTANT_Fieldref},
+ * or {@link #CONSTANT_Methodref},
+ * or {@link #CONSTANT_InterfaceMethodref}
+ * @param className the class name (using dot separator)
+ * @param memberName name of the field or method
+ * @param signature the field or method signature
+ * @param classNameIndex index of the Utf8 string for the class name
+ * @param descriptorIndex index of the NameAndType descriptor constant
+ *
+ * @see ConstantPoolPatch#putMemberRef(int, byte, String, String, String)
+ */
+ public void visitMemberRef(int index, byte tag,
+ String className, String memberName, String signature,
+ int classNameIndex, int descriptorIndex) {
+ // do nothing
+ }
+
+ public static final byte
+ CONSTANT_None = 0,
+ CONSTANT_Utf8 = 1,
+ //CONSTANT_Unicode = 2, /* unused */
+ CONSTANT_Integer = 3,
+ CONSTANT_Float = 4,
+ CONSTANT_Long = 5,
+ CONSTANT_Double = 6,
+ CONSTANT_Class = 7,
+ CONSTANT_String = 8,
+ CONSTANT_Fieldref = 9,
+ CONSTANT_Methodref = 10,
+ CONSTANT_InterfaceMethodref = 11,
+ CONSTANT_NameAndType = 12;
+
+ private static String[] TAG_NAMES = {
+ "Empty",
+ "Utf8",
+ null, //"Unicode",
+ "Integer",
+ "Float",
+ "Long",
+ "Double",
+ "Class",
+ "String",
+ "Fieldref",
+ "Methodref",
+ "InterfaceMethodref",
+ "NameAndType"
+ };
+
+ public static String tagName(byte tag) {
+ String name = null;
+ if ((tag & 0xFF) < TAG_NAMES.length)
+ name = TAG_NAMES[tag];
+ if (name == null)
+ name = "Unknown#"+(tag&0xFF);
+ return name;
+ }
+}
diff --git a/src/share/classes/sun/dyn/anon/InvalidConstantPoolFormatException.java b/src/share/classes/sun/dyn/anon/InvalidConstantPoolFormatException.java
new file mode 100644
index 0000000000000000000000000000000000000000..652f7318b05db1fa6d9d9d75727dc77ce8c1982f
--- /dev/null
+++ b/src/share/classes/sun/dyn/anon/InvalidConstantPoolFormatException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.anon;
+
+/** Exception used when there is an error in the constant pool
+ * format.
+ */
+public class InvalidConstantPoolFormatException extends Exception {
+ private static final long serialVersionUID=-6103888330523770949L;
+
+ public InvalidConstantPoolFormatException(String message,Throwable cause) {
+ super(message,cause);
+ }
+
+ public InvalidConstantPoolFormatException(String message) {
+ super(message);
+ }
+
+ public InvalidConstantPoolFormatException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/share/classes/sun/dyn/empty/Empty.java b/src/share/classes/sun/dyn/empty/Empty.java
new file mode 100644
index 0000000000000000000000000000000000000000..4fb72d0be67d5bc88270dfd3dcdb68365c031332
--- /dev/null
+++ b/src/share/classes/sun/dyn/empty/Empty.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.empty;
+
+/**
+ * An empty class in an empty package.
+ * Used as a proxy for unprivileged code, since making access checks
+ * against it will only succeed against public methods in public types.
+ * @author jrose
+ */
+public class Empty {
+ private Empty() { throw new InternalError(); }
+}
diff --git a/src/share/classes/sun/dyn/package-info.java b/src/share/classes/sun/dyn/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..93aa427f3652ec2f5ae4aa7efb76ece229c9c152
--- /dev/null
+++ b/src/share/classes/sun/dyn/package-info.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * Implementation details for JSR 292 RI, package java.dyn.
+ * This particular version is specific to Hotspot.
+ * There is also a backport version of this sub-package which uses reflection,
+ * and can therefore run (slowly) on older versions of Java.
+ * Other JVM vendors may create their own versions of this sub-package.
+ * @author jrose
+ */
+
+package sun.dyn;
diff --git a/src/share/classes/sun/dyn/util/BytecodeName.java b/src/share/classes/sun/dyn/util/BytecodeName.java
new file mode 100644
index 0000000000000000000000000000000000000000..2abe780a5ef4aaa99b92b16ce8b98cf97a569d75
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/BytecodeName.java
@@ -0,0 +1,711 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.util;
+
+/**
+ * Utility routines for dealing with bytecode-level names.
+ * Includes universal mangling rules for the JVM.
+ *
+ * Avoiding Dangerous Characters
+ *
+ *
+ * The JVM defines a very small set of characters which are illegal
+ * in name spellings. We will slightly extend and regularize this set
+ * into a group of dangerous characters .
+ * These characters will then be replaced, in mangled names, by escape sequences.
+ * In addition, accidental escape sequences must be further escaped.
+ * Finally, a special prefix will be applied if and only if
+ * the mangling would otherwise fail to begin with the escape character.
+ * This happens to cover the corner case of the null string,
+ * and also clearly marks symbols which need demangling.
+ *
+ *
+ * Dangerous characters are the union of all characters forbidden
+ * or otherwise restricted by the JVM specification,
+ * plus their mates, if they are brackets
+ * ([
and ]
,
+ * <
and >
),
+ * plus, arbitrarily, the colon character :
.
+ * There is no distinction between type, method, and field names.
+ * This makes it easier to convert between mangled names of different
+ * types, since they do not need to be decoded (demangled).
+ *
+ *
+ * The escape character is backslash \
+ * (also known as reverse solidus).
+ * This character is, until now, unheard of in bytecode names,
+ * but traditional in the proposed role.
+ *
+ *
+ * Replacement Characters
+ *
+ *
+ *
+ * Every escape sequence is two characters
+ * (in fact, two UTF8 bytes) beginning with
+ * the escape character and followed by a
+ * replacement character .
+ * (Since the replacement character is never a backslash,
+ * iterated manglings do not double in size.)
+ *
+ *
+ * Each dangerous character has some rough visual similarity
+ * to its corresponding replacement character.
+ * This makes mangled symbols easier to recognize by sight.
+ *
+ *
+ * The dangerous characters are
+ * /
(forward slash, used to delimit package components),
+ * .
(dot, also a package delimiter),
+ * ;
(semicolon, used in signatures),
+ * $
(dollar, used in inner classes and synthetic members),
+ * <
(left angle),
+ * >
(right angle),
+ * [
(left square bracket, used in array types),
+ * ]
(right square bracket, reserved in this scheme for language use),
+ * and :
(colon, reserved in this scheme for language use).
+ * Their replacements are, respectively,
+ * |
(vertical bar),
+ * ,
(comma),
+ * ?
(question mark),
+ * %
(percent),
+ * ^
(caret),
+ * _
(underscore), and
+ * {
(left curly bracket),
+ * }
(right curly bracket),
+ * !
(exclamation mark).
+ * In addition, the replacement character for the escape character itself is
+ * -
(hyphen),
+ * and the replacement character for the null prefix is
+ * =
(equal sign).
+ *
+ *
+ * An escape character \
+ * followed by any of these replacement characters
+ * is an escape sequence, and there are no other escape sequences.
+ * An equal sign is only part of an escape sequence
+ * if it is the second character in the whole string, following a backslash.
+ * Two consecutive backslashes do not form an escape sequence.
+ *
+ *
+ * Each escape sequence replaces a so-called original character
+ * which is either one of the dangerous characters or the escape character.
+ * A null prefix replaces an initial null string, not a character.
+ *
+ *
+ * All this implies that escape sequences cannot overlap and may be
+ * determined all at once for a whole string. Note that a spelling
+ * string can contain accidental escapes , apparent escape
+ * sequences which must not be interpreted as manglings.
+ * These are disabled by replacing their leading backslash with an
+ * escape sequence (\-
). To mangle a string, three logical steps
+ * are required, though they may be carried out in one pass:
+ *
+ *
+ * In each accidental escape, replace the backslash with an escape sequence
+ * (\-
).
+ * Replace each dangerous character with an escape sequence
+ * (\|
for /
, etc.).
+ * If the first two steps introduced any change, and
+ * if the string does not already begin with a backslash, prepend a null prefix (\=
).
+ *
+ *
+ * To demangle a mangled string that begins with an escape,
+ * remove any null prefix, and then replace (in parallel)
+ * each escape sequence by its original character.
+ * Spelling strings which contain accidental
+ * escapes must have them replaced, even if those
+ * strings do not contain dangerous characters.
+ * This restriction means that mangling a string always
+ * requires a scan of the string for escapes.
+ * But then, a scan would be required anyway,
+ * to check for dangerous characters.
+ *
+ *
+ * Nice Properties
+ *
+ *
+ * If a bytecode name does not contain any escape sequence,
+ * demangling is a no-op: The string demangles to itself.
+ * Such a string is called self-mangling .
+ * Almost all strings are self-mangling.
+ * In practice, to demangle almost any name “found in nature”,
+ * simply verify that it does not begin with a backslash.
+ *
+ *
+ * Mangling is a one-to-one function, while demangling
+ * is a many-to-one function.
+ * A mangled string is defined as validly mangled if
+ * it is in fact the unique mangling of its spelling string.
+ * Three examples of invalidly mangled strings are \=foo
,
+ * \-bar
, and baz\!
, which demangle to foo
, \bar
, and
+ * baz\!
, but then remangle to foo
, \bar
, and \=baz\-!
.
+ * If a language back-end or runtime is using mangled names,
+ * it should never present an invalidly mangled bytecode
+ * name to the JVM. If the runtime encounters one,
+ * it should also report an error, since such an occurrence
+ * probably indicates a bug in name encoding which
+ * will lead to errors in linkage.
+ * However, this note does not propose that the JVM verifier
+ * detect invalidly mangled names.
+ *
+ *
+ * As a result of these rules, it is a simple matter to
+ * compute validly mangled substrings and concatenations
+ * of validly mangled strings, and (with a little care)
+ * these correspond to corresponding operations on their
+ * spelling strings.
+ *
+ *
+ * Any prefix of a validly mangled string is also validly mangled,
+ * although a null prefix may need to be removed.
+ * Any suffix of a validly mangled string is also validly mangled,
+ * although a null prefix may need to be added.
+ * Two validly mangled strings, when concatenated,
+ * are also validly mangled, although any null prefix
+ * must be removed from the second string,
+ * and a trailing backslash on the first string may need escaping,
+ * if it would participate in an accidental escape when followed
+ * by the first character of the second string.
+ *
+ * If languages that include non-Java symbol spellings use this
+ * mangling convention, they will enjoy the following advantages:
+ *
+ *
+ * They can interoperate via symbols they share in common.
+ * Low-level tools, such as backtrace printers, will have readable displays.
+ * Future JVM and language extensions can safely use the dangerous characters
+ * for structuring symbols, but will never interfere with valid spellings.
+ * Runtimes and compilers can use standard libraries for mangling and demangling.
+ * Occasional transliterations and name composition will be simple and regular,
+ * for classes, methods, and fields.
+ * Bytecode names will continue to be compact.
+ * When mangled, spellings will at most double in length, either in
+ * UTF8 or UTF16 format, and most will not change at all.
+ *
+ *
+ *
+ * Suggestions for Human Readable Presentations
+ *
+ *
+ *
+ * For human readable displays of symbols,
+ * it will be better to present a string-like quoted
+ * representation of the spelling, because JVM users
+ * are generally familiar with such tokens.
+ * We suggest using single or double quotes before and after
+ * mangled symbols which are not valid Java identifiers,
+ * with quotes, backslashes, and non-printing characters
+ * escaped as if for literals in the Java language.
+ *
+ *
+ * For example, an HTML-like spelling
+ * <pre>
mangles to
+ * \^pre\_
and could
+ * display more cleanly as
+ * '<pre>'
,
+ * with the quotes included.
+ * Such string-like conventions are not suitable
+ * for mangled bytecode names, in part because
+ * dangerous characters must be eliminated, rather
+ * than just quoted. Otherwise internally structured
+ * strings like package prefixes and method signatures
+ * could not be reliably parsed.
+ *
+ *
+ * In such human-readable displays, invalidly mangled
+ * names should not be demangled and quoted,
+ * for this would be misleading. Likewise, JVM symbols
+ * which contain dangerous characters (like dots in field
+ * names or brackets in method names) should not be
+ * simply quoted. The bytecode names
+ * \=phase\,1
and
+ * phase.1
are distinct,
+ * and in demangled displays they should be presented as
+ * 'phase.1'
and something like
+ * 'phase'.1
, respectively.
+ *
+ *
+ * @author John Rose
+ * @version 1.2, 02/06/2008
+ * @see http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm
+ */
+public class BytecodeName {
+ private BytecodeName() { } // static only class
+
+ /** Given a source name, produce the corresponding bytecode name.
+ * The source name should not be qualified, because any syntactic
+ * markers (dots, slashes, dollar signs, colons, etc.) will be mangled.
+ * @param s the source name
+ * @return a valid bytecode name which represents the source name
+ */
+ public static String toBytecodeName(String s) {
+ String bn = mangle(s);
+ assert((Object)bn == s || looksMangled(bn)) : bn;
+ assert(s.equals(toSourceName(bn))) : s;
+ return bn;
+ }
+
+ /** Given an unqualified bytecode name, produce the corresponding source name.
+ * The bytecode name must not contain dangerous characters.
+ * In particular, it must not be qualified or segmented by colon {@code ':'}.
+ * @param s the bytecode name
+ * @return the source name, which may possibly have unsafe characters
+ * @throws IllegalArgumentException if the bytecode name is not {@link #isSafeBytecodeName safe}
+ * @see #isSafeBytecodeName(java.lang.String)
+ */
+ public static String toSourceName(String s) {
+ checkSafeBytecodeName(s);
+ String sn = s;
+ if (looksMangled(s)) {
+ sn = demangle(s);
+ assert(s.equals(mangle(sn))) : s+" => "+sn+" => "+mangle(sn);
+ }
+ return sn;
+ }
+
+ /**
+ * Given a bytecode name from a classfile, separate it into
+ * components delimited by dangerous characters.
+ * Each resulting array element will be either a dangerous character,
+ * or else a safe bytecode name.
+ * (The safe name might possibly be mangled to hide further dangerous characters.)
+ * For example, the qualified class name {@code java/lang/String}
+ * will be parsed into the array {@code {"java", '/', "lang", '/', "String"}}.
+ * The name {@code <init>} will be parsed into { '<', "init", '>'}}
+ * The name {@code foo/bar$:baz} will be parsed into
+ * {@code {"foo", '/', "bar", '$', ':', "baz"}}.
+ */
+ public static Object[] parseBytecodeName(String s) {
+ int slen = s.length();
+ Object[] res = null;
+ for (int pass = 0; pass <= 1; pass++) {
+ int fillp = 0;
+ int lasti = 0;
+ for (int i = 0; i <= slen; i++) {
+ int whichDC = -1;
+ if (i < slen) {
+ whichDC = DANGEROUS_CHARS.indexOf(s.charAt(i));
+ if (whichDC < DANGEROUS_CHAR_FIRST_INDEX) continue;
+ }
+ // got to end of string or next dangerous char
+ if (lasti < i) {
+ // normal component
+ if (pass != 0)
+ res[fillp] = s.substring(lasti, i);
+ fillp++;
+ lasti = i+1;
+ }
+ if (whichDC >= DANGEROUS_CHAR_FIRST_INDEX) {
+ if (pass != 0)
+ res[fillp] = DANGEROUS_CHARS_CA[whichDC];
+ fillp++;
+ }
+ }
+ if (pass != 0) break;
+ // between passes, build the result array
+ res = new String[fillp];
+ if (fillp <= 1) {
+ if (fillp != 0) res[0] = s;
+ break;
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Given a series of components, create a bytecode name for a classfile.
+ * This is the inverse of {@link #parseBytecodeName(java.lang.String)}.
+ * Each component must either be an interned one-character string of
+ * a dangerous character, or else a safe bytecode name.
+ * @param components a series of name components
+ * @return the concatenation of all components
+ * @throws IllegalArgumentException if any component contains an unsafe
+ * character, and is not an interned one-character string
+ * @throws NullPointerException if any component is null
+ */
+ public static String unparseBytecodeName(Object[] components) {
+ for (Object c : components) {
+ if (c instanceof String)
+ checkSafeBytecodeName((String) c); // may fail
+ }
+ return appendAll(components);
+ }
+ private static String appendAll(Object[] components) {
+ if (components.length <= 1) {
+ if (components.length == 1) {
+ return String.valueOf(components[0]);
+ }
+ return "";
+ }
+ int slen = 0;
+ for (Object c : components) {
+ if (c instanceof String)
+ slen += String.valueOf(c).length();
+ else
+ slen += 1;
+ }
+ StringBuilder sb = new StringBuilder(slen);
+ for (Object c : components) {
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Given a bytecode name, produce the corresponding display name.
+ * This is the source name, plus quotes if needed.
+ * If the bytecode name contains dangerous characters,
+ * assume that they are being used as punctuation,
+ * and pass them through unchanged.
+ * @param s the original bytecode name (which may be qualified)
+ * @return a human-readable presentation
+ */
+ public static String toDisplayName(String s) {
+ Object[] components = parseBytecodeName(s);
+ for (int i = 0; i < components.length; i++) {
+ if (!(components[i] instanceof String))
+ continue;
+ String c = (String) components[i];
+ // pretty up the name by demangling it
+ String sn = toSourceName(c);
+ if ((Object)sn != c || !isJavaIdent(sn)) {
+ components[i] = quoteDisplay(sn);
+ }
+ }
+ return appendAll(components);
+ }
+ private static boolean isJavaIdent(String s) {
+ int slen = s.length();
+ if (slen == 0) return false;
+ if (!Character.isUnicodeIdentifierStart(s.charAt(0)))
+ return false;
+ for (int i = 1; i < slen; i++) {
+ if (!Character.isUnicodeIdentifierPart(s.charAt(0)))
+ return false;
+ }
+ return true;
+ }
+ private static String quoteDisplay(String s) {
+ // TO DO: Replace wierd characters in s by C-style escapes.
+ return "'"+s.replaceAll("['\\\\]", "\\\\$0")+"'";
+ }
+
+ private static void checkSafeBytecodeName(String s)
+ throws IllegalArgumentException {
+ if (!isSafeBytecodeName(s)) {
+ throw new IllegalArgumentException(s);
+ }
+ }
+
+ /**
+ * Report whether a simple name is safe as a bytecode name.
+ * Such names are acceptable in class files as class, method, and field names.
+ * Additionally, they are free of "dangerous" characters, even if those
+ * characters are legal in some (or all) names in class files.
+ * @param s the proposed bytecode name
+ * @return true if the name is non-empty and all of its characters are safe
+ */
+ public static boolean isSafeBytecodeName(String s) {
+ if (s.length() == 0) return false;
+ // check occurrences of each DANGEROUS char
+ for (char xc : DANGEROUS_CHARS_A) {
+ if (xc == ESCAPE_C) continue; // not really that dangerous
+ if (s.indexOf(xc) >= 0) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Report whether a character is safe in a bytecode name.
+ * This is true of any unicode character except the following
+ * dangerous characters : {@code ".;:$[]<>/"}.
+ * @param s the proposed character
+ * @return true if the character is safe to use in classfiles
+ */
+ public static boolean isSafeBytecodeChar(char c) {
+ return DANGEROUS_CHARS.indexOf(c) < DANGEROUS_CHAR_FIRST_INDEX;
+ }
+
+ private static boolean looksMangled(String s) {
+ return s.charAt(0) == ESCAPE_C;
+ }
+
+ private static String mangle(String s) {
+ if (s.length() == 0)
+ return NULL_ESCAPE;
+
+ // build this lazily, when we first need an escape:
+ StringBuilder sb = null;
+
+ for (int i = 0, slen = s.length(); i < slen; i++) {
+ char c = s.charAt(i);
+
+ boolean needEscape = false;
+ if (c == ESCAPE_C) {
+ if (i+1 < slen) {
+ char c1 = s.charAt(i+1);
+ if ((i == 0 && c1 == NULL_ESCAPE_C)
+ || c1 != originalOfReplacement(c1)) {
+ // an accidental escape
+ needEscape = true;
+ }
+ }
+ } else {
+ needEscape = isDangerous(c);
+ }
+
+ if (!needEscape) {
+ if (sb != null) sb.append(c);
+ continue;
+ }
+
+ // build sb if this is the first escape
+ if (sb == null) {
+ sb = new StringBuilder(s.length()+10);
+ // mangled names must begin with a backslash:
+ if (s.charAt(0) != ESCAPE_C && i > 0)
+ sb.append(NULL_ESCAPE);
+ // append the string so far, which is unremarkable:
+ sb.append(s.substring(0, i));
+ }
+
+ // rewrite \ to \-, / to \|, etc.
+ sb.append(ESCAPE_C);
+ sb.append(replacementOf(c));
+ }
+
+ if (sb != null) return sb.toString();
+
+ return s;
+ }
+
+ private static String demangle(String s) {
+ // build this lazily, when we first meet an escape:
+ StringBuilder sb = null;
+
+ int stringStart = 0;
+ if (s.startsWith(NULL_ESCAPE))
+ stringStart = 2;
+
+ for (int i = stringStart, slen = s.length(); i < slen; i++) {
+ char c = s.charAt(i);
+
+ if (c == ESCAPE_C && i+1 < slen) {
+ // might be an escape sequence
+ char rc = s.charAt(i+1);
+ char oc = originalOfReplacement(rc);
+ if (oc != rc) {
+ // build sb if this is the first escape
+ if (sb == null) {
+ sb = new StringBuilder(s.length());
+ // append the string so far, which is unremarkable:
+ sb.append(s.substring(stringStart, i));
+ }
+ ++i; // skip both characters
+ c = oc;
+ }
+ }
+
+ if (sb != null)
+ sb.append(c);
+ }
+
+ if (sb != null) return sb.toString();
+
+ return s.substring(stringStart);
+ }
+
+ static char ESCAPE_C = '\\';
+ // empty escape sequence to avoid a null name or illegal prefix
+ static char NULL_ESCAPE_C = '=';
+ static String NULL_ESCAPE = ESCAPE_C+""+NULL_ESCAPE_C;
+
+ static final String DANGEROUS_CHARS = "\\/.;:$[]<>"; // \\ must be first
+ static final String REPLACEMENT_CHARS = "-|,?!%{}^_";
+ static final int DANGEROUS_CHAR_FIRST_INDEX = 1; // index after \\
+ static char[] DANGEROUS_CHARS_A = DANGEROUS_CHARS.toCharArray();
+ static char[] REPLACEMENT_CHARS_A = REPLACEMENT_CHARS.toCharArray();
+ static final Character[] DANGEROUS_CHARS_CA;
+ static {
+ Character[] dcca = new Character[DANGEROUS_CHARS.length()];
+ for (int i = 0; i < dcca.length; i++)
+ dcca[i] = Character.valueOf(DANGEROUS_CHARS.charAt(i));
+ DANGEROUS_CHARS_CA = dcca;
+ }
+
+ static final long[] SPECIAL_BITMAP = new long[2]; // 128 bits
+ static {
+ String SPECIAL = DANGEROUS_CHARS + REPLACEMENT_CHARS;
+ //System.out.println("SPECIAL = "+SPECIAL);
+ for (char c : SPECIAL.toCharArray()) {
+ SPECIAL_BITMAP[c >>> 6] |= 1L << c;
+ }
+ }
+ static boolean isSpecial(char c) {
+ if ((c >>> 6) < SPECIAL_BITMAP.length)
+ return ((SPECIAL_BITMAP[c >>> 6] >> c) & 1) != 0;
+ else
+ return false;
+ }
+ static char replacementOf(char c) {
+ if (!isSpecial(c)) return c;
+ int i = DANGEROUS_CHARS.indexOf(c);
+ if (i < 0) return c;
+ return REPLACEMENT_CHARS.charAt(i);
+ }
+ static char originalOfReplacement(char c) {
+ if (!isSpecial(c)) return c;
+ int i = REPLACEMENT_CHARS.indexOf(c);
+ if (i < 0) return c;
+ return DANGEROUS_CHARS.charAt(i);
+ }
+ static boolean isDangerous(char c) {
+ if (!isSpecial(c)) return false;
+ return (DANGEROUS_CHARS.indexOf(c) >= DANGEROUS_CHAR_FIRST_INDEX);
+ }
+ static int indexOfDangerousChar(String s, int from) {
+ for (int i = from, slen = s.length(); i < slen; i++) {
+ if (isDangerous(s.charAt(i)))
+ return i;
+ }
+ return -1;
+ }
+ static int lastIndexOfDangerousChar(String s, int from) {
+ for (int i = Math.min(from, s.length()-1); i >= 0; i--) {
+ if (isDangerous(s.charAt(i)))
+ return i;
+ }
+ return -1;
+ }
+
+ // test driver
+ static void main(String[] av) {
+ // If verbose is enabled, quietly check everything.
+ // Otherwise, print the output for the user to check.
+ boolean verbose = false;
+
+ int maxlen = 0;
+
+ while (av.length > 0 && av[0].startsWith("-")) {
+ String flag = av[0].intern();
+ av = java.util.Arrays.copyOfRange(av, 1, av.length); // Java 1.6 or later
+ if (flag == "-" || flag == "--") break;
+ else if (flag == "-q")
+ verbose = false;
+ else if (flag == "-v")
+ verbose = true;
+ else if (flag.startsWith("-l"))
+ maxlen = Integer.valueOf(flag.substring(2));
+ else
+ throw new Error("Illegal flag argument: "+flag);
+ }
+
+ if (maxlen == 0)
+ maxlen = (verbose ? 2 : 4);
+ if (verbose) System.out.println("Note: maxlen = "+maxlen);
+
+ switch (av.length) {
+ case 0: av = new String[] {
+ DANGEROUS_CHARS.substring(0) +
+ REPLACEMENT_CHARS.substring(0, 1) +
+ NULL_ESCAPE + "x"
+ }; // and fall through:
+ case 1:
+ char[] cv = av[0].toCharArray();
+ av = new String[cv.length];
+ int avp = 0;
+ for (char c : cv) {
+ String s = String.valueOf(c);
+ if (c == 'x') s = "foo"; // tradition...
+ av[avp++] = s;
+ }
+ }
+ if (verbose)
+ System.out.println("Note: Verbose output mode enabled. Use '-q' to suppress.");
+ Tester t = new Tester();
+ t.maxlen = maxlen;
+ t.verbose = verbose;
+ t.tokens = av;
+ t.test("", 0);
+ }
+
+ static class Tester {
+ boolean verbose;
+ int maxlen;
+ java.util.Map map = new java.util.HashMap();
+ String[] tokens;
+
+ void test(String stringSoFar, int tokensSoFar) {
+ test(stringSoFar);
+ if (tokensSoFar <= maxlen) {
+ for (String token : tokens) {
+ if (token.length() == 0) continue; // skip empty tokens
+ if (stringSoFar.indexOf(token) != stringSoFar.lastIndexOf(token))
+ continue; // there are already two occs. of this token
+ if (token.charAt(0) == ESCAPE_C && token.length() == 1 && maxlen < 4)
+ test(stringSoFar+token, tokensSoFar); // want lots of \'s
+ else if (tokensSoFar < maxlen)
+ test(stringSoFar+token, tokensSoFar+1);
+ }
+ }
+ }
+
+ void test(String s) {
+ // for small batches, do not test the null string
+ if (s.length() == 0 && maxlen >=1 && maxlen <= 2) return;
+ String bn = testSourceName(s);
+ if (bn == null) return;
+ if (bn == s) {
+ //if (verbose) System.out.println(s+" == id");
+ } else {
+ if (verbose) System.out.println(s+" => "+bn+" "+toDisplayName(bn));
+ String bnbn = testSourceName(bn);
+ if (bnbn == null) return;
+ if (verbose) System.out.println(bn+" => "+bnbn+" "+toDisplayName(bnbn));
+ /*
+ String bn3 = testSourceName(bnbn);
+ if (bn3 == null) return;
+ if (verbose) System.out.println(bnbn+" => "+bn3);
+ */
+ }
+ }
+
+ String testSourceName(String s) {
+ if (map.containsKey(s)) return null;
+ String bn = toBytecodeName(s);
+ map.put(s, bn);
+ String sn = toSourceName(bn);
+ if (!sn.equals(s)) {
+ String bad = (s+" => "+bn+" != "+sn);
+ if (!verbose) throw new Error("Bad mangling: "+bad);
+ System.out.println("*** "+bad);
+ return null;
+ }
+ return bn;
+ }
+ }
+}
diff --git a/src/share/classes/sun/dyn/util/BytecodeSignature.java b/src/share/classes/sun/dyn/util/BytecodeSignature.java
new file mode 100644
index 0000000000000000000000000000000000000000..37d8708cb2938baa6057576e0916349c1fadb7ac
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/BytecodeSignature.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.util;
+
+import java.dyn.MethodType;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility routines for dealing with bytecode-level signatures.
+ * @author jrose
+ */
+public class BytecodeSignature {
+
+ private BytecodeSignature() { } // cannot instantiate
+
+ public static List> parseMethod(String bytecodeSignature, ClassLoader loader) {
+ return parseMethod(bytecodeSignature, 0, bytecodeSignature.length(), loader);
+ }
+
+ static List> parseMethod(String bytecodeSignature,
+ int start, int end, ClassLoader loader) {
+ if (loader == null)
+ loader = ClassLoader.getSystemClassLoader();
+ String str = bytecodeSignature;
+ int[] i = {start};
+ ArrayList> ptypes = new ArrayList>();
+ if (i[0] < end && str.charAt(i[0]) == '(') {
+ ++i[0]; // skip '('
+ while (i[0] < end && str.charAt(i[0]) != ')') {
+ Class> pt = parseSig(str, i, end, loader);
+ if (pt == null || pt == void.class)
+ parseError(str, "bad argument type");
+ ptypes.add(pt);
+ }
+ ++i[0]; // skip ')'
+ } else {
+ parseError(str, "not a method type");
+ }
+ Class> rtype = parseSig(str, i, end, loader);
+ if (rtype == null || i[0] != end)
+ parseError(str, "bad return type");
+ ptypes.add(rtype);
+ return ptypes;
+ }
+
+ static private void parseError(String str, String msg) {
+ throw new IllegalArgumentException("bad signature: "+str+": "+msg);
+ }
+
+ static private Class> parseSig(String str, int[] i, int end, ClassLoader loader) {
+ if (i[0] == end) return null;
+ char c = str.charAt(i[0]++);
+ if (c == 'L') {
+ int begc = i[0], endc = str.indexOf(';', begc);
+ if (endc < 0) return null;
+ i[0] = endc+1;
+ String name = str.substring(begc, endc).replace('/', '.');
+ try {
+ return loader.loadClass(name);
+ } catch (ClassNotFoundException ex) {
+ throw new TypeNotPresentException(name, ex);
+ }
+ } else if (c == '[') {
+ Class> t = parseSig(str, i, end, loader);
+ if (t != null)
+ t = java.lang.reflect.Array.newInstance(t, 0).getClass();
+ return t;
+ } else {
+ return Wrapper.forBasicType(c).primitiveType();
+ }
+ }
+
+ public static String unparse(Class> type) {
+ StringBuilder sb = new StringBuilder();
+ unparseSig(type, sb);
+ return sb.toString();
+ }
+
+ public static String unparse(MethodType type) {
+ return unparseMethod(type.returnType(), type.parameterList());
+ }
+
+ public static String unparse(Object type) {
+ if (type instanceof Class>)
+ return unparse((Class>) type);
+ if (type instanceof MethodType)
+ return unparse((MethodType) type);
+ return (String) type;
+ }
+
+ public static String unparseMethod(Class> rtype, List> ptypes) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('(');
+ for (Class> pt : ptypes)
+ unparseSig(pt, sb);
+ sb.append(')');
+ unparseSig(rtype, sb);
+ return sb.toString();
+ }
+
+ static private void unparseSig(Class> t, StringBuilder sb) {
+ char c = Wrapper.forBasicType(t).basicTypeChar();
+ if (c != 'L') {
+ sb.append(c);
+ } else {
+ boolean lsemi = (!t.isArray());
+ if (lsemi) sb.append('L');
+ sb.append(t.getName().replace('.', '/'));
+ if (lsemi) sb.append(';');
+ }
+ }
+
+}
diff --git a/src/share/classes/sun/dyn/util/ValueConversions.java b/src/share/classes/sun/dyn/util/ValueConversions.java
new file mode 100644
index 0000000000000000000000000000000000000000..817c1c5cc29b5b9c78b43125ae93683345ba16bb
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/ValueConversions.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.util;
+
+import java.dyn.*;
+import java.dyn.MethodHandles.Lookup;
+import java.util.EnumMap;
+import sun.dyn.Access;
+import sun.dyn.AdapterMethodHandle;
+import sun.dyn.MethodHandleImpl;
+
+public class ValueConversions {
+ private static final Access IMPL_TOKEN = Access.getToken();
+ private static final Lookup IMPL_LOOKUP = MethodHandleImpl.getLookup(IMPL_TOKEN);
+
+ private static EnumMap[] newWrapperCaches(int n) {
+ EnumMap[] caches
+ = (EnumMap[]) new EnumMap[n]; // unchecked warning expected here
+ for (int i = 0; i < n; i++)
+ caches[i] = new EnumMap(Wrapper.class);
+ return caches;
+ }
+
+ /// Converting references to values.
+
+ static int unboxInteger(Object x) {
+ if (x == null) return 0; // never NPE
+ return ((Integer) x).intValue();
+ }
+
+ static byte unboxByte(Object x) {
+ if (x == null) return 0; // never NPE
+ return ((Byte) x).byteValue();
+ }
+
+ static short unboxShort(Object x) {
+ if (x == null) return 0; // never NPE
+ return ((Short) x).shortValue();
+ }
+
+ static boolean unboxBoolean(Object x) {
+ if (x == null) return false; // never NPE
+ return ((Boolean) x).booleanValue();
+ }
+
+ static char unboxCharacter(Object x) {
+ if (x == null) return 0; // never NPE
+ return ((Character) x).charValue();
+ }
+
+ static long unboxLong(Object x) {
+ if (x == null) return 0; // never NPE
+ return ((Long) x).longValue();
+ }
+
+ static float unboxFloat(Object x) {
+ if (x == null) return 0; // never NPE
+ return ((Float) x).floatValue();
+ }
+
+ static double unboxDouble(Object x) {
+ if (x == null) return 0; // never NPE
+ return ((Double) x).doubleValue();
+ }
+
+ /// Converting references to "raw" values.
+ /// A raw primitive value is always an int or long.
+
+ static int unboxByteRaw(Object x) {
+ return unboxByte(x);
+ }
+
+ static int unboxShortRaw(Object x) {
+ return unboxShort(x);
+ }
+
+ static int unboxBooleanRaw(Object x) {
+ return unboxBoolean(x) ? 1 : 0;
+ }
+
+ static int unboxCharacterRaw(Object x) {
+ return unboxCharacter(x);
+ }
+
+ static int unboxFloatRaw(Object x) {
+ return Float.floatToIntBits(unboxFloat(x));
+ }
+
+ static long unboxDoubleRaw(Object x) {
+ return Double.doubleToRawLongBits(unboxDouble(x));
+ }
+
+ private static MethodType unboxType(Wrapper wrap, boolean raw) {
+ return MethodType.make(rawWrapper(wrap, raw).primitiveType(), wrap.wrapperType());
+ }
+
+ private static final EnumMap[]
+ UNBOX_CONVERSIONS = newWrapperCaches(4);
+
+ private static MethodHandle unbox(Wrapper wrap, boolean exact, boolean raw) {
+ EnumMap cache = UNBOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
+ MethodHandle mh = cache.get(wrap);
+ if (mh != null) {
+ return mh;
+ }
+ // slow path
+ switch (wrap) {
+ case OBJECT:
+ mh = IDENTITY; break;
+ case VOID:
+ mh = raw ? ALWAYS_ZERO : IGNORE; break;
+ case INT: case LONG:
+ // these guys don't need separate raw channels
+ if (raw) mh = unbox(wrap, exact, false);
+ break;
+ }
+ if (mh != null) {
+ cache.put(wrap, mh);
+ return mh;
+ }
+ // look up the method
+ String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
+ MethodType type = unboxType(wrap, raw);
+ if (!exact)
+ // actually, type is wrong; the Java method takes Object
+ mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
+ else
+ mh = retype(type, unbox(wrap, !exact, raw));
+ if (mh != null) {
+ cache.put(wrap, mh);
+ return mh;
+ }
+ throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + (raw ? " (raw)" : ""));
+ }
+
+ public static MethodHandle unbox(Wrapper type, boolean exact) {
+ return unbox(type, exact, false);
+ }
+
+ public static MethodHandle unboxRaw(Wrapper type, boolean exact) {
+ return unbox(type, exact, true);
+ }
+
+ public static MethodHandle unbox(Class> type, boolean exact) {
+ return unbox(Wrapper.forPrimitiveType(type), exact, false);
+ }
+
+ public static MethodHandle unboxRaw(Class> type, boolean exact) {
+ return unbox(Wrapper.forPrimitiveType(type), exact, true);
+ }
+
+ /// Converting primitives to references
+
+ static Integer boxInteger(int x) {
+ return x;
+ }
+
+ static Byte boxByte(byte x) {
+ return x;
+ }
+
+ static Short boxShort(short x) {
+ return x;
+ }
+
+ static Boolean boxBoolean(boolean x) {
+ return x;
+ }
+
+ static Character boxCharacter(char x) {
+ return x;
+ }
+
+ static Long boxLong(long x) {
+ return x;
+ }
+
+ static Float boxFloat(float x) {
+ return x;
+ }
+
+ static Double boxDouble(double x) {
+ return x;
+ }
+
+ /// Converting raw primitives to references
+
+ static Byte boxByteRaw(int x) {
+ return boxByte((byte)x);
+ }
+
+ static Short boxShortRaw(int x) {
+ return boxShort((short)x);
+ }
+
+ static Boolean boxBooleanRaw(int x) {
+ return boxBoolean(x != 0);
+ }
+
+ static Character boxCharacterRaw(int x) {
+ return boxCharacter((char)x);
+ }
+
+ static Float boxFloatRaw(int x) {
+ return boxFloat(Float.intBitsToFloat(x));
+ }
+
+ static Double boxDoubleRaw(long x) {
+ return boxDouble(Double.longBitsToDouble(x));
+ }
+
+ // a raw void value is (arbitrarily) a garbage int
+ static Void boxVoidRaw(int x) {
+ return null;
+ }
+
+ private static MethodType boxType(Wrapper wrap, boolean raw) {
+ // be exact, since return casts are hard to compose
+ Class> boxType = wrap.wrapperType();
+ return MethodType.make(boxType, rawWrapper(wrap, raw).primitiveType());
+ }
+
+ private static Wrapper rawWrapper(Wrapper wrap, boolean raw) {
+ if (raw) return wrap.isDoubleWord() ? Wrapper.LONG : Wrapper.INT;
+ return wrap;
+ }
+
+ private static final EnumMap[]
+ BOX_CONVERSIONS = newWrapperCaches(4);
+
+ private static MethodHandle box(Wrapper wrap, boolean exact, boolean raw) {
+ EnumMap cache = BOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
+ MethodHandle mh = cache.get(wrap);
+ if (mh != null) {
+ return mh;
+ }
+ // slow path
+ switch (wrap) {
+ case OBJECT:
+ mh = IDENTITY; break;
+ case VOID:
+ if (!raw) mh = ZERO_OBJECT;
+ break;
+ case INT: case LONG:
+ // these guys don't need separate raw channels
+ if (raw) mh = box(wrap, exact, false);
+ break;
+ }
+ if (mh != null) {
+ cache.put(wrap, mh);
+ return mh;
+ }
+ // look up the method
+ String name = "box" + wrap.simpleName() + (raw ? "Raw" : "");
+ MethodType type = boxType(wrap, raw);
+ if (exact)
+ mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
+ else
+ mh = retype(type.erase(), box(wrap, !exact, raw));
+ if (mh != null) {
+ cache.put(wrap, mh);
+ return mh;
+ }
+ throw new IllegalArgumentException("cannot find box adapter for " + wrap + (raw ? " (raw)" : ""));
+ }
+
+ public static MethodHandle box(Class> type, boolean exact) {
+ return box(Wrapper.forPrimitiveType(type), exact, false);
+ }
+
+ public static MethodHandle boxRaw(Class> type, boolean exact) {
+ return box(Wrapper.forPrimitiveType(type), exact, true);
+ }
+
+ public static MethodHandle box(Wrapper type, boolean exact) {
+ return box(type, exact, false);
+ }
+
+ public static MethodHandle boxRaw(Wrapper type, boolean exact) {
+ return box(type, exact, true);
+ }
+
+ /// Kludges for when raw values get accidentally boxed.
+
+ static Byte reboxRawByte(Object x) {
+ if (x instanceof Byte) return (Byte) x;
+ return boxByteRaw(unboxInteger(x));
+ }
+
+ static Short reboxRawShort(Object x) {
+ if (x instanceof Short) return (Short) x;
+ return boxShortRaw(unboxInteger(x));
+ }
+
+ static Boolean reboxRawBoolean(Object x) {
+ if (x instanceof Boolean) return (Boolean) x;
+ return boxBooleanRaw(unboxInteger(x));
+ }
+
+ static Character reboxRawCharacter(Object x) {
+ if (x instanceof Character) return (Character) x;
+ return boxCharacterRaw(unboxInteger(x));
+ }
+
+ static Float reboxRawFloat(Object x) {
+ if (x instanceof Float) return (Float) x;
+ return boxFloatRaw(unboxInteger(x));
+ }
+
+ static Double reboxRawDouble(Object x) {
+ if (x instanceof Double) return (Double) x;
+ return boxDoubleRaw(unboxLong(x));
+ }
+
+ private static MethodType reboxType(Wrapper wrap) {
+ Class> boxType = wrap.wrapperType();
+ return MethodType.make(boxType, Object.class);
+ }
+
+ private static final EnumMap[]
+ REBOX_CONVERSIONS = newWrapperCaches(2);
+
+ public static MethodHandle rebox(Wrapper wrap, boolean exact) {
+ EnumMap cache = REBOX_CONVERSIONS[exact?1:0];
+ MethodHandle mh = cache.get(wrap);
+ if (mh != null) {
+ return mh;
+ }
+ // slow path
+ switch (wrap) {
+ case OBJECT:
+ mh = IDENTITY; break;
+ case VOID:
+ throw new IllegalArgumentException("cannot rebox a void");
+ case INT: case LONG:
+ mh = cast(wrap.wrapperType(), exact);
+ break;
+ }
+ if (mh != null) {
+ cache.put(wrap, mh);
+ return mh;
+ }
+ // look up the method
+ String name = "reboxRaw" + wrap.simpleName();
+ MethodType type = reboxType(wrap);
+ if (exact)
+ mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
+ else
+ mh = retype(IDENTITY.type(), rebox(wrap, !exact));
+ if (mh != null) {
+ cache.put(wrap, mh);
+ return mh;
+ }
+ throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
+ }
+
+ public static MethodHandle rebox(Class> type, boolean exact) {
+ return rebox(Wrapper.forPrimitiveType(type), exact);
+ }
+
+ /// Width-changing conversions between int and long.
+
+ static long widenInt(int x) {
+ return x;
+ }
+
+ static int narrowLong(long x) {
+ return (int) x;
+ }
+
+ /// Constant functions
+
+ static void ignore(Object x) {
+ // no value to return; this is an unbox of null
+ return;
+ }
+
+ static void empty() {
+ return;
+ }
+
+ static Object zeroObject() {
+ return null;
+ }
+
+ static int zeroInteger() {
+ return 0;
+ }
+
+ static long zeroLong() {
+ return 0;
+ }
+
+ static float zeroFloat() {
+ return 0;
+ }
+
+ static double zeroDouble() {
+ return 0;
+ }
+
+ private static final EnumMap[]
+ ZERO_CONSTANT_FUNCTIONS = newWrapperCaches(1);
+
+ public static MethodHandle zeroConstantFunction(Wrapper wrap) {
+ EnumMap cache = ZERO_CONSTANT_FUNCTIONS[0];
+ MethodHandle mh = cache.get(wrap);
+ if (mh != null) {
+ return mh;
+ }
+ // slow path
+ MethodType type = MethodType.make(wrap.primitiveType());
+ switch (wrap) {
+ case VOID:
+ mh = EMPTY;
+ break;
+ case INT: case LONG: case FLOAT: case DOUBLE:
+ mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
+ break;
+ }
+ if (mh != null) {
+ cache.put(wrap, mh);
+ return mh;
+ }
+
+ // use the raw method
+ Wrapper rawWrap = wrap.rawPrimitive();
+ if (rawWrap != wrap) {
+ mh = retype(type, zeroConstantFunction(rawWrap));
+ }
+ if (mh != null) {
+ cache.put(wrap, mh);
+ return mh;
+ }
+ throw new IllegalArgumentException("cannot find zero constant for " + wrap);
+ }
+
+ /// Converting references to references.
+
+ /**
+ * Value-killing function.
+ * @param x an arbitrary reference value
+ * @return a null
+ */
+ static Object alwaysNull(Object x) {
+ return null;
+ }
+
+ /**
+ * Value-killing function.
+ * @param x an arbitrary reference value
+ * @return a zero
+ */
+ static int alwaysZero(Object x) {
+ return 0;
+ }
+
+ /**
+ * Identity function.
+ * @param x an arbitrary reference value
+ * @return the same value x
+ */
+ static T identity(T x) {
+ return x;
+ }
+
+ /**
+ * Identity function, with reference cast.
+ * @param t an arbitrary reference type
+ * @param x an arbitrary reference value
+ * @return the same value x
+ */
+ static T castReference(Class extends T> t, U x) {
+ return t.cast(x);
+ }
+
+ private static final MethodHandle IDENTITY, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY;
+ static {
+ try {
+ MethodType idType = MethodType.makeGeneric(1);
+ MethodType castType = idType.insertParameterType(0, Class.class);
+ MethodType alwaysZeroType = idType.changeReturnType(int.class);
+ MethodType ignoreType = idType.changeReturnType(void.class);
+ MethodType zeroObjectType = MethodType.makeGeneric(0);
+ IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType);
+ //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
+ CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType);
+ ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType);
+ ALWAYS_ZERO = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysZero", alwaysZeroType);
+ ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType);
+ IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType);
+ EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterType(0));
+ } catch (RuntimeException ex) {
+ throw ex;
+ }
+ }
+
+ private static final EnumMap WRAPPER_CASTS
+ = new EnumMap(Wrapper.class);
+
+ private static final EnumMap EXACT_WRAPPER_CASTS
+ = new EnumMap(Wrapper.class);
+
+ /** Return a method that casts its sole argument (an Object) to the given type
+ * and returns it as the given type (if exact is true), or as plain Object (if erase is true).
+ */
+ public static MethodHandle cast(Class> type, boolean exact) {
+ if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type);
+ MethodHandle mh = null;
+ Wrapper wrap = null;
+ EnumMap cache = null;
+ if (Wrapper.isWrapperType(type)) {
+ wrap = Wrapper.forWrapperType(type);
+ cache = (exact ? EXACT_WRAPPER_CASTS : WRAPPER_CASTS);
+ mh = cache.get(wrap);
+ if (mh != null) return mh;
+ }
+ if (VerifyType.isNullReferenceConversion(Object.class, type))
+ mh = IDENTITY;
+ else if (VerifyType.isNullType(type))
+ mh = ALWAYS_NULL;
+ else
+ mh = MethodHandles.insertArgument(CAST_REFERENCE, 0, type);
+ if (exact) {
+ MethodType xmt = MethodType.make(type, Object.class);
+ mh = AdapterMethodHandle.makeRawRetypeOnly(IMPL_TOKEN, xmt, mh);
+ }
+ if (cache != null)
+ cache.put(wrap, mh);
+ return mh;
+ }
+
+ public static MethodHandle identity() {
+ return IDENTITY;
+ }
+
+ private static MethodHandle retype(MethodType type, MethodHandle mh) {
+ return AdapterMethodHandle.makeRetypeOnly(IMPL_TOKEN, type, mh);
+ }
+}
diff --git a/src/share/classes/sun/dyn/util/VerifyAccess.java b/src/share/classes/sun/dyn/util/VerifyAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..25c997a98b38ca1a46cbf83fabcae503dc468495
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/VerifyAccess.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.util;
+
+import java.dyn.LinkagePermission;
+import java.lang.reflect.Modifier;
+import sun.dyn.Access;
+
+/**
+ * This class centralizes information about the JVM's linkage access control.
+ * @author jrose
+ */
+public class VerifyAccess {
+
+ private VerifyAccess() { } // cannot instantiate
+
+ /**
+ * Evaluate the JVM linkage rules for access to the given method on behalf of caller.
+ * Return non-null if and only if the given accessing class has at least partial
+ * privileges to invoke the given method. The return value {@code Object.class}
+ * denotes unlimited privileges.
+ *
+ * Some circumstances require an additional check on the
+ * leading parameter (the receiver) of the method, if it is non-static.
+ * In the case of {@code invokespecial} ({@code doDispatch} is false),
+ * the leading parameter must be the accessing class or a subclass.
+ * In the case of a call to a {@code protected} method outside the same
+ * package, the same constraint applies.
+ * @param m the proposed callee
+ * @param doDispatch if false, a non-static m will be invoked as if by {@code invokespecial}
+ * @param lookupClass the class for which the access check is being made
+ * @return null if the method is not accessible, else a receiver type constraint, else {@code Object.class}
+ */
+ public static Class> isAccessible(Class> defc, int mods,
+ boolean doDispatch, Class> lookupClass) {
+ if (!isAccessible(defc, lookupClass))
+ return null;
+ Class> constraint = Object.class;
+ if (!doDispatch && !Modifier.isStatic(mods)) {
+ constraint = lookupClass;
+ }
+ if (Modifier.isPublic(mods))
+ return constraint;
+ if (Modifier.isPrivate(mods))
+ return isSamePackageMember(defc, lookupClass) ? constraint : null;
+ if (isSamePackage(defc, lookupClass))
+ return constraint;
+ if (Modifier.isProtected(mods) && defc.isAssignableFrom(lookupClass))
+ return constraint;
+ // else it is private or package scoped, and not close enough
+ return null;
+ }
+
+ /**
+ * Evaluate the JVM linkage rules for access to the given class on behalf of caller.
+ */
+ public static boolean isAccessible(Class> refc, Class> lookupClass) {
+ int mods = refc.getModifiers();
+ if (Modifier.isPublic(mods))
+ return true;
+ if (isSamePackage(lookupClass, refc))
+ return true;
+ return false;
+ }
+
+ /**
+ * Test if two classes have the same class loader and package qualifier.
+ * @param class1
+ * @param class2
+ * @return whether they are in the same package
+ */
+ public static boolean isSamePackage(Class> class1, Class> class2) {
+ if (class1 == class2)
+ return true;
+ if (loadersAreRelated(class1.getClassLoader(), class2.getClassLoader()))
+ return false;
+ String name1 = class1.getName(), name2 = class2.getName();
+ int dot = name1.lastIndexOf('.');
+ if (dot != name2.lastIndexOf('.'))
+ return false;
+ for (int i = 0; i < dot; i++) {
+ if (name1.charAt(i) != name2.charAt(i))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Test if two classes are defined as part of the same package member (top-level class).
+ * If this is true, they can share private access with each other.
+ * @param class1
+ * @param class2
+ * @return whether they are identical or nested together
+ */
+ public static boolean isSamePackageMember(Class> class1, Class> class2) {
+ if (class1 == class2)
+ return true;
+ if (!isSamePackage(class1, class2))
+ return false;
+ if (getOutermostEnclosingClass(class1) != getOutermostEnclosingClass(class2))
+ return false;
+ return true;
+ }
+
+ private static Class> getOutermostEnclosingClass(Class> c) {
+ Class> pkgmem = c;
+ for (Class> enc = c; (enc = enc.getEnclosingClass()) != null; )
+ pkgmem = enc;
+ return pkgmem;
+ }
+
+ private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2) {
+ if (loader1 == loader2 || loader1 == null || loader2 == null) {
+ return true;
+ }
+ for (ClassLoader scan1 = loader1;
+ scan1 != null; scan1 = scan1.getParent()) {
+ if (scan1 == loader2) return true;
+ }
+ for (ClassLoader scan2 = loader2;
+ scan2 != null; scan2 = scan2.getParent()) {
+ if (scan2 == loader1) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Ensure the requesting class have privileges to perform invokedynamic
+ * linkage operations on subjectClass. True if requestingClass is
+ * Access.class (meaning the request originates from the JVM) or if the
+ * classes are in the same package and have consistent class loaders.
+ * (The subject class loader must be identical with or be a child of
+ * the requesting class loader.)
+ * @param requestingClass
+ * @param subjectClass
+ */
+ public static void checkBootstrapPrivilege(Class requestingClass, Class subjectClass,
+ String permissionName) {
+ if (requestingClass == Access.class) return;
+ if (requestingClass == subjectClass) return;
+ SecurityManager security = System.getSecurityManager();
+ if (security == null) return; // open season
+ if (isSamePackage(requestingClass, subjectClass)) return;
+ security.checkPermission(new LinkagePermission(permissionName, requestingClass));
+ }
+}
diff --git a/src/share/classes/sun/dyn/util/VerifyType.java b/src/share/classes/sun/dyn/util/VerifyType.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d12c74afa9b69720f554fc7d94063d0779b55ed
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/VerifyType.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.util;
+
+import java.dyn.MethodType;
+
+/**
+ * This class centralizes information about the JVM verifier
+ * and its requirements about type correctness.
+ * @author jrose
+ */
+public class VerifyType {
+
+ private VerifyType() { } // cannot instantiate
+
+ /**
+ * True if a value can be stacked as the source type and unstacked as the
+ * destination type, without violating the JVM's type consistency.
+ *
+ * @param call the type of a stacked value
+ * @param recv the type by which we'd like to treat it
+ * @return whether the retyping can be done without motion or reformatting
+ */
+ public static boolean isNullConversion(Class> src, Class> dst) {
+ if (src == dst) return true;
+ // Verifier allows any interface to be treated as Object:
+ if (dst.isInterface()) dst = Object.class;
+ if (src.isInterface()) src = Object.class;
+ if (src == dst) return true; // check again
+ if (dst == void.class) return true; // drop any return value
+ if (isNullType(src)) return !dst.isPrimitive();
+ if (!src.isPrimitive()) return dst.isAssignableFrom(src);
+ // Verifier allows an int to carry byte, short, char, or even boolean:
+ if (dst == int.class) return Wrapper.forPrimitiveType(src).isSubwordOrInt();
+ return false;
+ }
+
+ /**
+ * Specialization of isNullConversion to reference types.
+
+ * @param call the type of a stacked value
+ * @param recv the reference type by which we'd like to treat it
+ * @return whether the retyping can be done without a cast
+ */
+ public static boolean isNullReferenceConversion(Class> src, Class> dst) {
+ assert(!dst.isPrimitive());
+ if (dst.isInterface()) return true; // verifier allows this
+ if (isNullType(src)) return true;
+ return dst.isAssignableFrom(src);
+ }
+
+ /**
+ * Is the given type either java.lang.Void or java.lang.Null?
+ * These types serve as markers for bare nulls and therefore
+ * may be promoted to any type. This is secure, since
+ */
+ public static boolean isNullType(Class> type) {
+ if (type == null) return false;
+ return type == NULL_CLASS_1 || type == NULL_CLASS_2;
+ }
+ private static final Class> NULL_CLASS_1, NULL_CLASS_2;
+ static {
+ Class> nullClass1 = null, nullClass2 = null;
+ try {
+ nullClass1 = Class.forName("java.lang.Null");
+ } catch (ClassNotFoundException ex) {
+ // OK, we'll cope
+ }
+ NULL_CLASS_1 = nullClass1;
+
+ // This one may also be used as a null type.
+ // TO DO: Decide if we really want to legitimize it here.
+ // Probably we do, unless java.lang.Null really makes it into Java 7
+ nullClass2 = Void.class;
+ NULL_CLASS_2 = nullClass2;
+ }
+
+ /**
+ * True if a method handle can receive a call under a slightly different
+ * method type, without moving or reformatting any stack elements.
+ *
+ * @param call the type of call being made
+ * @param recv the type of the method handle receiving the call
+ * @return whether the retyping can be done without motion or reformatting
+ */
+ public static boolean isNullConversion(MethodType call, MethodType recv) {
+ if (call == recv) return true;
+ int len = call.parameterCount();
+ if (len != recv.parameterCount()) return false;
+ for (int i = 0; i < len; i++)
+ if (!isNullConversion(call.parameterType(i), recv.parameterType(i)))
+ return false;
+ return isNullConversion(recv.returnType(), call.returnType());
+ }
+
+ //TO DO: isRawConversion
+
+ /**
+ * Determine if the JVM verifier allows a value of type call to be
+ * passed to a formal parameter (or return variable) of type recv.
+ * Returns 1 if the verifier allows the types to match without conversion.
+ * Returns -1 if the types can be made to match by a JVM-supported adapter.
+ * Cases supported are:
+ *
checkcast
+ * conversion between any two integral types (but not floats)
+ * unboxing from a wrapper to its corresponding primitive type
+ * conversion in either direction between float and double
+ *
+ * (Autoboxing is not supported here; it must be done via Java code.)
+ * Returns 0 otherwise.
+ */
+ public static int canPassUnchecked(Class> src, Class> dst) {
+ if (src == dst)
+ return 1;
+
+ if (dst.isPrimitive()) {
+ if (dst == void.class)
+ // Return anything to a caller expecting void.
+ // This is a property of the implementation, which links
+ // return values via a register rather than via a stack push.
+ // This makes it possible to ignore cleanly.
+ return 1;
+ if (src == void.class)
+ return 0; // void-to-something?
+ if (!src.isPrimitive())
+ // Cannot pass a reference to any primitive type (exc. void).
+ return 0;
+ Wrapper sw = Wrapper.forPrimitiveType(src);
+ Wrapper dw = Wrapper.forPrimitiveType(dst);
+ if (sw.isSubwordOrInt() && dw.isSubwordOrInt()) {
+ if (sw.bitWidth() >= dw.bitWidth())
+ return -1; // truncation may be required
+ if (!dw.isSigned() && sw.isSigned())
+ return -1; // sign elimination may be required
+ }
+ if (src == float.class || dst == float.class) {
+ if (src == double.class || dst == double.class)
+ return -1; // floating conversion may be required
+ else
+ return 0; // other primitive conversions NYI
+ } else {
+ // all fixed-point conversions are supported
+ return 0;
+ }
+ } else if (src.isPrimitive()) {
+ // Cannot pass a primitive to any reference type.
+ // (Maybe allow null.class?)
+ return 0;
+ }
+
+ // Handle reference types in the rest of the block:
+
+ // The verifier treats interfaces exactly like Object.
+ if (isNullReferenceConversion(src, dst))
+ // pass any reference to object or an arb. interface
+ return 1;
+ // else it's a definite "maybe" (cast is required)
+ return -1;
+ }
+
+ public static int canPassRaw(Class> src, Class> dst) {
+ if (dst.isPrimitive()) {
+ if (dst == void.class)
+ // As above, return anything to a caller expecting void.
+ return 1;
+ if (src == void.class)
+ // Special permission for raw conversions: allow a void
+ // to be captured as a garbage int.
+ // Caller promises that the actual value will be disregarded.
+ return dst == int.class ? 1 : 0;
+ if (!src.isPrimitive())
+ return 0;
+ Wrapper sw = Wrapper.forPrimitiveType(src);
+ Wrapper dw = Wrapper.forPrimitiveType(dst);
+ if (sw.stackSlots() == dw.stackSlots())
+ return 1; // can do a reinterpret-cast on a stacked primitive
+ if (sw.isSubwordOrInt() && dw == Wrapper.VOID)
+ return 1; // can drop an outgoing int value
+ return 0;
+ } else if (src.isPrimitive()) {
+ return 0;
+ }
+
+ // Both references.
+ if (isNullReferenceConversion(src, dst))
+ return 1;
+ return -1;
+ }
+
+ public static boolean isSpreadArgType(Class> spreadArg) {
+ return spreadArg.isArray();
+ }
+ public static Class> spreadArgElementType(Class> spreadArg, int i) {
+ return spreadArg.getComponentType();
+ }
+}
diff --git a/src/share/classes/sun/dyn/util/Wrapper.java b/src/share/classes/sun/dyn/util/Wrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..556fda3bc7b928bd7ccfb7f401e29c673e36f211
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/Wrapper.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.util;
+
+public enum Wrapper {
+ INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)),
+ LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)),
+ BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)),
+ SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)),
+ CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)),
+ BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)),
+ FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)),
+ DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)),
+ VOID(Void.class, void.class, 'V', null, Format.other(0)),
+ //NULL(Null.class, null.class, 'N', null, Format.other(1)),
+ OBJECT(Object.class, Object.class, 'L', null, Format.other(1)),
+ ;
+
+ private final Class> wrapperType;
+ private final Class> primitiveType;
+ private final char basicTypeChar;
+ private final Object zero;
+ private final int format;
+ private final String simpleName;
+
+ private Wrapper(Class> wtype, Class> ptype, char tchar, Object zero, int format) {
+ this.wrapperType = wtype;
+ this.primitiveType = ptype;
+ this.basicTypeChar = tchar;
+ this.zero = zero;
+ this.format = format;
+ this.simpleName = wtype.getSimpleName();
+ }
+
+ private static abstract class Format {
+ static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12;
+ static final int
+ SIGNED = (-1) << KIND_SHIFT,
+ UNSIGNED = 0 << KIND_SHIFT,
+ FLOATING = 1 << KIND_SHIFT;
+ static final int
+ SLOT_MASK = ((1<<(SIZE_SHIFT-SLOT_SHIFT))-1),
+ SIZE_MASK = ((1<<(KIND_SHIFT-SIZE_SHIFT))-1);
+ static int format(int kind, int size, int slots) {
+ assert(((kind >> KIND_SHIFT) << KIND_SHIFT) == kind);
+ assert((size & (size-1)) == 0); // power of two
+ assert((kind == SIGNED) ? (size > 0) :
+ (kind == UNSIGNED) ? (size > 0) :
+ (kind == FLOATING) ? (size == 32 || size == 64) :
+ false);
+ assert((slots == 2) ? (size == 64) :
+ (slots == 1) ? (size <= 32) :
+ false);
+ return kind | (size << SIZE_SHIFT) | (slots << SLOT_SHIFT);
+ }
+ static int
+ INT = SIGNED | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
+ BOOLEAN = UNSIGNED | (1 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
+ FLOAT = FLOATING | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
+ VOID = UNSIGNED | (0 << SIZE_SHIFT) | (0 << SLOT_SHIFT),
+ NUM_MASK = (-1) << SIZE_SHIFT;
+ static int signed(int size) { return format(SIGNED, size, (size > 32 ? 2 : 1)); }
+ static int unsigned(int size) { return format(UNSIGNED, size, (size > 32 ? 2 : 1)); }
+ static int floating(int size) { return format(FLOATING, size, (size > 32 ? 2 : 1)); }
+ static int other(int slots) { return slots << SLOT_SHIFT; }
+ }
+
+ /// format queries:
+
+ /** How many bits are in the wrapped value? Returns 0 for OBJECT or VOID. */
+ public int bitWidth() { return (format >> Format.SIZE_SHIFT) & Format.SIZE_MASK; }
+ /** How many JVM stack slots occupied by the wrapped value? Returns 0 for VOID. */
+ public int stackSlots() { return (format >> Format.SLOT_SHIFT) & Format.SLOT_MASK; }
+ /** Does the wrapped value occupy a single JVM stack slot? */
+ public boolean isSingleWord() { return (format & (1 << Format.SLOT_SHIFT)) != 0; }
+ /** Does the wrapped value occupy two JVM stack slots? */
+ public boolean isDoubleWord() { return (format & (2 << Format.SLOT_SHIFT)) != 0; }
+ /** Is the wrapped type numeric (not void or object)? */
+ public boolean isNumeric() { return (format & Format.NUM_MASK) != 0; }
+ /** Is the wrapped type a primitive other than float, double, or void? */
+ public boolean isIntegral() { return isNumeric() && format < Format.FLOAT; }
+ /** Is the wrapped type one of int, boolean, byte, char, or short? */
+ public boolean isSubwordOrInt() { return isIntegral() && isSingleWord(); }
+ /* Is the wrapped value a signed integral type (one of byte, short, int, or long)? */
+ public boolean isSigned() { return format < Format.VOID; }
+ /* Is the wrapped value an unsigned integral type (one of boolean or char)? */
+ public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; }
+ /** Is the wrapped type either float or double? */
+ public boolean isFloating() { return format >= Format.FLOAT; }
+
+ /** Produce a zero value for the given wrapper type.
+ * This will be a numeric zero for a number or character,
+ * false for a boolean, and null for a reference or void.
+ * The common thread is that this is what is contained
+ * in a default-initialized variable of the given primitive
+ * type. (For void, it is what a reflective method returns
+ * instead of no value at all.)
+ */
+ public Object zero() { return zero; }
+
+ /** Produce a zero value for the given wrapper type T.
+ * The optinoal argument must a type compatible with this wrapper.
+ * Equivalent to {@code this.cast(this.zero(), type)}.
+ */
+ public T zero(Class type) { return cast(zero, type); }
+
+// /** Produce a wrapper for the given wrapper or primitive type. */
+// public static Wrapper valueOf(Class> type) {
+// if (isPrimitiveType(type))
+// return forPrimitiveType(type);
+// else
+// return forWrapperType(type);
+// }
+
+ /** Return the wrapper that wraps values of the given type.
+ * The type may be {@code Object}, meaning the {@code OBJECT} wrapper.
+ * Otherwise, the type must be a primitive.
+ * @throws IllegalArgumentException for unexpected types
+ */
+ public static Wrapper forPrimitiveType(Class> type) {
+ Wrapper w = FROM_PRIM[hashPrim(type)];
+ if (w != null && w.primitiveType == type) {
+ return w;
+ }
+ if (type.isPrimitive())
+ throw new InternalError(); // redo hash function
+ throw newIllegalArgumentException("not primitive: "+type);
+ }
+
+ /** Return the wrapper that wraps values into the given wrapper type.
+ * If it is {@code Object} or an interface, return {@code OBJECT}.
+ * Otherwise, it must be a wrapper type.
+ * The type must not be a primitive type.
+ * @throws IllegalArgumentException for unexpected types
+ */
+ public static Wrapper forWrapperType(Class> type) {
+ Wrapper w = findWrapperType(type);
+ if (w != null) return w;
+ for (Wrapper x : values())
+ if (w.wrapperType == type)
+ throw new InternalError(); // redo hash function
+ throw newIllegalArgumentException("not wrapper: "+type);
+ }
+
+ static Wrapper findWrapperType(Class> type) {
+ Wrapper w = FROM_WRAP[hashWrap(type)];
+ if (w != null && w.wrapperType == type) {
+ return w;
+ }
+ if (type.isInterface())
+ return OBJECT;
+ return null;
+ }
+
+ /** Return the wrapper that corresponds to the given bytecode
+ * signature character. Return {@code OBJECT} for the character 'L'.
+ * @throws IllegalArgumentException for any non-signature character or {@code '['}.
+ */
+ public static Wrapper forBasicType(char type) {
+ Wrapper w = FROM_CHAR[hashChar(type)];
+ if (w != null && w.basicTypeChar == type) {
+ return w;
+ }
+ for (Wrapper x : values())
+ if (w.basicTypeChar == type)
+ throw new InternalError(); // redo hash function
+ throw newIllegalArgumentException("not basic type char: "+type);
+ }
+
+ /** Return the wrapper for the given type, if it is
+ * a primitive type, else return {@code OBJECT}.
+ */
+ public static Wrapper forBasicType(Class> type) {
+ if (type.isPrimitive())
+ return forPrimitiveType(type);
+ return OBJECT; // any reference, including wrappers or arrays
+ }
+
+ // Note on perfect hashes:
+ // for signature chars c, do (c + (c >> 1)) % 16
+ // for primitive type names n, do (n[0] + n[2]) % 16
+ // The type name hash works for both primitive and wrapper names.
+ // You can add "java/lang/Object" to the primitive names.
+ // But you add the wrapper name Object, use (n[2] + (3*n[1])) % 16.
+ private static final Wrapper[] FROM_PRIM = new Wrapper[16];
+ private static final Wrapper[] FROM_WRAP = new Wrapper[16];
+ private static final Wrapper[] FROM_CHAR = new Wrapper[16];
+ private static int hashPrim(Class> x) {
+ String xn = x.getName();
+ if (xn.length() < 3) return 0;
+ return (xn.charAt(0) + xn.charAt(2)) % 16;
+ }
+ private static int hashWrap(Class> x) {
+ String xn = x.getName();
+ final int offset = 10; assert(offset == "java.lang.".length());
+ if (xn.length() < offset+3) return 0;
+ return (3*xn.charAt(offset+1) + xn.charAt(offset+2)) % 16;
+ }
+ private static int hashChar(char x) {
+ return (x + (x >> 1)) % 16;
+ }
+ static {
+ for (Wrapper w : values()) {
+ int pi = hashPrim(w.primitiveType);
+ int wi = hashWrap(w.wrapperType);
+ int ci = hashChar(w.basicTypeChar);
+ assert(FROM_PRIM[pi] == null);
+ assert(FROM_WRAP[wi] == null);
+ assert(FROM_CHAR[ci] == null);
+ FROM_PRIM[pi] = w;
+ FROM_WRAP[wi] = w;
+ FROM_CHAR[ci] = w;
+ }
+ //assert(jdk.sun.dyn.util.WrapperTest.test(false));
+ }
+
+ /** What is the primitive type wrapped by this wrapper? */
+ public Class> primitiveType() { return primitiveType; }
+
+ /** What is the wrapper type for this wrapper? */
+ public Class> wrapperType() { return wrapperType; }
+
+ /** What is the wrapper type for this wrapper?
+ * The example type must be the wrapper type,
+ * or the corresponding primitive type.
+ * The resulting class type has the same type parameter.
+ */
+ public Class wrapperType(Class exampleType) {
+ if (exampleType == wrapperType) {
+ return exampleType;
+ } else if (exampleType == primitiveType ||
+ wrapperType == Object.class ||
+ exampleType.isInterface()) {
+ return forceType(wrapperType, exampleType);
+ }
+ throw new ClassCastException(exampleType + " not <:" + wrapperType);
+ }
+
+ /** If {@code type} is a primitive type, return the corresponding
+ * wrapper type, else return {@code type} unchanged.
+ */
+ public static Class asWrapperType(Class type) {
+ if (type.isPrimitive()) {
+ return forPrimitiveType(type).wrapperType(type);
+ }
+ return type;
+ }
+
+ /** If {@code type} is a wrapper type, return the corresponding
+ * primitive type, else return {@code type} unchanged.
+ */
+ public static Class asPrimitiveType(Class type) {
+ Wrapper w = findWrapperType(type);
+ if (w != null) {
+ return forceType(w.primitiveType(), type);
+ }
+ return type;
+ }
+
+ /** Query: Is the given type a wrapper, such as {@code Integer} or {@code Void}? */
+ public static boolean isWrapperType(Class> type) {
+ return findWrapperType(type) != null;
+ }
+
+ /** Query: Is the given type a primitive, such as {@code int} or {@code void}? */
+ public static boolean isPrimitiveType(Class> type) {
+ return type.isPrimitive();
+ }
+
+ /** What is the bytecode signature character for this wrapper's
+ * primitive type?
+ */
+ public char basicTypeChar() { return basicTypeChar; }
+
+ /** What is the simple name of the wrapper type?
+ */
+ public String simpleName() { return simpleName; }
+
+// /** Wrap a value in the given type, which may be either a primitive or wrapper type.
+// * Performs standard primitive conversions, including truncation and float conversions.
+// */
+// public static T wrap(Object x, Class type) {
+// return Wrapper.valueOf(type).cast(x, type);
+// }
+
+ /** Cast a wrapped value to the given type, which may be either a primitive or wrapper type.
+ * Performs standard primitive conversions, including truncation and float conversions.
+ * The given type must be compatible with this wrapper. That is, it must either
+ * be the wrapper type (or a subtype, in the case of {@code OBJECT} or else
+ * it must be the wrapper's primitive type.
+ * @throws ClassCastException if the given type is not compatible with this wrapper
+ */
+ public T cast(Object x, Class type) {
+ Class wtype = wrapperType(type);
+ if (wtype.isInstance(x))
+ return wtype.cast(x);
+ return wtype.cast(wrap(x));
+ }
+
+ /** Cast a reference type to another reference type.
+ * If the target type is an interface, perform no runtime check.
+ * (This loophole is safe, and is allowed by the JVM verifier.)
+ * If the target type is a primitive, change it to a wrapper.
+ */
+ static Class forceType(Class> type, Class exampleType) {
+ assert(type == exampleType ||
+ type == asWrapperType(exampleType) ||
+ type == Object.class && exampleType.isInterface());
+ Class result = (Class) type; // unchecked warning is expected here
+ return result;
+ }
+
+ /** Wrap a value in this wrapper's type.
+ * Performs standard primitive conversions, including truncation and float conversions.
+ * Performs returns the unchanged reference for {@code OBJECT}.
+ * Returns null for {@code VOID}.
+ * Returns a zero value for a null input.
+ * @throws ClassCastException if this wrapper is numeric and the operand
+ * is not a number, character, boolean, or null
+ */
+ public Object wrap(Object x) {
+ // do non-numeric wrappers first
+ switch (basicTypeChar) {
+ case 'L': return x;
+ case 'V': return null;
+ }
+ Number xn = numberValue(x);
+ switch (basicTypeChar) {
+ case 'I': return Integer.valueOf(xn.intValue());
+ case 'J': return Long.valueOf(xn.longValue());
+ case 'F': return Float.valueOf(xn.floatValue());
+ case 'D': return Double.valueOf(xn.doubleValue());
+ case 'S': return Short.valueOf((short) xn.intValue());
+ case 'B': return Byte.valueOf((byte) xn.intValue());
+ case 'C': return Character.valueOf((char) xn.intValue());
+ case 'Z': return Boolean.valueOf(boolValue(xn.longValue()));
+ }
+ throw new InternalError("bad wrapper");
+ }
+
+ /** Wrap a value (an int or smaller value) in this wrapper's type.
+ * Performs standard primitive conversions, including truncation and float conversions.
+ * Produces an {@code Integer} for {@code OBJECT}, although the exact type
+ * of the operand is not known.
+ * Returns null for {@code VOID}.
+ */
+ public Object wrap(int x) {
+ if (basicTypeChar == 'L') return (Integer)x;
+ switch (basicTypeChar) {
+ case 'L': throw newIllegalArgumentException("cannot wrap to object type");
+ case 'V': return null;
+ case 'I': return Integer.valueOf((int)x);
+ case 'J': return Long.valueOf(x);
+ case 'F': return Float.valueOf(x);
+ case 'D': return Double.valueOf(x);
+ case 'S': return Short.valueOf((short) x);
+ case 'B': return Byte.valueOf((byte) x);
+ case 'C': return Character.valueOf((char) x);
+ case 'Z': return Boolean.valueOf(boolValue(x));
+ }
+ throw new InternalError("bad wrapper");
+ }
+
+ /** Wrap a value (a long or smaller value) in this wrapper's type.
+ * Does not perform floating point conversion.
+ * Produces a {@code Long} for {@code OBJECT}, although the exact type
+ * of the operand is not known.
+ * Returns null for {@code VOID}.
+ */
+ public Object wrapRaw(long x) {
+ switch (basicTypeChar) {
+ case 'F': return Float.valueOf(Float.intBitsToFloat((int)x));
+ case 'D': return Double.valueOf(Double.longBitsToDouble(x));
+ case 'L': // same as 'J':
+ case 'J': return (Long) x;
+ }
+ // Other wrapping operations are just the same, given that the
+ // operand is already promoted to an int.
+ return wrap((int)x);
+ }
+
+ /** Produce bitwise value which encodes the given wrapped value.
+ * Does not perform floating point conversion.
+ * Returns zero for {@code VOID}.
+ */
+ public long unwrapRaw(Object x) {
+ switch (basicTypeChar) {
+ case 'F': return Float.floatToRawIntBits((Float) x);
+ case 'D': return Double.doubleToRawLongBits((Double) x);
+
+ case 'L': throw newIllegalArgumentException("cannot unwrap from sobject type");
+ case 'V': return 0;
+ case 'I': return (int)(Integer) x;
+ case 'J': return (long)(Long) x;
+ case 'S': return (short)(Short) x;
+ case 'B': return (byte)(Byte) x;
+ case 'C': return (char)(Character) x;
+ case 'Z': return (boolean)(Boolean) x ? 1 : 0;
+ }
+ throw new InternalError("bad wrapper");
+ }
+
+ /** Report what primitive type holds this guy's raw value. */
+ public Class> rawPrimitiveType() {
+ return rawPrimitive().primitiveType();
+ }
+
+ /** Report, as a wrapper, what primitive type holds this guy's raw value.
+ * Returns self for INT, LONG, OBJECT; returns LONG for DOUBLE,
+ * else returns INT.
+ */
+ public Wrapper rawPrimitive() {
+ switch (basicTypeChar) {
+ case 'S': case 'B':
+ case 'C': case 'Z':
+ case 'V':
+ case 'F':
+ return INT;
+ case 'D':
+ return LONG;
+ }
+ return this;
+ }
+
+ private static Number numberValue(Object x) {
+ if (x instanceof Number) return (Number)x;
+ if (x instanceof Character) return (int)(Character)x;
+ if (x instanceof Boolean) return (Boolean)x ? 1 : 0;
+ // Remaining allowed case of void: Must be a null reference.
+ return (Number)x;
+ }
+
+ private static boolean boolValue(long bits) {
+ //bits &= 1; // simple 31-bit zero extension
+ return (bits != 0);
+ }
+
+ private static RuntimeException newIllegalArgumentException(String message, Object x) {
+ return newIllegalArgumentException(message + x);
+ }
+ private static RuntimeException newIllegalArgumentException(String message) {
+ return new IllegalArgumentException(message);
+ }
+}
diff --git a/src/share/classes/sun/dyn/util/package-info.java b/src/share/classes/sun/dyn/util/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..257d7df1c26c72af025b34d95fb24ba91dd44c5c
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * Extra support for using JSR 292 RI, package java.dyn.
+ * @author jrose
+ */
+
+package sun.dyn.util;
diff --git a/src/share/classes/sun/misc/Unsafe.java b/src/share/classes/sun/misc/Unsafe.java
index 3a3fde82de06769925c5897db02a4dab84a0e7b6..c81c1eb8737089eae3b1077326fa50895dce1599 100644
--- a/src/share/classes/sun/misc/Unsafe.java
+++ b/src/share/classes/sun/misc/Unsafe.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -811,6 +811,25 @@ public final class Unsafe {
public native Class defineClass(String name, byte[] b, int off, int len);
+ /**
+ * Define a class but do not make it known to the class loader or system dictionary.
+ *
+ * For each CP entry, the corresponding CP patch must either be null or have
+ * the a format that matches its tag:
+ *
+ * Integer, Long, Float, Double: the corresponding wrapper object type from java.lang
+ * Utf8: a string (must have suitable syntax if used as signature or name)
+ * Class: any java.lang.Class object
+ * String: any object (not just a java.lang.String)
+ * InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments
+ *
+ * @params hostClass context for linkage, access control, protection domain, and class loader
+ * @params data bytes of a class file
+ * @params cpPatches where non-null entries exist, they replace corresponding CP entries in data
+ */
+ public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches);
+
+
/** Allocate an instance but do not run any constructor.
Initializes the class if it has not yet been. */
public native Object allocateInstance(Class cls)
diff --git a/src/share/javavm/export/classfile_constants.h b/src/share/javavm/export/classfile_constants.h
index 8906ba5f3908f9365f7e7ab10fdb21283c4599f8..d225fa4ce7e72c85019ed525f42cf23dbeec9736 100644
--- a/src/share/javavm/export/classfile_constants.h
+++ b/src/share/javavm/export/classfile_constants.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Sun Microsystems, Inc. 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
@@ -306,7 +306,7 @@ enum {
JVM_OPC_invokespecial = 183,
JVM_OPC_invokestatic = 184,
JVM_OPC_invokeinterface = 185,
- JVM_OPC_xxxunusedxxx = 186,
+ JVM_OPC_invokedynamic = 186,
JVM_OPC_new = 187,
JVM_OPC_newarray = 188,
JVM_OPC_anewarray = 189,
@@ -515,7 +515,7 @@ enum {
3, /* invokespecial */ \
3, /* invokestatic */ \
5, /* invokeinterface */ \
- 0, /* xxxunusedxxx */ \
+ 5, /* invokedynamic */ \
3, /* new */ \
2, /* newarray */ \
3, /* anewarray */ \
diff --git a/src/share/native/common/check_code.c b/src/share/native/common/check_code.c
index 7e1613255dcf48924fd92b93447469ebcf049644..f76c8f81735271044f5a0a8287ff7096468a5bad 100644
--- a/src/share/native/common/check_code.c
+++ b/src/share/native/common/check_code.c
@@ -1,5 +1,5 @@
/*
- * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1994-2009 Sun Microsystems, Inc. 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
@@ -1223,16 +1223,20 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset)
case JVM_OPC_invokevirtual:
case JVM_OPC_invokespecial:
case JVM_OPC_invokestatic:
+ case JVM_OPC_invokedynamic:
case JVM_OPC_invokeinterface: {
/* Make sure the constant pool item is the right type. */
int key = (code[offset + 1] << 8) + code[offset + 2];
const char *methodname;
jclass cb = context->class;
fullinfo_type clazz_info;
- int is_constructor, is_internal;
+ int is_constructor, is_internal, is_invokedynamic;
int kind = (opcode == JVM_OPC_invokeinterface
? 1 << JVM_CONSTANT_InterfaceMethodref
+ : opcode == JVM_OPC_invokedynamic
+ ? 1 << JVM_CONSTANT_NameAndType
: 1 << JVM_CONSTANT_Methodref);
+ is_invokedynamic = opcode == JVM_OPC_invokedynamic;
/* Make sure the constant pool item is the right type. */
verify_constant_pool_type(context, key, kind);
methodname = JVM_GetCPMethodNameUTF(env, cb, key);
@@ -1241,8 +1245,11 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset)
is_internal = methodname[0] == '<';
pop_and_free(context);
- clazz_info = cp_index_to_class_fullinfo(context, key,
- JVM_CONSTANT_Methodref);
+ if (is_invokedynamic)
+ clazz_info = context->object_info; // anything will do
+ else
+ clazz_info = cp_index_to_class_fullinfo(context, key,
+ JVM_CONSTANT_Methodref);
this_idata->operand.i = key;
this_idata->operand2.fi = clazz_info;
if (is_constructor) {
@@ -1304,6 +1311,11 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset)
"Fourth operand byte of invokeinterface must be zero");
}
pop_and_free(context);
+ } else if (opcode == JVM_OPC_invokedynamic) {
+ if (code[offset + 3] != 0 || code[offset + 4] != 0) {
+ CCerror(context,
+ "Third and fourth operand bytes of invokedynamic must be zero");
+ }
} else if (opcode == JVM_OPC_invokevirtual
|| opcode == JVM_OPC_invokespecial)
set_protected(context, inumber, key, opcode);
@@ -1990,6 +2002,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
case JVM_OPC_invokeinit: /* invokespecial call to */
+ case JVM_OPC_invokedynamic:
case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: {
/* The top stuff on the stack depends on the method signature */
int operand = this_idata->operand.i;
@@ -2005,7 +2018,8 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac
print_formatted_methodname(context, operand);
}
#endif
- if (opcode != JVM_OPC_invokestatic)
+ if (opcode != JVM_OPC_invokestatic &&
+ opcode != JVM_OPC_invokedynamic)
/* First, push the object */
*ip++ = (opcode == JVM_OPC_invokeinit ? '@' : 'A');
for (p = signature + 1; *p != JVM_SIGNATURE_ENDFUNC; ) {
@@ -2290,6 +2304,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
case JVM_OPC_invokeinit:
+ case JVM_OPC_invokedynamic:
case JVM_OPC_invokeinterface: case JVM_OPC_invokestatic: {
int operand = this_idata->operand.i;
const char *signature =
@@ -2299,7 +2314,8 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac
int item;
const char *p;
check_and_push(context, signature, VM_STRING_UTF);
- if (opcode == JVM_OPC_invokestatic) {
+ if (opcode == JVM_OPC_invokestatic ||
+ opcode == JVM_OPC_invokedynamic) {
item = 0;
} else if (opcode == JVM_OPC_invokeinit) {
fullinfo_type init_type = this_idata->operand2.fi;
@@ -2680,6 +2696,7 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
case JVM_OPC_invokeinit:
+ case JVM_OPC_invokedynamic:
case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: {
/* Look to signature to determine correct result. */
int operand = this_idata->operand.i;
diff --git a/src/share/native/common/opcodes.in_out b/src/share/native/common/opcodes.in_out
index 364e8abdda5f0da81291fb07f107378ebe5f49c5..757532c7b08f90c8185922d6bfd48535fc391914 100644
--- a/src/share/native/common/opcodes.in_out
+++ b/src/share/native/common/opcodes.in_out
@@ -1,5 +1,5 @@
/*
- * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1998-2009 Sun Microsystems, Inc. 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
@@ -210,7 +210,7 @@ char * const opcode_in_out[][2] = {
{"?", "?"}, /* invokespecial */
{"?", "?"}, /* invokestatic */
{"?", "?"}, /* invokeinterface */
- {"?", "?"}, /* xxxunusedxxx */
+ {"?", "?"}, /* invokedynamic */
{"", "A"}, /* new */
{"I", "A"}, /* newarray */
{"I", "A"}, /* anewarray */