Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save chanwit/104074 to your computer and use it in GitHub Desktop.
Save chanwit/104074 to your computer and use it in GitHub Desktop.
diff --git a/make/common/internal/BinaryPlugs.gmk b/make/common/internal/BinaryPlugs.gmk
index 296157b..84b019e 100644
--- a/make/common/internal/BinaryPlugs.gmk
+++ b/make/common/internal/BinaryPlugs.gmk
@@ -58,6 +58,12 @@ com/sun/jmx/snmp/SnmpPeer.class \
com/sun/jmx/snmp/SnmpTimeticks.class \
com/sun/jmx/snmp/SnmpVarBind.class \
com/sun/jmx/snmp/SnmpVarBindList.class \
+com/sun/jmx/snmp/Timestamp.class \
+com/sun/jmx/snmp/daemon/SnmpRequestCounter.class \
+com/sun/jmx/snmp/daemon/SnmpSocket.class \
+com/sun/jmx/snmp/daemon/SnmpQManager.class \
+com/sun/jmx/snmp/daemon/WaitQ.class \
+com/sun/jmx/snmp/daemon/SnmpResponseHandler.class \
com/sun/jmx/snmp/daemon/SendQ.class \
com/sun/jmx/snmp/daemon/SnmpInformRequest.class \
com/sun/jmx/snmp/daemon/SnmpSession.class
diff --git a/make/docs/CORE_PKGS.gmk b/make/docs/CORE_PKGS.gmk
index 4a41a20..3e71c48 100644
--- a/make/docs/CORE_PKGS.gmk
+++ b/make/docs/CORE_PKGS.gmk
@@ -1,5 +1,5 @@
#
-# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved.
+# Copyright 2001-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
@@ -55,6 +55,9 @@ EXCLUDE_PKGS = \
# This is a list of regular expressions. So foo.* matches "foo" and "foo.bar".
#
ACTIVE_JSR_PKGS= \
+ impl.java.dyn \
+ impl.java.dyn.util \
+ java.dyn \
java.sql \
javax.activation \
javax.annotation.* \
diff --git a/make/java/Makefile b/make/java/Makefile
index 3679d01..74055e1 100644
--- a/make/java/Makefile
+++ b/make/java/Makefile
@@ -1,5 +1,5 @@
#
-# Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
+# Copyright 1995-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
@@ -39,7 +39,7 @@ SUBDIRS += hpi version jvm redist verify fdlibm java sun_nio jli main zip
# Others
# Note: java_crw_demo java_hprof_demo are demos but must be delivered built in sdk
SUBDIRS += security npt java_crw_demo java_hprof_demo \
- math awt util text applet net nio \
+ math awt util text applet net nio dyn \
sql rmi jar beans logging management instrument
diff --git a/make/java/dyn/Makefile b/make/java/dyn/Makefile
new file mode 100644
index 0000000..1dfa5d1
--- /dev/null
+++ b/make/java/dyn/Makefile
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+BUILDDIR = ../..
+
+PACKAGE = java.dyn
+PRODUCT = java
+include $(BUILDDIR)/common/Defs.gmk
+
+AUTO_FILES_JAVA_DIRS = java/dyn impl/java/dyn impl/java/dyn/util
+
+include $(BUILDDIR)/common/Classes.gmk
diff --git a/src/share/classes/java/dyn/CallSite.java b/src/share/classes/java/dyn/CallSite.java
new file mode 100644
index 0000000..17b4aee
--- /dev/null
+++ b/src/share/classes/java/dyn/CallSite.java
@@ -0,0 +1,176 @@
+/*
+ * 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.util.BytecodeName;
+
+/**
+ * An <code>invokedynamic</code> call site, as reified to the bootstrap method.
+ * Every instance of a call site corresponds to a distinct instance
+ * of the <code>invokedynamic</code> instruction.
+ * Call sites have state, one reference word, called the <code>target</code>,
+ * and typed as a {@link MethodHandle}. When this state is null (as it is
+ * initially) the call site is in the unlinked state. Otherwise, it is said
+ * to be linked to its target.
+ * <p>
+ * When an unlinked call site is executed, a bootstrap routine is called
+ * to finish the execution of the call site, and optionally to link
+ * the call site.
+ * <p>
+ * @author John Rose, JSR 292 EG
+ */
+public abstract class CallSite {
+ protected MethodHandle target;
+ protected final Object caller; // usually a class
+ protected final String name;
+ protected final MethodType type;
+
+ protected CallSite(Object caller, String name, MethodType type) {
+ this.caller = caller;
+ this.name = name;
+ this.type = type;
+ }
+
+ /**
+ * Report the current linkage state of the call site. (This is mutable.)
+ * The value is null if and only if the call site is currently unlinked.
+ * When a linked call site is invoked, the target method is used directly.
+ * When an unlinked call site is invoked, its bootstrap method receives
+ * the call, as if via {@link Linkage#bootstrapInvokeDynamic}.
+ * <p>
+ * The interactions of {@code getTarget} with memory are the same
+ * as of a read from an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, the current thread may choose to reuse the result
+ * of a previous read of the target from memory, and may fail to see
+ * a recent update to the target by another thread.
+ * @return the current linkage state of the call site
+ * @see #setTarget
+ */
+ public MethodHandle getTarget() {
+ return target;
+ }
+
+ /**
+ * Link or relink the call site, by setting its target method.
+ * <p>
+ * The interactions of {@code setTarget} with memory are the same
+ * as of a write to an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, unrelated threads may fail to see the updated target
+ * until they perform a read from memory.
+ * Stronger guarantees can be created by putting appropriate operations
+ * into the bootstrap method and/or the target methods used
+ * at any given call site.
+ * @param target the new target, or null if it is to be unlinked
+ * @throws WrongMethodTypeException if the new target is not null
+ * and has a method type that differs from the call site's {@link #type}
+ */
+ public void setTarget(MethodHandle target) {
+ checkTarget(target);
+ this.target = target;
+ }
+
+ protected void checkTarget(MethodHandle target) {
+ if (!canSetTarget(target))
+ throw new WrongMethodTypeException(String.valueOf(target));
+ }
+
+ protected boolean canSetTarget(MethodHandle target) {
+ return (target == null || target.type() == type());
+ }
+
+ /**
+ * Report the class containing the call site.
+ * This is immutable static context.
+ * @return class containing the call site
+ */
+ public Class<?> callerClass() {
+ return (Class) caller;
+ }
+
+ /**
+ * Report the method name specified in the {@code invokedynamic} instruction.
+ * This is immutable static context.
+ * <p>
+ * Note that the name is a JVM bytecode name, and as such can be any
+ * non-empty string, as long as it does not contain certain "dangerous"
+ * characters such as slash {@code '/'} and dot {@code '.'}.
+ * See the Java Virtual Machine specification for more details.
+ * <p>
+ * Application such as a language runtimes may need to encode
+ * arbitrary program element names and other configuration information
+ * into the name. A standard convention for doing this is
+ * <a href="http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm">specified here</a>.
+ * @return method name specified by the call site
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * Report the method name specified in the {@code invokedynamic} instruction,
+ * as a series of components, individually demangled according to
+ * the standard convention
+ * <a href="http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm">specified here</a>.
+ * <p>
+ * Non-empty runs of characters between dangerous characters are demangled.
+ * Each component is either a completely arbitrary demangled string,
+ * or else a character constant for a punctuation character, typically ':'.
+ * (In principle, the character can be any dangerous character that the
+ * JVM lets through in a method name, such as '$' or ']'.
+ * Runtime implementors are encouraged to use colon ':' for building
+ * structured names.)
+ * <p>
+ * In the common case where the name contains no dangerous characters,
+ * the result is an array whose only element array is the demangled
+ * name at the call site. Such a demangled name can be any sequence
+ * of any number of any unicode characters.
+ * @return method name components specified by the call site
+ */
+ public Object[] nameComponents() {
+ return BytecodeName.parseBytecodeName(name);
+ }
+
+ /**
+ * Report the resolved result and parameter types of this call site,
+ * which are derived from its bytecode-level invocation descriptor.
+ * The types are packaged into a {@link MethodType}.
+ * Any linked target of this call site must be exactly this method type.
+ * This is immutable static context.
+ * @return method type specified by the call site
+ */
+ public MethodType type() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return "CallSite#"+hashCode()+"["+name+type+" => "+target+"]";
+ }
+}
diff --git a/src/share/classes/java/dyn/Dynamic.java b/src/share/classes/java/dyn/Dynamic.java
new file mode 100644
index 0000000..39c7ce4
--- /dev/null
+++ b/src/share/classes/java/dyn/Dynamic.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+/**
+ * Syntactic marker interface to request javac to emit an {@code invokedynamic} instruction.
+ * A language compiler might use this interface to express invokedynamic instructions as follows:
+ * <blockquote><pre>
+ * Dynamic.greet("hello world", 123);
+ * // previous line generates invokedynamic "greet" "(Ljava/lang/String;I)Ljava/lang/Object;"
+ * Dynamic.&gt;void&lt;println(123);
+ * // previous line generates invokedynamic "println" "(I)V"
+ * Dynamic.#"long:strange:name"();
+ * // previous line generates invokedynamic "long:strange:name" "()Ljava/lang/Object;"
+ * </pre></blockquote>
+ * <p>
+ * This type has no particular meaning as a class or interface supertype, and need never be implemented by any class.
+ * Logically, it denotes a dynamically typed reference to any object.
+ * As such it may be viewed as logically containing all methods on any of those types.
+ * <p>
+ * This type may be used as a marker interface for arguments to method handles, in order
+ * to distinguish the static type {@code Object} from a dynamically typed reference.
+ * @author John Rose, JSR 292 EG
+ */
+public interface Dynamic {
+ // no methods
+}
diff --git a/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java
new file mode 100644
index 0000000..00718c5
--- /dev/null
+++ b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java
@@ -0,0 +1,53 @@
+/*
+ * 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 an {@code invokedynamic} instruction has
+ * failed to find its bootstrap method.
+ * This must have been declared during a class's initialization
+ * by a call to {@link Linkage#registerBootstrapMethod}.
+ *
+ * @author John Rose, JSR 292 EG
+ */
+public class InvokeDynamicBootstrapError extends LinkageError {
+ /**
+ * Constructs a {@code InvokeDynamicBootstrapError} with no detail message.
+ */
+ public InvokeDynamicBootstrapError() {
+ super();
+ }
+
+ /**
+ * Constructs a {@code InvokeDynamicBootstrapError} with the specified
+ * detail message.
+ *
+ * @param s the detail message.
+ */
+ public InvokeDynamicBootstrapError(String s) {
+ super(s);
+ }
+}
diff --git a/src/share/classes/java/dyn/Linkage.java b/src/share/classes/java/dyn/Linkage.java
new file mode 100644
index 0000000..28a738a
--- /dev/null
+++ b/src/share/classes/java/dyn/Linkage.java
@@ -0,0 +1,252 @@
+/*
+ * 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.util.MethodHandleInvoker;
+import java.util.WeakHashMap;
+import sun.reflect.Reflection;
+
+/**
+ * Static methods which control the linkage of invokedynamic call sites.
+ * @author John Rose, JSR 292 EG
+ */
+public class Linkage {
+ private Linkage() {} // do not instantiate
+
+ /**
+ * Register a bootstrap method for use for a given caller class.
+ * The method handle must be of a type equivalent to {@link Linkage#bootstrapInvokeDynamic}.
+ * <p>
+ * The operation will fail with an exception if any of the following conditions hold:
+ * <ul>
+ * <li>The caller of this method is in a different package than the {@code callerClass},
+ * and there is a security manager, and its {@code checkPermission} call throws
+ * when passed {@link LinkagePermission}("registerBootstrapMethod",callerClass).
+ * <li>The given class already has a bootstrap method, either from an embedded
+ * {@code BootstrapInvokeDynamic} classfile attribute, or from a previous
+ * call to this method.
+ * <li>The given class is already fully initialized.
+ * <li>The given class is in the process of initialization, in another thread.
+ * </ul>
+ * Because of these rules, a class may install its own bootstrap method in
+ * a static initializer.
+ */
+ public static
+ void registerBootstrapMethod(Class callerClass, MethodHandle mh) {
+ Class callc = Reflection.getCallerClass(2);
+ checkPackagePrivilege(callc, callerClass, "registerBootstrapMethod");
+ if (mh != null && mh.type() != BOOTSTRAP_METHOD_TYPE)
+ throw new WrongMethodTypeException(mh.type().toString());
+ synchronized (bootstrapMethods) {
+ if (bootstrapMethods.containsKey(callerClass))
+ throw new IllegalStateException("bootstrap method already declared in "+callerClass);
+ bootstrapMethods.put(callerClass, mh);
+ }
+ }
+
+ /**
+ * Simplified version of registerBootstrapMethod for self-registration,
+ * to be called from a static initializer.
+ * Finds a static method of type (CallSite, Object[]) -> Object in the
+ * caller's class, and installs it on the caller.
+ * @throws IllegalArgumentException if there is no such method
+ */
+ public static
+ void registerBootstrapMethod(String name) {
+ Class callc = Reflection.getCallerClass(2);
+ MethodHandle bootstrapMethod =
+ MethodHandles.findStaticFrom(callc, callc, name, BOOTSTRAP_METHOD_TYPE);
+ if (bootstrapMethod == null)
+ throw new IllegalArgumentException("cannot find bootstrap method: "+name);
+ Linkage.registerBootstrapMethod(callc, bootstrapMethod);
+ }
+
+ /**
+ * Report the bootstrap method registered for a given class.
+ * Returns null if the class has never yet registered a bootstrap method,
+ * or if the class has explicitly registered a null bootstrap method.
+ * Only callers privileged to set the bootstrap method may inquire
+ * about it, because a bootstrap method is potentially a back-door entry
+ * point into its class.
+ */
+ public static
+ MethodHandle getBootstrapMethod(Class callerClass) {
+ Class callc = Reflection.getCallerClass(2);
+ checkPackagePrivilege(callc, callerClass, "registerBootstrapMethod");
+ synchronized (bootstrapMethods) {
+ return bootstrapMethods.get(callerClass);
+ }
+ }
+
+ /** The type of any bootstrap method is (CallSite,Object...)Object.
+ * The varargs marker is required.
+ */
+ public static final MethodType BOOTSTRAP_METHOD_TYPE
+ = MethodType.make(Object.class,
+ CallSite.class, Object[].class);
+
+ private static MethodHandleInvoker bootstrapMethodInvoker;
+
+ private static final WeakHashMap<Class, MethodHandle> bootstrapMethods =
+ new WeakHashMap<Class, MethodHandle>();
+
+ /**
+ * Determine if the caller class has declared or registered its own bootstrap method.
+ * If so, delegate this call to it. Otherwise, throw an IncompatibleClassChangeError.
+ * <p>
+ * This routine, or an equivalent sequence of actions, is called from the
+ * JVM when an {@code invokedynamic} instruction is executed
+ * but its call site is unlinked (has a null target method).
+ * <p>
+ * This routine can be called from Java code whether or not the call site
+ * currently has a target, and will invoke the bootstrap method regardless
+ * of he call site's linkage state.
+ * <p>
+ * Although invoking the bootstrap method does not in and of itself cause
+ * state change in the call site, the actions eventually performed by
+ * the bootstrap method may include installed a new target on the call site.
+ * <p>
+ * Note that linkage state changes are individually atomic, but are not
+ * serialized in any way with respect to calls to the bootstrap method,
+ * or executions of the {@code invokedynamic} instruction. Therefore,
+ * an {@code invokedynamic} call site may be linked several times if
+ * several threads concurrently execute it in an unlinked state.
+ * It is up to the user-defined bootstrap method to make sure this
+ * race condition is resolved safely, either by performing linkage
+ * decisions under suitable locks (as the JVM does) or by ensuring
+ * that all racing threads come to the same conclusion, and independently
+ * install equivalent target methods.
+ */
+ public static
+ Object bootstrapInvokeDynamic(CallSite site, Object... arguments) {
+ Class callerClass = site.callerClass();
+ MethodHandle mh;
+ synchronized (bootstrapMethods) {
+ mh = bootstrapMethods.get(callerClass);
+ }
+ if (mh == null)
+ throw new IllegalStateException("no bootstrap method declared in "+callerClass);
+
+ System.out.println(site+": calling bootstrap "+mh); // FIXME
+ if (bootstrapMethodInvoker == null)
+ bootstrapMethodInvoker = MethodHandleInvoker.make(BOOTSTRAP_METHOD_TYPE);
+ return bootstrapMethodInvoker.invoke(mh, site, arguments);
+ }
+
+ /**
+ * Invalidate all <code>invokedynamic</code> call sites everywhere.
+ * <p>
+ * When this method returns, every <code>invokedynamic</code> instruction
+ * will invoke its bootstrap method on next call.
+ * <p>
+ * It is unspecified whether call sites already known to the Java
+ * code will continue to be associated with <code>invokedynamic</code>
+ * instructions. If any call site is still so associated, its
+ * {@link CallSite#getTarget()} method is guaranteed to return null
+ * the invalidation operation completes.
+ * <p>
+ * 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 <code>invokedynamic</code> call sites associated
+ * with the given class.
+ * (These are exactly those sites which report the given class
+ * via the {@link CallSite#callerClass()} method.)
+ * <p>
+ * When this method returns, every matching <code>invokedynamic</code>
+ * instruction will invoke its bootstrap method on next call.
+ * <p>
+ * 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");
+ }
+
+ /**
+ * Ensure the requesting class have privileges to perform invokedynamic
+ * linkage operations on subjectClass. True if requestingClass is
+ * null (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
+ */
+ // FIXME: factor this logic into sun.dyn.util.VerifyAccess
+ static void checkPackagePrivilege(Class requestingClass, Class subjectClass,
+ String permissionName) {
+ if (requestingClass == null) return;
+ if (requestingClass == subjectClass) return;
+ SecurityManager security = System.getSecurityManager();
+ if (security == null) return; // open season
+ ClassLoader rcl = requestingClass.getClassLoader();
+ ClassLoader scl = subjectClass.getClassLoader();
+ if (isParent(rcl, scl)) {
+ String rn = requestingClass.getName();
+ if (rn.startsWith("java.dyn.")) return;
+ String sn = subjectClass.getName();
+ if (samePackage(rn, sn)) return;
+ }
+ security.checkPermission(new LinkagePermission(permissionName, requestingClass));
+ }
+
+ static
+ MethodHandle findBootstrapMethod(Class callerClass, Class searchBootstrapClass) {
+ if (searchBootstrapClass != null) throw new UnsupportedOperationException("NYI");
+ MethodHandle mh = getBootstrapMethod(callerClass);
+ System.out.println("reporting bootstrap method to JVM: "+mh); //FIXME
+ return mh;
+ }
+
+ private static boolean isParent(ClassLoader rcl, ClassLoader scl) {
+ while (scl != null && scl != rcl)
+ scl = scl.getParent();
+ return (scl == rcl);
+ }
+
+ private static boolean samePackage(String rn, String sn) {
+ assert((rn.indexOf('/') & sn.indexOf('/')) < 0); // no bytecode names
+ int lastDot = rn.lastIndexOf('.');
+ if (lastDot != sn.lastIndexOf('.')) return false;
+ return rn.startsWith(sn.substring(0, lastDot+1));
+ }
+
+}
diff --git a/src/share/classes/java/dyn/LinkagePermission.java b/src/share/classes/java/dyn/LinkagePermission.java
new file mode 100644
index 0000000..9493e56
--- /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.
+ *
+ * <P>
+ * 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.
+ * <P>
+ * 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.
+ * <P>
+ *
+ * <table border=1 cellpadding=5 summary="permission target name,
+ * what the target allows,and associated risks">
+ * <tr>
+ * <th>Permission Target Name</th>
+ * <th>What the Permission Allows</th>
+ * <th>Risks of Allowing this Permission</th>
+ * </tr>
+ *
+ * <tr>
+ * <td>registerBootstrapMethod.{class name}</td>
+ * <td>Specifying a bootstrap method for invokedynamic, within a class of the given name</td>
+ * <td>An attacker could attempt to attach a bootstrap method to a class which
+ * has just been loaded, thus gaining control of its invokedynamic calls.</td>
+ * </tr>
+ *
+ * <tr>
+ * <td>invalidateAll</td>
+ * <td>Force the relinking of invokedynamic call sites everywhere.</td>
+ * <td>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.</td>
+ * </tr>
+ *
+ *
+ * <tr>
+ * <td>invalidateCallerClass.{class name}</td>
+ * <td>Force the relinking of invokedynamic call sites in the given class.</td>
+ * <td>See {@code invalidateAll}.</td>
+ * </tr>
+ * </table>
+ *
+ * @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 0000000..e95b706
--- /dev/null
+++ b/src/share/classes/java/dyn/MethodHandle.java
@@ -0,0 +1,129 @@
+/*
+ * 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.dyn.emu.*;
+import sun.dyn.*;
+
+/**
+ * A method handle is a typed reference to the entry point of a method.
+ * <p>
+ * 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.
+ * <p>
+ * Every method handle confesses its type via the <code>type</code> accessor.
+ * The structure of this type is a series of classes, one of which is
+ * the return type of the method (or <code>void.class</code> if none).
+ * <p>
+ * Every method handle appears as an object containing a method named
+ * <code>invoke</code>, whose signature exactly matches
+ * the method handle's type.
+ * A normal Java method call (using the <code>invokevirtual</code> instruction)
+ * can invoke this method from Java source code (if language support is present).
+ * <p>
+ * 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 <code>invokevirtual</code> instruction,
+ * via a {@code CONSTANT_NameAndType} constant pool entry.)
+ * The call looks within the receiver object for a method
+ * named <code>invoke</code> of the intended method type.
+ * The call fails with a {@link WrongMethodTypeException}
+ * if the method does not exist, even if there is an <code>invoke</code>
+ * method of a closely similar signature.
+ * <p>
+ * 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.
+ * <p>
+ * Bytecode in an extended JVM can directly call a method handle's
+ * <code>invoke</code> from an <code>invokevirtual</code> instruction.
+ * The receiver class type must be <code>MethodHandle</code> and the method name
+ * must be <code>invoke</code>. The signature of the invocation
+ * (after resolving symbolic type names) must exactly match the method type
+ * of the target method.
+ * <p>
+ * Bytecode in an extended JVM can directly obtain a method handle
+ * for any accessible method from a <code>ldc</code> instruction
+ * which refers to a <code>CONSTANT_Methodref</code> or
+ * <code>CONSTANT_InterfaceMethodref</code> constant pool entry.
+ * <p>
+ * All JVMs can also use a reflective API called <code>MethodHandles</code>
+ * for creating and calling method handles.
+ * <p>
+ * 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 <code>ldc</code>,
+ * the type of the receiver is the class named in the constant pool entry.)
+ * <p>
+ * When a method handle to a virtual method is invoked, the method is
+ * always looked up in the receiver (that is, the first argument).
+ * <p>
+ * 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 <code>invokespecial</code> instruction to the same method.
+ *
+ * @see MethodType
+ * @see MethodHandles
+ * @author John Rose, JSR 292 EG
+ */
+public class MethodHandle extends MethodHandleImpl {
+ // interface MethodHandle<T extends MethodType<R,A...>>
+ // { T type(); <R,A...> 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 0000000..c9c0b00
--- /dev/null
+++ b/src/share/classes/java/dyn/MethodHandles.java
@@ -0,0 +1,1041 @@
+/*
+ * 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.Access;
+import sun.dyn.MemberName;
+import sun.dyn.MethodHandleImpl;
+import sun.dyn.util.MethodHandleInvoker;
+import sun.dyn.util.VerifyAccess;
+import sun.dyn.util.Wrappers;
+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.reflect.Reflection;
+import static sun.dyn.MemberName.newIllegalArgumentException;
+import static sun.dyn.MemberName.newNoAccessException;
+
+/**
+ * Fundamental operations and utilities for MethodHandle.
+ * <p>
+ * <em>API Note:</em> The matching of method types in this API cannot
+ * be completely checked by Java's generic type system for three reasons:
+ * <ol>
+ * <li>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.</li>
+ * <li>Method types can specify arguments of primitive types,
+ * which Java generic types cannot range over.</li>
+ * <li>Method types can optionally specify varargs (ellipsis).</li>
+ * </ol>
+ * @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_LOOKUP = MemberName.getFactory(IMPL_TOKEN);
+
+ //// Method handle creation from ordinary methods.
+
+ /**
+ * 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 caller.
+ * 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, or null if no such method exists
+ * @exception SecurityException <em>TBD</em>
+ * @exception NoAccessException if access checking fails
+ */
+ public static
+ MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws NoAccessException {
+ Class<?> caller = Reflection.getCallerClass(2);
+ MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, caller);
+ checkStatic(true, method, caller);
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, caller);
+ }
+
+ // TODO: Factor this correctly.
+ // We need to make a suitably access-checked MethodHandleFactory class that "knows" the caller class, if appropriate.
+ // Then we can limit the getCallerClass hack to one or two places. (It is convenient but fatally inflexible.)
+ static /*must not be public*/
+ MethodHandle findStaticFrom(Class<?> caller,
+ Class<?> defc, String name, MethodType type) throws NoAccessException {
+ MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, caller);
+ checkStatic(true, method, caller);
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, caller);
+ }
+
+ private static void checkStatic(boolean wantStatic, MemberName m, Class<?> caller) {
+ if (wantStatic != m.isStatic()) {
+ String message = wantStatic ? "expected a static method" : "expected a non-static method";
+ throw newNoAccessException(message, m, caller);
+ }
+ }
+
+ /**
+ * 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 caller.
+ * <p>
+ * 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, or null if no such method exists
+ * @exception SecurityException <em>TBD</em>
+ * @exception NoAccessException if access checking fails
+ */
+ public static
+ MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws NoAccessException {
+ Class<?> caller = Reflection.getCallerClass(2);
+ MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type), true, caller);
+ checkStatic(false, method, caller);
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, caller);
+ }
+
+ /**
+ * 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.
+ * <p>
+ * 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.)
+ * <p>
+ * If the explicitly specified caller class is not identical with the actual
+ * caller of {@code findSpecial}, 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 "<init>" 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, or null if no such method exists
+ * @exception SecurityException <em>TBD</em>
+ * @exception NoAccessException if access checking fails
+ */
+ public static
+ MethodHandle findSpecial(Class<?> defc, String name, MethodType type,
+ Class<?> specialCaller) throws NoAccessException {
+ Class<?> caller = Reflection.getCallerClass(2);
+ checkSpecialCaller(specialCaller, caller);
+ MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type), false, specialCaller);
+ checkStatic(false, method, caller);
+ if (name.equals("<init>")) {
+ if (defc != specialCaller)
+ throw newNoAccessException("constructor must be local to caller", method, caller);
+ } else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) {
+ throw newNoAccessException("method must be in a superclass of caller", method, caller);
+ }
+ 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 caller.
+ * The method and all its argument types must be accessible to the caller.
+ * The type of the method handle will be that of the method.
+ * The given receiver will be bound into the method handle.
+ * <p>
+ * Equivalent to the following expression:
+ * <code>
+ * {@link #insertArgument}({@link #findVirtual}(defc, name, type), receiver)
+ * </code>
+ * @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, or null if no such method exists
+ * @exception SecurityException <em>TBD</em>
+ * @exception NoAccessException if access checking fails
+ */
+ public static
+ MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException {
+ Class<?> caller = Reflection.getCallerClass(2);
+ Class<? extends Object> rcvc = receiver.getClass(); // may get NPE
+ MemberName reference = new MemberName(rcvc, name, type);
+ MemberName method = IMPL_LOOKUP.resolveOrFail(reference, true, caller);
+ checkStatic(false, method, caller);
+ MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, caller);
+ MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
+ if (bmh == null)
+ throw newNoAccessException(method, caller);
+ return bmh;
+ }
+
+ /**
+ * Make a direct method handle to <i>m</i>, if the current caller has permission.
+ * If <i>m</i> is non-static, the receiver argument is treated as an initial argument.
+ * If <i>m</i> is virtual, overriding is respected on every call.
+ * Unlike the Core Reflection API, exceptions are <em>not</em> 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 caller.
+ * If <i>m</i> is not public, do not share the resulting handle with untrusted callers.
+ * @param m the reflected method
+ * @return a method handle which can invoke the reflected method
+ * @exception NoAccessException if access checking fails
+ */
+ public static
+ MethodHandle unreflect(Method m) throws NoAccessException {
+ Class<?> caller = Reflection.getCallerClass(2);
+ return unreflect(new MemberName(m), m.isAccessible(), true, caller);
+ }
+
+ /**
+ * 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 caller,
+ * 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 static
+ MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws NoAccessException {
+ Class<?> caller = Reflection.getCallerClass(2);
+ checkSpecialCaller(specialCaller, caller);
+ MemberName mname = new MemberName(m);
+ checkStatic(false, mname, caller);
+ return unreflect(mname, m.isAccessible(), false, specialCaller);
+ }
+
+// /**
+// * Produce a method handle for a reflected constructor.
+// * It will allow direct access to the constructor,
+// * 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 constructor's {@code accessible} flag is not set,
+// * access checking is performed immediately on behalf of the caller,
+// * as if {@code invokespecial} instruction were being linked.
+// * @param ctor the reflected constructor
+// * @param specialCaller the proposed calling class to perform the {@code invokespecial}
+// * @return a method handle which can invoke the reflected constructor
+// * @exception NoAccessException if access checking fails
+// */
+// public static
+// MethodHandle unreflectSpecial(Constructor ctor, Class<?> specialCaller) throws NoAccessException {
+// Class<?> caller = Reflection.getCallerClass(2);
+// checkSpecialCaller(specialCaller, caller);
+// MemberName m = new MemberName(ctor);
+// checkStatic(false, m, caller);
+// return unreflect(m, ctor.isAccessible(), false, specialCaller);
+// }
+
+ private static
+ void checkSpecialCaller(Class<?> specialCaller, Class<?> caller) {
+ if (caller != null && !VerifyAccess.isSamePackageMember(specialCaller, caller))
+ throw newNoAccessException("no private access", new MemberName(specialCaller), caller);
+ }
+
+ // Helper for creating handles on reflected methods and constructors.
+ private static
+ MethodHandle unreflect(MemberName m, boolean isAccessible, boolean doDispatch, Class<?> caller) {
+ MethodType mtype = m.getInvocationType();
+ Class<?> defc = m.getDeclaringClass();
+ int mods = m.getModifiers();
+ if (m.isStatic()) {
+ if (!isAccessible &&
+ VerifyAccess.isAccessible(defc, mods, false, caller) == null)
+ throw newNoAccessException(m, caller);
+ } else {
+ Class<?> constraint;
+ if (isAccessible) {
+ // abbreviated access check for "unlocked" method
+ constraint = doDispatch ? defc : caller;
+ } else {
+ constraint = VerifyAccess.isAccessible(defc, mods, doDispatch, caller);
+ }
+ if (constraint != defc && !constraint.isAssignableFrom(defc)) {
+ if (!defc.isAssignableFrom(constraint))
+ throw newNoAccessException("receiver must be in caller class", m, caller);
+ mtype = mtype.changeParameterType(0, constraint);
+ }
+ }
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, m, doDispatch, caller);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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 caller.
+ * @param f the reflected field
+ * @return a method handle which can load values from the reflected field
+ * @exception NoAccessException if access checking fails
+ */
+ public static
+ MethodHandle unreflectGetter(Field f) throws NoAccessException {
+ Class<?> caller = Reflection.getCallerClass(2);
+ return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), false, (Class)caller);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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 caller.
+ * @param f the reflected field
+ * @return a method handle which can store values into the reflected field
+ * @exception NoAccessException if access checking fails
+ */
+ public static
+ MethodHandle unreflectSetter(Field f) throws NoAccessException {
+ Class<?> caller = Reflection.getCallerClass(2);
+ return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), true, (Class)caller);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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 {
+ Class<?> caller = Reflection.getCallerClass(2);
+ return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, false, (Class)caller);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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 {
+ Class<?> caller = Reflection.getCallerClass(2);
+ return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, true, (Class)caller);
+ }
+
+
+ /// method handle invocation (reflective style)
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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.
+ * <p>
+ * In order to match the type of the target, the following argument
+ * conversions are applied as necessary:
+ * <ul>
+ * <li>reference casting
+ * <li>unboxing
+ * </ul>
+ * The following conversions are not applied:
+ * <ul>
+ * <li>primitive conversions (e.g., {@code byte} to {@code int}
+ * <li>varargs conversions other than the initial spread
+ * <li>any application-specific conversions (e.g., string to number)
+ * </ul>
+ * The result returned by the call is boxed if it is a primitive,
+ * or forced to null if the return type is void.
+ * <p>
+ * This call is a convenience method for the following code:
+ * <pre>
+ * MethodHandle invoker = MethodHandles.genericInvoker(target.type(), 0, true);
+ * Object result = invoker.invoke(arguments);
+ * </pre>
+ * @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) {
+ return MethodHandleInvoker.make(target.type()).invoke(target, arguments);
+ //MethodHandle invoker = MethodHandles.genericInvoker(target.type(), 0, true);
+ //return invoker.invoke(arguments);
+ }
+
+ public static
+ Object invoke_1(MethodHandle target, Object argument0, Object... arguments) {
+ int narguments = (arguments == null ? 0 : arguments.length);
+ Object[] args1 = new Object[1 + narguments];
+ args1[0] = argument0;
+ System.arraycopy(arguments, 0, args1, 1, narguments);
+ return MethodHandleInvoker.make(target.type()).invoke(target, args1);
+ //MethodHandle invoker = MethodHandles.genericInvoker(target.type(), 0, true);
+ //return invoker.invoke(arguments);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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:
+ * <ul>
+ * <li>a single {@code MethodHandle} target
+ * <li>zero or more {@code Object} values
+ * <li>an optional {@code Object[]} array containing more arguments
+ * </ul>
+ * 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.
+ * <p>
+ * This is a convenience method equivalent to the following code:
+ * <pre>
+ * 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);
+ * </pre>
+ * @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) {
+ 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);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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}.
+ * <p>
+ * This is a convenience method equivalent to the following code:
+ * <pre>
+ * return findVirtual(MethodHandle.class, "invoke", type);
+ * </pre>
+ * @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) {
+ MethodHandle invoker = findVirtual(MethodHandle.class, "invoke", type);
+ if (invoker == null) throw new InternalError("JVM cannot find invoker");
+ return invoker;
+ }
+
+ /**
+ * <em>WORK IN PROGRESS:</em>
+ * 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:
+ * <ul>
+ * <li>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.)
+ * <li>If T0 and T1 are primitives, then a widening or narrowing
+ * conversion is applied, if one exists.
+ * <li>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.
+ * <li>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.
+ * <li>If T1 is void, the return value is discarded
+ * <li>If T0 is void and T1 a reference, a null value is introduced.
+ * <li>If T0 is void and T1 a primitive, a zero value is introduced.
+ * </ul>
+ * 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
+ Object checkValue(Class<?> T0, Class<?> T1, Object value)
+ throws ClassCastException
+ {
+ if (T0 == T1) {
+ // no conversion needed
+ return value;
+ }
+ boolean prim0 = T0.isPrimitive(), prim1 = T1.isPrimitive();
+ Class<?> TW;
+ if (!prim0) {
+ if (!prim1) {
+ return T1.cast(T0.cast(value));
+ }
+ // convert reference to primitive by unboxing
+ TW = Wrappers.asWrapperType(T0);
+ return T1.cast(TW.cast(value));
+ }
+ if (!prim1) {
+ // convert primitive to reference type by boxing
+ TW = Wrappers.asWrapperType(T1);
+ return TW.cast(T0.cast(value));
+ }
+ // convert primitive to primitive; this requires a real value change
+ if (value == null)
+ return Wrappers.zeroValue(T1);
+ // convert non-Number primitives to Integer:
+ if (value instanceof Character) {
+ char ch = (char) (Character) value;
+ value = Integer.valueOf(ch);
+ if (T1 == Integer.class)
+ return value;
+ } else if (value instanceof Boolean) {
+ boolean z = (boolean) (Boolean) value;
+ value = Integer.valueOf(z ? 1 : 0);
+ if (T1 == Integer.class)
+ return value;
+ }
+ Number numval = (Number) value;
+ switch (Wrappers.basicTypeChar(T1)) {
+ case 'Z': return (numval.intValue() != 0);
+ case 'B': return numval.byteValue();
+ case 'C': return (char) numval.intValue();
+ case 'S': return numval.shortValue();
+ case 'I': return numval.intValue();
+ case 'J': return numval.longValue();
+ case 'F': return numval.floatValue();
+ case 'D': return numval.doubleValue();
+ }
+ return null;
+ }
+
+ 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)
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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.
+ * <p>
+ * If the original type and new type are equal, returns target.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * Given those types T0, T1, one of the following conversions is applied
+ * if possible:
+ * <ul>
+ * <li>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.)
+ * <li>If T0 and T1 are primitives, then a Java casting
+ * conversion (JLS 5.5) is applied, if one exists.
+ * <li>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.
+ * <li>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.
+ * <li>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.
+ * <li>If T1 is void, any returned value is discarded
+ * <li>If T0 is void and T1 a reference, a null value is introduced.
+ * <li>If T0 is void and T1 a primitive, a zero value is introduced.
+ * </ul>
+ * @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;
+ return MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
+ newType, false, oldType, false, null);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle which adapts the calling sequence of the
+ * given method handle to a new type, by reordering the arguments.
+ * Duplication and omission is allowed.
+ * The resulting method handle is guaranteed to confess a type
+ * which is equal to the desired new type.
+ * <p>
+ * The given permutation string controls the reordering.
+ * The characters of the string are treated as small integers.
+ * All the characters must be in the range zero (i.e., '\0') to
+ * the number of incoming arguments (the parameter count of the new type).
+ * The length of the string must be equal to the number of outgoing
+ * parameters (the parameter count of the original method handle's type).
+ * If the n-th character of the string denotes the integer k,
+ * then the n-th incoming argument becomes the k-th outgoing argument.
+ * <p>
+ * 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 permutation 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, String permutation) {
+ MethodType oldType = target.type();
+ return MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
+ newType, false, oldType, false,
+ permutation);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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.
+ * <p>
+ * The final parameter type of the new type must be an array type T[].
+ * This is the type of what is called the <i>spread</i> argument.
+ * All other arguments of the new type are called <i>ordinary</i> arguments.
+ * <p>
+ * 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.
+ * <p>
+ * 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);
+ if (spreadPos < 0 || numSpread < 0)
+ throw newIllegalArgumentException("wrong number of arguments");
+ return MethodHandleImpl.convertArguments(IMPL_TOKEN, target, newType, true, oldType, false, null);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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.
+ * <p>
+ * 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 <i>spread</i> 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.
+ * @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.convertArguments(IMPL_TOKEN, target, newType, false, oldType, true, null);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle which calls the original method handle,
+ * after inserting the given argument as the first argument.
+ * The type of the new method handle will drop the first argument
+ * type from the original handle's type.
+ * <p>
+ * Equivalent to {@code insertArgument(target, value, 0)}.
+ */
+ public static
+ MethodHandle insertArgument(MethodHandle target, Object value) {
+ return insertArgument(target, value, 0);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle which calls the original method handle,
+ * after appending the given argument as the final argument.
+ * The type of the new method handle will drop the last argument
+ * type from the original handle's type.
+ * <p>
+ * Equivalent to {@code insertArgument(target, value, N)},
+ * where <i>N</i> is the number of arguments to <i>target</i>.
+ */
+ public static
+ MethodHandle appendArgument(MethodHandle target, Object value) {
+ return insertArgument(target, value, target.type().parameterCount());
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * The <i>pos</i> may range between zero and <i>N</i> (inclusively),
+ * where <i>N</i> is the number of argument types in <i>target</i>,
+ * 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 value the argument to insert
+ * @param pos where to insert the argument (zero for the first)
+ * @return a new method handle which inserts an additional argument,
+ * before calling the original method handle
+ */
+ public static
+ MethodHandle insertArgument(MethodHandle target, Object value, int pos) {
+ MethodType oldType = target.type();
+ ArrayList<Class<?>> ptypes =
+ new ArrayList<Class<?>>(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);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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.
+ * <p>
+ * The <i>pos</i> may range between zero and <i>N-1</i>,
+ * where <i>N</i> is the number of argument types in <i>target</i>,
+ * 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<Class<?>> ptypes =
+ new ArrayList<Class<?>>(oldType.parameterList());
+ ptypes.addAll(pos, Arrays.asList(valueTypes));
+ MethodType newType = MethodType.make(oldType.returnType(), ptypes);
+ return MethodHandleImpl.dropArguments(IMPL_TOKEN, target, newType, pos);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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.
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>
+ * 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...);
+ * }
+ * </pre></blockquote>
+ * @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);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * (Note that {@link #dropArguments} can be used to remove any arguments
+ * that either the {@code combiner} or {@code target} does not wish to receive.)
+ * <p>
+ * 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.
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>
+ * 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...);
+ * }
+ * </pre></blockquote>
+ * @param target the method handle to invoke after arguments are combined
+ * @param combiner method handle to call initially on the incoming arguments
+ * @param pos where the return value of {@code combiner} is to
+ * be inserted as an argument to {@code target}
+ * @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, MethodHandle combiner, int pos) {
+ MethodType mhType = target.type();
+ Class<?> combinType = combiner.type().returnType();
+ MethodType incomingArgs;
+ if (pos < 0) {
+ // No inserted argument; target & combiner must have same argument types.
+ incomingArgs = mhType;
+ if (!incomingArgs.changeReturnType(combinType).equals(combiner.type()))
+ throw newIllegalArgumentException("target and combiner types do not match");
+ } else {
+ // Inserted argument.
+ if (pos >= mhType.parameterCount()
+ || mhType.parameterType(pos) != combinType)
+ throw newIllegalArgumentException("inserted combiner argument does not match target");
+ incomingArgs = mhType.dropParameterType(pos);
+ }
+ if (!incomingArgs.changeReturnType(combinType).equals(combiner.type())) {
+ throw newIllegalArgumentException("target and combiner types do not match");
+ }
+ return MethodHandleImpl.combineArguments(IMPL_TOKEN, target, combiner, pos);
+ }
+
+ /// standard method handles
+
+ /**
+ * Identity function.
+ * @param x an arbitrary reference value
+ * @return the same value x
+ */
+ /*public*/ static <T>
+ T identity(T x) {
+ return x;
+ }
+
+ /**
+ * A method handle for {@link #identity}
+ */
+ /*public*/ static
+ MethodHandle identityMethod() {
+ if (IDENTITY == null)
+ IDENTITY = findStatic(MethodHandle.class, "identity", IDENTITY_TYPE);
+ return IDENTITY;
+ }
+ private static
+ MethodHandle IDENTITY;
+ private static final
+ MethodType IDENTITY_TYPE = MethodType.make(Object.class, Object.class);
+
+ /**
+ * Empty function.
+ */
+ /*public*/ static
+ void empty() {
+ }
+
+ /**
+ * A method handle for {@link #empty}
+ */
+ /*public*/ static
+ MethodHandle emptyMethod() {
+ if (EMPTY == null)
+ EMPTY = findStatic(MethodHandle.class, "empty", EMPTY_TYPE);
+ return EMPTY;
+ }
+ private static
+ MethodHandle EMPTY;
+ private static final
+ MethodType EMPTY_TYPE = MethodType.make(void.class);
+}
diff --git a/src/share/classes/java/dyn/MethodType.java b/src/share/classes/java/dyn/MethodType.java
new file mode 100644
index 0000000..28a2edc
--- /dev/null
+++ b/src/share/classes/java/dyn/MethodType.java
@@ -0,0 +1,536 @@
+/*
+ * 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.*;
+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 <code>MethodType</code> are immutable.
+ * Two instances are completely interchangeable if they compare equal.
+ * Equality depends exactly on the return and parameter types.
+ * <p>
+ * This type can be created only by factory methods, which manage interning.
+ *
+ * @author John Rose, JSR 292 EG
+ */
+public final
+class MethodType {
+ final Class<?> rtype;
+ final Class<?>[] ptypes;
+ MethodTypeForm form; // erased form, plus cached data about primitives
+ MethodType wrapAlt; // alternative wrapped/unwrapped version
+
+ private static final Access IMPL_TOKEN = Access.getToken();
+
+ 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<MethodType,MethodType> internTable
+ = new HashMap<MethodType, MethodType>();
+
+ 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<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
+ */
+ 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
+ mt1.form = MethodTypeForm.findForm(mt1);
+ if (mt1.form.erasedType == mt1)
+ MethodHandleImpl.init(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
+ * @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.primCounts != 0;
+ }
+
+ /** 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 #make(java.lang.Class, java.lang.Class[])}.
+ * Erase all reference types to Object, and all primitive types to wrappers.
+ * @return a version of the original type with references erasedType and primitives wrapped
+ */
+ public MethodType eraseWrap() {
+ return form.wrappedType;
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Convert all types, both reference and primitive, to Object.
+ * @return a version of the original type with all types replaced
+ * @see #makeGeneric(int)
+ */
+ public MethodType generic() {
+ return form.wrappedType.erase();
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Convert all primitive types to their corresponding wrapper types.
+ * @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.
+ * @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 = MethodTypeForm.canonType(pt, MethodTypeForm.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 = MethodTypeForm.canonType(wt, MethodTypeForm.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<Class<?>> 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 <tt>true</tt> 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.
+ * <p>
+ * 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).
+ * @return the number of JVM stack slots for this type's parameters
+ */
+ public int parameterSlotCount() {
+ return form.parameterSlotCount();
+ }
+
+ /** The JVM stack slot which carries the given parameter.
+ * The last parameter ({@link #parameterCount()}-1) is carried in the
+ * the most shallowly stacked argument slot, which is numbered as slot zero.
+ * The first parameter is carried in the most deeply stacked argument slot,
+ * which is {@link #parameterSlotCount()} minus the number of slots used
+ * by that parameter.
+ * <p>
+ * As a useful edge case, {@code parameterSlot(-1)} returns
+ * {@link #parameterSlotCount()}. No other negative values are accepted.
+ * <p>
+ * In general, the number returned is the number of arguments <em>after</em>
+ * the given parameter, <em>plus</em> the number of long or double arguments
+ * after the argument for the given parameter.
+ * @param num the index (zero-based) of the desired parameter type
+ * @return the index of the (shallowest) JVM stack slot transmitting the
+ * given parameter
+ */
+ public int parameterSlot(int num) {
+ return form.parameterToArgSlot(num);
+ }
+
+ /** 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.
+ * @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).
+ * <p>
+ * 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.
+ * @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<Class<?>> 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
+ * {@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 0000000..47a96e6
--- /dev/null
+++ b/src/share/classes/java/dyn/MethodTypeForm.java
@@ -0,0 +1,249 @@
+/*
+ * 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.util.Wrappers;
+
+/**
+ * Shared information for a group of method types, which differ
+ * only by reference types, and therefore share a common erasure
+ * and wrapping.
+ * <p>
+ * For an empirical discussion of the structure of method types,
+ * see <a href="http://groups.google.com/group/jvm-languages/browse_thread/thread/ac9308ae74da9b7e/">
+ * the thread "Avoiding Boxing" on jvm-languages</a>.
+ * 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
+ */
+class MethodTypeForm {
+ 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
+ final MethodType wrappedType; // erasure, with primitives wrapped
+
+ public static MethodTypeForm of(MethodType type) {
+ return type.form;
+ }
+
+ private MethodTypeForm(MethodType erase) {
+ this.erasedType = erase;
+ MethodType wt = canonType(erase, WRAP);
+ this.wrappedType = (wt == null) ? erase : wt;
+
+ int ptypeCount = erase.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.
+ if (wt != null) {
+ int pac = 0, lac = 0, prc = 0, lrc = 0;
+ Class<?> epts[] = erase.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 = erase.rtype;
+ 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);
+ } else {
+ this.primCounts = 0;
+ }
+ 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();
+ }
+
+ 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 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;
+ }
+
+ static final int ERASE = 1, WRAP = 2, UNWRAP = 4, ARGS_ONLY = 8;
+
+ static MethodTypeForm findForm(MethodType mt) {
+ MethodType erased = canonType(mt, ERASE);
+ if (erased == null) {
+ // It is already erased. Make a new MethodTypeForm.
+ return new MethodTypeForm(mt);
+ } else {
+ // Share the MethodTypeForm with the erased version.
+ return erased.form;
+ }
+ }
+
+ /** Canonicalize the types in the given method type.
+ * If any types change, intern the new type, and return it.
+ * Otherwise return null.
+ */
+ static MethodType canonType(MethodType mt, int how) {
+ Class<?>[] ptc = MethodTypeForm.canonTypes(mt.ptypes, how);
+ Class<?> rtc = null;
+ if ((how & ARGS_ONLY) == 0)
+ rtc = MethodTypeForm.canonType(mt.rtype, how);
+ if (ptc == null && rtc == null) {
+ // It is already canonical.
+ return null;
+ }
+ // Find the erased version of the method type:
+ if (rtc == null) rtc = mt.rtype;
+ if (ptc == null) ptc = mt.ptypes;
+ return MethodType.makeImpl(rtc, ptc, true);
+ }
+
+ /** Canonicalize the given return or param type.
+ * Return null if the type is already canonicalized.
+ */
+ static Class<?> canonType(Class<?> t, int how) {
+ if (t == Object.class) {
+ // no change, ever
+ } else if (!t.isPrimitive()) {
+ if ((how & UNWRAP) != 0) {
+ Class<?> pt = Wrappers.asPrimitiveType(t);
+ if (pt != t)
+ return pt;
+ }
+ if ((how & ERASE) != 0)
+ return Object.class;
+ } else {
+ if ((how & WRAP) != 0) {
+ if ((how & ERASE) != 0)
+ return Object.class;
+ return Wrappers.asWrapperType(t);
+ }
+ }
+ // 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<?>[] canonTypes(Class<?>[] ts, int how) {
+ Class<?>[] cs = null;
+ for (int imax = ts.length, i = 0; i < imax; i++) {
+ Class<?> c = canonType(ts[i], how);
+ if (c != null) {
+ if (cs == null)
+ cs = ts.clone();
+ cs[i] = c;
+ }
+ }
+ return cs;
+ }
+}
diff --git a/src/share/classes/java/dyn/NoAccessException.java b/src/share/classes/java/dyn/NoAccessException.java
new file mode 100644
index 0000000..c6c2c7b
--- /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 0000000..7edba18
--- /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.
+ * <p>
+ * 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 0000000..858d0e9
--- /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 0000000..b69ff45
--- /dev/null
+++ b/src/share/classes/sun/dyn/Access.java
@@ -0,0 +1,93 @@
+/*
+ * 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.MethodHandles;
+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.
+ */
+ static private 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 (callc.getClassLoader() == Access.class.getClassLoader()) {
+ String callcName = callc.getName();
+ for (String friend : FRIENDS) {
+ if (callcName.startsWith(friend))
+ return TOKEN;
+ }
+ }
+ throw new IllegalAccessError("bad caller: " + callc);
+ }
+
+ /**
+ * 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() {
+ Class<?> callc = Reflection.getCallerClass(3);
+ 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 0000000..22aa1ba
--- /dev/null
+++ b/src/share/classes/sun/dyn/AdapterMethodHandle.java
@@ -0,0 +1,593 @@
+/*
+ * 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.Wrappers;
+import java.dyn.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+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.parameterSlot(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
+ * @throws IllegalArgumentException if the adaptation cannot be made
+ * directly by a JVM-level adapter, without help from Java code
+ */
+ public static MethodHandle makePairwiseConvert(Access token,
+ MethodType newType, MethodHandle target) {
+ Access.check(token);
+ MethodType oldType = target.type();
+ if (newType == oldType) return target;
+
+ // caller responsibility:
+ assert(canPairwiseConvert(newType, oldType));
+
+ // 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.type() == midType);
+ }
+ if (adapter.type() != newType) {
+ // Only trivial conversions remain.
+ adapter = makeRetypeOnly(IMPL_TOKEN, newType, adapter);
+ // 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)
+ return makePairwiseConvert(token, newType, target); // well, that was easy
+
+ // 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;
+ char c = Wrappers.basicTypeChar(type);
+ switch (c) {
+ case 'Z': return T_BOOLEAN;
+ case 'C': return T_CHAR;
+ case 'F': return T_FLOAT;
+ case 'D': return T_DOUBLE;
+ case 'B': return T_BYTE;
+ case 'S': return T_SHORT;
+ case 'I': return T_INT;
+ case 'J': return T_LONG;
+ case 'L': return T_OBJECT;
+ }
+ 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);
+ }
+
+ @Override
+ public String toString() {
+ return addTypeString("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) {
+ Class<?> src = targetType.returnType();
+ Class<?> dst = adapterType.returnType();
+ if (VerifyType.canPassUnchecked(src, dst) <= 0)
+ return -1;
+ int nargs = adapterType.parameterCount();
+ if (nargs != targetType.parameterCount())
+ return -1;
+ int diff = diffTypes(adapterType, 0, targetType, 0, nargs);
+ System.out.println("diff "+adapterType);
+ System.out.println(" "+diff+" "+targetType);
+ return diff;
+ }
+ private static int diffTypes(MethodType adapterType, int tstart,
+ MethodType targetType, int astart,
+ int nargs) {
+ int res = 0;
+ for (int i = 0; i < nargs; i++) {
+ Class<?> src = adapterType.parameterType(tstart+i);
+ Class<?> dest = targetType.parameterType(astart+i);
+ if (VerifyType.canPassUnchecked(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) {
+ int diff = diffTypes(newType, targetType);
+ // %%% 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. */
+ public static MethodHandle makeRetypeOnly(Access token,
+ MethodType newType, MethodHandle target) {
+ Access.check(token);
+ assert(canRetypeOnly(newType, target.type()));
+ // %%% TO DO: If adapter is already an adapter, fold in the retyping.
+ 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) {
+ 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);
+ 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.
+ */
+ public static MethodHandle makeCheckCast(Access token,
+ MethodType newType, MethodHandle target,
+ int arg, Class<?> castType) {
+ Access.check(token);
+ assert(canCheckCast(newType, target.type(), arg, castType));
+ 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) {
+ 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);
+ 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 (Wrappers.isFloating(dst)) {
+ // both must be floating types
+ return Wrappers.isFloating(src);
+ } else {
+ // both are integral, and all combinations work fine
+ assert(Wrappers.isIntegral(src) && Wrappers.isIntegral(dst));
+ 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.
+ */
+ 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);
+ assert(canPrimCast(newType, oldType, arg, convType));
+ 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) {
+ Class<?> src = newType.parameterType(arg);
+ Class<?> dst = targetType.parameterType(arg);
+ Class<?> boxType = Wrappers.asWrapperType(convType);
+ convType = Wrappers.asPrimitiveType(convType);
+ if (!canCheckCast(src, boxType)
+ || boxType == convType
+ || !VerifyType.isNullConversion(convType, dst))
+ return false;
+ int diff = diffTypes(newType, targetType);
+ 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() && Wrappers.asPrimitiveType(dst).isPrimitive());
+ }
+
+ /** Factory method: Unbox the given argument. */
+ 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 = Wrappers.asWrapperType(convType);
+ Class<?> primType = Wrappers.asPrimitiveType(convType);
+ assert(canUnboxArgument(newType, oldType, arg, convType));
+ 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) {
+ return false; // PRIM_TO_REF NYI
+ }
+
+ /** Factory method: Unbox the given argument. */
+ 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
+ throw new InternalError("PRIM_TO_REF NYI");
+ }
+
+ // 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) {
+ List<Class<?>> ptypes = targetType.parameterList();
+ int nptypes = ptypes.size();
+ if ((dropArgPos | dropArgCount) < 0)
+ return false;
+ if (dropArgPos == 0)
+ ptypes = ptypes.subList(dropArgCount, nptypes);
+ else if (dropArgPos + dropArgCount == nptypes)
+ ptypes = ptypes.subList(0, nptypes - dropArgCount);
+ else {
+ if (dropArgPos > nptypes ||
+ dropArgPos + dropArgCount > nptypes)
+ return false;
+ ptypes = new ArrayList<Class<?>>(ptypes);
+ ptypes.subList(dropArgPos, dropArgPos + dropArgCount).clear();
+ }
+ MethodType midType = MethodType.make(targetType.returnType(), ptypes);
+ return diffTypes(newType, midType) == 0;
+ }
+
+ /** Factory method: Drop selected initial or final arguments. */
+ public static MethodHandle makeDropArguments(Access token,
+ MethodType newType, MethodHandle target,
+ int dropArgPos, int dropArgCount) {
+ Access.check(token);
+ assert(canDropArguments(newType, target.type(), dropArgPos, dropArgCount));
+ MethodType mt = target.type();
+ int argCount = mt.parameterCount();
+ int dropSlotCount, dropSlotPos;
+ if (dropArgCount <= 0) {
+ return makeRetypeOnly(IMPL_TOKEN, newType, target);
+ } else 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.parameterSlot(lastDroppedArg);
+ int lastKeptSlot = mt.parameterSlot(lastKeptArg);
+ dropSlotCount = lastKeptSlot - dropSlotPos;
+ assert(dropSlotCount >= dropArgCount);
+ }
+ long conv = makeConv(OP_DROP_ARGS, dropArgPos, +dropSlotCount);
+ return new AdapterMethodHandle(target, newType, dropSlotCount, conv);
+ }
+
+ // TO DO: makeCollectArguments, makeSpreadArguments, 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 0000000..22057f4
--- /dev/null
+++ b/src/share/classes/sun/dyn/BoundMethodHandle.java
@@ -0,0 +1,88 @@
+/*
+ * 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 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 = mh.type().parameterType(0).cast(argument);
+ 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;
+ }
+ }
+
+ /** Insert an argument into an arbitrary method handle.
+ * If argnum is zero, inserts the first argument, etc.
+ */
+ BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
+ super(Access.TOKEN, mh.type().dropParameterType(argnum));
+ this.argument = mh.type().parameterType(argnum).cast(argument);
+ this.vmargslot = this.type().parameterSlot(argnum-1);
+ System.out.println("init BMH type="+type()+" argnum="+argnum+" vmargslot="+vmargslot);
+ if (MethodHandleNatives.JVM_SUPPORT) {
+ this.vmtarget = null; // maybe updated by JVM
+ MethodHandleNatives.init(this, mh, argnum);
+ } else {
+ this.vmtarget = mh;
+ }
+ }
+
+ /** For subclasses only.
+ */
+ BoundMethodHandle(MethodType type, Object argument, int vmargslot) {
+ super(Access.TOKEN, type);
+ this.argument = argument;
+ this.vmargslot = vmargslot;
+ assert(this.getClass() != BoundMethodHandle.class);
+ }
+
+ @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 100755
index 0000000..41349fb
--- /dev/null
+++ b/src/share/classes/sun/dyn/CallSiteImpl.java
@@ -0,0 +1,64 @@
+/*
+ * 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:
+ final int callerMID, callerBCI;
+
+ private CallSiteImpl(Class<?> caller, String name, MethodType type,
+ int callerMID, int callerBCI) {
+ super(caller, name, type);
+ this.callerMID = callerMID;
+ this.callerBCI = callerBCI;
+ }
+
+ @Override
+ public void setTarget(MethodHandle mh) {
+ checkTarget(mh);
+ if (MethodHandleNatives.JVM_SUPPORT)
+ MethodHandleNatives.linkCallSite(this, (MethodHandle) mh);
+ else
+ super.setTarget(mh);
+ }
+
+ // this is the up-call from the JVM:
+ static CallSite makeSite(Class<?> caller, String name, MethodType type,
+ int callerMID, int callerBCI) {
+ CallSiteImpl site = new CallSiteImpl(caller, name, type,
+ 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 0000000..e571fda
--- /dev/null
+++ b/src/share/classes/sun/dyn/DirectMethodHandle.java
@@ -0,0 +1,53 @@
+/*
+ * 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<?> caller) {
+ super(Access.TOKEN, mtype);
+
+ assert(m.isMethod() || !doDispatch && m.isConstructor());
+ if (!m.isResolved())
+ throw new InternalError();
+ MethodHandleNatives.init(this, (Object) m, doDispatch, caller);
+ }
+
+ boolean isValid() {
+ return (vmindex != VM_INDEX_UNINITIALIZED);
+ }
+}
diff --git a/src/share/classes/sun/dyn/JavaMethodHandle.java b/src/share/classes/sun/dyn/JavaMethodHandle.java
new file mode 100644
index 0000000..33a688c
--- /dev/null
+++ b/src/share/classes/sun/dyn/JavaMethodHandle.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.*;
+import java.dyn.MethodHandle;
+import java.dyn.MethodHandles;
+import java.dyn.MethodType;
+
+/**
+ * General-purpose method handle combinator.
+ * It is invoked on an argument list, and performs whatever processing
+ * is needed, eventually returning the required return value.
+ * Note the distinctions from the low-level adapters supported directly
+ * by the JVM. Those low-level adapters perform argument conversion
+ * and delegate directly to target method handles, without executing
+ * any Java code. These Java-level adapters execute Java code to define
+ * their method handle semantics; whether they delegate to other method
+ * handles (as they often do) is a detail of that Java code.
+ * <p>
+ * If you are creating a method handle for a specific method type,
+ * do not use this class; simply use {@link MethodHandles#bind}.
+ * This class is designed for applications which must be polymorphic
+ * across a range of method types.
+ *
+ * @author jrose
+ */
+public abstract class JavaMethodHandle {
+ private final MethodType exactType;
+ private final MethodType rawType;
+ private final String rawName;
+
+ // summary of the structure of type
+ //private final int fingerprint;
+
+ /** Intended type of any method handle made from this adapter. */
+ public final MethodType type() {
+ return exactType;
+ }
+
+ /** The (exact) type of my invoke function. */
+ protected final MethodType rawType() {
+ return rawType;
+ }
+
+ /** The name of my categorical invoke function, e.g., invoke_L2. */
+ protected final String rawName() {
+ return rawName;
+ }
+
+ /**
+ * Subclasses must provide the method type which this adapter
+ * implements.
+ * @param type
+ */
+ protected JavaMethodHandle(MethodType type) {
+ this.exactType = type;
+ this.rawType = computeRawType(type);
+ this.rawName = computeRawName(type, rawType);
+ }
+
+ /** Return the type of one of my invoke functions.
+ * Can be extended by subclasses to increase the repertoire
+ * of raw invocation actions.
+ */
+ protected MethodType computeRawType(MethodType type) {
+ return categoricalMethodType(methodTypeCategory(type));
+ }
+
+ /** Return the type of one of my invoke functions.
+ * By default, of the form "invoke_$X$N" where $X is a
+ * single basic-type character (as found in bytecode signatures)
+ * and $N is the (decimal) number of parameters in the raw type.
+ * Can be extended by subclasses to increase the repertoire
+ * of raw invocation actions.
+ */
+ protected String computeRawName(MethodType exactType, MethodType rawType) {
+ char rc = Wrappers.basicTypeChar(rawType.returnType());
+ int nargs = rawType.parameterCount();
+ return "invoke_"+rc+nargs;
+ }
+
+ /** Return a permutation array which tells, for each argument
+ * to the categorical invoker, which incoming argument
+ * will supply the value (perhaps after suitable conversion).
+ * Returns null if the conversions are simply pairwise,
+ * with no reordering of argument values.
+ */
+ protected char[] computePermutation(MethodType exactType, MethodType rawType) {
+ return methodTypeCategoricalPermutation(exactType);
+ }
+
+ protected final RuntimeException illegal() {
+ return new UnsupportedOperationException();
+ }
+
+ /** Produce a method handle that binds the appropriate invoke
+ * method of this combinator.
+ * @return a method handle of this combinator's type
+ */
+ public MethodHandle handle() {
+ //return permuteAndRetypeArguments(rawHandle(), type(), computePermutation());
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ protected MethodHandle rawHandle() {
+ return MethodHandles.bind(this, rawName(), rawType());
+ }
+
+ // Pluggable behaviors, specific to particular arguments or return values.
+ protected Object res(Object x) { return exactType.returnType().cast(x); }
+ protected int res(int x) { return x; }
+ protected long res(long x) { return x; }
+
+ // A categorical method type is one which (a) avoids boxing,
+ // (b) avoids passing unused arguments, and (c) can represent
+ // the calling sequence of an original exact type.
+ //
+ // It's good to limit the number of categorical types,
+ // so that we don't have to generate too many different
+ // code shapes for Java-based adapters.
+ // We (1) erase all references to Object,
+ // (2) widen primitives to int when they fit,
+ // (3) widen other primitive arguments to long,
+ // (4) sort the argument list (refs, ints, then longs), and
+ // (5) promote ints to longs if any longs are present.
+ //
+ // Because it is harmless to ignore an int return value,
+ // if the caller is intending a void result, void can be
+ // erased to int, as with other short primitives.
+ // The distinction in return values between reference,
+ // int, long, float, and double.
+ //
+ // Therefore, the grammar of signatures is:
+ // {Object|int|long|float|double} ( Object* {int* | long*} )
+ // This gives 5(2N+1) as the number of categorical signatures
+ // of N arguments, or 5(N+1)^2 for signatures of <=N arguments.
+ // Clearly some of this needs to get code-generated lazily,
+ // but we supoprt a core set of signatures here.
+
+ protected static int category(Class<?> type) {
+ if (!type.isPrimitive()) return 0;
+ if (type == void.class) return -1;
+ if (Wrappers.bitWidth(type) > 32) return 2;
+ return 1;
+ }
+ protected static Class<?> categoricalType(int cat) {
+ switch (cat) {
+ case 0: return Object.class;
+ case 1: return int.class;
+ case 2: return long.class;
+ case 3: return void.class;
+ }
+ throw new InternalError();
+ }
+ protected static int methodTypeCategory(MethodType type) {
+ int nargs = type.parameterCount();
+ int nprims = 0;
+ int nlongs = 0;
+ for (int i = 0; i < nargs; i++) {
+ int cat = category(type.parameterType(i));
+ if (cat != 0) {
+ ++nprims;
+ if (cat == 2) ++nlongs;
+ }
+ }
+ int nrefs = nargs - nprims;
+ int nints = nprims - nlongs;
+ int rcat = category(type.returnType());
+ if (nlongs != 0) { nlongs += nints; nints = 0; } // point (5)
+ return (nlongs << 24) | (nints << 16) | (nrefs << 8) | rcat;
+ }
+ protected static char[] methodTypeCategoricalPermutation(MethodType type) {
+ return methodTypeCategoricalPermutation(type, methodTypeCategory(type));
+ }
+ protected static char[] methodTypeCategoricalPermutation(MethodType type, int category) {
+ int x = category;
+ int rcat = (x & 0xFF); x >>= 8;
+ int nrefs = (x & 0xFF); x >>= 8;
+ int nints = (x & 0xFF); x >>= 8;
+ int nlongs = (x & 0xFF); x >>= 8;
+ int nargs = type.parameterCount();
+ assert(nargs == nrefs + nints + nlongs);
+ char[] permutation = new char[nargs];
+ int nextref = 0, nextint = nrefs, nextlong = nrefs + nints;
+ int reflimit = nextint, intlimit = nextlong, longlimit = nargs;
+ boolean sawReorder = false;
+ for (int i = 0; i < nargs; i++) {
+ int cat = category(type.parameterType(i));
+ int nextarg;
+ if (cat == 0)
+ nextarg = nextref++;
+ else if (nextint == intlimit || cat == 2)
+ nextarg = nextlong++;
+ else
+ nextarg = nextint++;
+ // Next outgoing argument (to the categorical invoke method)
+ // is going to come from exactType.parameterType(i)
+ assert(permutation[nextarg] == 0);
+ permutation[nextarg] = (char) i;
+ if (nextarg != i) sawReorder = true;
+ }
+ assert(nextref == reflimit);
+ assert(nextint == intlimit);
+ assert(nextlong == longlimit);
+ if (!sawReorder) return null; // this is always a special case
+ return permutation;
+ }
+ protected static MethodType categoricalMethodType(int category) {
+ int x = category;
+ int rcat = (x & 0xFF); x >>= 8;
+ int nrefs = (x & 0xFF); x >>= 8;
+ int nints = (x & 0xFF); x >>= 8;
+ int nlongs = (x & 0xFF); x >>= 8;
+ return categoricalMethodType(rcat, nrefs, nints, nlongs);
+ }
+ protected static MethodType categoricalMethodType(int rcat, int nrefs, int nints, int nlongs) {
+ int nshorts = nints + nlongs;
+ Class<?> ptypes[] = new Class<?>[nshorts + nlongs];
+ int cat = 0;
+ Class<?> ptype = categoricalType(cat);
+ for (int i = 0; i < ptypes.length; i++) {
+ if (i == nrefs) ptype = categoricalType(++cat);
+ if (i == nshorts) ptype = categoricalType(++cat);
+ ptypes[i] = ptype;
+ }
+ Class<?> rtype = categoricalType(rcat);
+ return MethodType.make(rtype, ptypes);
+ }
+
+ // Categorical methods up to arity 5 (180 of them).
+ // They are (arbitrarily) split up in subclasses.
+ /*
+ (defun insert-categorical-methods (max-arity &optional max-rcat)
+ (let* ((types (list "Object" "int " "long " "float " "double"))
+ (tchars (list "L" "I" "J" "F" "D"))
+ invoke acat nrefs
+ (do-case (function (lambda (rcat nargs nrefs nints nlongs)
+ (setq invoke (format "invoke_%s%d" (elt tchars rcat) nargs))
+ (insert "\n protected " (elt types rcat) " " invoke "(")
+ (dotimes (arg nargs)
+ (setq acat (cond ((< arg nrefs) 0) ((< arg (+ nrefs nints)) 1) (t 2)))
+ (insert (if (= 0 arg) "" ", ") (elt types acat) (format " a%d" arg)))
+ (insert ") { throw illegal(); }")))))
+ (dotimes (rcat (1+ (or max-rcat 4))) (dotimes (nargs (1+ max-arity))
+ (setq invoke (format "%s%d" (elt tchars rcat) nargs))
+ (insert "\n static class " invoke " extends JavaMethodHandle {")
+ (insert "\n " invoke "(MethodType type) { super(type); }")
+ (funcall do-case rcat nargs nargs 0 0)
+ (dotimes (j 2) (dotimes (i nargs) (setq nrefs (- nargs i 1))
+ (if (= j 0) (funcall do-case rcat nargs nrefs (- nargs nrefs) 0)
+ (funcall do-case rcat nargs nrefs 0 (- nargs nrefs)))))
+ (insert "\n }")
+ ))))
+ */
+ // (progn (insert-categorical-methods 5 4) (insert "\n}"))
+ /*
+ static class L0 extends JavaMethodHandle {
+ L0(MethodType type) { super(type); }
+ protected Object invoke_L0() { throw illegal(); }
+ }
+ static class L1 extends JavaMethodHandle {
+ L1(MethodType type) { super(type); }
+ protected Object invoke_L1(Object a0) { throw illegal(); }
+ protected Object invoke_L1(int a0) { throw illegal(); }
+ protected Object invoke_L1(long a0) { throw illegal(); }
+ }
+ static class L2 extends JavaMethodHandle {
+ L2(MethodType type) { super(type); }
+ protected Object invoke_L2(Object a0, Object a1) { throw illegal(); }
+ protected Object invoke_L2(Object a0, int a1) { throw illegal(); }
+ protected Object invoke_L2(int a0, int a1) { throw illegal(); }
+ protected Object invoke_L2(Object a0, long a1) { throw illegal(); }
+ protected Object invoke_L2(long a0, long a1) { throw illegal(); }
+ }
+ static class L3 extends JavaMethodHandle {
+ L3(MethodType type) { super(type); }
+ protected Object invoke_L3(Object a0, Object a1, Object a2) { throw illegal(); }
+ protected Object invoke_L3(Object a0, Object a1, int a2) { throw illegal(); }
+ protected Object invoke_L3(Object a0, int a1, int a2) { throw illegal(); }
+ protected Object invoke_L3(int a0, int a1, int a2) { throw illegal(); }
+ protected Object invoke_L3(Object a0, Object a1, long a2) { throw illegal(); }
+ protected Object invoke_L3(Object a0, long a1, long a2) { throw illegal(); }
+ protected Object invoke_L3(long a0, long a1, long a2) { throw illegal(); }
+ }
+ static class L4 extends JavaMethodHandle {
+ L4(MethodType type) { super(type); }
+ protected Object invoke_L4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); }
+ protected Object invoke_L4(Object a0, Object a1, Object a2, int a3) { throw illegal(); }
+ protected Object invoke_L4(Object a0, Object a1, int a2, int a3) { throw illegal(); }
+ protected Object invoke_L4(Object a0, int a1, int a2, int a3) { throw illegal(); }
+ protected Object invoke_L4(int a0, int a1, int a2, int a3) { throw illegal(); }
+ protected Object invoke_L4(Object a0, Object a1, Object a2, long a3) { throw illegal(); }
+ protected Object invoke_L4(Object a0, Object a1, long a2, long a3) { throw illegal(); }
+ protected Object invoke_L4(Object a0, long a1, long a2, long a3) { throw illegal(); }
+ protected Object invoke_L4(long a0, long a1, long a2, long a3) { throw illegal(); }
+ }
+ static class L5 extends JavaMethodHandle {
+ L5(MethodType type) { super(type); }
+ protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); }
+ protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); }
+ protected Object invoke_L5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); }
+ protected Object invoke_L5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); }
+ protected Object invoke_L5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected Object invoke_L5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); }
+ protected Object invoke_L5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); }
+ protected Object invoke_L5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); }
+ protected Object invoke_L5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ protected Object invoke_L5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ }
+ static class I0 extends JavaMethodHandle {
+ I0(MethodType type) { super(type); }
+ protected int invoke_I0() { throw illegal(); }
+ }
+ static class I1 extends JavaMethodHandle {
+ I1(MethodType type) { super(type); }
+ protected int invoke_I1(Object a0) { throw illegal(); }
+ protected int invoke_I1(int a0) { throw illegal(); }
+ protected int invoke_I1(long a0) { throw illegal(); }
+ }
+ static class I2 extends JavaMethodHandle {
+ I2(MethodType type) { super(type); }
+ protected int invoke_I2(Object a0, Object a1) { throw illegal(); }
+ protected int invoke_I2(Object a0, int a1) { throw illegal(); }
+ protected int invoke_I2(int a0, int a1) { throw illegal(); }
+ protected int invoke_I2(Object a0, long a1) { throw illegal(); }
+ protected int invoke_I2(long a0, long a1) { throw illegal(); }
+ }
+ static class I3 extends JavaMethodHandle {
+ I3(MethodType type) { super(type); }
+ protected int invoke_I3(Object a0, Object a1, Object a2) { throw illegal(); }
+ protected int invoke_I3(Object a0, Object a1, int a2) { throw illegal(); }
+ protected int invoke_I3(Object a0, int a1, int a2) { throw illegal(); }
+ protected int invoke_I3(int a0, int a1, int a2) { throw illegal(); }
+ protected int invoke_I3(Object a0, Object a1, long a2) { throw illegal(); }
+ protected int invoke_I3(Object a0, long a1, long a2) { throw illegal(); }
+ protected int invoke_I3(long a0, long a1, long a2) { throw illegal(); }
+ }
+ static class I4 extends JavaMethodHandle {
+ I4(MethodType type) { super(type); }
+ protected int invoke_I4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); }
+ protected int invoke_I4(Object a0, Object a1, Object a2, int a3) { throw illegal(); }
+ protected int invoke_I4(Object a0, Object a1, int a2, int a3) { throw illegal(); }
+ protected int invoke_I4(Object a0, int a1, int a2, int a3) { throw illegal(); }
+ protected int invoke_I4(int a0, int a1, int a2, int a3) { throw illegal(); }
+ protected int invoke_I4(Object a0, Object a1, Object a2, long a3) { throw illegal(); }
+ protected int invoke_I4(Object a0, Object a1, long a2, long a3) { throw illegal(); }
+ protected int invoke_I4(Object a0, long a1, long a2, long a3) { throw illegal(); }
+ protected int invoke_I4(long a0, long a1, long a2, long a3) { throw illegal(); }
+ }
+ static class I5 extends JavaMethodHandle {
+ I5(MethodType type) { super(type); }
+ protected int invoke_I5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); }
+ protected int invoke_I5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); }
+ protected int invoke_I5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); }
+ protected int invoke_I5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); }
+ protected int invoke_I5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected int invoke_I5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected int invoke_I5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); }
+ protected int invoke_I5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); }
+ protected int invoke_I5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); }
+ protected int invoke_I5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ protected int invoke_I5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ }
+ static class J0 extends JavaMethodHandle {
+ J0(MethodType type) { super(type); }
+ protected long invoke_J0() { throw illegal(); }
+ }
+ static class J1 extends JavaMethodHandle {
+ J1(MethodType type) { super(type); }
+ protected long invoke_J1(Object a0) { throw illegal(); }
+ protected long invoke_J1(int a0) { throw illegal(); }
+ protected long invoke_J1(long a0) { throw illegal(); }
+ }
+ static class J2 extends JavaMethodHandle {
+ J2(MethodType type) { super(type); }
+ protected long invoke_J2(Object a0, Object a1) { throw illegal(); }
+ protected long invoke_J2(Object a0, int a1) { throw illegal(); }
+ protected long invoke_J2(int a0, int a1) { throw illegal(); }
+ protected long invoke_J2(Object a0, long a1) { throw illegal(); }
+ protected long invoke_J2(long a0, long a1) { throw illegal(); }
+ }
+ static class J3 extends JavaMethodHandle {
+ J3(MethodType type) { super(type); }
+ protected long invoke_J3(Object a0, Object a1, Object a2) { throw illegal(); }
+ protected long invoke_J3(Object a0, Object a1, int a2) { throw illegal(); }
+ protected long invoke_J3(Object a0, int a1, int a2) { throw illegal(); }
+ protected long invoke_J3(int a0, int a1, int a2) { throw illegal(); }
+ protected long invoke_J3(Object a0, Object a1, long a2) { throw illegal(); }
+ protected long invoke_J3(Object a0, long a1, long a2) { throw illegal(); }
+ protected long invoke_J3(long a0, long a1, long a2) { throw illegal(); }
+ }
+ static class J4 extends JavaMethodHandle {
+ J4(MethodType type) { super(type); }
+ protected long invoke_J4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); }
+ protected long invoke_J4(Object a0, Object a1, Object a2, int a3) { throw illegal(); }
+ protected long invoke_J4(Object a0, Object a1, int a2, int a3) { throw illegal(); }
+ protected long invoke_J4(Object a0, int a1, int a2, int a3) { throw illegal(); }
+ protected long invoke_J4(int a0, int a1, int a2, int a3) { throw illegal(); }
+ protected long invoke_J4(Object a0, Object a1, Object a2, long a3) { throw illegal(); }
+ protected long invoke_J4(Object a0, Object a1, long a2, long a3) { throw illegal(); }
+ protected long invoke_J4(Object a0, long a1, long a2, long a3) { throw illegal(); }
+ protected long invoke_J4(long a0, long a1, long a2, long a3) { throw illegal(); }
+ }
+ static class J5 extends JavaMethodHandle {
+ J5(MethodType type) { super(type); }
+ protected long invoke_J5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); }
+ protected long invoke_J5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); }
+ protected long invoke_J5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); }
+ protected long invoke_J5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); }
+ protected long invoke_J5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected long invoke_J5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected long invoke_J5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); }
+ protected long invoke_J5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); }
+ protected long invoke_J5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); }
+ protected long invoke_J5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ protected long invoke_J5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ }
+ static class F0 extends JavaMethodHandle {
+ F0(MethodType type) { super(type); }
+ protected float invoke_F0() { throw illegal(); }
+ }
+ static class F1 extends JavaMethodHandle {
+ F1(MethodType type) { super(type); }
+ protected float invoke_F1(Object a0) { throw illegal(); }
+ protected float invoke_F1(int a0) { throw illegal(); }
+ protected float invoke_F1(long a0) { throw illegal(); }
+ }
+ static class F2 extends JavaMethodHandle {
+ F2(MethodType type) { super(type); }
+ protected float invoke_F2(Object a0, Object a1) { throw illegal(); }
+ protected float invoke_F2(Object a0, int a1) { throw illegal(); }
+ protected float invoke_F2(int a0, int a1) { throw illegal(); }
+ protected float invoke_F2(Object a0, long a1) { throw illegal(); }
+ protected float invoke_F2(long a0, long a1) { throw illegal(); }
+ }
+ static class F3 extends JavaMethodHandle {
+ F3(MethodType type) { super(type); }
+ protected float invoke_F3(Object a0, Object a1, Object a2) { throw illegal(); }
+ protected float invoke_F3(Object a0, Object a1, int a2) { throw illegal(); }
+ protected float invoke_F3(Object a0, int a1, int a2) { throw illegal(); }
+ protected float invoke_F3(int a0, int a1, int a2) { throw illegal(); }
+ protected float invoke_F3(Object a0, Object a1, long a2) { throw illegal(); }
+ protected float invoke_F3(Object a0, long a1, long a2) { throw illegal(); }
+ protected float invoke_F3(long a0, long a1, long a2) { throw illegal(); }
+ }
+ static class F4 extends JavaMethodHandle {
+ F4(MethodType type) { super(type); }
+ protected float invoke_F4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); }
+ protected float invoke_F4(Object a0, Object a1, Object a2, int a3) { throw illegal(); }
+ protected float invoke_F4(Object a0, Object a1, int a2, int a3) { throw illegal(); }
+ protected float invoke_F4(Object a0, int a1, int a2, int a3) { throw illegal(); }
+ protected float invoke_F4(int a0, int a1, int a2, int a3) { throw illegal(); }
+ protected float invoke_F4(Object a0, Object a1, Object a2, long a3) { throw illegal(); }
+ protected float invoke_F4(Object a0, Object a1, long a2, long a3) { throw illegal(); }
+ protected float invoke_F4(Object a0, long a1, long a2, long a3) { throw illegal(); }
+ protected float invoke_F4(long a0, long a1, long a2, long a3) { throw illegal(); }
+ }
+ static class F5 extends JavaMethodHandle {
+ F5(MethodType type) { super(type); }
+ protected float invoke_F5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); }
+ protected float invoke_F5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); }
+ protected float invoke_F5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); }
+ protected float invoke_F5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); }
+ protected float invoke_F5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected float invoke_F5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected float invoke_F5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); }
+ protected float invoke_F5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); }
+ protected float invoke_F5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); }
+ protected float invoke_F5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ protected float invoke_F5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ }
+ static class D0 extends JavaMethodHandle {
+ D0(MethodType type) { super(type); }
+ protected double invoke_D0() { throw illegal(); }
+ }
+ static class D1 extends JavaMethodHandle {
+ D1(MethodType type) { super(type); }
+ protected double invoke_D1(Object a0) { throw illegal(); }
+ protected double invoke_D1(int a0) { throw illegal(); }
+ protected double invoke_D1(long a0) { throw illegal(); }
+ }
+ static class D2 extends JavaMethodHandle {
+ D2(MethodType type) { super(type); }
+ protected double invoke_D2(Object a0, Object a1) { throw illegal(); }
+ protected double invoke_D2(Object a0, int a1) { throw illegal(); }
+ protected double invoke_D2(int a0, int a1) { throw illegal(); }
+ protected double invoke_D2(Object a0, long a1) { throw illegal(); }
+ protected double invoke_D2(long a0, long a1) { throw illegal(); }
+ }
+ static class D3 extends JavaMethodHandle {
+ D3(MethodType type) { super(type); }
+ protected double invoke_D3(Object a0, Object a1, Object a2) { throw illegal(); }
+ protected double invoke_D3(Object a0, Object a1, int a2) { throw illegal(); }
+ protected double invoke_D3(Object a0, int a1, int a2) { throw illegal(); }
+ protected double invoke_D3(int a0, int a1, int a2) { throw illegal(); }
+ protected double invoke_D3(Object a0, Object a1, long a2) { throw illegal(); }
+ protected double invoke_D3(Object a0, long a1, long a2) { throw illegal(); }
+ protected double invoke_D3(long a0, long a1, long a2) { throw illegal(); }
+ }
+ static class D4 extends JavaMethodHandle {
+ D4(MethodType type) { super(type); }
+ protected double invoke_D4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); }
+ protected double invoke_D4(Object a0, Object a1, Object a2, int a3) { throw illegal(); }
+ protected double invoke_D4(Object a0, Object a1, int a2, int a3) { throw illegal(); }
+ protected double invoke_D4(Object a0, int a1, int a2, int a3) { throw illegal(); }
+ protected double invoke_D4(int a0, int a1, int a2, int a3) { throw illegal(); }
+ protected double invoke_D4(Object a0, Object a1, Object a2, long a3) { throw illegal(); }
+ protected double invoke_D4(Object a0, Object a1, long a2, long a3) { throw illegal(); }
+ protected double invoke_D4(Object a0, long a1, long a2, long a3) { throw illegal(); }
+ protected double invoke_D4(long a0, long a1, long a2, long a3) { throw illegal(); }
+ }
+ static class D5 extends JavaMethodHandle {
+ D5(MethodType type) { super(type); }
+ protected double invoke_D5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); }
+ protected double invoke_D5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); }
+ protected double invoke_D5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); }
+ protected double invoke_D5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); }
+ protected double invoke_D5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected double invoke_D5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); }
+ protected double invoke_D5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); }
+ protected double invoke_D5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); }
+ protected double invoke_D5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); }
+ protected double invoke_D5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ protected double invoke_D5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); }
+ }
+ */
+}
diff --git a/src/share/classes/sun/dyn/MemberName.java b/src/share/classes/sun/dyn/MemberName.java
new file mode 100644
index 0000000..1342134
--- /dev/null
+++ b/src/share/classes/sun/dyn/MemberName.java
@@ -0,0 +1,550 @@
+/*
+ * 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.
+ * <p>
+ * 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 = "<init>"; // 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<?> caller) {
+ return newNoAccessException("cannot access", name, caller);
+ }
+ public static NoAccessException newNoAccessException(String message,
+ MemberName name, Class<?> caller) {
+ message += ": " + name;
+ if (caller != null) message += ", from " + caller.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<MemberName> getMembers(Class<?> defc,
+ String matchName, Object matchType,
+ int matchFlags, Class<?> caller) {
+ 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<MemberName[]> bufs = null;
+ for (;;) {
+ int bufCount = MethodHandleNatives.getMembers(defc,
+ matchName, matchSig, matchFlags, caller,
+ 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<MemberName[]>(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<MemberName> result = new ArrayList<MemberName>(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<MemberName> 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<?> caller) {
+ 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<?> caller) {
+ MemberName result = m.clone();
+ if (resolveInPlace(result, searchSupers, caller))
+ return result;
+ return null;
+ }
+ public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> caller) {
+ MemberName result = resolveOrNull(m, searchSupers, caller);
+ if (result != null)
+ return result;
+ throw newNoAccessException(m, caller);
+ }
+ public List<MemberName> getMethods(Class<?> defc, boolean searchSupers,
+ Class<?> caller) {
+ return getMethods(defc, searchSupers, null, null, caller);
+ }
+ public List<MemberName> getMethods(Class<?> defc, boolean searchSupers,
+ String name, MethodType type, Class<?> caller) {
+ int matchFlags = IS_METHOD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
+ return getMembers(defc, name, type, matchFlags, caller);
+ }
+ public List<MemberName> getConstructors(Class<?> defc, Class<?> caller) {
+ return getMembers(defc, null, null, IS_CONSTRUCTOR, caller);
+ }
+ public List<MemberName> getFields(Class<?> defc, boolean searchSupers,
+ Class<?> caller) {
+ return getFields(defc, searchSupers, null, null, caller);
+ }
+ public List<MemberName> getFields(Class<?> defc, boolean searchSupers,
+ String name, Class<?> type, Class<?> caller) {
+ int matchFlags = IS_FIELD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
+ return getMembers(defc, name, type, matchFlags, caller);
+ }
+ public List<MemberName> getNestedTypes(Class<?> defc, boolean searchSupers,
+ Class<?> caller) {
+ int matchFlags = IS_TYPE | (searchSupers ? SEARCH_ALL_SUPERS : 0);
+ return getMembers(defc, null, null, matchFlags, caller);
+ }
+ 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 0000000..3de3f27
--- /dev/null
+++ b/src/share/classes/sun/dyn/MethodHandleImpl.java
@@ -0,0 +1,274 @@
+/*
+ * 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;
+import sun.dyn.JavaMethodHandle;
+import sun.dyn.util.MethodHandleInvoker;
+import java.dyn.NoAccessException;
+import org.omg.PortableInterceptor.AdapterManagerIdHelper;
+import static sun.dyn.MemberName.newIllegalArgumentException;
+import static sun.dyn.MemberName.newNoAccessException;
+
+/**
+ * Base class for method handles which are known to the Hotspot JVM.
+ * @author jrose
+ */
+public abstract class MethodHandleImpl {
+
+ // Fields 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
+
+ // 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;
+
+
+ /** Look up a given method.
+ * Callable only from java.dyn and related packages.
+ * <p>
+ * The resulting method handle type will be of the given type,
+ * with a receiver type {@code rcvc} prepended if the member is not static.
+ * <p>
+ * Access checks are made as of the given caller.
+ * In particular, if the method is protected and {@code defc} is in a
+ * different package from the caller, then {@code rcvc} must be
+ * caller or a subclass.
+ * @param token Proof that the caller 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 caller if not null, access-check relative to this class ????
+ * @return a direct handle to the matching method
+ * @throws NoAccessException if the given method cannot be accessed by caller
+ */
+ public static
+ MethodHandle findMethod(Access token, MemberName method,
+ boolean doDispatch, Class<?> caller) {
+ 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, caller);
+ if (!mh.isValid())
+ throw newNoAccessException(method, caller);
+ return mh;
+ }
+
+ public static
+ MethodHandle accessField(Access token,
+ MemberName member, boolean isSetter,
+ Class<?> caller) {
+ 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,
+ Class<?> caller) {
+ 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, boolean newVarargs,
+ MethodType oldType, boolean oldVarargs,
+ String permutationOrNull) {
+ Access.check(token);
+ if (AdapterMethodHandle.canPairwiseConvert(newType, oldType))
+ return AdapterMethodHandle.makePairwiseConvert(token, newType, target);
+ 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).
+ final MethodHandleInvoker invoke1
+ = MethodHandleInvoker.make(test.type());
+ final MethodHandleInvoker invoke2
+ = MethodHandleInvoker.make(target.type());
+ class Guarder {
+ Object invoke(Object x) {
+ // If javac supports MethodHandle.invoke directly:
+ //z = vatest.invoke<boolean>(arguments);
+ // If javac does not support direct MH.invoke calls:
+ boolean z = (Boolean) invoke1.invoke_1(test, x);
+ MethodHandle mh = (z ? target : fallback);
+ return invoke2.invoke_1(mh, x);
+ }
+ MethodHandle handle() {
+ MethodType invokeType = MethodType.makeGeneric(0, true);
+ MethodHandle vh = MethodHandles.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 "<unknown>";
+ return name.toString();
+ }
+
+ protected String addTypeString(String name) {
+ return name+((MethodHandle)this).type();
+ }
+ static RuntimeException newIllegalArgumentException(String string) {
+ return new IllegalArgumentException(string);
+ }
+
+ @Override
+ public String toString() {
+ return addTypeString(basicToString((MethodHandle)this));
+ }
+}
diff --git a/src/share/classes/sun/dyn/MethodHandleNatives.java b/src/share/classes/sun/dyn/MethodHandleNatives.java
new file mode 100644
index 0000000..19a1544
--- /dev/null
+++ b/src/share/classes/sun/dyn/MethodHandleNatives.java
@@ -0,0 +1,245 @@
+/*
+ * 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);
+
+ /// 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_;
+ }
+
+ // 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;
+
+ /**
+ * Basic types as encoded in the JVM. These code values are not
+ * intended for use outside this class. They are used as part of
+ * a private interface between the JVM and this class.
+ */
+ static final int
+ T_BOOLEAN = 4,
+ T_CHAR = 5,
+ T_FLOAT = 6,
+ T_DOUBLE = 7,
+ T_BYTE = 8,
+ T_SHORT = 9,
+ T_INT = 10,
+ T_LONG = 11,
+ T_OBJECT = 12,
+ //T_ARRAY = 13
+ T_VOID = 14;
+ //T_ADDRESS = 15
+ }
+
+ private static native int getNamedCon(int which, Object[] name);
+ static boolean verifyConstants() {
+ Object[] box = { null };
+ for (int i = 0; ; i++) {
+ box[0] = null;
+ int vmval = getNamedCon(i, box);
+ if (box[0] == null) break;
+ String name = (String) box[0];
+ try {
+ Field con = Constants.class.getDeclaredField(name);
+ int jval = con.getInt(null);
+ if (jval != vmval)
+ throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval);
+ } catch (Exception ex) {
+ throw new InternalError(name+": access failed, got "+ex);
+ }
+ }
+ return true;
+ }
+ static {
+ if (JVM_SUPPORT) verifyConstants();
+ }
+}
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 0000000..93aa427
--- /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 0000000..9a8b8a1
--- /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.
+ *
+ * <h3>Avoiding Dangerous Characters </h3>
+ *
+ * <p>
+ * 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 <cite>dangerous characters</cite>.
+ * 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.
+ * </p>
+ * <p>
+ * Dangerous characters are the union of all characters forbidden
+ * or otherwise restricted by the JVM specification,
+ * plus their mates, if they are brackets
+ * (<code><big><b>[</b></big></code> and <code><big><b>]</b></big></code>,
+ * <code><big><b>&lt;</b></big></code> and <code><big><b>&gt;</b></big></code>),
+ * plus, arbitrarily, the colon character <code><big><b>:</b></big></code>.
+ * 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).
+ * </p>
+ * <p>
+ * The escape character is backslash <code><big><b>\</b></big></code>
+ * (also known as reverse solidus).
+ * This character is, until now, unheard of in bytecode names,
+ * but traditional in the proposed role.
+ *
+ * </p>
+ * <h3> Replacement Characters </h3>
+ *
+ *
+ * <p>
+ * Every escape sequence is two characters
+ * (in fact, two UTF8 bytes) beginning with
+ * the escape character and followed by a
+ * <cite>replacement character</cite>.
+ * (Since the replacement character is never a backslash,
+ * iterated manglings do not double in size.)
+ * </p>
+ * <p>
+ * Each dangerous character has some rough visual similarity
+ * to its corresponding replacement character.
+ * This makes mangled symbols easier to recognize by sight.
+ * </p>
+ * <p>
+ * The dangerous characters are
+ * <code><big><b>/</b></big></code> (forward slash, used to delimit package components),
+ * <code><big><b>.</b></big></code> (dot, also a package delimiter),
+ * <code><big><b>;</b></big></code> (semicolon, used in signatures),
+ * <code><big><b>$</b></big></code> (dollar, used in inner classes and synthetic members),
+ * <code><big><b>&lt;</b></big></code> (left angle),
+ * <code><big><b>&gt;</b></big></code> (right angle),
+ * <code><big><b>[</b></big></code> (left square bracket, used in array types),
+ * <code><big><b>]</b></big></code> (right square bracket, reserved in this scheme for language use),
+ * and <code><big><b>:</b></big></code> (colon, reserved in this scheme for language use).
+ * Their replacements are, respectively,
+ * <code><big><b>|</b></big></code> (vertical bar),
+ * <code><big><b>,</b></big></code> (comma),
+ * <code><big><b>?</b></big></code> (question mark),
+ * <code><big><b>%</b></big></code> (percent),
+ * <code><big><b>^</b></big></code> (caret),
+ * <code><big><b>_</b></big></code> (underscore), and
+ * <code><big><b>{</b></big></code> (left curly bracket),
+ * <code><big><b>}</b></big></code> (right curly bracket),
+ * <code><big><b>!</b></big></code> (exclamation mark).
+ * In addition, the replacement character for the escape character itself is
+ * <code><big><b>-</b></big></code> (hyphen),
+ * and the replacement character for the null prefix is
+ * <code><big><b>=</b></big></code> (equal sign).
+ * </p>
+ * <p>
+ * An escape character <code><big><b>\</b></big></code>
+ * 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 <em>not</em> form an escape sequence.
+ * </p>
+ * <p>
+ * Each escape sequence replaces a so-called <cite>original character</cite>
+ * which is either one of the dangerous characters or the escape character.
+ * A null prefix replaces an initial null string, not a character.
+ * </p>
+ * <p>
+ * 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 <cite>accidental escapes</cite>, apparent escape
+ * sequences which must not be interpreted as manglings.
+ * These are disabled by replacing their leading backslash with an
+ * escape sequence (<code><big><b>\-</b></big></code>). To mangle a string, three logical steps
+ * are required, though they may be carried out in one pass:
+ * </p>
+ * <ol>
+ * <li>In each accidental escape, replace the backslash with an escape sequence
+ * (<code><big><b>\-</b></big></code>).</li>
+ * <li>Replace each dangerous character with an escape sequence
+ * (<code><big><b>\|</b></big></code> for <code><big><b>/</b></big></code>, etc.).</li>
+ * <li>If the first two steps introduced any change, <em>and</em>
+ * if the string does not already begin with a backslash, prepend a null prefix (<code><big><b>\=</b></big></code>).</li>
+ * </ol>
+ *
+ * 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.
+ * <p>Spelling strings which contain accidental
+ * escapes <em>must</em> 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.
+ *
+ * </p>
+ * <h3> Nice Properties </h3>
+ *
+ * <p>
+ * 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 <cite>self-mangling</cite>.
+ * Almost all strings are self-mangling.
+ * In practice, to demangle almost any name &ldquo;found in nature&rdquo;,
+ * simply verify that it does not begin with a backslash.
+ * </p>
+ * <p>
+ * Mangling is a one-to-one function, while demangling
+ * is a many-to-one function.
+ * A mangled string is defined as <cite>validly mangled</cite> if
+ * it is in fact the unique mangling of its spelling string.
+ * Three examples of invalidly mangled strings are <code><big><b>\=foo</b></big></code>,
+ * <code><big><b>\-bar</b></big></code>, and <code><big><b>baz\!</b></big></code>, which demangle to <code><big><b>foo</b></big></code>, <code><big><b>\bar</b></big></code>, and
+ * <code><big><b>baz\!</b></big></code>, but then remangle to <code><big><b>foo</b></big></code>, <code><big><b>\bar</b></big></code>, and <code><big><b>\=baz\-!</b></big></code>.
+ * 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ * <ul>
+ * <li>Any prefix of a validly mangled string is also validly mangled,
+ * although a null prefix may need to be removed.</li>
+ * <li>Any suffix of a validly mangled string is also validly mangled,
+ * although a null prefix may need to be added.</li>
+ * <li>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.</li>
+ * </ul>
+ * <p>If languages that include non-Java symbol spellings use this
+ * mangling convention, they will enjoy the following advantages:
+ * </p>
+ * <ul>
+ * <li>They can interoperate via symbols they share in common.</li>
+ * <li>Low-level tools, such as backtrace printers, will have readable displays.</li>
+ * <li>Future JVM and language extensions can safely use the dangerous characters
+ * for structuring symbols, but will never interfere with valid spellings.</li>
+ * <li>Runtimes and compilers can use standard libraries for mangling and demangling.</li>
+ * <li>Occasional transliterations and name composition will be simple and regular,
+ * for classes, methods, and fields.</li>
+ * <li>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.</li>
+ * </ul>
+ *
+ *
+ * <h3> Suggestions for Human Readable Presentations </h3>
+ *
+ *
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * For example, an HTML-like spelling
+ * <code><big><b>&lt;pre&gt;</b></big></code> mangles to
+ * <code><big><b>\^pre\_</b></big></code> and could
+ * display more cleanly as
+ * <code><big><b>'&lt;pre&gt;'</b></big></code>,
+ * with the quotes included.
+ * Such string-like conventions are <em>not</em> 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.
+ * </p>
+ * <p>
+ * In such human-readable displays, invalidly mangled
+ * names should <em>not</em> 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
+ * <code><big><b>\=phase\,1</b></big></code> and
+ * <code><big><b>phase.1</b></big></code> are distinct,
+ * and in demangled displays they should be presented as
+ * <code><big><b>'phase.1'</b></big></code> and something like
+ * <code><big><b>'phase'.1</b></big></code>, respectively.
+ * </p>
+ *
+ * @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 &lt;init&gt;} will be parsed into { '&lt;', "init", '&gt;'}}
+ * 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
+ * <em>dangerous characters</em>: {@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<String,String> map = new java.util.HashMap<String,String>();
+ 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 0000000..2cf4494
--- /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<Class<?>> parseMethod(String bytecodeSignature, ClassLoader loader) {
+ return parseMethod(bytecodeSignature, 0, bytecodeSignature.length(), loader);
+ }
+
+ static List<Class<?>> parseMethod(String bytecodeSignature,
+ int start, int end, ClassLoader loader) {
+ if (loader == null)
+ loader = ClassLoader.getSystemClassLoader();
+ String str = bytecodeSignature;
+ int[] i = {start};
+ ArrayList<Class<?>> ptypes = new ArrayList<Class<?>>();
+ 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 Wrappers.basicTypeFromChar(c);
+ }
+ }
+
+ 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<Class<?>> 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 = Wrappers.basicTypeChar(t);
+ 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/MethodHandleInvoker.java b/src/share/classes/sun/dyn/util/MethodHandleInvoker.java
new file mode 100644
index 0000000..ada039c
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/MethodHandleInvoker.java
@@ -0,0 +1,454 @@
+/*
+ * 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 sun.dyn.Access;
+import sun.dyn.AdapterMethodHandle;
+import java.dyn.AnonymousClassLoader;
+import java.dyn.ConstantPoolParser;
+import java.dyn.ConstantPoolPatch;
+import java.dyn.ConstantPoolVisitor;
+import java.dyn.InvalidConstantPoolFormatException;
+import java.dyn.MethodHandle;
+import java.dyn.MethodHandles;
+import java.dyn.MethodType;
+import java.dyn.WrongMethodTypeException;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.IdentityHashMap;
+
+/**
+ * Emulation of method handle invocation.
+ * Not needed if javac supports direct invocation of MethodHandle.invoke.
+ * @author jrose
+ */
+public abstract
+class MethodHandleInvoker implements Cloneable {
+ private static final Access IMPL_TOKEN = Access.getToken();
+
+
+ /** Exact type for all handles targeted by this invoker. */
+ protected final MethodType exactType;
+
+ /** Condensed information about the return type, one of "VLIJFDZBSC". */
+ protected final char rtypec;
+
+ /** Adapter which converts the approximate type to the exact exactType. */
+ protected final MethodHandle adapter;
+
+ /** Maximum number of arguments allowed.
+ * This is an implementation limit.
+ */
+ public static final int ARGUMENT_MAX;
+ /** Maximum number of double or long arguments allowed.
+ * This is an implementation limit.
+ */
+ public static final int LONG_ARGUMENT_MAX;
+ static {
+ LONG_ARGUMENT_MAX = 3; // %%% depends on stack headroom
+ }
+
+ public MethodType type() { return exactType; }
+
+ protected MethodHandleInvoker(MethodType exactType, MethodHandle adapter) {
+ this.exactType = exactType;
+ this.rtypec = Wrappers.basicTypeChar(exactType.returnType());
+ this.adapter = adapter;
+ }
+
+ static MethodHandle makeAdapter(MethodType exactType, MethodType approxType) {
+ // For each argument, convert incoming Object to the exact type needed.
+ int len = exactType.parameterCount();
+ assert(len == approxType.parameterCount());
+ if (exactType.parameterSlotCount() > len + LONG_ARGUMENT_MAX)
+ throw new IllegalArgumentException("too many long arguments in "+exactType);
+ MethodHandle invoker = MethodHandles.findVirtual(MethodHandle.class, "invoke", exactType);
+ MethodType adapterType = approxType.insertParameterType(0, MethodHandle.class);
+ return AdapterMethodHandle.makePairwiseConvert(IMPL_TOKEN, adapterType, invoker);
+ }
+
+ public Object invoke_0(MethodHandle mh)
+ { throw wrongType(mh); }
+ public Object invoke_1(MethodHandle mh, Object a0)
+ { throw wrongType(mh); }
+ public Object invoke_2(MethodHandle mh, Object a0, Object a1)
+ { throw wrongType(mh); }
+ public Object invoke_3(MethodHandle mh, Object a0, Object a1, Object a2)
+ { throw wrongType(mh); }
+ public Object invoke_4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3)
+ { throw wrongType(mh); }
+ public Object invoke_5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4)
+ { throw wrongType(mh); }
+
+ /** Reflective style generic invocation. This always delegates
+ * to one of the invoke_X methods.
+ * @param mh method handle to invoke (must be of exactly correct type)
+ * @param args array of arguments to send to method (maybe null if empty)
+ * @return
+ */
+ final // try this...
+ public Object invoke(MethodHandle mh, Object ... args) {
+ switch (args == null ? 0 : args.length) {
+ case 0: return invoke_0(mh);
+ case 1: return invoke_1(mh, args[0]);
+ case 2: return invoke_2(mh, args[0], args[1]);
+ case 3: return invoke_3(mh, args[0], args[1], args[2]);
+ case 4: return invoke_4(mh, args[0], args[1], args[2], args[3]);
+ case 5: return invoke_5(mh, args[0], args[1], args[2], args[3], args[4]);
+ }
+ throw wrongType(mh);
+ }
+
+ // This class is not used after compile time.
+ // It is renamed away to MethodHandle itself, to call the MHI.adapter.
+ // TO DO: Update javac so we can call directly to polymorphic MH.invoke.
+ private static abstract class FakeMethodHandle extends MethodHandle {
+ public FakeMethodHandle() { super(null, null); }
+ // here are all the invokes we need to link against:
+ public abstract void fake_invoke_V0(MethodHandle mh);
+ public abstract Object fake_invoke_L0(MethodHandle mh);
+ public abstract int fake_invoke_I0(MethodHandle mh);
+ public abstract long fake_invoke_J0(MethodHandle mh);
+ public abstract double fake_invoke_F0(MethodHandle mh);
+ public abstract double fake_invoke_D0(MethodHandle mh);
+ public abstract void fake_invoke_V1(MethodHandle mh, Object a0);
+ public abstract Object fake_invoke_L1(MethodHandle mh, Object a0);
+ public abstract int fake_invoke_I1(MethodHandle mh, Object a0);
+ public abstract long fake_invoke_J1(MethodHandle mh, Object a0);
+ public abstract float fake_invoke_F1(MethodHandle mh, Object a0);
+ public abstract double fake_invoke_D1(MethodHandle mh, Object a0);
+ public abstract void fake_invoke_V2(MethodHandle mh, Object a0, Object a1);
+ public abstract Object fake_invoke_L2(MethodHandle mh, Object a0, Object a1);
+ public abstract int fake_invoke_I2(MethodHandle mh, Object a0, Object a1);
+ public abstract long fake_invoke_J2(MethodHandle mh, Object a0, Object a1);
+ public abstract float fake_invoke_F2(MethodHandle mh, Object a0, Object a1);
+ public abstract double fake_invoke_D2(MethodHandle mh, Object a0, Object a1);
+ public abstract void fake_invoke_V3(MethodHandle mh, Object a0, Object a1, Object a2);
+ public abstract Object fake_invoke_L3(MethodHandle mh, Object a0, Object a1, Object a2);
+ public abstract int fake_invoke_I3(MethodHandle mh, Object a0, Object a1, Object a2);
+ public abstract long fake_invoke_J3(MethodHandle mh, Object a0, Object a1, Object a2);
+ public abstract float fake_invoke_F3(MethodHandle mh, Object a0, Object a1, Object a2);
+ public abstract double fake_invoke_D3(MethodHandle mh, Object a0, Object a1, Object a2);
+ public abstract void fake_invoke_V4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
+ public abstract Object fake_invoke_L4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
+ public abstract int fake_invoke_I4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
+ public abstract long fake_invoke_J4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
+ public abstract float fake_invoke_F4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
+ public abstract double fake_invoke_D4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
+ public abstract void fake_invoke_V5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
+ public abstract Object fake_invoke_L5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
+ public abstract int fake_invoke_I5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
+ public abstract long fake_invoke_J5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
+ public abstract float fake_invoke_F5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
+ public abstract double fake_invoke_D5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
+ }
+ private static String FMHInvokeName(MethodType approxType) {
+ assert(isFMHInvokeType(approxType)) : approxType;
+ return "fake_invoke_"
+ + Wrappers.basicTypeChar(approxType.returnType())
+ + approxType.parameterCount();
+ }
+ private static boolean isFMHInvokeType(MethodType approxType) {
+ Class<?> rtype = approxType.returnType();
+ char rtc = Wrappers.basicTypeChar(approxType.returnType());
+ if ("VIJFD".indexOf(rtc) < 0 && rtype != Object.class)
+ return false;
+ int len = approxType.parameterCount();
+ for (int i = 0; i < len; i++)
+ if (approxType.parameterType(i) != Object.class)
+ return false;
+ return true;
+ }
+
+ protected Object wrap(int value) {
+ switch (rtypec) {
+ case 'Z': return (value != 0);
+ case 'B': return (byte)value;
+ case 'S': return (short)value;
+ case 'C': return (char)value;
+ }
+ return value;
+ }
+
+ static class L0 extends MethodHandleInvoker {
+ public L0(MethodType type, MethodHandle adapter) { super(type, adapter); }
+ @Override public Object invoke_0(MethodHandle mh) {
+ checkType(mh);
+ switch (rtypec) {
+ // Note: Could unswitch this into 5 classes, but too messy.
+ case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L0(mh);
+ default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I0(mh));
+ case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J0(mh);
+ case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F0(mh);
+ case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D0(mh);
+ case 'V': ((FakeMethodHandle)adapter).fake_invoke_V0(mh);
+ /* Here is the sort of code we will use when we get javac support:
+ case 'L': return adapter.invoke(mh);
+ default: return wrap(adapter.<int>invoke(mh));
+ case 'J': return adapter.<long>invoke(mh);
+ case 'F': return adapter.<float>invoke(mh);
+ case 'D': return adapter.<double>invoke(mh);
+ case 'V': adapter.<void>invoke(mh);
+ */
+ }
+ return null;
+ }
+ }
+
+ static class L1 extends MethodHandleInvoker {
+ public L1(MethodType type, MethodHandle adapter) { super(type, adapter); }
+ @Override public Object invoke_1(MethodHandle mh, Object a0) {
+ checkType(mh);
+ switch (rtypec) {
+ case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L1(mh, a0);
+ default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I1(mh, a0));
+ case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J1(mh, a0);
+ case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F1(mh, a0);
+ case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D1(mh, a0);
+ case 'V': ((FakeMethodHandle)adapter).fake_invoke_V1(mh, a0);
+ }
+ return null;
+ }
+ }
+
+ static class L2 extends MethodHandleInvoker {
+ public L2(MethodType type, MethodHandle adapter) { super(type, adapter); }
+ @Override public Object invoke_2(MethodHandle mh, Object a0, Object a1) {
+ checkType(mh);
+ switch (rtypec) {
+ case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L2(mh, a0, a1);
+ default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I2(mh, a0, a1));
+ case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J2(mh, a0, a1);
+ case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F2(mh, a0, a1);
+ case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D2(mh, a0, a1);
+ case 'V': ((FakeMethodHandle)adapter).fake_invoke_V2(mh, a0, a1);
+ }
+ return null;
+ }
+ }
+
+ static class L3 extends MethodHandleInvoker {
+ public L3(MethodType type, MethodHandle adapter) { super(type, adapter); }
+ @Override public Object invoke_3(MethodHandle mh, Object a0, Object a1, Object a2) {
+ checkType(mh);
+ switch (rtypec) {
+ case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L3(mh, a0, a1, a2);
+ default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I3(mh, a0, a1, a2));
+ case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J3(mh, a0, a1, a2);
+ case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F3(mh, a0, a1, a2);
+ case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D3(mh, a0, a1, a2);
+ case 'V': ((FakeMethodHandle)adapter).fake_invoke_V3(mh, a0, a1, a2);
+ }
+ return null;
+ }
+ }
+
+ static class L4 extends MethodHandleInvoker {
+ public L4(MethodType type, MethodHandle adapter) { super(type, adapter); }
+ @Override public Object invoke_4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3) {
+ checkType(mh);
+ switch (rtypec) {
+ case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L4(mh, a0, a1, a2, a3);
+ default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I4(mh, a0, a1, a2, a3));
+ case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J4(mh, a0, a1, a2, a3);
+ case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F4(mh, a0, a1, a2, a3);
+ case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D4(mh, a0, a1, a2, a3);
+ case 'V': ((FakeMethodHandle)adapter).fake_invoke_V4(mh, a0, a1, a2, a3);
+ }
+ return null;
+ }
+ }
+
+ static class L5 extends MethodHandleInvoker {
+ public L5(MethodType type, MethodHandle adapter) { super(type, adapter); }
+ @Override public Object invoke_5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4) {
+ checkType(mh);
+ switch (rtypec) {
+ case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L5(mh, a0, a1, a2, a3, a4);
+ default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I5(mh, a0, a1, a2, a3, a4));
+ case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J5(mh, a0, a1, a2, a3, a4);
+ case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F5(mh, a0, a1, a2, a3, a4);
+ case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D5(mh, a0, a1, a2, a3, a4);
+ case 'V': ((FakeMethodHandle)adapter).fake_invoke_V5(mh, a0, a1, a2, a3, a4);
+ }
+ return null;
+ }
+ }
+
+ private static Class<?>[] L_CLASSES
+ = { L0.class, L1.class, L2.class, L3.class, L4.class, L5.class };
+ static { ARGUMENT_MAX = L_CLASSES.length + 1; }
+
+ public static MethodHandleInvoker make(MethodType type) {
+ MethodHandleInvoker inv = null;
+ synchronized (invokers) {
+ inv = invokers.get(type);
+ }
+ if (inv != null) return inv;
+ inv = makeNew(type);
+ synchronized (invokers) {
+ MethodHandleInvoker inv2 = invokers.get(type);
+ if (inv2 == null)
+ invokers.put(type, inv);
+ else
+ inv = inv2;
+ }
+ System.out.println("new invoker: "+inv);
+ return inv;
+ }
+
+ static MethodType approxType(MethodType exactType) {
+ int len = exactType.parameterCount();
+ if (len > ARGUMENT_MAX)
+ throw new IllegalArgumentException("too many arguments for invoker: "+exactType);
+ // The JVM can insert casts and unboxing for us in a native adapter.
+ MethodType approxType = MethodType.makeGeneric(len);
+ // But the return type must be exact, except for subword types.
+ // Convert subwords to int, since the JVM an narrow them back down.
+ Class<?> rtype = exactType.returnType();
+ switch (Wrappers.basicTypeChar(rtype)) {
+ case 'L':
+ rtype = Object.class; break;
+ case 'Z': case 'B': case 'S': case 'C':
+ rtype = int.class; break;
+ }
+ approxType = approxType.changeReturnType(rtype);
+ return approxType;
+ }
+
+ private static MethodHandleInvoker makeNew(MethodType exactType) {
+ MethodHandleInvoker inv = null;
+ Exception ex1 = null;
+ MethodType approxType = approxType(exactType);
+ MethodHandle adapter = makeAdapter(exactType, approxType);
+ Class<? extends MethodHandleInvoker> template = null;
+ Class<? extends MethodHandleInvoker> instance = null;
+ Constructor<? extends MethodHandleInvoker> constr = null;
+ template = L_CLASSES[approxType.parameterCount()].asSubclass(MethodHandleInvoker.class);
+ {
+ try {
+ instance = expandTemplate(template, approxType, exactType);
+ // When we get rid of the fakery, it will be just
+ // constr = template.getConstructor
+ constr = instance.getConstructor(MethodType.class, MethodHandle.class);
+ inv = constr.newInstance(exactType, adapter);
+ } catch (IOException ex) {
+ ex1 = ex;
+ } catch (InvalidConstantPoolFormatException ex) {
+ ex1 = ex;
+ } catch (InstantiationException ex) {
+ ex1 = ex;
+ } catch (IllegalAccessException ex) {
+ ex1 = ex;
+ } catch (NoSuchMethodException ex) {
+ ex1 = ex;
+ } catch (IllegalArgumentException ex) {
+ ex1 = ex;
+ } catch (InvocationTargetException ex) {
+ ex1 = ex;
+ }
+ }
+ if (inv == null) {
+ printex(ex1);
+ throw new InternalError();
+ }
+ return inv;
+ }
+ private static void printex(Exception ex) {
+ System.out.println("*** Unexpected exception in "+MethodHandleInvoker.class);
+ System.out.println(ex);
+ ex.printStackTrace(System.out);
+ }
+
+ private static final AnonymousClassLoader LOADER
+ = new AnonymousClassLoader(MethodHandleInvoker.class);
+
+ private static String utf8Name(Class<?> cls) {
+ return cls.getName().replace('.', '/');
+ }
+
+ private static class TemplateExpander extends ConstantPoolVisitor {
+ ConstantPoolParser cp;
+ ConstantPoolPatch patch;
+
+ // Pairs of strings to be rewritten:
+ String fakeMHName = utf8Name(FakeMethodHandle.class);
+ String realMHName = utf8Name(MethodHandle.class);
+ boolean didMHName;
+
+ String fakeInvokeName, realInvokeName = "invoke";
+ boolean didInvokeName;
+
+ @Override
+ public void visitUTF8(int index, byte tag, String utf8) {
+ String orig = utf8;
+ if (utf8.equals(fakeMHName)) {
+ utf8 = realMHName; didMHName = true;
+ }
+ if (utf8.equals(fakeInvokeName)) {
+ utf8 = realInvokeName; didInvokeName = true;
+ }
+ if ((Object)utf8 != orig)
+ patch.putUTF8(index, utf8);
+ }
+
+ public TemplateExpander(Class<? extends MethodHandleInvoker> template,
+ MethodType approxType, MethodType exactType)
+ throws IOException, InvalidConstantPoolFormatException {
+ // construct a descriptor of something like:
+ // int fake_invoke_I2(MethodHandle, Object, Object);
+ fakeInvokeName = FMHInvokeName(approxType);
+ cp = new ConstantPoolParser(template);
+ patch = cp.createPatch();
+ cp.parse(this);
+ if (!(didMHName && didInvokeName))
+ throw new RuntimeException("utf8 rewrites failed: "
+ +(!didMHName?"":fakeMHName)+(!didInvokeName?"":fakeInvokeName));
+ }
+ }
+
+ static final IdentityHashMap<MethodType,MethodHandleInvoker> invokers
+ = new IdentityHashMap<MethodType, MethodHandleInvoker>();
+
+ private static Class<? extends MethodHandleInvoker>
+ expandTemplate(Class<? extends MethodHandleInvoker> template,
+ MethodType approxType, MethodType exactType)
+ throws IOException, InvalidConstantPoolFormatException {
+ TemplateExpander tex = new TemplateExpander(template, approxType, exactType);
+ return LOADER.loadClass(tex.patch).asSubclass(MethodHandleInvoker.class);
+ }
+
+ /** Throw this if a bad entry point is taken. */
+ protected RuntimeException wrongType(MethodHandle mh) {
+ return new WrongMethodTypeException("wrong call type for "+mh+
+ " should be "+exactType+" in "+this);
+ }
+ protected void checkType(MethodHandle mh) {
+ if (mh.type() != exactType)
+ throw wrongType(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 0000000..3bb26f4
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/VerifyAccess.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 sun.dyn.util;
+
+import sun.dyn.MemberName;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+/**
+ * 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.
+ * <p>
+ * 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 accessingClass 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<?> accessingClass) {
+ if (!isAccessible(defc, accessingClass))
+ return null;
+ Class<?> constraint = Object.class;
+ if (!doDispatch && !Modifier.isStatic(mods)) {
+ constraint = accessingClass;
+ }
+ if (Modifier.isPublic(mods))
+ return constraint;
+ if (Modifier.isPrivate(mods))
+ return (defc == accessingClass) ? constraint : null;
+ if (isSamePackage(defc, accessingClass))
+ return constraint;
+ if (Modifier.isProtected(mods) && defc.isAssignableFrom(accessingClass))
+ 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<?> accessingClass) {
+ int mods = refc.getModifiers();
+ if (Modifier.isPublic(mods))
+ return true;
+ if (isSamePackage(accessingClass, 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 (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;
+ }
+}
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 0000000..668c4a7
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/VerifyType.java
@@ -0,0 +1,140 @@
+/*
+ * 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 (!src.isPrimitive()) return dst.isAssignableFrom(src);
+ // Verifier allows an int to carry byte, short, char, or even boolean:
+ if (dst == int.class) return Wrappers.isSubwordOrInt(src);
+ return false;
+ }
+
+ /**
+ * 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());
+ }
+
+ /**
+ * 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:
+ * <ul><li>checkcast
+ * </li><li>conversion between any two integral types (but not floats)
+ * </li><li>unboxing from a wrapper to its corresponding primitive type
+ * </li><li>conversion in either direction between float and double
+ * </li></ul>
+ * (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.
+ 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;
+ boolean swt = Wrappers.isSubwordOrInt(src);
+ boolean dwt = Wrappers.isSubwordOrInt(dst);
+ if (swt && dwt) {
+ if (Wrappers.bitWidth(src) >= Wrappers.bitWidth(dst))
+ return -1; // truncation may be required
+ if (!Wrappers.isSigned(dst) && Wrappers.isSigned(src))
+ 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 (dst.isInterface()) dst = Object.class;
+ //if (call.isInterface()) call = Object.class;
+ if (dst == Object.class)
+ // pass any reference to object or an arb. interface
+ return 1;
+ // else it's a definite "maybe" (cast is required)
+ return -1;
+ }
+
+}
diff --git a/src/share/classes/sun/dyn/util/Wrappers.java b/src/share/classes/sun/dyn/util/Wrappers.java
new file mode 100644
index 0000000..a94ae37
--- /dev/null
+++ b/src/share/classes/sun/dyn/util/Wrappers.java
@@ -0,0 +1,272 @@
+/*
+ * 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.util.HashMap;
+
+public class Wrappers {
+
+ private Wrappers() { } // cannot instantiate
+
+ /** If {@code type} is a primitive type, return the corresponding
+ * wrapper type, else return {@code type} unchanged.
+ */
+ public static <T> Class<T> asWrapperType(Class<T> type) {
+ if (!type.isPrimitive()) {
+ return type;
+ }
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
+ Object[] memo = wrappers.get(type);
+ assert (memo != null);
+ return (Class<T>) memo[0]; // unchecked warning is OK here
+ }
+
+ /** If {@code type} is a wrapper type, return the corresponding
+ * primitive type, else return {@code type} unchanged.
+ */
+ public static <T> Class<T> asPrimitiveType(Class<T> type) {
+ if (type.isPrimitive()) {
+ return type;
+ }
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
+ Object[] memo = wrappers.get(type);
+ if (memo == null) {
+ return type;
+ }
+ return (Class<T>) memo[1]; // unchecked warning is OK here
+ }
+
+ public static boolean isWrapperType(Class<?> type) {
+ return asPrimitiveType(type) != type;
+ }
+
+ public static boolean isPrimitiveType(Class<?> type) {
+ return type.isPrimitive();
+ }
+
+ public static char basicTypeChar(Class<?> type) {
+ if (!type.isPrimitive()) {
+ return 'L';
+ }
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
+ Object[] memo = wrappers.get(type);
+ assert (memo != null);
+ return (char) (Character) memo[2];
+ }
+
+ static final String PRIMITIVE_BITS_TABLE = "LZBCSFIZZDJ";
+ // "--01234 78"
+
+ /** Return the number of bits in the given type, or zero for refs. */
+ public static int bitWidth(Class<?> type) {
+ return bitWidth(basicTypeChar(type));
+ }
+
+ /** Return the number of bits in the given basic type, or zero for refs. */
+ public static int bitWidth(char c) {
+ int i = PRIMITIVE_BITS_TABLE.indexOf(c);
+ if (i < 0) throw new IllegalArgumentException("not a basic type char: "+c);
+ i -= 2;
+ switch (i) {
+ case -2: return 0; // L
+ case -1: return 1; // Z
+ case 0: return 8; // B
+ default: return (i + (i & 1)) * 8;
+ }
+ }
+
+ static final String PRIMITIVE_SIGN_TABLE = "JZICSZB";
+ // "SuSuS S"
+ /** Return whether the given type is a signed integral type. */
+ public static boolean isSigned(Class<?> type) {
+ return isSigned(basicTypeChar(type));
+ }
+
+ /** Return whether the given type is a signed integral type. */
+ public static boolean isSigned(char c) {
+ int i = PRIMITIVE_SIGN_TABLE.indexOf(c);
+ return (i & 1) == 0;
+ }
+
+ /** Return whether the given type is a unsigned integral type. */
+ public static boolean isUnsigned(Class<?> type) {
+ return isUnsigned(basicTypeChar(type));
+ }
+
+ /** Return whether the given type is a unsigned integral type. */
+ public static boolean isUnsigned(char c) {
+ int i = PRIMITIVE_SIGN_TABLE.indexOf(c);
+ return (i & 0x11) == 1;
+ }
+
+ /** Return whether the given type is an integral type. */
+ public static boolean isIntegral(Class<?> type) {
+ return isIntegral(basicTypeChar(type));
+ }
+
+ /** Return whether the given type is an integral type. */
+ public static boolean isIntegral(char c) {
+ int i = PRIMITIVE_SIGN_TABLE.indexOf(c);
+ return i >= 0;
+ }
+
+ /** Report if the type is one of int, boolean, byte, char, or short. */
+ public static boolean isSubwordOrInt(Class<?> type) {
+ return isSubwordOrInt(basicTypeChar(type));
+ }
+
+ /** Report if the type char is one of "IZBCS". */
+ public static boolean isSubwordOrInt(char c) {
+ return PRIMITIVE_SIGN_TABLE.indexOf(c) > 0;
+ }
+
+ /** Return whether the given type is a floating primitive type. */
+ public static boolean isFloating(Class<?> type) {
+ return isFloating(basicTypeChar(type));
+ }
+
+ /** Return whether the given type is a floating primitive type. */
+ public static boolean isFloating(char c) {
+ return c == 'F' || c == 'D';
+ }
+
+ /** Return the primitive type that corresponds to the given bytecode
+ * signature character. Return {@code Object.class} for the character
+ * 'L', and null for any non-signature character or '['.
+ */
+ public static Class<?> basicTypeFromChar(char c) {
+ if (c == 'L') {
+ return Object.class;
+ }
+// if (c == '[') {
+// return Object[].class;
+// }
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
+ Object[] memo = wrappers.get((Character)c);
+ if (memo == null)
+ return null; // random junk character
+ return (Class<?>) memo[1];
+ }
+
+ public static Object zeroValue(Class<?> type) {
+ if (!type.isPrimitive()) {
+ return null;
+ }
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
+ Object[] memo = wrappers.get(type);
+ assert (memo != null);
+ return memo[3];
+ }
+
+ public static <T> T wrap(Object x, Class<T> numClass) {
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
+ Object[] memo = wrappers.get(numClass);
+ if (memo == null) return numClass.cast(x); // no change
+ Class<T> wrapType = (Class<T>) memo[0]; // unchecked warning is OK here
+ return wrapType.cast(wrap(x, (Character) memo[2]));
+ }
+ public static Object wrap(Object x, char c) {
+ Number xn = numberValue(x);
+ switch (c) {
+ 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()));
+ case 'V': return null;
+ }
+ return xn;
+ }
+
+ 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 final HashMap<Object, Object[]> wrappers
+ = new HashMap<Object, Object[]>(20);
+
+ private static void fillWrappers() {
+ Object[][] memos = {
+ {Boolean.class, Boolean.TYPE, 'Z', (Boolean) false},
+ {Character.class, Character.TYPE, 'C', (Character) '\000'},
+ {Byte.class, Byte.TYPE, 'B', (Byte) (byte) 0},
+ {Short.class, Short.TYPE, 'S', (Short) (short) 0},
+ {Integer.class, Integer.TYPE, 'I', (Integer) 0},
+ {Long.class, Long.TYPE, 'J', (Long) 0L},
+ {Float.class, Float.TYPE, 'F', (Float) 0.0F},
+ {Double.class, Double.TYPE, 'D', (Double) 0.0},
+ {Void.class, Void.TYPE, 'V', null}
+ };
+ for (Object[] memo : memos) {
+ wrappers.put(memo[0], memo);
+ wrappers.put(memo[1], memo);
+ wrappers.put(memo[2], memo);
+ }
+ }
+
+ // TO DO: Put these into a unit test.
+ private static Class<Integer> PTYPE = int.class, WTYPE = Integer.class;
+ static {
+ assert(PTYPE != WTYPE);
+ assert(asPrimitiveType(PTYPE) == PTYPE);
+ assert(asPrimitiveType(WTYPE) == PTYPE);
+ assert( asWrapperType(PTYPE) == WTYPE);
+ assert( asWrapperType(WTYPE) == WTYPE);
+
+ assert(bitWidth(String.class) == 0);
+ assert(bitWidth(Integer.class) == 0);
+ assert(bitWidth(int.class) == 32);
+ assert(bitWidth(char.class) == 16);
+ assert(bitWidth(short.class) == 16);
+ assert(bitWidth(byte.class) == 8);
+ assert(bitWidth(boolean.class) == 1);
+ assert(bitWidth(double.class) == 64);
+ assert(bitWidth(float.class) == 32);
+ }
+}
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 0000000..257d7df
--- /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/javavm/export/classfile_constants.h b/src/share/javavm/export/classfile_constants.h
index 8906ba5..507424f 100644
--- a/src/share/javavm/export/classfile_constants.h
+++ b/src/share/javavm/export/classfile_constants.h
@@ -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 5c97ead..5bd5aa0 100644
--- a/src/share/native/common/check_code.c
+++ b/src/share/native/common/check_code.c
@@ -243,6 +243,7 @@ struct context_type {
fullinfo_type throwable_info; /* fullinfo for java/lang/Throwable */
fullinfo_type cloneable_info; /* fullinfo for java/lang/Cloneable */
fullinfo_type serializable_info; /* fullinfo for java/io/Serializable */
+ fullinfo_type dynamic_info; /* fullinfo for java/dyn/Dynamic */
fullinfo_type currentclass_info; /* fullinfo for context->class */
fullinfo_type superclass_info; /* fullinfo for superclass */
@@ -767,6 +768,8 @@ VerifyClassForMajorVersion(JNIEnv *env, jclass cb, char *buffer, jint len,
make_class_info_from_name(context, "java/lang/Cloneable");
context->serializable_info =
make_class_info_from_name(context, "java/io/Serializable");
+ context->dynamic_info =
+ make_class_info_from_name(context, "java/dyn/Dynamic");
context->currentclass_info = make_loadable_class_info(context, cb);
@@ -1223,16 +1226,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 +1248,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 +1314,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 +2005,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 <init> */
+ 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 +2021,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 +2307,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 +2317,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 +2699,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;
@@ -3782,6 +3802,15 @@ merge_fullinfo_types(context_type *context,
return target;
}
return value;
+ } else if (for_assignment && target == context->dynamic_info) {
+ /* Special case: Any reference type converts to interface Dynamic.
+ * The new verifier does not need this special case, because it
+ * treats all interface types this way.
+ * N.B. Make sure Dynamic actually resolves to an interface.
+ */
+ jclass cb = object_fullinfo_to_classclass(context, target);
+ if (cb && JVM_IsInterface(env, cb))
+ return target;
}
if (GET_INDIRECTION(value) > 0 || GET_INDIRECTION(target) > 0) {
/* At least one is an array. Neither is java/lang/Object or NULL.
diff --git a/src/share/native/common/opcodes.in_out b/src/share/native/common/opcodes.in_out
index 364e8ab..6343ccb 100644
--- a/src/share/native/common/opcodes.in_out
+++ b/src/share/native/common/opcodes.in_out
@@ -210,7 +210,7 @@ char * const opcode_in_out[][2] = {
{"?", "?"}, /* invokespecial */
{"?", "?"}, /* invokestatic */
{"?", "?"}, /* invokeinterface */
- {"?", "?"}, /* xxxunusedxxx */
+ {"?", "?"}, /* invokedynamic */
{"", "A"}, /* new */
{"I", "A"}, /* newarray */
{"I", "A"}, /* anewarray */
diff --git a/src/share/native/java/lang/fdlibm/include/fdlibm.h b/src/share/native/java/lang/fdlibm/include/fdlibm.h
index 87582db..4bcd805 100644
--- a/src/share/native/java/lang/fdlibm/include/fdlibm.h
+++ b/src/share/native/java/lang/fdlibm/include/fdlibm.h
@@ -27,7 +27,7 @@
#ifdef __OpenBSD__
#include <sys/types.h>
#endif
-#include <machine/endian.h>
+#include <endian.h>
#include "jfdlibm.h"
#ifdef __NEWVALID /* special setup for Sun test regime */
diff --git a/src/share/native/sun/awt/medialib/mlib_image.h b/src/share/native/sun/awt/medialib/mlib_image.h
index f32bac5..cc7827f 100644
--- a/src/share/native/sun/awt/medialib/mlib_image.h
+++ b/src/share/native/sun/awt/medialib/mlib_image.h
@@ -30,7 +30,7 @@
#ifdef __OpenBSD__
#include <sys/types.h>
#endif
-#include <machine/endian.h>
+#include <endian.h>
#include <mlib_types.h>
#include <mlib_status.h>
diff --git a/src/solaris/back/util_md.h b/src/solaris/back/util_md.h
index 7a0f7d6..538792f 100644
--- a/src/solaris/back/util_md.h
+++ b/src/solaris/back/util_md.h
@@ -31,7 +31,7 @@
#include <limits.h>
#include <sys/types.h>
-#include <machine/endian.h>
+#include <endian.h>
#ifdef _LP64
typedef unsigned long UNSIGNED_JLONG;
diff --git a/src/solaris/native/sun/java2d/loops/mlib_ImageZoom_NN.c b/src/solaris/native/sun/java2d/loops/mlib_ImageZoom_NN.c
index 498f62f..d2c5e55 100644
--- a/src/solaris/native/sun/java2d/loops/mlib_ImageZoom_NN.c
+++ b/src/solaris/native/sun/java2d/loops/mlib_ImageZoom_NN.c
@@ -66,7 +66,7 @@
#ifdef __OpenBSD__
#include <sys/types.h>
#endif
-#include <machine/endian.h>
+#include <endian.h>
#include <mlib_image.h>
#include <mlib_ImageZoom.h>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment