Created
April 29, 2009 21:44
-
-
Save chanwit/104074 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.>void<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><</b></big></code> and <code><big><b>></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><</b></big></code> (left angle), | |
+ * <code><big><b>></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 “found in nature”, | |
+ * 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><pre></b></big></code> mangles to | |
+ * <code><big><b>\^pre\_</b></big></code> and could | |
+ * display more cleanly as | |
+ * <code><big><b>'<pre>'</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 <init>} will be parsed into { '<', "init", '>'}} | |
+ * The name {@code foo/bar$:baz} will be parsed into | |
+ * {@code {"foo", '/', "bar", '$', ':', "baz"}}. | |
+ */ | |
+ public static Object[] parseBytecodeName(String s) { | |
+ int slen = s.length(); | |
+ Object[] res = null; | |
+ for (int pass = 0; pass <= 1; pass++) { | |
+ int fillp = 0; | |
+ int lasti = 0; | |
+ for (int i = 0; i <= slen; i++) { | |
+ int whichDC = -1; | |
+ if (i < slen) { | |
+ whichDC = DANGEROUS_CHARS.indexOf(s.charAt(i)); | |
+ if (whichDC < DANGEROUS_CHAR_FIRST_INDEX) continue; | |
+ } | |
+ // got to end of string or next dangerous char | |
+ if (lasti < i) { | |
+ // normal component | |
+ if (pass != 0) | |
+ res[fillp] = s.substring(lasti, i); | |
+ fillp++; | |
+ lasti = i+1; | |
+ } | |
+ if (whichDC >= DANGEROUS_CHAR_FIRST_INDEX) { | |
+ if (pass != 0) | |
+ res[fillp] = DANGEROUS_CHARS_CA[whichDC]; | |
+ fillp++; | |
+ } | |
+ } | |
+ if (pass != 0) break; | |
+ // between passes, build the result array | |
+ res = new String[fillp]; | |
+ if (fillp <= 1) { | |
+ if (fillp != 0) res[0] = s; | |
+ break; | |
+ } | |
+ } | |
+ return res; | |
+ } | |
+ | |
+ /** | |
+ * Given a series of components, create a bytecode name for a classfile. | |
+ * This is the inverse of {@link #parseBytecodeName(java.lang.String)}. | |
+ * Each component must either be an interned one-character string of | |
+ * a dangerous character, or else a safe bytecode name. | |
+ * @param components a series of name components | |
+ * @return the concatenation of all components | |
+ * @throws IllegalArgumentException if any component contains an unsafe | |
+ * character, and is not an interned one-character string | |
+ * @throws NullPointerException if any component is null | |
+ */ | |
+ public static String unparseBytecodeName(Object[] components) { | |
+ for (Object c : components) { | |
+ if (c instanceof String) | |
+ checkSafeBytecodeName((String) c); // may fail | |
+ } | |
+ return appendAll(components); | |
+ } | |
+ private static String appendAll(Object[] components) { | |
+ if (components.length <= 1) { | |
+ if (components.length == 1) { | |
+ return String.valueOf(components[0]); | |
+ } | |
+ return ""; | |
+ } | |
+ int slen = 0; | |
+ for (Object c : components) { | |
+ if (c instanceof String) | |
+ slen += String.valueOf(c).length(); | |
+ else | |
+ slen += 1; | |
+ } | |
+ StringBuilder sb = new StringBuilder(slen); | |
+ for (Object c : components) { | |
+ sb.append(c); | |
+ } | |
+ return sb.toString(); | |
+ } | |
+ | |
+ /** | |
+ * Given a bytecode name, produce the corresponding display name. | |
+ * This is the source name, plus quotes if needed. | |
+ * If the bytecode name contains dangerous characters, | |
+ * assume that they are being used as punctuation, | |
+ * and pass them through unchanged. | |
+ * @param s the original bytecode name (which may be qualified) | |
+ * @return a human-readable presentation | |
+ */ | |
+ public static String toDisplayName(String s) { | |
+ Object[] components = parseBytecodeName(s); | |
+ for (int i = 0; i < components.length; i++) { | |
+ if (!(components[i] instanceof String)) | |
+ continue; | |
+ String c = (String) components[i]; | |
+ // pretty up the name by demangling it | |
+ String sn = toSourceName(c); | |
+ if ((Object)sn != c || !isJavaIdent(sn)) { | |
+ components[i] = quoteDisplay(sn); | |
+ } | |
+ } | |
+ return appendAll(components); | |
+ } | |
+ private static boolean isJavaIdent(String s) { | |
+ int slen = s.length(); | |
+ if (slen == 0) return false; | |
+ if (!Character.isUnicodeIdentifierStart(s.charAt(0))) | |
+ return false; | |
+ for (int i = 1; i < slen; i++) { | |
+ if (!Character.isUnicodeIdentifierPart(s.charAt(0))) | |
+ return false; | |
+ } | |
+ return true; | |
+ } | |
+ private static String quoteDisplay(String s) { | |
+ // TO DO: Replace wierd characters in s by C-style escapes. | |
+ return "'"+s.replaceAll("['\\\\]", "\\\\$0")+"'"; | |
+ } | |
+ | |
+ private static void checkSafeBytecodeName(String s) | |
+ throws IllegalArgumentException { | |
+ if (!isSafeBytecodeName(s)) { | |
+ throw new IllegalArgumentException(s); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * Report whether a simple name is safe as a bytecode name. | |
+ * Such names are acceptable in class files as class, method, and field names. | |
+ * Additionally, they are free of "dangerous" characters, even if those | |
+ * characters are legal in some (or all) names in class files. | |
+ * @param s the proposed bytecode name | |
+ * @return true if the name is non-empty and all of its characters are safe | |
+ */ | |
+ public static boolean isSafeBytecodeName(String s) { | |
+ if (s.length() == 0) return false; | |
+ // check occurrences of each DANGEROUS char | |
+ for (char xc : DANGEROUS_CHARS_A) { | |
+ if (xc == ESCAPE_C) continue; // not really that dangerous | |
+ if (s.indexOf(xc) >= 0) return false; | |
+ } | |
+ return true; | |
+ } | |
+ | |
+ /** | |
+ * Report whether a character is safe in a bytecode name. | |
+ * This is true of any unicode character except the following | |
+ * <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