Mercurial > hg > release > icedtea8-forest-3.0 > jdk
changeset 1212:9ba256e2e5c1 jdk7-b58
Merge
author | tbell |
---|---|
date | Tue, 05 May 2009 23:12:47 -0700 |
parents | f8b061ea131c (current diff) d201987cb76c (diff) |
children | 5e3f080257c4 878863c9072d 39d93fb6926c e387bb1367a7 ae62878e6eef |
files | |
diffstat | 62 files changed, 12002 insertions(+), 151 deletions(-) [+] |
line wrap: on
line diff
--- a/make/docs/CORE_PKGS.gmk Tue May 05 09:09:24 2009 -0700 +++ b/make/docs/CORE_PKGS.gmk Tue May 05 23:12:47 2009 -0700 @@ -1,5 +1,5 @@ # -# Copyright 2001-2009 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2001-2008 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,7 @@ # This is a list of regular expressions. So foo.* matches "foo" and "foo.bar". # ACTIVE_JSR_PKGS= \ + java.dyn \ java.sql \ javax.activation \ javax.annotation.* \
--- a/make/java/Makefile Tue May 05 09:09:24 2009 -0700 +++ b/make/java/Makefile Tue May 05 23:12:47 2009 -0700 @@ -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 @@ # 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/java/dyn/Makefile Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,44 @@ +# +# 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 sun/dyn + +# The sources built here use new language syntax to generate +# method handle calls. Let's be sure we are using that format. +#LANGUAGE_VERSION = -source 7 +#CLASS_VERSION = -target 7 + +# Actually, it will be less disruptive to compile with the same +# -target option as the rest of the system, and just turn on +# the specific compiler option we need here: +OTHER_JAVACFLAGS = -XDinvokedynamic + +include $(BUILDDIR)/common/Classes.gmk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/CallSite.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,201 @@ +/* + * 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 class CallSite { + // Fields used only by the JVM. Do not use or change. + private Object vmmethod; + int callerMID, callerBCI; // supplied by the JVM + + MethodHandle target; + final Object caller; // usually a class + final String name; + final MethodType type; + + public CallSite(Object caller, String name, MethodType type) { + this.caller = caller; + this.name = name; + this.type = type; + } + + private static void privateInitializeCallSite(CallSite site, int callerMID, int callerBCI) { + site.callerMID = callerMID; + site.callerBCI = callerBCI; + if (site.target == null) + site.setTarget(site.initialTarget()); + } + + /** + * Just after a call site is created by a bootstrap method handle, + * if the target has not been initialized by the factory method itself, + * the method {@code initialTarget} is called to produce an initial + * non-null target. (Live call sites must never have null targets.) + * <p> + * If the bootstrap method itself does not initialize the call site, + * this method must be overridden, because it just raises an + * {@code InvokeDynamicBootstrapError}. + */ + protected MethodHandle initialTarget() { + throw new InvokeDynamicBootstrapError("target must be initialized before call site is linked: "+this); + } + + /** + * 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+"]"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/InvokeDynamic.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +/** + * Syntactic marker interface to request javac to emit an {@code invokedynamic} instruction. + * <p> + * This type has no particular meaning as a class or interface supertype, and can never be instantiated. + * Logically, it denotes a source of all dynamically typed methods. + * @author John Rose, JSR 292 EG + */ +public final class InvokeDynamic { + private InvokeDynamic() { throw new InternalError(); } // do not instantiate + + // no statically defined static methods +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,55 @@ +/* + * 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, or the bootstrap method has + * failed to provide a call site with a non-null target. + * <p> + * The boostrap method 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); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/JavaMethodHandle.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,83 @@ +/* + * 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; + +/** + * A Java method handle extends the basic method handle type with additional + * programmer defined methods and fields. + * Its behavior as a method handle is determined at instance creation time, + * by providing the new instance with an "entry point" method handle + * to handle calls. This entry point must accept a leading argument + * whose type is the Java method handle itself or a supertype, and the + * entry point is always called with the Java method handle itself as + * the first argument. This is similar to ordinary virtual methods, which also + * accept the receiver object {@code this} as an implicit leading argument. + * The {@code MethodType} of the Java method handle is the same as that + * of the entry point method handle, with the leading parameter type + * omitted. + * <p> + * Here is an example of usage: + * <p><blockquote><pre> + * class Greeter extends JavaMethodHandle { + * public void run() { System.out.println("hello, "+greetee); } + * private final String greetee; + * Greeter(String greetee) { + * super(RUN); + * this.greetee = greetee; + * } + * // the entry point function is computed once: + * private static final MethodHandle RUN + * = MethodHandles.findVirtual(MyMethodHandle.class, "run", + * MethodType.make(void.class)); + * } + * Greeter greeter = new Greeter("world"); + * greeter.run(); // prints "hello, world" + * MethodHandle mh = greeter; + * mh.invoke(); // also prints "hello, world" + * </pre></blockquote> + * <p> + * In this example, the method {@code run} provides the entry point. + * The entry point need not be a constant value; it may be independently + * computed in each call to the constructor. The entry point does not + * even need to be a method on the Java method handle class, though + * that is the typical case. + * @see MethodHandle + * @author John Rose, JSR 292 EG + */ +public abstract class JavaMethodHandle + // Note: This is an implementation inheritance hack, and will be removed + // with a JVM change which moves the required hidden behavior onto this class. + extends sun.dyn.BoundMethodHandle +{ + /** + * When creating a, pass in {@code entryPoint}, any method handle which + * can take the current object + * @param entryPoint + */ + protected JavaMethodHandle(MethodHandle entryPoint) { + super(entryPoint, 0); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/Linkage.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,199 @@ +/* + * 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.WeakHashMap; +import sun.reflect.Reflection; +import static sun.dyn.util.VerifyAccess.checkBootstrapPrivilege; + +/** + * 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#makeCallSite}. + * <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); + checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod"); + checkBSM(mh); + synchronized (bootstrapMethods) { + if (bootstrapMethods.containsKey(callerClass)) + throw new IllegalStateException("bootstrap method already declared in "+callerClass); + bootstrapMethods.put(callerClass, mh); + } + } + + static void checkBSM(MethodHandle mh) { + if (mh == null) throw new IllegalArgumentException("null bootstrap method"); + if (mh.type() == OLD_BOOTSTRAP_METHOD_TYPE) // FIXME: delete at EDR/PFD + throw new WrongMethodTypeException("bootstrap method must be a CallSite factory"); + if (mh.type() != BOOTSTRAP_METHOD_TYPE) + throw new WrongMethodTypeException(mh.toString()); + } + + /** + * 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 + * given class, and installs it on the caller. + * @throws IllegalArgumentException if there is no such method + */ + public static + void registerBootstrapMethod(Class<?> runtime, String name) { + Class callc = Reflection.getCallerClass(2); + MethodHandle bootstrapMethod = + MethodHandles.findStaticFrom(callc, runtime, name, BOOTSTRAP_METHOD_TYPE); + // FIXME: exception processing wrong here + checkBSM(bootstrapMethod); + Linkage.registerBootstrapMethod(callc, bootstrapMethod); + } + + /** + * 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); + // FIXME: exception processing wrong here + checkBSM(bootstrapMethod); + 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); + checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod"); + synchronized (bootstrapMethods) { + return bootstrapMethods.get(callerClass); + } + } + + /** The type of any bootstrap method is a three-argument method + * {@code (Class<?>, String, MethodType)} returning a {@code CallSite}. + */ + public static final MethodType BOOTSTRAP_METHOD_TYPE + = MethodType.make(CallSite.class, + Class.class, String.class, MethodType.class); + + private static final MethodType OLD_BOOTSTRAP_METHOD_TYPE + = MethodType.make(Object.class, + CallSite.class, Object[].class); + + private static final WeakHashMap<Class, MethodHandle> bootstrapMethods = + new WeakHashMap<Class, MethodHandle>(); + + /** + * 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"); + } + + private static Object doNotBootstrap(CallSite site, Object... arguments) { + throw new UnsupportedOperationException("call site must not have null target: "+site); + } + + private static final MethodHandle DO_NOT_BOOTSTRAP = + MethodHandles.Lookup.IMPL_LOOKUP.findStatic(Linkage.class, "doNotBootstrap", + OLD_BOOTSTRAP_METHOD_TYPE); + + // Up-call from the JVM. Obsolete. FIXME: Delete from VM then from here. + static + MethodHandle findBootstrapMethod(Class callerClass, Class searchBootstrapClass) { + return DO_NOT_BOOTSTRAP; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/LinkagePermission.java Tue May 05 23:12:47 2009 -0700 @@ -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()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/MethodHandle.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,135 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +//import sun.dyn.*; + +import sun.dyn.Access; +import sun.dyn.MethodHandleImpl; + +/** + * A method handle is a typed reference to the entry point of a method. + * <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 abstract class MethodHandle + // Note: This is an implementation inheritance hack, and will be removed + // with a JVM change which moves the required hidden state onto this class. + extends MethodHandleImpl +{ + // interface MethodHandle<T 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; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/MethodHandles.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,1092 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +import java.lang.reflect.Constructor; +import sun.dyn.Access; +import sun.dyn.MemberName; +import sun.dyn.MethodHandleImpl; +import sun.dyn.util.VerifyAccess; +import sun.dyn.util.Wrapper; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import sun.dyn.Invokers; +import sun.dyn.MethodTypeImpl; +import sun.reflect.Reflection; +import static sun.dyn.MemberName.newIllegalArgumentException; +import static sun.dyn.MemberName.newNoAccessException; + +/** + * Fundamental operations and utilities for MethodHandle. + * <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_NAMES = MemberName.getFactory(IMPL_TOKEN); + static { MethodHandleImpl.initStatics(); } + // See IMPL_LOOKUP below. + + //// Method handle creation from ordinary methods. + + public static Lookup lookup() { + return new Lookup(); + } + + /** + * A factory object for creating method handles, when the creation + * requires access checking. Method handles do not perform + * access checks when they are called; this is a major difference + * from reflective {@link Method}, which performs access checking + * against every caller, on every call. Method handle access + * restrictions are enforced when a method handle is created. + * The caller class against which those restrictions are enforced + * is known as the "lookup class". {@link Lookup} embodies an + * authenticated lookup class, and can be used to create any number + * of access-checked method handles, all checked against a single + * lookup class. + * <p> + * A class which needs to create method handles will call + * {@code MethodHandles.lookup()} to create a factory for itself. + * It may then use this factory to create method handles on + * all of its methods, including private ones. + * It may also delegate the lookup (e.g., to a metaobject protocol) + * by passing the {@code Lookup} object to other code. + * If this other code creates method handles, they will be access + * checked against the original lookup class, and not with any higher + * privileges. + * <p> + * Note that access checks only apply to named and reflected methods. + * Other method handle creation methods, such as {@link #convertArguments}, + * do not require any access checks, and can be done independently + * of any lookup class. + * <p> + * <em>A note about error conditions:<em> A lookup can fail, because + * the containing class is not accessible to the lookup class, or + * because the desired class member is missing, or because the + * desired class member is not accessible to the lookup class. + * It can also fail if a security manager is installed and refuses + * access. In any of these cases, an exception will be + * thrown from the attempted lookup. + * In general, the conditions under which a method handle may be + * created for a method M are exactly as restrictive as the conditions + * under which the lookup class could have compiled a call to M. + */ + public static final + class Lookup { + private final Class<?> lookupClass; + + /** Which class is performing the lookup? It is this class against + * which checks are performed for visibility and access permissions. + * <p> + * This value is null if and only if this lookup is {@link #PUBLIC_LOOKUP}. + */ + public Class<?> lookupClass() { + return lookupClass; + } + + /** Embody the current class (the lookupClass) as a lookup class + * for method handle creation. + * Must be called by from a method in this package, + * which in turn is called by a method not in this package. + * Also, don't make it private, lest javac interpose + * an access$N method. + */ + Lookup() { + Class caller = getCallerClassAtEntryPoint(); + // make sure we haven't accidentally picked up this class: + checkUnprivilegedlookupClass(caller); + this.lookupClass = caller; + } + + private Lookup(Class<?> lookupClass) { + this.lookupClass = lookupClass; + } + + /** Version of lookup which is trusted minimally. + * It can only be used to create method handles to + * publicly accessible members. + */ + public static final Lookup PUBLIC_LOOKUP = new Lookup(null); + + /** Package-private version of lookup which is trusted. */ + static final Lookup IMPL_LOOKUP = new Lookup(Access.class); + static { MethodHandleImpl.initLookup(IMPL_TOKEN, IMPL_LOOKUP); } + + private static void checkUnprivilegedlookupClass(Class<?> lookupClass) { + if (lookupClass == null || + lookupClass == Access.class || + lookupClass.getName().startsWith("java.dyn.")) + throw newIllegalArgumentException("illegal lookupClass: "+lookupClass); + } + + @Override + public String toString() { + if (lookupClass == null) + return "public"; + return lookupClass.getName(); + } + + // call this from an entry point method in Lookup with extraFrames=0. + private static Class<?> getCallerClassAtEntryPoint() { + final int CALLER_DEPTH = 4; + // 0: Reflection.getCC, 1: getCallerClassAtEntryPoint, + // 2: Lookup.<init>, 3: MethodHandles.*, 4: caller + // Note: This should be the only use of getCallerClass in this file. + return Reflection.getCallerClass(CALLER_DEPTH); + } + + /** + * Produce a method handle for a static method. + * The type of the method handle will be that of the method. + * The method and all its argument types must be accessible to the lookup class. + * If the method's class has not yet been initialized, that is done + * immediately, before the method handle is returned. + * @param defc the class from which the method is accessed + * @param name the name of the method + * @param type the type of the method + * @return the desired method handle + * @exception SecurityException <em>TBD</em> + * @exception NoAccessException if the method does not exist or access checking fails + */ + public + MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws NoAccessException { + MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass); + checkStatic(true, method, lookupClass); + //throw NoSuchMethodException + return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass); + } + + /** + * Produce a method handle for a virtual method. + * The type of the method handle will be that of the method, + * with the receiver type ({@code defc}) prepended. + * The method and all its argument types must be accessible to the lookup class. + * <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 + * @exception SecurityException <em>TBD</em> + * @exception NoAccessException if the method does not exist or access checking fails + */ + public MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws NoAccessException { + MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), true, lookupClass); + checkStatic(false, method, lookupClass); + return MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass); + } + + /** + * Produce an early-bound method handle for a virtual method, + * or a handle for a constructor, as if called from an {@code invokespecial} + * instruction from {@code caller}. + * The type of the method handle will be that of the method or constructor, + * with a suitably restricted receiver type (such as {@code caller}) prepended. + * The method or constructor and all its argument types must be accessible + * to the caller. + * <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 + * lookup class, a security check TBD is performed. + * @param defc the class or interface from which the method is accessed + * @param name the name of the method, or "<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 + * @exception SecurityException <em>TBD</em> + * @exception NoAccessException if the method does not exist or access checking fails + */ + public MethodHandle findSpecial(Class<?> defc, String name, MethodType type, + Class<?> specialCaller) throws NoAccessException { + checkSpecialCaller(specialCaller, lookupClass); + MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), false, specialCaller); + checkStatic(false, method, lookupClass); + if (name.equals("<init>")) { + if (defc != specialCaller) + throw newNoAccessException("constructor must be local to lookup class", method, lookupClass); + } else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) { + throw newNoAccessException("method must be in a superclass of lookup class", method, lookupClass); + } + return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, specialCaller); + } + + /** + * Produce an early-bound method handle for a non-static method. + * The receiver must have a supertype {@code defc} in which a method + * of the given name and type is accessible to the lookup class. + * The method and all its argument types must be accessible to the lookup class. + * The type of the method handle will be that of the method. + * The given receiver will be bound into the method handle. + * <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 + * @exception SecurityException <em>TBD</em> + * @exception NoAccessException if the method does not exist or access checking fails + */ + public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException { + Class<? extends Object> rcvc = receiver.getClass(); // may get NPE + MemberName reference = new MemberName(rcvc, name, type); + MemberName method = IMPL_NAMES.resolveOrFail(reference, true, lookupClass); + checkStatic(false, method, lookupClass); + MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass); + MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver); + if (bmh == null) + throw newNoAccessException(method, lookupClass); + return bmh; + } + + /** + * Make a direct method handle to <i>m</i>, if the lookup class 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 lookup class. + * If <i>m</i> is not public, do not share the resulting handle with untrusted parties. + * @param m the reflected method + * @return a method handle which can invoke the reflected method + * @exception NoAccessException if access checking fails + */ + public MethodHandle unreflect(Method m) throws NoAccessException { + return unreflectImpl(new MemberName(m), m.isAccessible(), true, lookupClass); + } + + /** + * Produce a method handle for a reflected method. + * It will bypass checks for overriding methods on the receiver, + * as if by the {@code invokespecial} instruction. + * The type of the method handle will be that of the method, + * with the receiver type prepended. + * If the method's {@code accessible} flag is not set, + * access checking is performed immediately on behalf of the lookup class, + * as if {@code invokespecial} instruction were being linked. + * @param m the reflected method + * @return a method handle which can invoke the reflected method + * @exception NoAccessException if access checking fails + */ + public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws NoAccessException { + checkSpecialCaller(specialCaller, lookupClass); + MemberName mname = new MemberName(m); + checkStatic(false, mname, lookupClass); + return unreflectImpl(mname, m.isAccessible(), false, specialCaller); + } + + /** + * Produce a method handle for a reflected constructor. + * The type of the method handle will be that of the constructor. + * The method handle will perform a {@code newInstance} operation, + * creating a new instance of the constructor's class on the + * arguments passed to the method handle. + * <p> + * If the constructor's {@code accessible} flag is not set, + * access checking is performed immediately on behalf of the lookup class, + * as if {@code invokespecial} instruction were being linked. + * @param ctor the reflected constructor + * @return a method handle which can invoke the reflected constructor + * @exception NoAccessException if access checking fails + */ + public MethodHandle unreflectConstructor(Constructor ctor) throws NoAccessException { + MemberName m = new MemberName(ctor); + return unreflectImpl(m, ctor.isAccessible(), false, lookupClass); + } + + /** + * <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 lookup class. + * @param f the reflected field + * @return a method handle which can load values from the reflected field + * @exception NoAccessException if access checking fails + */ + public MethodHandle unreflectGetter(Field f) throws NoAccessException { + return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), false, lookupClass); + } + + /** + * <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 lookup class. + * @param f the reflected field + * @return a method handle which can store values into the reflected field + * @exception NoAccessException if access checking fails + */ + public MethodHandle unreflectSetter(Field f) throws NoAccessException { + return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), true, lookupClass); + } + + } + + static /*must not be public*/ + MethodHandle findStaticFrom(Class<?> lookupClass, + Class<?> defc, String name, MethodType type) throws NoAccessException { + MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass); + checkStatic(true, method, lookupClass); + return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass); + } + + static void checkStatic(boolean wantStatic, MemberName m, Class<?> lookupClass) { + if (wantStatic != m.isStatic()) { + String message = wantStatic ? "expected a static method" : "expected a non-static method"; + throw newNoAccessException(message, m, lookupClass); + } + } + + static void checkSpecialCaller(Class<?> specialCaller, Class<?> lookupClass) { + if (lookupClass == Lookup.IMPL_LOOKUP.lookupClass()) + return; // privileged action + if (lookupClass == null || // public-only access + !VerifyAccess.isSamePackageMember(specialCaller, lookupClass)) + throw newNoAccessException("no private access", new MemberName(specialCaller), lookupClass); + } + + // Helper for creating handles on reflected methods and constructors. + static MethodHandle unreflectImpl(MemberName m, boolean isAccessible, + boolean doDispatch, Class<?> lookupClass) { + MethodType mtype = m.getInvocationType(); + Class<?> defc = m.getDeclaringClass(); + int mods = m.getModifiers(); + if (m.isStatic()) { + if (!isAccessible && + VerifyAccess.isAccessible(defc, mods, false, lookupClass) == null) + throw newNoAccessException(m, lookupClass); + } else { + Class<?> constraint; + if (isAccessible) { + // abbreviated access check for "unlocked" method + constraint = doDispatch ? defc : lookupClass; + } else { + constraint = VerifyAccess.isAccessible(defc, mods, doDispatch, lookupClass); + } + if (constraint != defc && !constraint.isAssignableFrom(defc)) { + if (!defc.isAssignableFrom(constraint)) + throw newNoAccessException("receiver must be in caller class", m, lookupClass); + mtype = mtype.changeParameterType(0, constraint); + } + } + return MethodHandleImpl.findMethod(IMPL_TOKEN, m, doDispatch, lookupClass); + } + + /** + * <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 { + return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, false); + } + + /** + * <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 { + return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, true); + } + + + /// 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) { + int argc = arguments == null ? 0 : arguments.length; + MethodType type = target.type(); + if (argc <= 4) { + MethodHandle invoker = invokers(type).genericInvoker(); + switch (argc) { + case 0: return invoker.<Object>invoke(target); + case 1: return invoker.<Object>invoke(target, + arguments[0]); + case 2: return invoker.<Object>invoke(target, + arguments[0], arguments[1]); + case 3: return invoker.<Object>invoke(target, + arguments[0], arguments[1], arguments[2]); + case 4: return invoker.<Object>invoke(target, + arguments[0], arguments[1], arguments[2], arguments[3]); + } + } + MethodHandle invoker = invokers(type).varargsInvoker(); + return invoker.<Object>invoke(target, arguments); + } + + public static + Object invoke_0(MethodHandle target) { + MethodHandle invoker = invokers(target.type()).genericInvoker(); + return invoker.<Object>invoke(target); + } + public static + Object invoke_1(MethodHandle target, Object a0) { + MethodHandle invoker = invokers(target.type()).genericInvoker(); + return invoker.<Object>invoke(target, a0); + } + public static + Object invoke_2(MethodHandle target, Object a0, Object a1) { + MethodHandle invoker = invokers(target.type()).genericInvoker(); + return invoker.<Object>invoke(target, a0, a1); + } + public static + Object invoke_3(MethodHandle target, Object a0, Object a1, Object a2) { + MethodHandle invoker = invokers(target.type()).genericInvoker(); + return invoker.<Object>invoke(target, a0, a1, a2); + } + public static + Object invoke_4(MethodHandle target, Object a0, Object a1, Object a2, Object a3) { + MethodHandle invoker = invokers(target.type()).genericInvoker(); + return invoker.<Object>invoke(target, a0, a1, a2, a3); + } + + /** + * <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) { + return invokers(type).genericInvoker(); + } + + /** + * <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> + * MethodHandles.lookup().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) { + return invokers(type).exactInvoker(); + } + + static private Invokers invokers(MethodType type) { + return MethodTypeImpl.invokers(IMPL_TOKEN, type); + } + + /** + * <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 + <T0, T1> T1 checkValue(Class<T0> t0, Class<T1> t1, Object value) + throws ClassCastException + { + if (t0 == t1) { + // no conversion needed; just reassert the same type + if (t0.isPrimitive()) + return Wrapper.asPrimitiveType(t1).cast(value); + else + return Wrapper.OBJECT.cast(value, t1); + } + boolean prim0 = t0.isPrimitive(), prim1 = t1.isPrimitive(); + if (!prim0) { + // check contract with caller + Wrapper.OBJECT.cast(value, t0); + if (!prim1) { + return Wrapper.OBJECT.cast(value, t1); + } + // convert reference to primitive by unboxing + Wrapper w1 = Wrapper.forPrimitiveType(t1); + return w1.cast(value, t1); + } + // check contract with caller: + Wrapper.asWrapperType(t0).cast(value); + Wrapper w1 = Wrapper.forPrimitiveType(t1); + return w1.cast(value, t1); + } + + static + Object checkValue(Class<?> T1, Object value) + throws ClassCastException + { + Class<?> T0; + if (value == null) + T0 = Object.class; + else + T0 = value.getClass(); + return checkValue(T0, T1, value); + } + + /// method handle modification (creation from other method handles) + + /** + * <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; + MethodHandle res = MethodHandleImpl.convertArguments(IMPL_TOKEN, target, + newType, oldType, null); + if (res == null) + throw newIllegalArgumentException("cannot convert to "+newType+": "+target); + return res; + } + + /** + * <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. + * The resulting method handle is guaranteed to confess a type + * which is equal to the desired new type. + * <p> + * The given array controls the reordering. + * Call {@code #I} the number of incoming parameters (the value + * {@code newType.parameterCount()}, and call {@code #O} the number + * of outgoing parameters (the value {@code target.type().parameterCount()}). + * Then the length of the reordering array must be {@code #O}, + * and each element must be a non-negative number less than {@code #I}. + * For every {@code N} less than {@code #O}, the {@code N}-th + * outgoing argument will be taken from the {@code I}-th incoming + * argument, where {@code I} is {@code reorder[N]}. + * <p> + * The reordering array need not specify an actual permutation. + * An incoming argument will be duplicated if its index appears + * more than once in the array, and an incoming argument will be dropped + * if its index does not appear in the array. + * <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 reorder a string which controls the reordering + * @return a method handle which delegates to {@code target} after performing + * any necessary argument motion and conversions, and arranges for any + * necessary return value conversions + */ + public static + MethodHandle permuteArguments(MethodHandle target, MethodType newType, int[] reorder) { + MethodType oldType = target.type(); + checkReorder(reorder, newType, oldType); + return MethodHandleImpl.convertArguments(IMPL_TOKEN, target, + newType, oldType, + reorder); + } + + private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) { + if (reorder.length == oldType.parameterCount()) { + int limit = newType.parameterCount(); + boolean bad = false; + for (int i : reorder) { + if (i < 0 || i >= limit) { + bad = true; break; + } + } + if (!bad) return; + } + throw newIllegalArgumentException("bad reorder array"); + } + + /** + * <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); + MethodHandle res = null; + if (spreadPos >= 0 && numSpread >= 0) { + res = MethodHandleImpl.spreadArguments(IMPL_TOKEN, target, newType, spreadPos); + } + if (res == null) { + throw newIllegalArgumentException("cannot spread "+newType+" to " +oldType); + } + return res; + } + + /** + * <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. + * <p> + * ISSUE: Unify this with combineArguments. CollectArguments + * is combineArguments with (a) new Object[]{...} as a combiner, + * and (b) the combined arguments dropped, in favor of the combined result. + * @param target the method handle to invoke after the argument is prepended + * @param newType the expected type of the new method handle + * @return a new method handle which collects some trailings argument + * into an array, before calling the original method handle + */ + public static + MethodHandle collectArguments(MethodHandle target, MethodType newType) { + MethodType oldType = target.type(); + int inargs = newType.parameterCount(); + int outargs = oldType.parameterCount(); + int collectPos = outargs - 1; + int numCollect = (inargs - collectPos); + if (collectPos < 0 || numCollect < 0) + throw newIllegalArgumentException("wrong number of arguments"); + return MethodHandleImpl.collectArguments(IMPL_TOKEN, target, newType, collectPos); + } + + /** + * <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 pos where to insert the argument (zero for the first) + * @param value the argument to insert + * @return a new method handle which inserts an additional argument, + * before calling the original method handle + */ + public static + MethodHandle insertArgument(MethodHandle target, int pos, Object value) { + MethodType oldType = target.type(); + ArrayList<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 pos where the return value of {@code combiner} is to + * be inserted as an argument to {@code target} + * @param combiner method handle to call initially on the incoming arguments + * @return method handle which incorporates the specified dispatch logic + * @throws IllegalArgumentException if {@code combiner} does not itself + * return either void or the {@code pos}-th argument of {@code target}, + * or does not have the same argument types as {@code target} + * (minus the inserted argument) + */ + public static + MethodHandle combineArguments(MethodHandle target, int pos, MethodHandle combiner) { + MethodType mhType = target.type(); + Class<?> combineType = combiner.type().returnType(); + MethodType incomingArgs; + if (pos < 0) { + // No inserted argument; target & combiner must have same argument types. + incomingArgs = mhType; + if (!incomingArgs.changeReturnType(combineType).equals(combiner.type())) + throw newIllegalArgumentException("target and combiner types do not match"); + } else { + // Inserted argument. + if (pos >= mhType.parameterCount() + || mhType.parameterType(pos) != combineType) + throw newIllegalArgumentException("inserted combiner argument does not match target"); + incomingArgs = mhType.dropParameterType(pos); + } + if (!incomingArgs.changeReturnType(combineType).equals(combiner.type())) { + throw newIllegalArgumentException("target and combiner types do not match"); + } + return MethodHandleImpl.combineArguments(IMPL_TOKEN, target, combiner, pos); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/MethodType.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,575 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import sun.dyn.Access; +import sun.dyn.Invokers; +import sun.dyn.MethodTypeImpl; +import sun.dyn.util.BytecodeSignature; +import static sun.dyn.MemberName.newIllegalArgumentException; + +/** + * Run-time token used to match call sites with method handles. + * The structure is a return type accompanied by any number of parameter types. + * The types (primitive, void, and reference) are represented by Class objects. + * All instances of <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 { + private final Class<?> rtype; + private final Class<?>[] ptypes; + private MethodTypeForm form; // erased form, plus cached data about primitives + private MethodType wrapAlt; // alternative wrapped/unwrapped version + private Invokers invokers; // cache of handy higher-order adapters + + private static final Access IMPL_TOKEN = Access.getToken(); + + // share a cache with a friend in this package + Invokers getInvokers() { return invokers; } + void setInvokers(Invokers inv) { invokers = inv; } + + static { + // This hack allows the implementation package special access to + // the internals of MethodType. In particular, the Form has all sorts + // of cached information useful to the implementation code. + MethodTypeImpl.setMethodTypeFriend(IMPL_TOKEN, new MethodTypeImpl.MethodTypeFriend() { + public Class<?>[] ptypes(MethodType mt) { return mt.ptypes; } + public MethodTypeImpl form(MethodType mt) { return mt.form; } + public void setForm(MethodType mt, MethodTypeImpl form) { + assert(mt.form == null); + mt.form = (MethodTypeForm) form; + } + public MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) { + return MethodType.makeImpl(rtype, ptypes, trusted); + } + public MethodTypeImpl newMethodTypeForm(MethodType mt) { + return new MethodTypeForm(mt); + } + public Invokers getInvokers(MethodType mt) { return mt.invokers; } + public void setInvokers(MethodType mt, Invokers inv) { mt.invokers = inv; } + }); + } + + private MethodType(Class<?> rtype, Class<?>[] ptypes) { + checkRtype(rtype); + checkPtypes(ptypes); + this.rtype = rtype; + this.ptypes = ptypes; + } + + private void checkRtype(Class<?> rtype) { + rtype.equals(rtype); // null check + } + private void checkPtypes(Class<?>[] ptypes) { + for (Class<?> ptype : ptypes) { + ptype.equals(ptype); // null check + if (ptype == void.class) + throw newIllegalArgumentException("void parameter: "+this); + } + } + + static final HashMap<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<? extends Class<?>> ptypes) { + return makeImpl(rtype, ptypes.toArray(NO_PTYPES), true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * The leading parameter type is prepended to the remaining array. + */ + public static + MethodType make(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) { + Class<?>[] ptypes1 = new Class<?>[1+ptypes.length]; + ptypes1[0] = ptype0; + System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length); + return makeImpl(rtype, ptypes1, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * The resulting method has no parameter types. + */ + public static + MethodType make(Class<?> rtype) { + return makeImpl(rtype, NO_PTYPES, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * The resulting method has the single given parameter type. + */ + public static + MethodType make(Class<?> rtype, Class<?> ptype0) { + return makeImpl(rtype, new Class<?>[]{ ptype0 }, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * The resulting method has the same parameter types as {@code ptypes}, + * and the specified return type. + */ + public static + MethodType make(Class<?> rtype, MethodType ptypes) { + return makeImpl(rtype, ptypes.ptypes, true); + } + + /** + * Sole factory method to find or create an interned method type. + * @param rtype desired return type + * @param ptypes desired parameter types + * @param trusted whether the ptypes can be used without cloning + * @return the unique method type of the desired structure + */ + private static + MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) { + if (ptypes == null || ptypes.length == 0) { + ptypes = NO_PTYPES; trusted = true; + } + MethodType mt1 = new MethodType(rtype, ptypes); + MethodType mt0; + synchronized (internTable) { + mt0 = internTable.get(mt1); + if (mt0 != null) + return mt0; + } + if (!trusted) + // defensively copy the array passed in by the user + mt1 = new MethodType(rtype, ptypes.clone()); + // promote the object to the Real Thing, and reprobe + MethodTypeImpl.initForm(IMPL_TOKEN, mt1); + synchronized (internTable) { + mt0 = internTable.get(mt1); + if (mt0 != null) + return mt0; + internTable.put(mt1, mt1); + } + return mt1; + } + + // Entry point from JVM. TODO: Change the name & signature. + private static MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, + boolean ignore1, boolean ignore2) { + return makeImpl(rtype, ptypes, true); + } + + private static final MethodType[] objectOnlyTypes = new MethodType[20]; + + /** + * Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * All parameters and the return type will be Object, except the final varargs parameter if any. + * @param objectArgCount number of parameters (excluding the varargs parameter if any) + * @param varargs whether there will be a varargs parameter, of type Object[] + * @return a totally generic method type, given only its count of parameters and varargs + * @see #makeGeneric(int) + */ + public static + MethodType makeGeneric(int objectArgCount, boolean varargs) { + MethodType mt; + int ivarargs = (!varargs ? 0 : 1); + int ootIndex = objectArgCount*2 + ivarargs; + if (ootIndex < objectOnlyTypes.length) { + mt = objectOnlyTypes[ootIndex]; + if (mt != null) return mt; + } + Class<?>[] ptypes = new Class<?>[objectArgCount + ivarargs]; + Arrays.fill(ptypes, Object.class); + if (ivarargs != 0) ptypes[objectArgCount] = Object[].class; + mt = makeImpl(Object.class, ptypes, true); + if (ootIndex < objectOnlyTypes.length) { + objectOnlyTypes[ootIndex] = mt; // cache it here also! + } + return mt; + } + + /** + * All parameters and the return type will be Object. + * @param objectArgCount number of parameters + * @return a totally generic method type, given only its count of parameters + * @see #makeGeneric(int, boolean) + */ + public static + MethodType makeGeneric(int objectArgCount) { + return makeGeneric(objectArgCount, false); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * @param num the index (zero-based) of the parameter type to change + * @param nptype a new parameter type to replace the old one with + * @return the same type, except with the selected parameter changed + */ + public MethodType changeParameterType(int num, Class<?> nptype) { + if (parameterType(num) == nptype) return this; + Class<?>[] nptypes = ptypes.clone(); + nptypes[num] = nptype; + return makeImpl(rtype, nptypes, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * @param num the position (zero-based) of the inserted parameter type + * @param nptype a new parameter type to insert into the parameter list + * @return the same type, except with the selected parameter inserted + */ + public MethodType insertParameterType(int num, Class<?> nptype) { + int len = ptypes.length; + Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+1); + System.arraycopy(nptypes, num, nptypes, num+1, len-num); + nptypes[num] = nptype; + return makeImpl(rtype, nptypes, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * @param num the index (zero-based) of the parameter type to remove + * @return the same type, except with the selected parameter removed + */ + public MethodType dropParameterType(int num) { + int len = ptypes.length; + Class<?>[] nptypes; + if (num == 0) { + nptypes = Arrays.copyOfRange(ptypes, 1, len); + } else { + nptypes = Arrays.copyOfRange(ptypes, 0, len-1); + System.arraycopy(ptypes, num+1, nptypes, num, (len-1)-num); + } + return makeImpl(rtype, nptypes, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * @param nrtype a return parameter type to replace the old one with + * @return the same type, except with the return type change + */ + public MethodType changeReturnType(Class<?> nrtype) { + if (returnType() == nrtype) return this; + return makeImpl(nrtype, ptypes, true); + } + + /** Convenience method. + * Report if this type contains a primitive argument or return value. + * @return true if any of the types are primitives + */ + public boolean hasPrimitives() { + return form.hasPrimitives(); + } + + /** Convenience method. + * Report if this type contains a wrapper argument or return value. + * Wrappers are types which box primitive values, such as {@link Integer}. + * @return true if any of the types are wrappers + */ + public boolean hasWrappers() { + return unwrap() != this; + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Erase all reference types to Object. + * @return a version of the original type with all reference types replaced + */ + public MethodType erase() { + return form.erasedType(); + } + + /** Convenience method for {@link #makeGeneric(int)}. + * Convert all types, both reference and primitive, to Object. + * @return a version of the original type with all types replaced + */ + public MethodType generic() { + return makeGeneric(parameterCount()); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Convert all primitive types to their corresponding wrapper types. + * A {@code void} return type is changed to the type {@code java.lang.Void}. + * @return a version of the original type with all primitive types replaced + */ + public MethodType wrap() { + return hasPrimitives() ? wrapWithPrims(this) : this; + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Convert all wrapper types to their corresponding primitive types. + * A return type of {@java.lang.Void} is changed to {@code void}. + * @return a version of the original type with all wrapper types replaced + */ + public MethodType unwrap() { + MethodType noprims = !hasPrimitives() ? this : wrapWithPrims(this); + return unwrapWithNoPrims(noprims); + } + + private static MethodType wrapWithPrims(MethodType pt) { + assert(pt.hasPrimitives()); + MethodType wt = pt.wrapAlt; + if (wt == null) { + // fill in lazily + wt = MethodTypeImpl.canonicalize(pt, MethodTypeImpl.WRAP, MethodTypeImpl.WRAP); + assert(wt != null); + pt.wrapAlt = wt; + } + return wt; + } + + private static MethodType unwrapWithNoPrims(MethodType wt) { + assert(!wt.hasPrimitives()); + MethodType uwt = wt.wrapAlt; + if (uwt == null) { + // fill in lazily + uwt = MethodTypeImpl.canonicalize(wt, MethodTypeImpl.UNWRAP, MethodTypeImpl.UNWRAP); + if (uwt == null) + uwt = wt; // type has no wrappers or prims at all + wt.wrapAlt = uwt; + } + return uwt; + } + + /** @param num the index (zero-based) of the desired parameter type + * @return the selected parameter type + */ + public Class<?> parameterType(int num) { + return ptypes[num]; + } + /** @return the number of parameter types */ + public int parameterCount() { + return ptypes.length; + } + /** @return the return type */ + public Class<?> returnType() { + return rtype; + } + + /** + * Convenience method to present the arguments as a list. + * @return the parameter types (as an immutable list) + */ + public List<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). + * <p> + * This method is included for the benfit of applications that must + * generate bytecodes that process method handles and invokedynamic. + * @return the number of JVM stack slots for this type's parameters + */ + public int parameterSlotCount() { + return form.parameterSlotCount(); + } + + /** Number of JVM stack slots which carry all parameters after + * the given position, which must be in the range of 0 to + * {@code parameterCount} inclusive. Successive parameters are + * more shallowly stacked, and parameters are indexed in the bytecodes + * according to their trailing edge. Thus, to obtain the depth + * in the outgoing call stack of parameter {@code N}, obtain + * the {@code parameterSlotDepth} of its trailing edge + * at position {@code N+1}. + * <p> + * Parameters of type {@code long} and {@code double} occupy + * two stack slots (for historical reasons) and all others occupy one. + * Therefore, the number returned is the number of arguments + * <em>including</em> and <em>after</em> the given parameter, + * <em>plus</em> the number of long or double arguments + * at or after after the argument for the given parameter. + * <p> + * This method is included for the benfit of applications that must + * generate bytecodes that process method handles and invokedynamic. + * @param num an index (zero-based, inclusive) within the parameter types + * @return the index of the (shallowest) JVM stack slot transmitting the + * given parameter + */ + public int parameterSlotDepth(int num) { + if (num < 0 || num > ptypes.length) + parameterType(num); // force a range check + return form.parameterToArgSlot(num-1); + } + + /** The number of JVM stack slots required to receive a return value + * from a method of this type. + * If the {@link #returnType() return type} is void, it will be zero, + * else if the return type is long or double, it will be two, else one. + * <p> + * This method is included for the benfit of applications that must + * generate bytecodes that process method handles and invokedynamic. + * @return the number of JVM stack slots (0, 1, or 2) for this type's return value + */ + public int returnSlotCount() { + return form.returnSlotCount(); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Find or create an instance (interned) of the given method type. + * Any class or interface name embedded in the signature string + * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)} + * on the given loader (or if it is null, on the system class loader). + * <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. + * <p> + * This method is included for the benfit of applications that must + * generate bytecodes that process method handles and invokedynamic. + * @param bytecodeSignature a bytecode-level signature string "(T...)T" + * @param loader the class loader in which to look up the types + * @return a method type matching the bytecode-level signature + * @throws IllegalArgumentException if the string is not well-formed + * @throws TypeNotPresentException if a named type cannot be found + */ + public static MethodType fromBytecodeString(String bytecodeSignature, ClassLoader loader) + throws IllegalArgumentException, TypeNotPresentException + { + List<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 + * <p> + * This method is included for the benfit of applications that must + * generate bytecodes that process method handles and invokedynamic. + * {@link #fromBytecodeString(java.lang.String, java.lang.ClassLoader)}, + * because the latter requires a suitable class loader argument. + * @return the bytecode signature representation + */ + public String toBytecodeString() { + return BytecodeSignature.unparse(this); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/MethodTypeForm.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +/** + * TO DO: Temporary shim; remove after refactoring effects are complete in JVM. + * @author John Rose + */ +import sun.dyn.MethodTypeImpl; + +class MethodTypeForm extends MethodTypeImpl { + + MethodTypeForm(MethodType erasedType) { + super(erasedType); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/NoAccessException.java Tue May 05 23:12:47 2009 -0700 @@ -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); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/WrongMethodTypeException.java Tue May 05 23:12:47 2009 -0700 @@ -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); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/package-info.java Tue May 05 23:12:47 2009 -0700 @@ -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;
--- a/src/share/classes/java/io/Console.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/java/io/Console.java Tue May 05 23:12:47 2009 -0700 @@ -503,20 +503,25 @@ // Set up JavaIOAccess in SharedSecrets static { - - // Add a shutdown hook to restore console's echo state should - // it be necessary. - sun.misc.SharedSecrets.getJavaLangAccess() - .registerShutdownHook(0 /* shutdown hook invocation order */, - new Runnable() { - public void run() { - try { - if (echoOff) { - echo(true); - } - } catch (IOException x) { } - } - }); + try { + // Add a shutdown hook to restore console's echo state should + // it be necessary. + sun.misc.SharedSecrets.getJavaLangAccess() + .registerShutdownHook(0 /* shutdown hook invocation order */, + false /* only register if shutdown is not in progress */, + new Runnable() { + public void run() { + try { + if (echoOff) { + echo(true); + } + } catch (IOException x) { } + } + }); + } catch (IllegalStateException e) { + // shutdown is already in progress and console is first used + // by a shutdown hook + } sun.misc.SharedSecrets.setJavaIOAccess(new sun.misc.JavaIOAccess() { public Console console() {
--- a/src/share/classes/java/io/DeleteOnExitHook.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/java/io/DeleteOnExitHook.java Tue May 05 23:12:47 2009 -0700 @@ -34,23 +34,31 @@ */ class DeleteOnExitHook { + private static LinkedHashSet<String> files = new LinkedHashSet<String>(); static { - sun.misc.SharedSecrets.getJavaLangAccess() - .registerShutdownHook(2 /* Shutdown hook invocation order */, - new Runnable() { - public void run() { - runHooks(); - } - }); + // DeleteOnExitHook must be the last shutdown hook to be invoked. + // Application shutdown hooks may add the first file to the + // delete on exit list and cause the DeleteOnExitHook to be + // registered during shutdown in progress. So set the + // registerShutdownInProgress parameter to true. + sun.misc.SharedSecrets.getJavaLangAccess() + .registerShutdownHook(2 /* Shutdown hook invocation order */, + true /* register even if shutdown in progress */, + new Runnable() { + public void run() { + runHooks(); + } + } + ); } - private static LinkedHashSet<String> files = new LinkedHashSet<String>(); - private DeleteOnExitHook() {} static synchronized void add(String file) { - if(files == null) + if(files == null) { + // DeleteOnExitHook is running. Too late to add a file throw new IllegalStateException("Shutdown in progress"); + } files.add(file); }
--- a/src/share/classes/java/lang/ApplicationShutdownHooks.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/java/lang/ApplicationShutdownHooks.java Tue May 05 23:12:47 2009 -0700 @@ -35,17 +35,26 @@ */ class ApplicationShutdownHooks { + /* The set of registered hooks */ + private static IdentityHashMap<Thread, Thread> hooks; static { - Shutdown.add(1 /* shutdown hook invocation order */, - new Runnable() { - public void run() { - runHooks(); + try { + Shutdown.add(1 /* shutdown hook invocation order */, + false /* not registered if shutdown in progress */, + new Runnable() { + public void run() { + runHooks(); + } } - }); + ); + hooks = new IdentityHashMap<Thread, Thread>(); + } catch (IllegalStateException e) { + // application shutdown hooks cannot be added if + // shutdown is in progress. + hooks = null; + } } - /* The set of registered hooks */ - private static IdentityHashMap<Thread, Thread> hooks = new IdentityHashMap<Thread, Thread>(); private ApplicationShutdownHooks() {}
--- a/src/share/classes/java/lang/Shutdown.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/java/lang/Shutdown.java Tue May 05 23:12:47 2009 -0700 @@ -53,6 +53,9 @@ private static final int MAX_SYSTEM_HOOKS = 10; private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS]; + // the index of the currently running shutdown hook to the hooks array + private static int currentRunningHook = 0; + /* The preceding static fields are protected by this lock */ private static class Lock { }; private static Object lock = new Lock(); @@ -68,17 +71,39 @@ } - /* Add a new shutdown hook. Checks the shutdown state and the hook itself, + /** + * Add a new shutdown hook. Checks the shutdown state and the hook itself, * but does not do any security checks. + * + * The registerShutdownInProgress parameter should be false except + * registering the DeleteOnExitHook since the first file may + * be added to the delete on exit list by the application shutdown + * hooks. + * + * @params slot the slot in the shutdown hook array, whose element + * will be invoked in order during shutdown + * @params registerShutdownInProgress true to allow the hook + * to be registered even if the shutdown is in progress. + * @params hook the hook to be registered + * + * @throw IllegalStateException + * if registerShutdownInProgress is false and shutdown is in progress; or + * if registerShutdownInProgress is true and the shutdown process + * already passes the given slot */ - static void add(int slot, Runnable hook) { + static void add(int slot, boolean registerShutdownInProgress, Runnable hook) { synchronized (lock) { - if (state > RUNNING) - throw new IllegalStateException("Shutdown in progress"); - if (hooks[slot] != null) throw new InternalError("Shutdown hook at slot " + slot + " already registered"); + if (!registerShutdownInProgress) { + if (state > RUNNING) + throw new IllegalStateException("Shutdown in progress"); + } else { + if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook)) + throw new IllegalStateException("Shutdown in progress"); + } + hooks[slot] = hook; } } @@ -86,11 +111,15 @@ /* Run all registered shutdown hooks */ private static void runHooks() { - /* We needn't bother acquiring the lock just to read the hooks field, - * since the hooks can't be modified once shutdown is in progress - */ - for (Runnable hook : hooks) { + for (int i=0; i < MAX_SYSTEM_HOOKS; i++) { try { + Runnable hook; + synchronized (lock) { + // acquire the lock to make sure the hook registered during + // shutdown is visible here. + currentRunningHook = i; + hook = hooks[i]; + } if (hook != null) hook.run(); } catch(Throwable t) { if (t instanceof ThreadDeath) {
--- a/src/share/classes/java/lang/System.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/java/lang/System.java Tue May 05 23:12:47 2009 -0700 @@ -1171,8 +1171,8 @@ public void blockedOn(Thread t, Interruptible b) { t.blockedOn(b); } - public void registerShutdownHook(int slot, Runnable r) { - Shutdown.add(slot, r); + public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) { + Shutdown.add(slot, registerShutdownInProgress, hook); } }); }
--- a/src/share/classes/java/util/zip/ZipFile.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/java/util/zip/ZipFile.java Tue May 05 23:12:47 2009 -0700 @@ -154,7 +154,7 @@ * @param file the ZIP file to be opened for reading * @param mode the mode in which the file is to be opened * @param charset - * the {@link java.nio.charset.Charset {@code charset}} to + * the {@linkplain java.nio.charset.Charset charset} to * be used to decode the ZIP entry name and comment that are not * encoded by using UTF-8 encoding (indicated by entry's general * purpose flag). @@ -206,7 +206,7 @@ * * @param name the name of the zip file * @param charset - * the {@link java.nio.charset.Charset {@code charset}} to + * the {@linkplain java.nio.charset.Charset charset} to * be used to decode the ZIP entry name and comment that are not * encoded by using UTF-8 encoding (indicated by entry's general * purpose flag). @@ -230,7 +230,7 @@ * Opens a ZIP file for reading given the specified File object. * @param file the ZIP file to be opened for reading * @param charset - * The {@link java.nio.charset.Charset {@code charset}} to be + * The {@linkplain java.nio.charset.Charset charset} to be * used to decode the ZIP entry name and comment (ignored if * the <a href="package-summary.html#lang_encoding"> language * encoding bit</a> of the ZIP entry's general purpose bit
--- a/src/share/classes/java/util/zip/ZipInputStream.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/java/util/zip/ZipInputStream.java Tue May 05 23:12:47 2009 -0700 @@ -84,7 +84,7 @@ * @param in the actual input stream * * @param charset - * The {@link java.nio.charset.Charset {@code charset}} to be + * The {@linkplain java.nio.charset.Charset charset} to be * used to decode the ZIP entry name (ignored if the * <a href="package-summary.html#lang_encoding"> language * encoding bit</a> of the ZIP entry's general purpose bit
--- a/src/share/classes/java/util/zip/ZipOutputStream.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/java/util/zip/ZipOutputStream.java Tue May 05 23:12:47 2009 -0700 @@ -108,7 +108,7 @@ * * @param out the actual output stream * - * @param charset the {@link java.nio.charset.Charset </code>charset<code>} + * @param charset the {@linkplain java.nio.charset.Charset charset} * to be used to encode the entry names and comments * * @since 1.7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/Access.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,109 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import sun.reflect.Reflection; + +/** + * Access control to this package. + * Classes in other packages can attempt to acquire the access token, + * but will fail if they are not recognized as friends. + * Certain methods in this package, although public, require a non-null + * access token in order to proceed; they act like package-private methods. + * @author jrose + */ + +public class Access { + + private Access() { } + + /** + * The heart of this pattern: The list of classes which are + * permitted to acquire the access token, and become honorary + * members of this package. + */ + private static final String[] FRIENDS = { + "java.dyn.", "sun.dyn." + }; + + /** + * The following object is NOT public. That's the point of the pattern. + * It is package-private, so that any member of this package + * can acquire the access token, and give it away to trusted friends. + */ + static final Access TOKEN = new Access(); + + /** + * @return Access.TOKEN, if the caller is a friend of this package + */ + public static Access getToken() { + Class<?> callc = Reflection.getCallerClass(2); + if (isFriend(callc)) + return TOKEN; + else + throw new IllegalAccessError("bad caller: " + callc); + } + + /** Is the given name the name of a class which could be our friend? */ + public static boolean isFriendName(String name) { + for (String friend : FRIENDS) { + if (name.startsWith(friend)) + return true; + } + return false; + } + + /** Is the given class a friend? True if {@link #isFriendName}, + * and the given class also shares a class loader with us. + */ + public static boolean isFriend(Class<?> c) { + return isFriendName(c.getName()) && c.getClassLoader() == CLASS_LOADER; + } + + private static final ClassLoader CLASS_LOADER = Access.class.getClassLoader(); + + /** + * Throw an IllegalAccessError if the caller does not possess + * the Access.TOKEN. + * @param must be Access.TOKEN + */ + public static void check(Access token) { + if (token == null) + fail(); + // else it must be the unique Access.TOKEN + assert(token == Access.TOKEN); + } + private static void fail() { + final int CALLER_DEPTH = 3; + // 0: Reflection.getCC, 1: this.fail, 2: Access.*, 3: caller + Class<?> callc = Reflection.getCallerClass(CALLER_DEPTH); + throw new IllegalAccessError("bad caller: " + callc); + } + + static { + //sun.reflect.Reflection.registerMethodsToFilter(MH.class, "getToken"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/AdapterMethodHandle.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,728 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import sun.dyn.util.VerifyType; +import sun.dyn.util.Wrapper; +import java.dyn.*; +import java.util.Arrays; +import static sun.dyn.MethodHandleNatives.Constants.*; +import static sun.dyn.MethodHandleImpl.newIllegalArgumentException; + +/** + * This method handle performs simple conversion or checking of a single argument. + * @author jrose + */ +public class AdapterMethodHandle extends BoundMethodHandle { + + //MethodHandle vmtarget; // next AMH or BMH in chain or final DMH + //Object argument; // parameter to the conversion if needed + //int vmargslot; // which argument slot is affected + private final int conversion; // the type of conversion: RETYPE_ONLY, etc. + + // Constructors in this class *must* be package scoped or private. + private AdapterMethodHandle(MethodHandle target, MethodType newType, + long conv, Object convArg) { + super(newType, convArg, newType.parameterSlotDepth(1+convArgPos(conv))); + this.conversion = convCode(conv); + if (MethodHandleNatives.JVM_SUPPORT) { + // JVM might update VM-specific bits of conversion (ignore) + MethodHandleNatives.init(this, target, convArgPos(conv)); + } + } + private AdapterMethodHandle(MethodHandle target, MethodType newType, + long conv) { + this(target, newType, conv, null); + } + + private static final Access IMPL_TOKEN = Access.getToken(); + + // TO DO: When adapting another MH with a null conversion, clone + // the target and change its type, instead of adding another layer. + + /** Can a JVM-level adapter directly implement the proposed + * argument conversions, as if by MethodHandles.convertArguments? + */ + public static boolean canPairwiseConvert(MethodType newType, MethodType oldType) { + // same number of args, of course + int len = newType.parameterCount(); + if (len != oldType.parameterCount()) + return false; + + // Check return type. (Not much can be done with it.) + Class<?> exp = newType.returnType(); + Class<?> ret = oldType.returnType(); + if (!VerifyType.isNullConversion(ret, exp)) + return false; + + // Check args pairwise. + for (int i = 0; i < len; i++) { + Class<?> src = newType.parameterType(i); // source type + Class<?> dst = oldType.parameterType(i); // destination type + if (!canConvertArgument(src, dst)) + return false; + } + + return true; + } + + /** Can a JVM-level adapter directly implement the proposed + * argument conversion, as if by MethodHandles.convertArguments? + */ + public static boolean canConvertArgument(Class<?> src, Class<?> dst) { + // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes, + // so we don't need to repeat so much decision making. + if (VerifyType.isNullConversion(src, dst)) { + return true; + } else if (src.isPrimitive()) { + if (dst.isPrimitive()) + return canPrimCast(src, dst); + else + return canBoxArgument(src, dst); + } else { + if (dst.isPrimitive()) + return canUnboxArgument(src, dst); + else + return true; // any two refs can be interconverted + } + } + + /** + * Create a JVM-level adapter method handle to conform the given method + * handle to the similar newType, using only pairwise argument conversions. + * For each argument, convert incoming argument to the exact type needed. + * Only null conversions are allowed on the return value (until + * the JVM supports ricochet adapters). + * The argument conversions allowed are casting, unboxing, + * integral widening or narrowing, and floating point widening or narrowing. + * @param token access check + * @param newType required call type + * @param target original method handle + * @return an adapter to the original handle with the desired new type, + * or the original target if the types are already identical + * or null if the adaptation cannot be made + */ + public static MethodHandle makePairwiseConvert(Access token, + MethodType newType, MethodHandle target) { + Access.check(token); + MethodType oldType = target.type(); + if (newType == oldType) return target; + + if (!canPairwiseConvert(newType, oldType)) + return null; + // (after this point, it is an assertion error to fail to convert) + + // Find last non-trivial conversion (if any). + int lastConv = newType.parameterCount()-1; + while (lastConv >= 0) { + Class<?> src = newType.parameterType(lastConv); // source type + Class<?> dst = oldType.parameterType(lastConv); // destination type + if (VerifyType.isNullConversion(src, dst)) { + --lastConv; + } else { + break; + } + } + // Now build a chain of one or more adapters. + MethodHandle adapter = target; + MethodType midType = oldType.changeReturnType(newType.returnType()); + for (int i = 0; i <= lastConv; i++) { + Class<?> src = newType.parameterType(i); // source type + Class<?> dst = midType.parameterType(i); // destination type + if (VerifyType.isNullConversion(src, dst)) { + // do nothing: difference is trivial + continue; + } + // Work the current type backward toward the desired caller type: + if (i != lastConv) { + midType = midType.changeParameterType(i, src); + } else { + // When doing the last (or only) real conversion, + // force all remaining null conversions to happen also. + assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src))); + midType = newType; + } + + // Tricky case analysis follows. + // It parallels canConvertArgument() above. + if (src.isPrimitive()) { + if (dst.isPrimitive()) { + adapter = makePrimCast(token, midType, adapter, i, dst); + } else { + adapter = makeBoxArgument(token, midType, adapter, i, dst); + } + } else { + if (dst.isPrimitive()) { + // Caller has boxed a primitive. Unbox it for the target. + // The box type must correspond exactly to the primitive type. + // This is simpler than the powerful set of widening + // conversions supported by reflect.Method.invoke. + // Those conversions require a big nest of if/then/else logic, + // which we prefer to make a user responsibility. + adapter = makeUnboxArgument(token, midType, adapter, i, dst); + } else { + // Simple reference conversion. + // Note: Do not check for a class hierarchy relation + // between src and dst. In all cases a 'null' argument + // will pass the cast conversion. + adapter = makeCheckCast(token, midType, adapter, i, dst); + } + } + assert(adapter != null); + assert(adapter.type() == midType); + } + if (adapter.type() != newType) { + // Only trivial conversions remain. + adapter = makeRetypeOnly(IMPL_TOKEN, newType, adapter); + assert(adapter != null); + // Actually, that's because there were no non-trivial ones: + assert(lastConv == -1); + } + assert(adapter.type() == newType); + return adapter; + } + + /** + * Create a JVM-level adapter method handle to permute the arguments + * of the given method. + * @param token access check + * @param newType required call type + * @param target original method handle + * @param argumentMap for each target argument, position of its source in newType + * @return an adapter to the original handle with the desired new type, + * or the original target if the types are already identical + * and the permutation is null + * @throws IllegalArgumentException if the adaptation cannot be made + * directly by a JVM-level adapter, without help from Java code + */ + public static MethodHandle makePermutation(Access token, + MethodType newType, MethodHandle target, + int[] argumentMap) { + MethodType oldType = target.type(); + boolean nullPermutation = true; + for (int i = 0; i < argumentMap.length; i++) { + int pos = argumentMap[i]; + if (pos != i) + nullPermutation = false; + if (pos < 0 || pos >= newType.parameterCount()) { + argumentMap = new int[0]; break; + } + } + if (argumentMap.length != oldType.parameterCount()) + throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap)); + if (nullPermutation) { + MethodHandle res = makePairwiseConvert(token, newType, target); + // well, that was easy + if (res == null) + throw newIllegalArgumentException("cannot convert pairwise: "+newType); + return res; + } + + // Check return type. (Not much can be done with it.) + Class<?> exp = newType.returnType(); + Class<?> ret = oldType.returnType(); + if (!VerifyType.isNullConversion(ret, exp)) + throw newIllegalArgumentException("bad return conversion for "+newType); + + // See if the argument types match up. + for (int i = 0; i < argumentMap.length; i++) { + int j = argumentMap[i]; + Class<?> src = newType.parameterType(j); + Class<?> dst = oldType.parameterType(i); + if (!VerifyType.isNullConversion(src, dst)) + throw newIllegalArgumentException("bad argument #"+j+" conversion for "+newType); + } + + // Now figure out a nice mix of SWAP, ROT, DUP, and DROP adapters. + // A workable greedy algorithm is as follows: + // Drop unused outgoing arguments (right to left: shallowest first). + // Duplicate doubly-used outgoing arguments (left to right: deepest first). + // Then the remaining problem is a true argument permutation. + // Marshal the outgoing arguments as required from left to right. + // That is, find the deepest outgoing stack position that does not yet + // have the correct argument value, and correct at least that position + // by swapping or rotating in the misplaced value (from a shallower place). + // If the misplaced value is followed by one or more consecutive values + // (also misplaced) issue a rotation which brings as many as possible + // into position. Otherwise make progress with either a swap or a + // rotation. Prefer the swap as cheaper, but do not use it if it + // breaks a slot pair. Prefer the rotation over the swap if it would + // preserve more consecutive values shallower than the target position. + // When more than one rotation will work (because the required value + // is already adjacent to the target position), then use a rotation + // which moves the old value in the target position adjacent to + // one of its consecutive values. Also, prefer shorter rotation + // spans, since they use fewer memory cycles for shuffling. + + throw new UnsupportedOperationException("NYI"); + } + + private static byte basicType(Class<?> type) { + if (type == null) return T_VOID; + switch (Wrapper.forBasicType(type)) { + case BOOLEAN: return T_BOOLEAN; + case CHAR: return T_CHAR; + case FLOAT: return T_FLOAT; + case DOUBLE: return T_DOUBLE; + case BYTE: return T_BYTE; + case SHORT: return T_SHORT; + case INT: return T_INT; + case LONG: return T_LONG; + case OBJECT: return T_OBJECT; + case VOID: return T_VOID; + } + return 99; // T_ILLEGAL or some such + } + + /** Number of stack slots for the given type. + * Two for T_DOUBLE and T_FLOAT, one for the rest. + */ + private static int type2size(int type) { + assert(type >= T_BOOLEAN && type <= T_OBJECT); + return (type == T_FLOAT || type == T_DOUBLE) ? 2 : 1; + } + + /** Construct an adapter conversion descriptor for a single-argument conversion. */ + private static long makeConv(int convOp, int argnum, int src, int dest) { + assert(src == (src & 0xF)); + assert(dest == (dest & 0xF)); + assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF); + long stackMove = type2size(dest) - type2size(src); + return ((long) argnum << 32 | + (long) convOp << CONV_OP_SHIFT | + (int) src << CONV_SRC_TYPE_SHIFT | + (int) dest << CONV_DEST_TYPE_SHIFT | + stackMove << CONV_STACK_MOVE_SHIFT + ); + } + private static long makeConv(int convOp, int argnum, int stackMove) { + assert(convOp >= OP_SWAP_ARGS && convOp <= OP_SPREAD_ARGS); + byte src = 0, dest = 0; + if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS) + src = dest = T_OBJECT; + return ((long) argnum << 32 | + (long) convOp << CONV_OP_SHIFT | + (int) src << CONV_SRC_TYPE_SHIFT | + (int) dest << CONV_DEST_TYPE_SHIFT | + stackMove << CONV_STACK_MOVE_SHIFT + ); + } + private static long makeConv(int convOp) { + assert(convOp == OP_RETYPE_ONLY); + return (long) convOp << CONV_OP_SHIFT; // stackMove, src, dst, argnum all zero + } + private static int convCode(long conv) { + return (int)conv; + } + private static int convArgPos(long conv) { + return (int)(conv >>> 32); + } + private static boolean convOpSupported(int convOp) { + assert(convOp >= 0 && convOp <= CONV_OP_LIMIT); + return ((1<<convOp) & CONV_OP_IMPLEMENTED_MASK) != 0; + } + + /** One of OP_RETYPE_ONLY, etc. */ + int conversionOp() { return (conversion & CONV_OP_MASK) >> CONV_OP_SHIFT; } + + @Override + public String toString() { + return addTypeString(this, "Adapted[" + basicToString(nonAdapter((MethodHandle)vmtarget)) + "]"); + } + + private static MethodHandle nonAdapter(MethodHandle mh) { + return (MethodHandle) + MethodHandleNatives.getTarget(mh, ETF_DIRECT_HANDLE); + } + + /* Return one plus the position of the first non-trivial difference + * between the given types. This is not a symmetric operation; + * we are considering adapting the targetType to adapterType. + * Trivial differences are those which could be ignored by the JVM + * without subverting the verifier. Otherwise, adaptable differences + * are ones for which we could create an adapter to make the type change. + * Return zero if there are no differences (other than trivial ones). + * Return 1+N if N is the only adaptable argument difference. + * Return the -2-N where N is the first of several adaptable + * argument differences. + * Return -1 if there there are differences which are not adaptable. + */ + private static int diffTypes(MethodType adapterType, + MethodType targetType, + boolean raw) { + int diff; + diff = diffReturnTypes(adapterType, targetType, raw); + if (diff != 0) return diff; + int nargs = adapterType.parameterCount(); + if (nargs != targetType.parameterCount()) + return -1; + diff = diffParamTypes(adapterType, 0, targetType, 0, nargs, raw); + //System.out.println("diff "+adapterType); + //System.out.println(" "+diff+" "+targetType); + return diff; + } + private static int diffReturnTypes(MethodType adapterType, + MethodType targetType, + boolean raw) { + Class<?> src = targetType.returnType(); + Class<?> dst = adapterType.returnType(); + if ((!raw + ? VerifyType.canPassUnchecked(src, dst) + : VerifyType.canPassRaw(src, dst) + ) > 0) + return 0; // no significant difference + if (raw && !src.isPrimitive() && !dst.isPrimitive()) + return 0; // can force a reference return (very carefully!) + //if (false) return 1; // never adaptable! + return -1; // some significant difference + } + private static int diffParamTypes(MethodType adapterType, int tstart, + MethodType targetType, int astart, + int nargs, boolean raw) { + assert(nargs >= 0); + int res = 0; + for (int i = 0; i < nargs; i++) { + Class<?> src = adapterType.parameterType(tstart+i); + Class<?> dest = targetType.parameterType(astart+i); + if ((!raw + ? VerifyType.canPassUnchecked(src, dest) + : VerifyType.canPassRaw(src, dest) + ) <= 0) { + // found a difference; is it the only one so far? + if (res != 0) + return -1-res; // return -2-i for prev. i + res = 1+i; + } + } + return res; + } + + /** Can a retyping adapter (alone) validly convert the target to newType? */ + public static boolean canRetypeOnly(MethodType newType, MethodType targetType) { + return canRetypeOnly(newType, targetType, false); + } + /** Can a retyping adapter (alone) convert the target to newType? + * It is allowed to widen subword types and void to int, to make bitwise + * conversions between float/int and double/long, and to perform unchecked + * reference conversions on return. This last feature requires that the + * caller be trusted, and perform explicit cast conversions on return values. + */ + static boolean canRawRetypeOnly(MethodType newType, MethodType targetType) { + return canRetypeOnly(newType, targetType, true); + } + static boolean canRetypeOnly(MethodType newType, MethodType targetType, boolean raw) { + if (!convOpSupported(OP_RETYPE_ONLY)) return false; + int diff = diffTypes(newType, targetType, raw); + // %%% This assert is too strong. Factor diff into VerifyType and reconcile. + assert((diff == 0) == VerifyType.isNullConversion(newType, targetType)); + return diff == 0; + } + + /** Factory method: Performs no conversions; simply retypes the adapter. + * Allows unchecked argument conversions pairwise, if they are safe. + * Returns null if not possible. + */ + public static MethodHandle makeRetypeOnly(Access token, + MethodType newType, MethodHandle target) { + return makeRetypeOnly(token, newType, target, false); + } + public static MethodHandle makeRawRetypeOnly(Access token, + MethodType newType, MethodHandle target) { + return makeRetypeOnly(token, newType, target, true); + } + static MethodHandle makeRetypeOnly(Access token, + MethodType newType, MethodHandle target, boolean raw) { + Access.check(token); + if (!canRetypeOnly(newType, target.type(), raw)) + return null; + // TO DO: clone the target guy, whatever he is, with new type. + return new AdapterMethodHandle(target, newType, makeConv(OP_RETYPE_ONLY)); + } + + /** Can a checkcast adapter validly convert the target to newType? + * The JVM supports all kind of reference casts, even silly ones. + */ + public static boolean canCheckCast(MethodType newType, MethodType targetType, + int arg, Class<?> castType) { + if (!convOpSupported(OP_CHECK_CAST)) return false; + Class<?> src = newType.parameterType(arg); + Class<?> dst = targetType.parameterType(arg); + if (!canCheckCast(src, castType) + || !VerifyType.isNullConversion(castType, dst)) + return false; + int diff = diffTypes(newType, targetType, false); + return (diff == arg+1); // arg is sole non-trivial diff + } + /** Can an primitive conversion adapter validly convert src to dst? */ + public static boolean canCheckCast(Class<?> src, Class<?> dst) { + return (!src.isPrimitive() && !dst.isPrimitive()); + } + + /** Factory method: Forces a cast at the given argument. + * The castType is the target of the cast, and can be any type + * with a null conversion to the corresponding target parameter. + * Return null if this cannot be done. + */ + public static MethodHandle makeCheckCast(Access token, + MethodType newType, MethodHandle target, + int arg, Class<?> castType) { + Access.check(token); + if (!canCheckCast(newType, target.type(), arg, castType)) + return null; + long conv = makeConv(OP_CHECK_CAST, arg, 0); + return new AdapterMethodHandle(target, newType, conv, castType); + } + + /** Can an primitive conversion adapter validly convert the target to newType? + * The JVM currently supports all conversions except those between + * floating and integral types. + */ + public static boolean canPrimCast(MethodType newType, MethodType targetType, + int arg, Class<?> convType) { + if (!convOpSupported(OP_PRIM_TO_PRIM)) return false; + Class<?> src = newType.parameterType(arg); + Class<?> dst = targetType.parameterType(arg); + if (!canPrimCast(src, convType) + || !VerifyType.isNullConversion(convType, dst)) + return false; + int diff = diffTypes(newType, targetType, false); + return (diff == arg+1); // arg is sole non-trivial diff + } + /** Can an primitive conversion adapter validly convert src to dst? */ + public static boolean canPrimCast(Class<?> src, Class<?> dst) { + if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) { + return false; + } else if (Wrapper.forPrimitiveType(dst).isFloating()) { + // both must be floating types + return Wrapper.forPrimitiveType(src).isFloating(); + } else { + // both are integral, and all combinations work fine + assert(Wrapper.forPrimitiveType(src).isIntegral() && + Wrapper.forPrimitiveType(dst).isIntegral()); + return true; + } + } + + /** Factory method: Truncate the given argument with zero or sign extension, + * and/or convert between single and doubleword versions of integer or float. + * The convType is the target of the conversion, and can be any type + * with a null conversion to the corresponding target parameter. + * Return null if this cannot be done. + */ + public static MethodHandle makePrimCast(Access token, + MethodType newType, MethodHandle target, + int arg, Class<?> convType) { + Access.check(token); + MethodType oldType = target.type(); + Class<?> src = newType.parameterType(arg); + Class<?> dst = oldType.parameterType(arg); + if (!canPrimCast(newType, oldType, arg, convType)) + return null; + long conv = makeConv(OP_PRIM_TO_PRIM, arg, basicType(src), basicType(convType)); + return new AdapterMethodHandle(target, newType, conv); + } + + /** Can an unboxing conversion validly convert src to dst? + * The JVM currently supports all kinds of casting and unboxing. + * The convType is the unboxed type; it can be either a primitive or wrapper. + */ + public static boolean canUnboxArgument(MethodType newType, MethodType targetType, + int arg, Class<?> convType) { + if (!convOpSupported(OP_REF_TO_PRIM)) return false; + Class<?> src = newType.parameterType(arg); + Class<?> dst = targetType.parameterType(arg); + Class<?> boxType = Wrapper.asWrapperType(convType); + convType = Wrapper.asPrimitiveType(convType); + if (!canCheckCast(src, boxType) + || boxType == convType + || !VerifyType.isNullConversion(convType, dst)) + return false; + int diff = diffTypes(newType, targetType, false); + return (diff == arg+1); // arg is sole non-trivial diff + } + /** Can an primitive unboxing adapter validly convert src to dst? */ + public static boolean canUnboxArgument(Class<?> src, Class<?> dst) { + return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive()); + } + + /** Factory method: Unbox the given argument. + * Return null if this cannot be done. + */ + public static MethodHandle makeUnboxArgument(Access token, + MethodType newType, MethodHandle target, + int arg, Class<?> convType) { + MethodType oldType = target.type(); + Class<?> src = newType.parameterType(arg); + Class<?> dst = oldType.parameterType(arg); + Class<?> boxType = Wrapper.asWrapperType(convType); + Class<?> primType = Wrapper.asPrimitiveType(convType); + if (!canUnboxArgument(newType, oldType, arg, convType)) + return null; + MethodType castDone = newType; + if (!VerifyType.isNullConversion(src, boxType)) + castDone = newType.changeParameterType(arg, boxType); + long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType)); + MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType); + if (castDone == newType) + return adapter; + return makeCheckCast(token, newType, adapter, arg, boxType); + } + + /** Can an primitive boxing adapter validly convert src to dst? */ + public static boolean canBoxArgument(Class<?> src, Class<?> dst) { + if (!convOpSupported(OP_PRIM_TO_REF)) return false; + throw new UnsupportedOperationException("NYI"); + } + + /** Factory method: Unbox the given argument. + * Return null if this cannot be done. + */ + public static MethodHandle makeBoxArgument(Access token, + MethodType newType, MethodHandle target, + int arg, Class<?> convType) { + // this is difficult to do in the JVM because it must GC + return null; + } + + // TO DO: makeSwapArguments, makeRotateArguments, makeDuplicateArguments + + /** Can an adapter simply drop arguments to convert the target to newType? */ + public static boolean canDropArguments(MethodType newType, MethodType targetType, + int dropArgPos, int dropArgCount) { + if (dropArgCount == 0) + return canRetypeOnly(newType, targetType); + if (!convOpSupported(OP_DROP_ARGS)) return false; + if (diffReturnTypes(newType, targetType, false) != 0) + return false; + int nptypes = newType.parameterCount(); + // parameter types must be the same up to the drop point + if (dropArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, dropArgPos, false) != 0) + return false; + int afterPos = dropArgPos + dropArgCount; + int afterCount = nptypes - afterPos; + if (dropArgPos < 0 || dropArgPos >= nptypes || + dropArgCount < 1 || afterPos > nptypes || + targetType.parameterCount() != nptypes - dropArgCount) + return false; + // parameter types after the drop point must also be the same + if (afterCount != 0 && diffParamTypes(newType, afterPos, targetType, dropArgPos, afterCount, false) != 0) + return false; + return true; + } + + /** Factory method: Drop selected arguments. + * Allow unchecked retyping of remaining arguments, pairwise. + * Return null if this is not possible. + */ + public static MethodHandle makeDropArguments(Access token, + MethodType newType, MethodHandle target, + int dropArgPos, int dropArgCount) { + Access.check(token); + if (dropArgCount == 0) + return makeRetypeOnly(IMPL_TOKEN, newType, target); + MethodType mt = target.type(); + int argCount = mt.parameterCount(); + if (!canDropArguments(newType, mt, dropArgPos, dropArgCount)) + return null; + int dropSlotCount, dropSlotPos; + if (dropArgCount >= argCount) { + assert(dropArgPos == argCount-1); + dropSlotPos = 0; + dropSlotCount = mt.parameterSlotCount(); + } else { + // arglist: [0: keep... | dpos: drop... | dpos+dcount: keep... ] + int lastDroppedArg = dropArgPos + dropArgCount - 1; + int lastKeptArg = dropArgPos - 1; // might be -1, which is OK + dropSlotPos = mt.parameterSlotDepth(1+lastDroppedArg); + int lastKeptSlot = mt.parameterSlotDepth(1+lastKeptArg); + dropSlotCount = lastKeptSlot - dropSlotPos; + assert(dropSlotCount >= dropArgCount); + } + long conv = makeConv(OP_DROP_ARGS, dropArgPos, +dropSlotCount); + return new AdapterMethodHandle(target, newType, dropSlotCount, conv); + } + + /** Can an adapter spread an argument to convert the target to newType? */ + public static boolean canSpreadArguments(MethodType newType, MethodType targetType, + Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) { + if (!convOpSupported(OP_SPREAD_ARGS)) return false; + if (diffReturnTypes(newType, targetType, false) != 0) + return false; + int nptypes = newType.parameterCount(); + // parameter types must be the same up to the spread point + if (spreadArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, spreadArgPos, false) != 0) + return false; + int afterPos = spreadArgPos + spreadArgCount; + int afterCount = nptypes - afterPos; + if (spreadArgPos < 0 || spreadArgPos >= nptypes || + spreadArgCount < 0 || + targetType.parameterCount() != nptypes - 1 + spreadArgCount) + return false; + // parameter types after the spread point must also be the same + if (afterCount != 0 && diffParamTypes(newType, spreadArgPos+1, targetType, afterPos, afterCount, false) != 0) + return false; + // match the array element type to the spread arg types + Class<?> rawSpreadArgType = newType.parameterType(spreadArgPos); + if (rawSpreadArgType != spreadArgType && !canCheckCast(rawSpreadArgType, spreadArgType)) + return false; + for (int i = 0; i < spreadArgCount; i++) { + Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i); + Class<?> dst = targetType.parameterType(spreadArgPos + i); + if (src == null || !VerifyType.isNullConversion(src, dst)) + return false; + } + return true; + } + + /** Factory method: Spread selected argument. */ + public static MethodHandle makeSpreadArguments(Access token, + MethodType newType, MethodHandle target, + Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) { + Access.check(token); + MethodType mt = target.type(); + int argCount = mt.parameterCount(); + if (!canSpreadArguments(newType, mt, spreadArgType, spreadArgPos, spreadArgCount)) + return null; + int spreadSlotCount, spreadSlotPos; + if (spreadArgCount >= argCount) { + assert(spreadArgPos == argCount-1); + spreadSlotPos = 0; + spreadSlotCount = mt.parameterSlotCount(); + } else { + // arglist: [0: keep... | dpos: spread... | dpos+dcount: keep... ] + int lastSpreadArg = spreadArgPos + spreadArgCount - 1; + int lastKeptArg = spreadArgPos - 1; // might be -1, which is OK + spreadSlotPos = mt.parameterSlotDepth(1+lastSpreadArg); + int lastKeptSlot = mt.parameterSlotDepth(1+lastKeptArg); + spreadSlotCount = lastKeptSlot - spreadSlotPos; + assert(spreadSlotCount >= spreadArgCount); + } + long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, spreadSlotCount); + return new AdapterMethodHandle(target, newType, conv, spreadArgType); + } + + // TO DO: makeCollectArguments, makeFlyby, makeRicochet +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/BoundMethodHandle.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,180 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import sun.dyn.util.VerifyType; +import sun.dyn.util.Wrapper; +import java.dyn.*; + +/** + * The flavor of method handle which emulates an invoke instruction + * on a predetermined argument. The JVM dispatches to the correct method + * when the handle is created, not when it is invoked. + * @author jrose + */ +public class BoundMethodHandle extends MethodHandle { + //MethodHandle vmtarget; // next BMH or final DMH or methodOop + private final Object argument; // argument to insert + private final int vmargslot; // position at which it is inserted + + // Constructors in this class *must* be package scoped or private. + + /** Bind a direct MH to its receiver (or first ref. argument). + * The JVM will pre-dispatch the MH if it is not already static. + */ + BoundMethodHandle(DirectMethodHandle mh, Object argument) { + super(Access.TOKEN, mh.type().dropParameterType(0)); + // check the type now, once for all: + this.argument = checkReferenceArgument(argument, mh, 0); + this.vmargslot = this.type().parameterSlotCount(); + if (MethodHandleNatives.JVM_SUPPORT) { + this.vmtarget = null; // maybe updated by JVM + MethodHandleNatives.init(this, mh, 0); + } else { + this.vmtarget = mh; + } + } + + private static final int REF_ARG = 0, PRIM_ARG = 1, SELF_ARG = 2; + + /** Insert an argument into an arbitrary method handle. + * If argnum is zero, inserts the first argument, etc. + * The argument type must be a reference. + */ + BoundMethodHandle(MethodHandle mh, Object argument, int argnum) { + this(mh, argument, argnum, mh.type().parameterType(argnum).isPrimitive() ? PRIM_ARG : REF_ARG); + } + + /** Insert an argument into an arbitrary method handle. + * If argnum is zero, inserts the first argument, etc. + */ + BoundMethodHandle(MethodHandle mh, Object argument, int argnum, int whichArg) { + super(Access.TOKEN, mh.type().dropParameterType(argnum)); + if (whichArg == PRIM_ARG) + this.argument = bindPrimitiveArgument(argument, mh, argnum); + else { + if (whichArg == SELF_ARG) argument = this; + this.argument = checkReferenceArgument(argument, mh, argnum); + } + this.vmargslot = this.type().parameterSlotDepth(argnum); + if (MethodHandleNatives.JVM_SUPPORT) { + this.vmtarget = null; // maybe updated by JVM + MethodHandleNatives.init(this, mh, argnum); + } else { + this.vmtarget = mh; + } + } + + /** For the AdapterMethodHandle subclass. + */ + BoundMethodHandle(MethodType type, Object argument, int vmargslot) { + super(Access.TOKEN, type); + this.argument = argument; + this.vmargslot = vmargslot; + assert(this.getClass() == AdapterMethodHandle.class); + } + + /** Initialize the current object as a method handle, binding it + * as the {@code argnum}th argument of the method handle {@code entryPoint}. + * The invocation type of the resulting method handle will be the + * same as {@code entryPoint}, except that the {@code argnum}th argument + * type will be dropped. + */ + public BoundMethodHandle(MethodHandle entryPoint, int argnum) { + this(entryPoint, null, argnum, SELF_ARG); + + // Note: If the conversion fails, perhaps because of a bad entryPoint, + // the MethodHandle.type field will not be filled in, and therefore + // no MH.invoke call will ever succeed. The caller may retain a pointer + // to the broken method handle, but no harm can be done with it. + } + + /** Initialize the current object as a method handle, binding it + * as the first argument of the method handle {@code entryPoint}. + * The invocation type of the resulting method handle will be the + * same as {@code entryPoint}, except that the first argument + * type will be dropped. + */ + public BoundMethodHandle(MethodHandle entryPoint) { + this(entryPoint, null, 0, SELF_ARG); + } + + /** Make sure the given {@code argument} can be used as {@code argnum}-th + * parameter of the given method handle {@code mh}, which must be a reference. + * <p> + * If this fails, throw a suitable {@code WrongMethodTypeException}, + * which will prevent the creation of an illegally typed bound + * method handle. + */ + final static Object checkReferenceArgument(Object argument, MethodHandle mh, int argnum) { + Class<?> ptype = mh.type().parameterType(argnum); + if (ptype.isPrimitive()) { + // fail + } else if (argument == null) { + return null; + } else if (VerifyType.isNullReferenceConversion(argument.getClass(), ptype)) { + return argument; + } + throw badBoundArgumentException(argument, mh, argnum); + } + + /** Make sure the given {@code argument} can be used as {@code argnum}-th + * parameter of the given method handle {@code mh}, which must be a primitive. + * <p> + * If this fails, throw a suitable {@code WrongMethodTypeException}, + * which will prevent the creation of an illegally typed bound + * method handle. + */ + final static Object bindPrimitiveArgument(Object argument, MethodHandle mh, int argnum) { + Class<?> ptype = mh.type().parameterType(argnum); + Wrapper wrap = Wrapper.forPrimitiveType(ptype); + Object zero = wrap.zero(); + if (zero == null) { + // fail + } else if (argument == null) { + if (ptype != int.class && wrap.isSubwordOrInt()) + return Integer.valueOf(0); + else + return zero; + } else if (VerifyType.isNullReferenceConversion(argument.getClass(), zero.getClass())) { + if (ptype != int.class && wrap.isSubwordOrInt()) + return Wrapper.INT.wrap(argument); + else + return argument; + } + throw badBoundArgumentException(argument, mh, argnum); + } + + final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) { + String atype = (argument == null) ? "null" : argument.getClass().toString(); + return new WrongMethodTypeException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type()); + } + + @Override + public String toString() { + return "Bound[" + super.toString() + "]"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/CallSiteImpl.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,70 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import java.dyn.*; + +/** + * The CallSite privately created by the JVM at every invokedynamic instruction. + * @author jrose + */ +class CallSiteImpl extends CallSite { + // Fields used only by the JVM. Do not use or change. + private Object vmmethod; + + // Values supplied by the JVM: + int callerMID, callerBCI; + + private CallSiteImpl(Class<?> caller, String name, MethodType type) { + super(caller, name, type); + } + + @Override + public void setTarget(MethodHandle mh) { + checkTarget(mh); + if (MethodHandleNatives.JVM_SUPPORT) + MethodHandleNatives.linkCallSite(this, (MethodHandle) mh); + else + super.setTarget(mh); + } + + private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE = + MethodHandleImpl.IMPL_LOOKUP.findStatic(CallSite.class, "privateInitializeCallSite", + MethodType.make(void.class, CallSite.class, int.class, int.class)); + + // this is the up-call from the JVM: + static CallSite makeSite(Class<?> caller, String name, MethodType type, + int callerMID, int callerBCI) { + MethodHandle bsm = Linkage.getBootstrapMethod(caller); + if (bsm == null) + throw new InvokeDynamicBootstrapError("class has no bootstrap method: "+caller); + CallSite site = bsm.<CallSite>invoke(caller, name, type); + if (site == null) + throw new InvokeDynamicBootstrapError("class bootstrap method failed to create a call site: "+caller); + PRIVATE_INITIALIZE_CALL_SITE.<void>invoke(site, callerMID, callerBCI); + return site; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/DirectMethodHandle.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,56 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import java.dyn.*; +import static sun.dyn.MethodHandleNatives.Constants.*; + +/** + * The flavor of method handle which emulates invokespecial or invokestatic. + * @author jrose + */ +class DirectMethodHandle extends MethodHandle { + //inherited oop vmtarget; // methodOop or virtual class/interface oop + private final int vmindex; // method index within class or interface + { vmindex = VM_INDEX_UNINITIALIZED; } // JVM may change this + + // Constructors in this class *must* be package scoped or private. + DirectMethodHandle(MethodType mtype, MemberName m, boolean doDispatch, Class<?> lookupClass) { + super(Access.TOKEN, mtype); + + assert(m.isMethod() || !doDispatch && m.isConstructor()); + if (!m.isResolved()) + throw new InternalError(); + + // Null check and replace privilege token (as passed to JVM) with null. + if (lookupClass.equals(Access.class)) lookupClass = null; + MethodHandleNatives.init(this, (Object) m, doDispatch, lookupClass); + } + + boolean isValid() { + return (vmindex != VM_INDEX_UNINITIALIZED); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/FilterGeneric.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,338 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Sf, tifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import java.dyn.JavaMethodHandle; +import java.dyn.MethodHandle; +import java.dyn.MethodType; +import java.dyn.NoAccessException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * "Flyby adapters" which apply arbitrary conversions to arguments + * on the way to a ultimate target. + * For simplicity, these are all generically typed. + * @author jrose + */ +class FilterGeneric { + // type for the outgoing call (will be generic) + private final MethodType targetType; + // position of (first) argument to participate in filtering + private final short argumentPosition; + // number of arguments to participate in filtering + private final short argumentCount; + // how the result interacts with the filtered arguments: Prepend, Append, Replace, Discard + private final char replaceMode; + // prototype adapter (clone and customize for each new target & conversion!) + private final Adapter adapter; + // entry point for adapter (Adapter mh, a...) => ... + private final MethodHandle entryPoint; + // more of them (loosely cached) + private FilterGeneric variations; + + /** Compute and cache information common to all unboxing adapters + * that can call out to targets of the erasure-family of the given erased type. + */ + // TO DO: Make this private. + FilterGeneric(MethodType targetType, short argumentPosition, short argumentCount, char replaceMode) { + if (argumentCount == 0) { + if (replaceMode == 'P' || replaceMode == 'A') replaceMode = 'R'; + if (replaceMode == 'I') argumentPosition = 0; + } + this.targetType = targetType; + this.argumentPosition = argumentPosition; + this.argumentCount = argumentCount; + this.replaceMode = replaceMode; + validate(targetType, argumentPosition, argumentCount, replaceMode); + Adapter ad = findAdapter(targetType, argumentPosition, argumentCount, replaceMode, filterType()); + if (ad == null) + ad = buildAdapterFromBytecodes(targetType, argumentPosition, argumentCount, replaceMode, filterType()); + this.adapter = ad; + this.entryPoint = ad.prototypeEntryPoint(); + } + + Adapter makeInstance(MethodHandle filter, MethodHandle target) { + return adapter.makeInstance(entryPoint, filter, target); + } + + /** Build an adapter of the given generic type, which invokes typedTarget + * on the incoming arguments, after unboxing as necessary. + * The return value is boxed if necessary. + * @param genericType the required type of the result + * @param typedTarget the target + * @return an adapter method handle + */ + public static MethodHandle make(MethodHandle target, int pos, MethodHandle filter) { + return FilterGeneric.of(target.type(), (short)pos, (short)1, 'R').makeInstance(filter, target); + } + + /** Return the adapter information for this type's erasure. */ + static FilterGeneric of(MethodType type, short ap, short ac, char mode) { + if (type.generic() != type) + throw new IllegalArgumentException("must be generic: "+type); + validate(type, ap, ac, mode); + MethodTypeImpl form = MethodTypeImpl.of(type); + FilterGeneric filterGen = form.filterGeneric; + if (filterGen == null) + form.filterGeneric = filterGen = new FilterGeneric(type, (short)0, (short)1, 'R'); + return find(filterGen, ap, ac, mode); + } + + static FilterGeneric find(FilterGeneric gen, short ap, short ac, char mode) { + for (;;) { + if (gen.argumentPosition == ap && + gen.argumentCount == ac && + gen.replaceMode == mode) { + return gen; + } + FilterGeneric gen2 = gen.variations; + if (gen2 == null) break; + gen = gen2; + } + FilterGeneric gen2 = new FilterGeneric(gen.targetType, ap, ac, mode); + gen.variations = gen2; // OK if this smashes another cached chain + return gen2; + } + + private static void validate(MethodType type, short ap, short ac, char mode) { + int endpos = ap + ac; + switch (mode) { + case 'P': case 'A': case 'R': case 'D': + if (ap >= 0 && ac >= 0 && + endpos >= 0 && endpos <= type.parameterCount()) + return; + default: + throw new InternalError("configuration "+patternName(ap, ac, mode)); + } + } + + public String toString() { + return "FilterGeneric/"+patternName()+targetType; + } + + String patternName() { + return patternName(argumentPosition, argumentCount, replaceMode); + } + + static String patternName(short ap, short ac, char mode) { + return ""+mode+ap+(ac>1?"_"+ac:""); + } + + Class<?> filterType() { + return Object.class; // result of filter operation; an uninteresting type + } + + static MethodType targetType(MethodType entryType, short ap, short ac, char mode, + Class<?> arg) { + MethodType type = entryType; + int pos = ap; + switch (mode) { + case 'A': + pos += ac; + case 'P': + type = type.insertParameterType(pos, arg); + break; + case 'I': + for (int i = 1; i < ac; i++) + type = type.dropParameterType(pos); + assert(type.parameterType(pos) == arg); + break; + case 'D': + break; + } + return type; + } + + static MethodType entryType(MethodType targetType, short ap, short ac, char mode, + Class<?> arg) { + MethodType type = targetType; + int pos = ap; + switch (mode) { + case 'A': + pos += ac; + case 'P': + type = type.dropParameterType(pos); + break; + case 'I': + for (int i = 1; i < ac; i++) + type = type.insertParameterType(pos, arg); + assert(type.parameterType(pos) == arg); + break; + case 'D': + break; + } + return type; + } + + /* Create an adapter that handles spreading calls for the given type. */ + static Adapter findAdapter(MethodType targetType, short ap, short ac, char mode, Class<?> arg) { + MethodType entryType = entryType(targetType, ap, ac, mode, arg); + int argc = targetType.parameterCount(); + String pname = patternName(ap, ac, mode); + String cname0 = "F"+argc; + String cname1 = "F"+argc+mode; + String cname2 = "F"+argc+pname; + String[] cnames = { cname0, cname1, cname1+"X", cname2 }; + String iname = "invoke_"+pname; + // e.g., F5R; invoke_R3 + for (String cname : cnames) { + Class<? extends Adapter> acls = Adapter.findSubClass(cname); + if (acls == null) continue; + // see if it has the required invoke method + MethodHandle entryPoint = null; + try { + entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls); + } catch (NoAccessException ex) { + } + if (entryPoint == null) continue; + Constructor<? extends Adapter> ctor = null; + try { + ctor = acls.getDeclaredConstructor(MethodHandle.class); + } catch (NoSuchMethodException ex) { + } catch (SecurityException ex) { + } + if (ctor == null) continue; + try { + // Produce an instance configured as a prototype. + return ctor.newInstance(entryPoint); + } catch (IllegalArgumentException ex) { + } catch (InvocationTargetException ex) { + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + } + return null; + } + + static Adapter buildAdapterFromBytecodes(MethodType targetType, short ap, short ac, char mode, Class<?> arg) { + throw new UnsupportedOperationException("NYI"); + } + + /** + * This adapter takes some untyped arguments, and returns an untyped result. + * Internally, it applies the invoker to the target, which causes the + * objects to be unboxed; the result is a raw type in L/I/J/F/D. + * This result is passed to convert, which is responsible for + * converting the raw result into a boxed object. + * The invoker is kept separate from the target because it can be + * generated once per type erasure family, and reused across adapters. + */ + static abstract class Adapter extends JavaMethodHandle { + protected final MethodHandle filter; + protected final MethodHandle target; + + protected boolean isPrototype() { return target == null; } + protected Adapter(MethodHandle entryPoint) { + this(entryPoint, entryPoint, null); + assert(isPrototype()); + } + protected MethodHandle prototypeEntryPoint() { + if (!isPrototype()) throw new InternalError(); + return filter; + } + + protected Adapter(MethodHandle entryPoint, + MethodHandle filter, MethodHandle target) { + super(entryPoint); + this.filter = filter; + this.target = target; + } + + /** Make a copy of self, with new fields. */ + protected abstract Adapter makeInstance(MethodHandle entryPoint, + MethodHandle filter, MethodHandle target); + // { return new ThisType(entryPoint, filter, target); } + + static private final String CLASS_PREFIX; // "sun.dyn.FilterGeneric$" + static { + String aname = Adapter.class.getName(); + String sname = Adapter.class.getSimpleName(); + if (!aname.endsWith(sname)) throw new InternalError(); + CLASS_PREFIX = aname.substring(0, aname.length() - sname.length()); + } + /** Find a sibing class of Adapter. */ + static Class<? extends Adapter> findSubClass(String name) { + String cname = Adapter.CLASS_PREFIX + name; + try { + return Class.forName(cname).asSubclass(Adapter.class); + } catch (ClassNotFoundException ex) { + return null; + } catch (ClassCastException ex) { + return null; + } + } + } + + //* generated classes follow this pattern: + static class F1RX extends Adapter { + protected F1RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected F1RX(MethodHandle e, MethodHandle f, MethodHandle t) + { super(e, f, t); } + protected F1RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t) + { return new F1RX(e, f, t); } + protected Object filter(Object a0) { return filter.<Object>invoke(a0); } + protected Object target(Object a0) { return target.<Object>invoke(a0); } + protected Object invoke_R0(Object a0) { return target(filter(a0)); } + } + static class F2RX extends Adapter { + protected F2RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected F2RX(MethodHandle e, MethodHandle f, MethodHandle t) + { super(e, f, t); } + protected F2RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t) + { return new F2RX(e, f, t); } + protected Object filter(Object a0) { return filter.<Object>invoke(a0); } + protected Object target(Object a0, Object a1) { return target.<Object>invoke(a0, a1); } + protected Object invoke_R0(Object a0, Object a1) { return target(filter(a0), a1); } + protected Object invoke_R1(Object a0, Object a1) { return target(a0, filter(a1)); } + } + static class F3RX extends Adapter { + protected F3RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected F3RX(MethodHandle e, MethodHandle f, MethodHandle t) + { super(e, f, t); } + protected F3RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t) + { return new F3RX(e, f, t); } + protected Object filter(Object a0) { return filter.<Object>invoke(a0); } + protected Object target(Object a0, Object a1, Object a2) { return target.<Object>invoke(a0, a1, a2); } + protected Object invoke_R0(Object a0, Object a1, Object a2) { return target(filter(a0), a1, a2); } + protected Object invoke_R1(Object a0, Object a1, Object a2) { return target(a0, filter(a1), a2); } + protected Object invoke_R2(Object a0, Object a1, Object a2) { return target(a0, a1, filter(a2)); } + } + static class F4RX extends Adapter { + protected F4RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected F4RX(MethodHandle e, MethodHandle f, MethodHandle t) + { super(e, f, t); } + protected F4RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t) + { return new F4RX(e, f, t); } + protected Object filter(Object a0) { return filter.<Object>invoke(a0); } + protected Object target(Object a0, Object a1, Object a2, Object a3) { return target.<Object>invoke(a0, a1, a2, a3); } + protected Object invoke_R0(Object a0, Object a1, Object a2, Object a3) { return target(filter(a0), a1, a2, a3); } + protected Object invoke_R1(Object a0, Object a1, Object a2, Object a3) { return target(a0, filter(a1), a2, a3); } + protected Object invoke_R2(Object a0, Object a1, Object a2, Object a3) { return target(a0, a1, filter(a2), a3); } + protected Object invoke_R3(Object a0, Object a1, Object a2, Object a3) { return target(a0, a1, a2, filter(a3)); } + } + // */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/FilterOneArgument.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,73 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import java.dyn.JavaMethodHandle; +import java.dyn.MethodHandle; +import java.dyn.MethodHandles; +import java.dyn.MethodType; + +/** + * Unary function composition, useful for many small plumbing jobs. + * The invoke method takes a single reference argument, and returns a reference + * Internally, it first calls the {@code filter} method on the argument, + * Making up the difference between the raw method type and the + * final method type is the responsibility of a JVM-level adapter. + * @author jrose + */ +public class FilterOneArgument extends JavaMethodHandle { + protected final MethodHandle filter; // Object -> Object + protected final MethodHandle target; // Object -> Object + + protected Object entryPoint(Object argument) { + Object filteredArgument = filter.<Object>invoke(argument); + return target.<Object>invoke(filteredArgument); + } + + private static final MethodHandle entryPoint = + MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "entryPoint", MethodType.makeGeneric(1)); + + protected FilterOneArgument(MethodHandle filter, MethodHandle target) { + super(entryPoint); + this.filter = filter; + this.target = target; + } + + public static MethodHandle make(MethodHandle filter, MethodHandle target) { + if (filter == null) return target; + if (target == null) return filter; + return new FilterOneArgument(filter, target); + } + + public String toString() { + return filter + "|>" + target; + } + +// MethodHandle make(MethodHandle filter1, MethodHandle filter2, MethodHandle target) { +// MethodHandle filter = make(filter1, filter2); +// return make(filter, target); +// } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/FromGeneric.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,627 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import java.dyn.JavaMethodHandle; +import java.dyn.MethodHandle; +import java.dyn.MethodHandles; +import java.dyn.MethodType; +import java.dyn.NoAccessException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import sun.dyn.util.ValueConversions; +import sun.dyn.util.Wrapper; + +/** + * Adapters which mediate between incoming calls which are not generic + * and outgoing calls which are. Any call can be represented generically + * boxing up its arguments, and (on return) unboxing the return value. + * <p> + * A call is "generic" (in MethodHandle terms) if its MethodType features + * only Object arguments. A non-generic call therefore features + * primitives and/or reference types other than Object. + * An adapter has types for its incoming and outgoing calls. + * The incoming call type is simply determined by the adapter's type + * (the MethodType it presents to callers). The outgoing call type + * is determined by the adapter's target (a MethodHandle that the adapter + * either binds internally or else takes as a leading argument). + * (To stretch the term, adapter-like method handles may have multiple + * targets or be polymorphic across multiple call types.) + * <p> + * This adapter can sometimes be more directly implemented + * by the JVM's built-in OP_SPREAD_ARGS adapter. + * @author jrose + */ +class FromGeneric { + // type for the outgoing call (may have primitives, etc.) + private final MethodType targetType; + // type of the outgoing call internal to the adapter + private final MethodType internalType; + // prototype adapter (clone and customize for each new target!) + private final Adapter adapter; + // entry point for adapter (Adapter mh, a...) => ... + private final MethodHandle entryPoint; + // unboxing invoker of type (MH, Object**N) => raw return value + // it makes up the difference of internalType => targetType + private final MethodHandle unboxingInvoker; + // conversion which boxes a the target's raw return value + private final MethodHandle returnConversion; + + /** Compute and cache information common to all unboxing adapters + * that can call out to targets of the erasure-family of the given erased type. + */ + private FromGeneric(MethodType targetType) { + this.targetType = targetType; + MethodType internalType0; + // the target invoker will generally need casts on reference arguments + Adapter ad = findAdapter(internalType0 = targetType.erase()); + if (ad != null) { + // Immediate hit to exactly the adapter we want, + // with no monkeying around with primitive types. + this.internalType = internalType0; + this.adapter = ad; + this.entryPoint = ad.prototypeEntryPoint(); + this.returnConversion = computeReturnConversion(targetType, internalType0); + this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0); + return; + } + + // outgoing primitive arguments will be wrapped; unwrap them + MethodType primsAsObj = MethodTypeImpl.of(targetType).primArgsAsBoxes(); + MethodType objArgsRawRet = MethodTypeImpl.of(primsAsObj).primsAsInts(); + if (objArgsRawRet != targetType) + ad = findAdapter(internalType0 = objArgsRawRet); + if (ad == null) { + ad = buildAdapterFromBytecodes(internalType0 = targetType); + } + this.internalType = internalType0; + this.adapter = ad; + MethodType tepType = targetType.insertParameterType(0, adapter.getClass()); + this.entryPoint = ad.prototypeEntryPoint(); + this.returnConversion = computeReturnConversion(targetType, internalType0); + this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0); + } + + /** + * The typed target will be called according to targetType. + * The adapter code will in fact see the raw result from internalType, + * and must box it into an object. Produce a converter for this. + */ + private static MethodHandle computeReturnConversion( + MethodType targetType, MethodType internalType) { + Class<?> tret = targetType.returnType(); + Class<?> iret = internalType.returnType(); + Wrapper wrap = Wrapper.forBasicType(tret); + if (!iret.isPrimitive()) { + assert(iret == Object.class); + return ValueConversions.identity(); + } else if (wrap.primitiveType() == iret) { + return ValueConversions.box(wrap, false); + } else { + assert(tret == double.class ? iret == long.class : iret == int.class); + return ValueConversions.boxRaw(wrap, false); + } + } + + /** + * The typed target will need an exact invocation point; provide it here. + * The adapter will possibly need to make a slightly different call, + * so adapt the invoker. This way, the logic for making up the + * difference between what the adapter can call and what the target + * needs can be cached once per type. + */ + private static MethodHandle computeUnboxingInvoker( + MethodType targetType, MethodType internalType) { + // All the adapters we have here have reference-untyped internal calls. + assert(internalType == internalType.erase()); + MethodHandle invoker = MethodHandles.exactInvoker(targetType); + // cast all narrow reference types, unbox all primitive arguments: + MethodType fixArgsType = internalType.changeReturnType(targetType.returnType()); + MethodHandle fixArgs = AdapterMethodHandle.convertArguments(Access.TOKEN, + invoker, Invokers.invokerType(fixArgsType), + invoker.type(), null); + if (fixArgs == null) + throw new InternalError("bad fixArgs"); + // reinterpret the calling sequence as raw: + MethodHandle retyper = AdapterMethodHandle.makeRawRetypeOnly(Access.TOKEN, + Invokers.invokerType(internalType), fixArgs); + if (retyper == null) + throw new InternalError("bad retyper"); + return retyper; + } + + Adapter makeInstance(MethodHandle typedTarget) { + MethodType type = typedTarget.type(); + if (type == targetType) { + return adapter.makeInstance(entryPoint, unboxingInvoker, returnConversion, typedTarget); + } + // my erased-type is not exactly the same as the desired type + assert(type.erase() == targetType); // else we are busted + MethodHandle invoker = computeUnboxingInvoker(type, internalType); + return adapter.makeInstance(entryPoint, invoker, returnConversion, typedTarget); + } + + /** Build an adapter of the given generic type, which invokes typedTarget + * on the incoming arguments, after unboxing as necessary. + * The return value is boxed if necessary. + * @param genericType the required type of the result + * @param typedTarget the target + * @return an adapter method handle + */ + public static MethodHandle make(MethodHandle typedTarget) { + MethodType type = typedTarget.type(); + if (type == type.generic()) return typedTarget; + return FromGeneric.of(type).makeInstance(typedTarget); + } + + /** Return the adapter information for this type's erasure. */ + static FromGeneric of(MethodType type) { + MethodTypeImpl form = MethodTypeImpl.of(type); + FromGeneric fromGen = form.fromGeneric; + if (fromGen == null) + form.fromGeneric = fromGen = new FromGeneric(form.erasedType()); + return fromGen; + } + + public String toString() { + return "FromGeneric"+targetType; + } + + /* Create an adapter that handles spreading calls for the given type. */ + static Adapter findAdapter(MethodType internalType) { + MethodType entryType = internalType.generic(); + MethodTypeImpl form = MethodTypeImpl.of(internalType); + Class<?> rtype = internalType.returnType(); + int argc = form.parameterCount(); + int lac = form.longPrimitiveParameterCount(); + int iac = form.primitiveParameterCount() - lac; + String intsAndLongs = (iac > 0 ? "I"+iac : "")+(lac > 0 ? "J"+lac : ""); + String rawReturn = String.valueOf(Wrapper.forPrimitiveType(rtype).basicTypeChar()); + String cname0 = rawReturn + argc; + String cname1 = "A" + argc; + String[] cnames = { cname0+intsAndLongs, cname0, cname1+intsAndLongs, cname1 }; + String iname = "invoke_"+cname0+intsAndLongs; + // e.g., D5I2, D5, L5I2, L5; invoke_D5 + for (String cname : cnames) { + Class<? extends Adapter> acls = Adapter.findSubClass(cname); + if (acls == null) continue; + // see if it has the required invoke method + MethodHandle entryPoint = null; + try { + entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls); + } catch (NoAccessException ex) { + } + if (entryPoint == null) continue; + Constructor<? extends Adapter> ctor = null; + try { + ctor = acls.getDeclaredConstructor(MethodHandle.class); + } catch (NoSuchMethodException ex) { + } catch (SecurityException ex) { + } + if (ctor == null) continue; + try { + // Produce an instance configured as a prototype. + return ctor.newInstance(entryPoint); + } catch (IllegalArgumentException ex) { + } catch (InvocationTargetException ex) { + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + } + return null; + } + + static Adapter buildAdapterFromBytecodes(MethodType internalType) { + throw new UnsupportedOperationException("NYI"); + } + + /** + * This adapter takes some untyped arguments, and returns an untyped result. + * Internally, it applies the invoker to the target, which causes the + * objects to be unboxed; the result is a raw type in L/I/J/F/D. + * This result is passed to convert, which is responsible for + * converting the raw result into a boxed object. + * The invoker is kept separate from the target because it can be + * generated once per type erasure family, and reused across adapters. + */ + static abstract class Adapter extends JavaMethodHandle { + /* + * class X<<R,int N>> extends Adapter { + * (MH, Object**N)=>raw(R) invoker; + * (any**N)=>R target; + * raw(R)=>Object convert; + * Object invoke(Object**N a) = convert(invoker(target, a...)) + * } + */ + protected final MethodHandle invoker; // (MH, Object**N) => raw(R) + protected final MethodHandle convert; // raw(R) => Object + protected final MethodHandle target; // (any**N) => R + + protected boolean isPrototype() { return target == null; } + protected Adapter(MethodHandle entryPoint) { + this(entryPoint, null, entryPoint, null); + assert(isPrototype()); + } + protected MethodHandle prototypeEntryPoint() { + if (!isPrototype()) throw new InternalError(); + return convert; + } + + protected Adapter(MethodHandle entryPoint, + MethodHandle invoker, MethodHandle convert, MethodHandle target) { + super(entryPoint); + this.invoker = invoker; + this.convert = convert; + this.target = target; + } + + /** Make a copy of self, with new fields. */ + protected abstract Adapter makeInstance(MethodHandle entryPoint, + MethodHandle invoker, MethodHandle convert, MethodHandle target); + // { return new ThisType(entryPoint, convert, target); } + + /// Conversions on the value returned from the target. + protected Object convert_L(Object result) { return convert.<Object>invoke(result); } + protected Object convert_I(int result) { return convert.<Object>invoke(result); } + protected Object convert_J(long result) { return convert.<Object>invoke(result); } + protected Object convert_F(float result) { return convert.<Object>invoke(result); } + protected Object convert_D(double result) { return convert.<Object>invoke(result); } + + static private final String CLASS_PREFIX; // "sun.dyn.FromGeneric$" + static { + String aname = Adapter.class.getName(); + String sname = Adapter.class.getSimpleName(); + if (!aname.endsWith(sname)) throw new InternalError(); + CLASS_PREFIX = aname.substring(0, aname.length() - sname.length()); + } + /** Find a sibing class of Adapter. */ + static Class<? extends Adapter> findSubClass(String name) { + String cname = Adapter.CLASS_PREFIX + name; + try { + return Class.forName(cname).asSubclass(Adapter.class); + } catch (ClassNotFoundException ex) { + return null; + } catch (ClassCastException ex) { + return null; + } + } + } + + /* generated classes follow this pattern: + static class xA2 extends Adapter { + protected xA2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected xA2(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected xA2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new xA2(e, i, c, t); } + protected Object invoke_L2(Object a0, Object a1) { return convert_L(invoker.<Object>invoke(target, a0, a1)); } + protected Object invoke_I2(Object a0, Object a1) { return convert_I(invoker.<int >invoke(target, a0, a1)); } + protected Object invoke_J2(Object a0, Object a1) { return convert_J(invoker.<long >invoke(target, a0, a1)); } + protected Object invoke_F2(Object a0, Object a1) { return convert_F(invoker.<float >invoke(target, a0, a1)); } + protected Object invoke_D2(Object a0, Object a1) { return convert_D(invoker.<double>invoke(target, a0, a1)); } + } + // */ + +/* +: SHELL; n=FromGeneric; cp -p $n.java $n.java-; sed < $n.java- > $n.java+ -e '/{{*{{/,/}}*}}/w /tmp/genclasses.java' -e '/}}*}}/q'; (cd /tmp; javac -d . genclasses.java; java -cp . genclasses) >> $n.java+; echo '}' >> $n.java+; mv $n.java+ $n.java; mv $n.java- $n.java~ +//{{{ +import java.util.*; +class genclasses { + static String[] TYPES = { "Object", "int ", "long ", "float ", "double" }; + static String[] TCHARS = { "L", "I", "J", "F", "D", "A" }; + static String[][] TEMPLATES = { { + "@for@ arity=0..10 rcat<=4 nrefs<=99 nints=0 nlongs=0", + " //@each-cat@", + " static class @cat@ extends Adapter {", + " protected @cat@(MethodHandle entryPoint) { super(entryPoint); } // to build prototype", + " protected @cat@(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)", + " { super(e, i, c, t); }", + " protected @cat@ makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)", + " { return new @cat@(e, i, c, t); }", + " //@each-R@", + " protected Object invoke_@catN@(@Tvav@) { return convert_@Rc@(invoker.<@R@>invoke(target@av@)); }", + " //@end-R@", + " }", + } }; + static final String NEWLINE_INDENT = "\n "; + enum VAR { + cat, catN, R, Rc, av, Tvav, Ovav; + public final String pattern = "@"+toString().replace('_','.')+"@"; + public String binding; + static void makeBindings(boolean topLevel, int rcat, int nrefs, int nints, int nlongs) { + int nargs = nrefs + nints + nlongs; + if (topLevel) + VAR.cat.binding = catstr(ALL_RETURN_TYPES ? TYPES.length : rcat, nrefs, nints, nlongs); + VAR.catN.binding = catstr(rcat, nrefs, nints, nlongs); + VAR.R.binding = TYPES[rcat]; + VAR.Rc.binding = TCHARS[rcat]; + String[] Tv = new String[nargs]; + String[] av = new String[nargs]; + String[] Tvav = new String[nargs]; + String[] Ovav = new String[nargs]; + for (int i = 0; i < nargs; i++) { + int tcat = (i < nrefs) ? 0 : (i < nrefs + nints) ? 1 : 2; + Tv[i] = TYPES[tcat]; + av[i] = arg(i); + Tvav[i] = param(Tv[i], av[i]); + Ovav[i] = param("Object", av[i]); + } + VAR.av.binding = comma(", ", av); + VAR.Tvav.binding = comma(Tvav); + VAR.Ovav.binding = comma(Ovav); + } + static String arg(int i) { return "a"+i; } + static String param(String t, String a) { return t+" "+a; } + static String comma(String[] v) { return comma("", v); } + static String comma(String sep, String[] v) { + if (v.length == 0) return ""; + String res = sep+v[0]; + for (int i = 1; i < v.length; i++) res += ", "+v[i]; + return res; + } + static String transform(String string) { + for (VAR var : values()) + string = string.replaceAll(var.pattern, var.binding); + return string; + } + } + static String[] stringsIn(String[] strings, int beg, int end) { + return Arrays.copyOfRange(strings, beg, Math.min(end, strings.length)); + } + static String[] stringsBefore(String[] strings, int pos) { + return stringsIn(strings, 0, pos); + } + static String[] stringsAfter(String[] strings, int pos) { + return stringsIn(strings, pos, strings.length); + } + static int indexAfter(String[] strings, int pos, String tag) { + return Math.min(indexBefore(strings, pos, tag) + 1, strings.length); + } + static int indexBefore(String[] strings, int pos, String tag) { + for (int i = pos, end = strings.length; ; i++) { + if (i == end || strings[i].endsWith(tag)) return i; + } + } + static int MIN_ARITY, MAX_ARITY, MAX_RCAT, MAX_REFS, MAX_INTS, MAX_LONGS; + static boolean ALL_ARG_TYPES, ALL_RETURN_TYPES; + static HashSet<String> done = new HashSet<String>(); + public static void main(String... av) { + for (String[] template : TEMPLATES) { + int forLinesLimit = indexBefore(template, 0, "@each-cat@"); + String[] forLines = stringsBefore(template, forLinesLimit); + template = stringsAfter(template, forLinesLimit); + for (String forLine : forLines) + expandTemplate(forLine, template); + } + } + static void expandTemplate(String forLine, String[] template) { + String[] params = forLine.split("[^0-9]+"); + if (params[0].length() == 0) params = stringsAfter(params, 1); + System.out.println("//params="+Arrays.asList(params)); + int pcur = 0; + MIN_ARITY = Integer.valueOf(params[pcur++]); + MAX_ARITY = Integer.valueOf(params[pcur++]); + MAX_RCAT = Integer.valueOf(params[pcur++]); + MAX_REFS = Integer.valueOf(params[pcur++]); + MAX_INTS = Integer.valueOf(params[pcur++]); + MAX_LONGS = Integer.valueOf(params[pcur++]); + if (pcur != params.length) throw new RuntimeException("bad extra param: "+forLine); + if (MAX_RCAT >= TYPES.length) MAX_RCAT = TYPES.length - 1; + ALL_ARG_TYPES = (indexBefore(template, 0, "@each-Tv@") < template.length); + ALL_RETURN_TYPES = (indexBefore(template, 0, "@each-R@") < template.length); + for (int nargs = MIN_ARITY; nargs <= MAX_ARITY; nargs++) { + for (int rcat = 0; rcat <= MAX_RCAT; rcat++) { + expandTemplate(template, true, rcat, nargs, 0, 0); + if (ALL_ARG_TYPES) break; + expandTemplateForPrims(template, true, rcat, nargs, 1, 1); + if (ALL_RETURN_TYPES) break; + } + } + } + static String catstr(int rcat, int nrefs, int nints, int nlongs) { + int nargs = nrefs + nints + nlongs; + String cat = TCHARS[rcat] + nargs; + if (!ALL_ARG_TYPES) cat += (nints==0?"":"I"+nints)+(nlongs==0?"":"J"+nlongs); + return cat; + } + static void expandTemplateForPrims(String[] template, boolean topLevel, int rcat, int nargs, int minints, int minlongs) { + for (int isLong = 0; isLong <= 1; isLong++) { + for (int nprims = 1; nprims <= nargs; nprims++) { + int nrefs = nargs - nprims; + int nints = ((1-isLong) * nprims); + int nlongs = (isLong * nprims); + expandTemplate(template, topLevel, rcat, nrefs, nints, nlongs); + } + } + } + static void expandTemplate(String[] template, boolean topLevel, + int rcat, int nrefs, int nints, int nlongs) { + int nargs = nrefs + nints + nlongs; + if (nrefs > MAX_REFS || nints > MAX_INTS || nlongs > MAX_LONGS) return; + VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs); + if (topLevel && !done.add(VAR.cat.binding)) { + System.out.println(" //repeat "+VAR.cat.binding); + return; + } + for (int i = 0; i < template.length; i++) { + String line = template[i]; + if (line.endsWith("@each-cat@")) { + // ignore + } else if (line.endsWith("@each-R@")) { + int blockEnd = indexAfter(template, i, "@end-R@"); + String[] block = stringsIn(template, i+1, blockEnd-1); + for (int rcat1 = rcat; rcat1 <= MAX_RCAT; rcat1++) + expandTemplate(block, false, rcat1, nrefs, nints, nlongs); + VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs); + i = blockEnd-1; continue; + } else if (line.endsWith("@each-Tv@")) { + int blockEnd = indexAfter(template, i, "@end-Tv@"); + String[] block = stringsIn(template, i+1, blockEnd-1); + expandTemplate(block, false, rcat, nrefs, nints, nlongs); + expandTemplateForPrims(block, false, rcat, nargs, nints+1, nlongs+1); + VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs); + i = blockEnd-1; continue; + } else { + System.out.println(VAR.transform(line)); + } + } + } +} +//}}} */ +//params=[0, 10, 4, 99, 0, 0] + static class A0 extends Adapter { + protected A0(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A0(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A0 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A0(e, i, c, t); } + protected Object invoke_L0() { return convert_L(invoker.<Object>invoke(target)); } + protected Object invoke_I0() { return convert_I(invoker.<int >invoke(target)); } + protected Object invoke_J0() { return convert_J(invoker.<long >invoke(target)); } + protected Object invoke_F0() { return convert_F(invoker.<float >invoke(target)); } + protected Object invoke_D0() { return convert_D(invoker.<double>invoke(target)); } + } + static class A1 extends Adapter { + protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A1(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A1(e, i, c, t); } + protected Object invoke_L1(Object a0) { return convert_L(invoker.<Object>invoke(target, a0)); } + protected Object invoke_I1(Object a0) { return convert_I(invoker.<int >invoke(target, a0)); } + protected Object invoke_J1(Object a0) { return convert_J(invoker.<long >invoke(target, a0)); } + protected Object invoke_F1(Object a0) { return convert_F(invoker.<float >invoke(target, a0)); } + protected Object invoke_D1(Object a0) { return convert_D(invoker.<double>invoke(target, a0)); } + } + static class A2 extends Adapter { + protected A2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A2(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A2(e, i, c, t); } + protected Object invoke_L2(Object a0, Object a1) { return convert_L(invoker.<Object>invoke(target, a0, a1)); } + protected Object invoke_I2(Object a0, Object a1) { return convert_I(invoker.<int >invoke(target, a0, a1)); } + protected Object invoke_J2(Object a0, Object a1) { return convert_J(invoker.<long >invoke(target, a0, a1)); } + protected Object invoke_F2(Object a0, Object a1) { return convert_F(invoker.<float >invoke(target, a0, a1)); } + protected Object invoke_D2(Object a0, Object a1) { return convert_D(invoker.<double>invoke(target, a0, a1)); } + } + static class A3 extends Adapter { + protected A3(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A3(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A3 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A3(e, i, c, t); } + protected Object invoke_L3(Object a0, Object a1, Object a2) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2)); } + protected Object invoke_I3(Object a0, Object a1, Object a2) { return convert_I(invoker.<int >invoke(target, a0, a1, a2)); } + protected Object invoke_J3(Object a0, Object a1, Object a2) { return convert_J(invoker.<long >invoke(target, a0, a1, a2)); } + protected Object invoke_F3(Object a0, Object a1, Object a2) { return convert_F(invoker.<float >invoke(target, a0, a1, a2)); } + protected Object invoke_D3(Object a0, Object a1, Object a2) { return convert_D(invoker.<double>invoke(target, a0, a1, a2)); } + } + static class A4 extends Adapter { + protected A4(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A4(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A4 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A4(e, i, c, t); } + protected Object invoke_L4(Object a0, Object a1, Object a2, Object a3) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3)); } + protected Object invoke_I4(Object a0, Object a1, Object a2, Object a3) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3)); } + protected Object invoke_J4(Object a0, Object a1, Object a2, Object a3) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3)); } + protected Object invoke_F4(Object a0, Object a1, Object a2, Object a3) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3)); } + protected Object invoke_D4(Object a0, Object a1, Object a2, Object a3) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3)); } + } + static class A5 extends Adapter { + protected A5(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A5(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A5 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A5(e, i, c, t); } + protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4)); } + protected Object invoke_I5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4)); } + protected Object invoke_J5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4)); } + protected Object invoke_F5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4)); } + protected Object invoke_D5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4)); } + } + static class A6 extends Adapter { + protected A6(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A6(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A6 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A6(e, i, c, t); } + protected Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5)); } + protected Object invoke_I6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5)); } + protected Object invoke_J6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5)); } + protected Object invoke_F6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5)); } + protected Object invoke_D6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5)); } + } + static class A7 extends Adapter { + protected A7(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A7(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A7 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A7(e, i, c, t); } + protected Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_I7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_J7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_F7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_D7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5, a6)); } + } + static class A8 extends Adapter { + protected A8(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A8(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A8 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A8(e, i, c, t); } + protected Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_I8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_J8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_F8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_D8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + } + static class A9 extends Adapter { + protected A9(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A9(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A9 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A9(e, i, c, t); } + protected Object invoke_L9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_I9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_J9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_F9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_D9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + } + static class A10 extends Adapter { + protected A10(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A10(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { super(e, i, c, t); } + protected A10 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) + { return new A10(e, i, c, t); } + protected Object invoke_L10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_I10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_J10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_F10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_D10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/Invokers.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,86 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import java.dyn.MethodHandle; +import java.dyn.MethodHandles; +import java.dyn.MethodType; + + +/** + * Construction and caching of often-used invokers. + * @author jrose + */ +public class Invokers { + // exact type (sans leading taget MH) for the outgoing call + private final MethodType targetType; + + // exact invoker for the outgoing call + private /*lazy*/ MethodHandle exactInvoker; + + // generic (untyped) invoker for the outgoing call + private /*lazy*/ MethodHandle genericInvoker; + + /** Compute and cache information common to all collecting adapters + * that implement members of the erasure-family of the given erased type. + */ + public Invokers(Access token, MethodType targetType) { + Access.check(token); + this.targetType = targetType; + } + + public static MethodType invokerType(MethodType targetType) { + return targetType.insertParameterType(0, MethodHandle.class); + } + + public MethodHandle exactInvoker() { + MethodHandle invoker = exactInvoker; + if (invoker != null) return invoker; + invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType); + if (invoker == null) throw new InternalError("JVM cannot find invoker for "+targetType); + assert(invokerType(targetType) == invoker.type()); + exactInvoker = invoker; + return invoker; + } + + public MethodHandle genericInvoker() { + MethodHandle invoker1 = exactInvoker(); + MethodHandle invoker = genericInvoker; + if (invoker != null) return invoker; + MethodType genericType = targetType.generic(); + invoker = MethodHandles.convertArguments(invoker1, invokerType(genericType)); + genericInvoker = invoker; + return invoker; + } + + public MethodHandle varargsInvoker() { + throw new UnsupportedOperationException("NYI"); + } + + public String toString() { + return "Invokers"+targetType; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/MemberName.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,552 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import sun.dyn.util.BytecodeSignature; +import java.dyn.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import static sun.dyn.MethodHandleNatives.Constants.*; + +/** + * Compact information which fully characterizes a method or field reference. + * When resolved, it includes a direct pointer to JVM metadata. + * This representation is stateless and only decriptive. + * It provides no private information and no capability to use the member. + * <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<?> lookupClass) { + return newNoAccessException("cannot access", name, lookupClass); + } + public static NoAccessException newNoAccessException(String message, + MemberName name, Class<?> lookupClass) { + message += ": " + name; + if (lookupClass != null) message += ", from " + lookupClass.getName(); + return new NoAccessException(message); + } + + /** Actually making a query requires an access check. */ + public static Factory getFactory(Access token) { + Access.check(token); + return Factory.INSTANCE; + } + public static Factory getFactory() { + return getFactory(Access.getToken()); + } + public static class Factory { + private Factory() { } // singleton pattern + static Factory INSTANCE = new Factory(); + + private static int ALLOWED_FLAGS = SEARCH_ALL_SUPERS | ALL_KINDS; + + /// Queries + List<MemberName> getMembers(Class<?> defc, + String matchName, Object matchType, + int matchFlags, Class<?> lookupClass) { + matchFlags &= ALLOWED_FLAGS; + String matchSig = null; + if (matchType != null) { + matchSig = BytecodeSignature.unparse(matchType); + if (matchSig.startsWith("(")) + matchFlags &= ~(ALL_KINDS & ~IS_INVOCABLE); + else + matchFlags &= ~(ALL_KINDS & ~IS_FIELD); + } + final int BUF_MAX = 0x2000; + int len1 = matchName == null ? 10 : matchType == null ? 4 : 1; + MemberName[] buf = newMemberBuffer(len1); + int totalCount = 0; + ArrayList<MemberName[]> bufs = null; + for (;;) { + int bufCount = MethodHandleNatives.getMembers(defc, + matchName, matchSig, matchFlags, + MethodHandleNatives.asNativeCaller(lookupClass), + totalCount, buf); + if (bufCount <= buf.length) { + if (bufCount >= 0) + totalCount += bufCount; + break; + } + // JVM returned tp us with an intentional overflow! + totalCount += buf.length; + int excess = bufCount - buf.length; + if (bufs == null) bufs = new ArrayList<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<?> lookupClass) { + Class<?> caller = MethodHandleNatives.asNativeCaller(lookupClass); + MethodHandleNatives.resolve(m, caller); + if (m.isResolved()) return true; + int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0); + String matchSig = m.getSignature(); + MemberName[] buf = { m }; + int n = MethodHandleNatives.getMembers(m.getDeclaringClass(), + m.getName(), matchSig, matchFlags, caller, 0, buf); + if (n != 1) return false; + return m.isResolved(); + } + public MemberName resolveOrNull(MemberName m, boolean searchSupers, Class<?> lookupClass) { + MemberName result = m.clone(); + if (resolveInPlace(result, searchSupers, lookupClass)) + return result; + return null; + } + public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) { + MemberName result = resolveOrNull(m, searchSupers, lookupClass); + if (result != null) + return result; + throw newNoAccessException(m, lookupClass); + } + public List<MemberName> getMethods(Class<?> defc, boolean searchSupers, + Class<?> lookupClass) { + return getMethods(defc, searchSupers, null, null, lookupClass); + } + public List<MemberName> getMethods(Class<?> defc, boolean searchSupers, + String name, MethodType type, Class<?> lookupClass) { + int matchFlags = IS_METHOD | (searchSupers ? SEARCH_ALL_SUPERS : 0); + return getMembers(defc, name, type, matchFlags, lookupClass); + } + public List<MemberName> getConstructors(Class<?> defc, Class<?> lookupClass) { + return getMembers(defc, null, null, IS_CONSTRUCTOR, lookupClass); + } + public List<MemberName> getFields(Class<?> defc, boolean searchSupers, + Class<?> lookupClass) { + return getFields(defc, searchSupers, null, null, lookupClass); + } + public List<MemberName> getFields(Class<?> defc, boolean searchSupers, + String name, Class<?> type, Class<?> lookupClass) { + int matchFlags = IS_FIELD | (searchSupers ? SEARCH_ALL_SUPERS : 0); + return getMembers(defc, name, type, matchFlags, lookupClass); + } + public List<MemberName> getNestedTypes(Class<?> defc, boolean searchSupers, + Class<?> lookupClass) { + int matchFlags = IS_TYPE | (searchSupers ? SEARCH_ALL_SUPERS : 0); + return getMembers(defc, null, null, matchFlags, lookupClass); + } + private static MemberName[] newMemberBuffer(int length) { + MemberName[] buf = new MemberName[length]; + // fill the buffer with dummy structs for the JVM to fill in + for (int i = 0; i < length; i++) + buf[i] = new MemberName(); + return buf; + } + } + +// static { +// System.out.println("Hello world! My methods are:"); +// System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null)); +// } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/MethodHandleImpl.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,355 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import java.dyn.MethodHandle; +import java.dyn.MethodHandles; +import java.dyn.MethodHandles.Lookup; +import java.dyn.MethodType; +import sun.dyn.util.VerifyType; +import java.dyn.NoAccessException; +import static sun.dyn.MemberName.newIllegalArgumentException; +import static sun.dyn.MemberName.newNoAccessException; + +/** + * Base class for method handles, containing JVM-specific fields and logic. + * TO DO: It should not be a base class. + * @author jrose + */ +public abstract class MethodHandleImpl { + + // Fields which really belong in MethodHandle: + private byte vmentry; // adapter stub or method entry point + //private int vmslots; // optionally, hoist type.form.vmslots + protected Object vmtarget; // VM-specific, class-specific target value + //MethodType type; // defined in MethodHandle + + // TO DO: vmtarget should be invisible to Java, since the JVM puts internal + // managed pointers into it. Making it visible exposes it to debuggers, + // which can cause errors when they treat the pointer as an Object. + + // These two dummy fields are present to force 'I' and 'J' signatures + // into this class's constant pool, so they can be transferred + // to vmentry when this class is loaded. + static final int INT_FIELD = 0; + static final long LONG_FIELD = 0; + + // type is defined in java.dyn.MethodHandle, which is platform-independent + + // vmentry (a void* field) is used *only* by by the JVM. + // The JVM adjusts its type to int or long depending on system wordsize. + // Since it is statically typed as neither int nor long, it is impossible + // to use this field from Java bytecode. (Please don't try to, either.) + + // The vmentry is an assembly-language stub which is jumped to + // immediately after the method type is verified. + // For a direct MH, this stub loads the vmtarget's entry point + // and jumps to it. + + /** + * VM-based method handles must have a security token. + * This security token can only be obtained by trusted code. + * Do not create method handles directly; use factory methods. + */ + public MethodHandleImpl(Access token) { + Access.check(token); + } + + /** Initialize the method type form to participate in JVM calls. + * This is done once for each erased type. + */ + public static void init(Access token, MethodType self) { + Access.check(token); + if (MethodHandleNatives.JVM_SUPPORT) + MethodHandleNatives.init(self); + } + + /// Factory methods to create method handles: + + private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE; + + static private Lookup IMPL_LOOKUP_INIT; + + public static void initLookup(Access token, Lookup lookup) { + Access.check(token); + if (IMPL_LOOKUP_INIT != null || lookup.lookupClass() != Access.class) + throw new InternalError(); + IMPL_LOOKUP_INIT = lookup; + } + + public static Lookup getLookup(Access token) { + Access.check(token); + return IMPL_LOOKUP; + } + + static { + // Force initialization: + Lookup.PUBLIC_LOOKUP.lookupClass(); + if (IMPL_LOOKUP_INIT == null) + throw new InternalError(); + } + + public static void initStatics() { + // Trigger preceding sequence. + } + + /** Shared secret with MethodHandles.Lookup, a copy of Lookup.IMPL_LOOKUP. */ + static final Lookup IMPL_LOOKUP = IMPL_LOOKUP_INIT; + + + /** Look up a given method. + * Callable only from java.dyn and related packages. + * <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 lookup class. + * In particular, if the method is protected and {@code defc} is in a + * different package from the lookup class, then {@code rcvc} must be + * the lookup class or a subclass. + * @param token Proof that the lookup class has access to this package. + * @param member Resolved method or constructor to call. + * @param name Name of the desired method. + * @param rcvc Receiver type of desired non-static method (else null) + * @param doDispatch whether the method handle will test the receiver type + * @param lookupClass access-check relative to this class + * @return a direct handle to the matching method + * @throws NoAccessException if the given method cannot be accessed by the lookup class + */ + public static + MethodHandle findMethod(Access token, MemberName method, + boolean doDispatch, Class<?> lookupClass) { + Access.check(token); // only trusted calls + MethodType mtype = method.getMethodType(); + if (method.isStatic()) { + doDispatch = false; + } else { + // adjust the advertised receiver type to be exactly the one requested + // (in the case of invokespecial, this will be the calling class) + mtype = mtype.insertParameterType(0, method.getDeclaringClass()); + if (method.isConstructor()) + doDispatch = true; + } + DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass); + if (!mh.isValid()) + throw newNoAccessException(method, lookupClass); + return mh; + } + + public static + MethodHandle accessField(Access token, + MemberName member, boolean isSetter, + Class<?> lookupClass) { + Access.check(token); + // FIXME: Use sun.misc.Unsafe to dig up the dirt on the field. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public static + MethodHandle accessArrayElement(Access token, + Class<?> arrayClass, boolean isSetter) { + Access.check(token); + if (!arrayClass.isArray()) + throw newIllegalArgumentException("not an array: "+arrayClass); + // FIXME: Use sun.misc.Unsafe to dig up the dirt on the array. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** Bind a predetermined first argument to the given direct method handle. + * Callable only from MethodHandles. + * @param token Proof that the caller has access to this package. + * @param target Any direct method handle. + * @param receiver Receiver (or first static method argument) to pre-bind. + * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist + */ + public static + MethodHandle bindReceiver(Access token, + MethodHandle target, Object receiver) { + Access.check(token); + if (target instanceof DirectMethodHandle) + return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0); + return null; // let caller try something else + } + + /** Bind a predetermined argument to the given arbitrary method handle. + * Callable only from MethodHandles. + * @param token Proof that the caller has access to this package. + * @param target Any method handle. + * @param receiver Argument (which can be a boxed primitive) to pre-bind. + * @return a suitable BoundMethodHandle + */ + public static + MethodHandle bindArgument(Access token, + MethodHandle target, int argnum, Object receiver) { + Access.check(token); + throw new UnsupportedOperationException("NYI"); + } + + public static MethodHandle convertArguments(Access token, + MethodHandle target, + MethodType newType, + MethodType oldType, + int[] permutationOrNull) { + Access.check(token); + MethodHandle res = AdapterMethodHandle.makePairwiseConvert(token, newType, target); + if (res != null) + return res; + int argc = oldType.parameterCount(); + // The JVM can't do it directly, so fill in the gap with a Java adapter. + // TO DO: figure out what to put here from case-by-case experience + // Use a heavier method: Convert all the arguments to Object, + // then back to the desired types. We might have to use Java-based + // method handles to do this. + MethodType objType = MethodType.makeGeneric(argc); + MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(token, objType, target); + if (objTarget == null) + objTarget = FromGeneric.make(target); + res = AdapterMethodHandle.makePairwiseConvert(token, newType, objTarget); + if (res != null) + return res; + return ToGeneric.make(newType, objTarget); + } + + public static MethodHandle spreadArguments(Access token, + MethodHandle target, + MethodType newType, + int spreadArg) { + Access.check(token); + // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[] + MethodType oldType = target.type(); + // spread the last argument of newType to oldType + int spreadCount = oldType.parameterCount() - spreadArg; + Class<Object[]> spreadArgType = Object[].class; + MethodHandle res = AdapterMethodHandle.makeSpreadArguments(token, newType, target, spreadArgType, spreadArg, spreadCount); + if (res != null) + return res; + // try an intermediate adapter + Class<?> spreadType = null; + if (spreadArg < 0 || spreadArg >= newType.parameterCount() + || !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg))) + throw newIllegalArgumentException("no restarg in "+newType); + Class<?>[] ptypes = oldType.parameterArray(); + for (int i = 0; i < spreadCount; i++) + ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i); + MethodType midType = MethodType.make(newType.returnType(), ptypes); + // after spreading, some arguments may need further conversion + target = convertArguments(token, target, midType, oldType, null); + if (target == null) + throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType); + res = AdapterMethodHandle.makeSpreadArguments(token, newType, target, spreadArgType, spreadArg, spreadCount); + return res; + } + + public static MethodHandle collectArguments(Access token, + MethodHandle target, + MethodType newType, + int collectArg) { + if (collectArg > 0) + throw new UnsupportedOperationException("NYI"); + throw new UnsupportedOperationException("NYI"); + } + public static + MethodHandle dropArguments(Access token, MethodHandle target, + MethodType newType, int argnum) { + Access.check(token); + throw new UnsupportedOperationException("NYI"); + } + + public static + MethodHandle makeGuardWithTest(Access token, + final MethodHandle test, + final MethodHandle target, + final MethodHandle fallback) { + Access.check(token); + // %%% This is just a sketch. It needs to be de-boxed. + // Adjust the handles to accept varargs lists. + MethodType type = target.type(); + Class<?> rtype = type.returnType(); + if (type.parameterCount() != 1 || type.parameterType(0).isPrimitive()) { + MethodType vatestType = MethodType.make(boolean.class, Object[].class); + MethodType vatargetType = MethodType.make(rtype, Object[].class); + MethodHandle vaguard = makeGuardWithTest(token, + MethodHandles.spreadArguments(test, vatestType), + MethodHandles.spreadArguments(target, vatargetType), + MethodHandles.spreadArguments(fallback, vatargetType)); + return MethodHandles.collectArguments(vaguard, type); + } + if (rtype.isPrimitive()) { + MethodType boxtype = type.changeReturnType(Object.class); + MethodHandle boxguard = makeGuardWithTest(token, + test, + MethodHandles.convertArguments(target, boxtype), + MethodHandles.convertArguments(fallback, boxtype)); + return MethodHandles.convertArguments(boxguard, type); + } + // Got here? Reduced calling sequence to Object(Object). + class Guarder { + Object invoke(Object x) { + // If javac supports MethodHandle.invoke directly: + //z = vatest.invoke<boolean>(arguments); + // If javac does not support direct MH.invoke calls: + boolean z = (Boolean) MethodHandles.invoke_1(test, x); + MethodHandle mh = (z ? target : fallback); + return MethodHandles.invoke_1(mh, x); + } + MethodHandle handle() { + MethodType invokeType = MethodType.makeGeneric(0, true); + MethodHandle vh = IMPL_LOOKUP.bind(this, "invoke", invokeType); + return MethodHandles.collectArguments(vh, target.type()); + } + } + return new Guarder().handle(); + } + + public static + MethodHandle combineArguments(Access token, MethodHandle target, MethodHandle checker, int pos) { + Access.check(token); + throw new UnsupportedOperationException("Not yet implemented"); + } + + protected static String basicToString(MethodHandle target) { + MemberName name = null; + if (target != null) + name = MethodHandleNatives.getMethodName(target); + if (name == null) + return "<unknown>"; + return name.getName(); + } + + protected static String addTypeString(MethodHandle target, String name) { + if (target == null) return name; + return name+target.type(); + } + static RuntimeException newIllegalArgumentException(String string) { + return new IllegalArgumentException(string); + } + + @Override + public String toString() { + MethodHandle self = (MethodHandle) this; + return addTypeString(self, basicToString(self)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/MethodHandleNatives.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,271 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import java.dyn.MethodHandle; +import java.dyn.MethodType; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import static sun.dyn.MethodHandleNatives.Constants.*; + +/** + * The JVM interface for the method handles package is all here. + * @author jrose + */ +class MethodHandleNatives { + + private MethodHandleNatives() { } // static only + + /// MethodName support + + static native void init(MemberName self, Object ref); + static native void expand(MemberName self); + static native void resolve(MemberName self, Class<?> caller); + static native int getMembers(Class<?> defc, String matchName, String matchSig, + int matchFlags, Class<?> caller, int skip, MemberName[] results); + + static Class<?> asNativeCaller(Class<?> lookupClass) { + if (lookupClass == null) // means "public only, non-privileged" + return sun.dyn.empty.Empty.class; + if (lookupClass == Access.class) // means "internal, privileged" + return null; // to the JVM, null means completely privileged + return lookupClass; + } + + /// MethodHandle support + + /** Initialize the method handle to adapt the call. */ + static native void init(AdapterMethodHandle self, MethodHandle target, int argnum); + /** Initialize the method handle to call the correct method, directly. */ + static native void init(BoundMethodHandle self, Object target, int argnum); + /** Initialize the method handle to call as if by an invoke* instruction. */ + static native void init(DirectMethodHandle self, Object ref, boolean doDispatch, Class<?> caller); + + /** Initialize a method type, once per form. */ + static native void init(MethodType self); + + /** Tell the JVM that we need to change the target of an invokedynamic. */ + static native void linkCallSite(CallSiteImpl site, MethodHandle target); + + /** Fetch the vmtarget field. + * It will be sanitized as necessary to avoid exposing non-Java references. + * This routine is for debugging and reflection. + */ + static native Object getTarget(MethodHandle self, int format); + + /** Fetch the name of the handled method, if available. + * This routine is for debugging and reflection. + */ + static MemberName getMethodName(MethodHandle self) { + if (!JVM_SUPPORT) return null; + return (MemberName) getTarget(self, ETF_METHOD_NAME); + } + + /** Fetch the reflective version of the handled method, if available. + */ + static AccessibleObject getTargetMethod(MethodHandle self) { + if (!JVM_SUPPORT) return null; + return (AccessibleObject) getTarget(self, ETF_REFLECT_METHOD); + } + + /** Fetch the target of this method handle. + * If it directly targets a method, return a tuple of method info. + * The info is of the form new Object[]{defclass, name, sig, refclass}. + * If it is chained to another method handle, return that handle. + */ + static Object getTargetInfo(MethodHandle self) { + if (!JVM_SUPPORT) return null; + return getTarget(self, ETF_HANDLE_OR_METHOD_NAME); + } + + static Object[] makeTarget(Class<?> defc, String name, String sig, int mods, Class<?> refc) { + return new Object[] { defc, name, sig, mods, refc }; + } + + /** Fetch MH-related JVM parameter. + * which=0 retrieves MethodHandlePushLimit + * which=1 retrieves stack slot push size (in address units) + */ + static native int getConstant(int which); + + /** True iff this HotSpot JVM has built-in support for method handles. + * If false, some test cases might run, but functionality will be missing. + */ + public static final boolean JVM_SUPPORT; + + /** Java copy of MethodHandlePushLimit in range 2..255. */ + static final int JVM_PUSH_LIMIT; + /** JVM stack motion (in words) after one slot is pushed, usually -1. + */ + static final int JVM_STACK_MOVE_UNIT; + + private static native void registerNatives(); + static { + boolean JVM_SUPPORT_; + int JVM_PUSH_LIMIT_; + int JVM_STACK_MOVE_UNIT_; + try { + registerNatives(); + JVM_SUPPORT_ = true; + JVM_PUSH_LIMIT_ = getConstant(Constants.GC_JVM_PUSH_LIMIT); + JVM_STACK_MOVE_UNIT_ = getConstant(Constants.GC_JVM_STACK_MOVE_LIMIT); + //sun.reflect.Reflection.registerMethodsToFilter(MethodHandleImpl.class, "init"); + } catch (UnsatisfiedLinkError ee) { + // ignore; if we use init() methods later we'll see linkage errors + JVM_SUPPORT_ = false; + JVM_PUSH_LIMIT_ = 3; // arbitrary + JVM_STACK_MOVE_UNIT_ = -1; // arbitrary + //System.out.println("Warning: Running with JVM_SUPPORT=false"); + //System.out.println(ee); + JVM_SUPPORT = JVM_SUPPORT_; + JVM_PUSH_LIMIT = JVM_PUSH_LIMIT_; + JVM_STACK_MOVE_UNIT = JVM_STACK_MOVE_UNIT_; + throw ee; // just die; hopeless to try to run with an older JVM + } + JVM_SUPPORT = JVM_SUPPORT_; + JVM_PUSH_LIMIT = JVM_PUSH_LIMIT_; + JVM_STACK_MOVE_UNIT = JVM_STACK_MOVE_UNIT_; + } + + // All compile-time constants go here. + // There is an opportunity to check them against the JVM's idea of them. + static class Constants { + Constants() { } // static only + // MethodHandleImpl + static final int // for getConstant + GC_JVM_PUSH_LIMIT = 0, + GC_JVM_STACK_MOVE_LIMIT = 1; + static final int + ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method) + ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self) + ETF_METHOD_NAME = 2, // ultimate method as MemberName + ETF_REFLECT_METHOD = 3; // ultimate method as java.lang.reflect object (sans refClass) + + // MemberName + // The JVM uses values of -2 and above for vtable indexes. + // Field values are simple positive offsets. + // Ref: src/share/vm/oops/methodOop.hpp + // This value is negative enough to avoid such numbers, + // but not too negative. + static final int + MN_IS_METHOD = 0x00010000, // method (not constructor) + MN_IS_CONSTRUCTOR = 0x00020000, // constructor + MN_IS_FIELD = 0x00040000, // field + MN_IS_TYPE = 0x00080000, // nested type + MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers + MN_SEARCH_INTERFACES = 0x00200000, // for MHN.getMembers + VM_INDEX_UNINITIALIZED = -99; + + // AdapterMethodHandle + /** Conversions recognized by the JVM. + * They must align with the constants in sun.dyn_AdapterMethodHandle, + * in the JVM file hotspot/src/share/vm/classfile/javaClasses.hpp. + */ + static final int + OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype + OP_CHECK_CAST = 0x1, // ref-to-ref conversion; requires a Class argument + OP_PRIM_TO_PRIM = 0x2, // converts from one primitive to another + OP_REF_TO_PRIM = 0x3, // unboxes a wrapper to produce a primitive + OP_PRIM_TO_REF = 0x4, // boxes a primitive into a wrapper (NYI) + OP_SWAP_ARGS = 0x5, // swap arguments (vminfo is 2nd arg) + OP_ROT_ARGS = 0x6, // rotate arguments (vminfo is displaced arg) + OP_DUP_ARGS = 0x7, // duplicates one or more arguments (at TOS) + OP_DROP_ARGS = 0x8, // remove one or more argument slots + OP_COLLECT_ARGS = 0x9, // combine one or more arguments into a varargs (NYI) + OP_SPREAD_ARGS = 0xA, // expand in place a varargs array (of known size) + OP_FLYBY = 0xB, // operate first on reified argument list (NYI) + OP_RICOCHET = 0xC, // run an adapter chain on the return value (NYI) + CONV_OP_LIMIT = 0xD; // limit of CONV_OP enumeration + /** Shift and mask values for decoding the AMH.conversion field. + * These numbers are shared with the JVM for creating AMHs. + */ + static final int + CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field + CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use + CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK + CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK + CONV_DEST_TYPE_SHIFT = 12, // byte 2 has the adapter BasicType (if needed) + CONV_SRC_TYPE_SHIFT = 16, // byte 2 has the source BasicType (if needed) + CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change + CONV_STACK_MOVE_MASK = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1; + + /** Which conv-ops are implemented by the JVM? */ + static final int CONV_OP_IMPLEMENTED_MASK = + // TODO: The following expression should be replaced by + // a JVM query. + ((1<<OP_RETYPE_ONLY) + |(1<<OP_CHECK_CAST) + |(1<<OP_PRIM_TO_PRIM) + |(1<<OP_REF_TO_PRIM) + |(1<<OP_SWAP_ARGS) + |(1<<OP_ROT_ARGS) + |(1<<OP_DUP_ARGS) + |(1<<OP_DROP_ARGS) + ); + + /** + * 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(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/MethodTypeImpl.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,502 @@ +/* + * 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 sun.dyn.util.Wrapper; + +/** + * 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 + */ +public class MethodTypeImpl { + final int[] argToSlotTable, slotToArgTable; + final long argCounts; // packed slot & value counts + final long primCounts; // packed prim & double counts + final int vmslots; // total number of parameter slots + final MethodType erasedType; // the canonical erasure + /*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers + /*lazy*/ MethodType primArgsAsBoxes; // wrap args only; make raw return + /*lazy*/ MethodType primsAsInts; // replace prims by int/long + /*lazy*/ MethodType primsAsLongs; // replace prims by long + /*lazy*/ MethodType primsAtEnd; // reorder primitives to the end + + // Cached adapter information: + /*lazy*/ ToGeneric toGeneric; // convert cs. with prims to w/o + /*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with + /*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly + ///*lazy*/ Invokers invokers; // cache of handy higher-order adapters + + public MethodType erasedType() { + return erasedType; + } + + public static MethodTypeImpl of(MethodType type) { + return METHOD_TYPE_FRIEND.form(type); + } + + /** Access methods for the internals of MethodType, supplied to + * MethodTypeForm as a trusted agent. + */ + static public interface MethodTypeFriend { + Class<?>[] ptypes(MethodType mt); + MethodTypeImpl form(MethodType mt); + void setForm(MethodType mt, MethodTypeImpl form); + MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted); + MethodTypeImpl newMethodTypeForm(MethodType mt); + Invokers getInvokers(MethodType mt); + void setInvokers(MethodType mt, Invokers inv); + } + public static void setMethodTypeFriend(Access token, MethodTypeFriend am) { + Access.check(token); + if (METHOD_TYPE_FRIEND != null) + throw new InternalError(); // just once + METHOD_TYPE_FRIEND = am; + } + static private MethodTypeFriend METHOD_TYPE_FRIEND; + + protected MethodTypeImpl(MethodType erasedType) { + this.erasedType = erasedType; + + Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(erasedType); + int ptypeCount = ptypes.length; + int pslotCount = ptypeCount; // temp. estimate + int rtypeCount = 1; // temp. estimate + int rslotCount = 1; // temp. estimate + + int[] argToSlotTab = null, slotToArgTab = null; + + // Walk the argument types, looking for primitives. + int pac = 0, lac = 0, prc = 0, lrc = 0; + Class<?> epts[] = ptypes; + for (int i = 0; i < epts.length; i++) { + Class<?> pt = epts[i]; + if (pt != Object.class) { + assert(pt.isPrimitive()); + ++pac; + if (hasTwoArgSlots(pt)) ++lac; + } + } + pslotCount += lac; // #slots = #args + #longs + Class<?> rt = erasedType.returnType(); + if (rt != Object.class) { + ++prc; // even void.class counts as a prim here + if (hasTwoArgSlots(rt)) ++lrc; + // adjust #slots, #args + if (rt == void.class) + rtypeCount = rslotCount = 0; + else + rslotCount += lrc; + } + if (lac != 0) { + int slot = ptypeCount + lac; + slotToArgTab = new int[slot+1]; + argToSlotTab = new int[1+ptypeCount]; + argToSlotTab[0] = slot; // argument "-1" is past end of slots + for (int i = 0; i < epts.length; i++) { + Class<?> pt = epts[i]; + if (hasTwoArgSlots(pt)) --slot; + --slot; + slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note + argToSlotTab[1+i] = slot; + } + assert(slot == 0); // filled the table + } + this.primCounts = pack(lrc, prc, lac, pac); + this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount); + if (slotToArgTab == null) { + int slot = ptypeCount; // first arg is deepest in stack + slotToArgTab = new int[slot+1]; + argToSlotTab = new int[1+ptypeCount]; + argToSlotTab[0] = slot; // argument "-1" is past end of slots + for (int i = 0; i < ptypeCount; i++) { + --slot; + slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note + argToSlotTab[1+i] = slot; + } + } + this.argToSlotTable = argToSlotTab; + this.slotToArgTable = slotToArgTab; + + if (pslotCount >= 256) throw new IllegalArgumentException("too many arguments"); + + // send a few bits down to the JVM: + this.vmslots = parameterSlotCount(); + + // short circuit some no-op canonicalizations: + if (!hasPrimitives()) { + primsAsBoxes = erasedType; + primArgsAsBoxes = erasedType; + primsAsInts = erasedType; + primsAsLongs = erasedType; + primsAtEnd = erasedType; + } + } + + /** Turn all primitive types to corresponding wrapper types. + */ + public MethodType primsAsBoxes() { + MethodType ct = primsAsBoxes; + if (ct != null) return ct; + MethodType t = erasedType; + ct = canonicalize(erasedType, WRAP, WRAP); + if (ct == null) ct = t; // no prims to box + return primsAsBoxes = ct; + } + + /** Turn all primitive argument types to corresponding wrapper types. + * Subword and void return types are promoted to int. + */ + public MethodType primArgsAsBoxes() { + MethodType ct = primArgsAsBoxes; + if (ct != null) return ct; + MethodType t = erasedType; + ct = canonicalize(erasedType, RAW_RETURN, WRAP); + if (ct == null) ct = t; // no prims to box + return primArgsAsBoxes = ct; + } + + /** Turn all primitive types to either int or long. + * Floating point return types are not changed, because + * they may require special calling sequences. + * A void return value is turned to int. + */ + public MethodType primsAsInts() { + MethodType ct = primsAsInts; + if (ct != null) return ct; + MethodType t = erasedType; + ct = canonicalize(t, RAW_RETURN, INTS); + if (ct == null) ct = t; // no prims to int-ify + return primsAsInts = ct; + } + + /** Turn all primitive types to either int or long. + * Floating point return types are not changed, because + * they may require special calling sequences. + * A void return value is turned to int. + */ + public MethodType primsAsLongs() { + MethodType ct = primsAsLongs; + if (ct != null) return ct; + MethodType t = erasedType; + ct = canonicalize(t, RAW_RETURN, LONGS); + if (ct == null) ct = t; // no prims to int-ify + return primsAsLongs = ct; + } + + /** Stably sort parameters into 3 buckets: ref, int, long. */ + public MethodType primsAtEnd() { + MethodType ct = primsAtEnd; + if (ct != null) return ct; + MethodType t = erasedType; + + int pac = primitiveParameterCount(); + if (pac == 0) + return primsAtEnd = t; + + int argc = parameterCount(); + int lac = longPrimitiveParameterCount(); + if (pac == argc && (lac == 0 || lac == argc)) + return primsAtEnd = t; + + // known to have a mix of 2 or 3 of ref, int, long + return primsAtEnd = reorderParameters(t, primsAtEndOrder(t), null); + + } + + /** Compute a new ordering of parameters so that all references + * are before all ints or longs, and all ints are before all longs. + * For this ordering, doubles count as longs, and all other primitive + * values count as ints. + * As a special case, if the parameters are already in the specified + * order, this method returns a null reference, rather than an array + * specifying a null permutation. + * <p> + * For example, the type {@code (int,boolean,int,Object,String)void} + * produces the order {@code {3,4,0,1,2}}, the type + * {@code (long,int,String)void} produces {@code {2,1,2}}, and + * the type {@code (Object,int)Object} produces {@code null}. + */ + public static int[] primsAtEndOrder(MethodType mt) { + MethodTypeImpl form = METHOD_TYPE_FRIEND.form(mt); + if (form.primsAtEnd == form.erasedType) + // quick check shows no reordering is necessary + return null; + + int argc = form.parameterCount(); + int[] paramOrder = new int[argc]; + + // 3-way bucket sort: + int pac = form.primitiveParameterCount(); + int lac = form.longPrimitiveParameterCount(); + int rfill = 0, ifill = argc - pac, lfill = argc - lac; + + Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt); + boolean changed = false; + for (int i = 0; i < ptypes.length; i++) { + Class<?> pt = ptypes[i]; + int ord; + if (!pt.isPrimitive()) ord = rfill++; + else if (!hasTwoArgSlots(pt)) ord = ifill++; + else ord = lfill++; + if (ord != i) changed = true; + paramOrder[i] = ord; + } + assert(rfill == argc - pac && ifill == argc - lac && lfill == argc); + if (!changed) { + form.primsAtEnd = form.erasedType; + return null; + } + return paramOrder; + } + + /** Put the existing parameters of mt into a new order, given by newParamOrder. + * The third argument is logically appended to mt.parameterArray, + * so that elements of newParamOrder can index either pre-existing or + * new parameter types. + */ + public static MethodType reorderParameters(MethodType mt, int[] newParamOrder, Class<?>[] moreParams) { + if (newParamOrder == null) return mt; // no-op reordering + Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt); + Class<?>[] ntypes = new Class<?>[newParamOrder.length]; + int ordMax = ptypes.length + (moreParams == null ? 0 : moreParams.length); + boolean changed = (ntypes.length != ptypes.length); + for (int i = 0; i < newParamOrder.length; i++) { + int ord = newParamOrder[i]; + if (ord != i) changed = true; + Class<?> nt; + if (ord < ptypes.length) nt = ptypes[ord]; + else if (ord == ordMax) nt = mt.returnType(); + else nt = moreParams[ord - ptypes.length]; + ntypes[i] = nt; + } + if (!changed) return mt; + return METHOD_TYPE_FRIEND.makeImpl(mt.returnType(), ntypes, true); + } + + private static boolean hasTwoArgSlots(Class<?> type) { + return type == long.class || type == double.class; + } + + private static long pack(int a, int b, int c, int d) { + assert(((a|b|c|d) & ~0xFFFF) == 0); + long hw = ((a << 16) | b), lw = ((c << 16) | d); + return (hw << 32) | lw; + } + private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d + assert(word <= 3); + return (char)(packed >> ((3-word) * 16)); + } + + public int parameterCount() { // # outgoing values + return unpack(argCounts, 3); + } + public int parameterSlotCount() { // # outgoing interpreter slots + return unpack(argCounts, 2); + } + public int returnCount() { // = 0 (V), or 1 + return unpack(argCounts, 1); + } + public int returnSlotCount() { // = 0 (V), 2 (J/D), or 1 + return unpack(argCounts, 0); + } + public int primitiveParameterCount() { + return unpack(primCounts, 3); + } + public int longPrimitiveParameterCount() { + return unpack(primCounts, 2); + } + public int primitiveReturnCount() { // = 0 (obj), or 1 + return unpack(primCounts, 1); + } + public int longPrimitiveReturnCount() { // = 1 (J/D), or 0 + return unpack(primCounts, 0); + } + public boolean hasPrimitives() { + return primCounts != 0; + } +// public boolean hasNonVoidPrimitives() { +// if (primCounts == 0) return false; +// if (primitiveParameterCount() != 0) return true; +// return (primitiveReturnCount() != 0 && returnCount() != 0); +// } + public boolean hasLongPrimitives() { + return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0; + } + public int parameterToArgSlot(int i) { + return argToSlotTable[1+i]; + } + public int argSlotToParameter(int argSlot) { + // Note: Empty slots are represented by zero in this table. + // Valid arguments slots contain incremented entries, so as to be non-zero. + // We return -1 the caller to mean an empty slot. + return slotToArgTable[argSlot] - 1; + } + + public static void initForm(Access token, MethodType mt) { + Access.check(token); + MethodTypeImpl form = findForm(mt); + METHOD_TYPE_FRIEND.setForm(mt, form); + if (form.erasedType == mt) { + // This is a principal (erased) type; show it to the JVM. + MethodHandleImpl.init(token, mt); + } + } + + static MethodTypeImpl findForm(MethodType mt) { + MethodType erased = canonicalize(mt, ERASE, ERASE); + if (erased == null) { + // It is already erased. Make a new MethodTypeForm. + return METHOD_TYPE_FRIEND.newMethodTypeForm(mt); + } else { + // Share the MethodTypeForm with the erased version. + return METHOD_TYPE_FRIEND.form(erased); + } + } + + /** Codes for {@link #canonicalize(java.lang.Class, int). + * ERASE means change every reference to {@code Object}. + * WRAP means convert primitives (including {@code void} to their + * corresponding wrapper types. UNWRAP means the reverse of WRAP. + * INTS means convert all non-void primitive types to int or long, + * according to size. LONGS means convert all non-void primitives + * to long, regardless of size. RAW_RETURN means convert a type + * (assumed to be a return type) to int if it is smaller than an int, + * or if it is void. + */ + public static final int NO_CHANGE = 0, ERASE = 1, WRAP = 2, UNWRAP = 3, INTS = 4, LONGS = 5, RAW_RETURN = 6; + + /** Canonicalize the types in the given method type. + * If any types change, intern the new type, and return it. + * Otherwise return null. + */ + public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) { + Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt); + Class<?>[] ptc = MethodTypeImpl.canonicalizes(ptypes, howArgs); + Class<?> rtype = mt.returnType(); + Class<?> rtc = MethodTypeImpl.canonicalize(rtype, howRet); + if (ptc == null && rtc == null) { + // It is already canonical. + return null; + } + // Find the erased version of the method type: + if (rtc == null) rtc = rtype; + if (ptc == null) ptc = ptypes; + return METHOD_TYPE_FRIEND.makeImpl(rtc, ptc, true); + } + + /** Canonicalize the given return or param type. + * Return null if the type is already canonicalized. + */ + static Class<?> canonicalize(Class<?> t, int how) { + Class<?> ct; + if (t == Object.class) { + // no change, ever + } else if (!t.isPrimitive()) { + switch (how) { + case UNWRAP: + ct = Wrapper.asPrimitiveType(t); + if (ct != t) return ct; + break; + case RAW_RETURN: + case ERASE: + return Object.class; + } + } else if (t == void.class) { + // no change, usually + switch (how) { + case RAW_RETURN: + return int.class; + case WRAP: + return Void.class; + } + } else { + // non-void primitive + switch (how) { + case WRAP: + return Wrapper.asWrapperType(t); + case INTS: + if (t == int.class || t == long.class) + return null; // no change + if (t == double.class) + return long.class; + return int.class; + case LONGS: + if (t == long.class) + return null; // no change + return long.class; + case RAW_RETURN: + if (t == int.class || t == long.class || + t == float.class || t == double.class) + return null; // no change + // everything else returns as an int + return int.class; + } + } + // no change; return null to signify + return null; + } + + /** Canonicalize each param type in the given array. + * Return null if all types are already canonicalized. + */ + static Class<?>[] canonicalizes(Class<?>[] ts, int how) { + Class<?>[] cs = null; + for (int imax = ts.length, i = 0; i < imax; i++) { + Class<?> c = canonicalize(ts[i], how); + if (c != null) { + if (cs == null) + cs = ts.clone(); + cs[i] = c; + } + } + return cs; + } + + public static Invokers invokers(Access token, MethodType type) { + Access.check(token); + Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type); + if (inv != null) return inv; + inv = new Invokers(token, type); + METHOD_TYPE_FRIEND.setInvokers(type, inv); + return inv; + } + + @Override + public String toString() { + return "Form"+erasedType; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/ToGeneric.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,1018 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn; + +import java.dyn.JavaMethodHandle; +import java.dyn.MethodHandle; +import java.dyn.MethodHandles; +import java.dyn.MethodType; +import java.dyn.NoAccessException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import sun.dyn.util.ValueConversions; +import sun.dyn.util.Wrapper; + +/** + * Adapters which mediate between incoming calls which are not generic + * and outgoing calls which are. Any call can be represented generically + * boxing up its arguments, and (on return) unboxing the return value. + * <p> + * A call is "generic" (in MethodHandle terms) if its MethodType features + * only Object arguments. A non-generic call therefore features + * primitives and/or reference types other than Object. + * An adapter has types for its incoming and outgoing calls. + * The incoming call type is simply determined by the adapter's type + * (the MethodType it presents to callers). The outgoing call type + * is determined by the adapter's target (a MethodHandle that the adapter + * either binds internally or else takes as a leading argument). + * (To stretch the term, adapter-like method handles may have multiple + * targets or be polymorphic across multiple call types.) + * @author jrose + */ +class ToGeneric { + // type for the incoming call (may be erased) + private final MethodType entryType; + // incoming type with primitives moved to the end and turned to int/long + private final MethodType rawEntryType; + // adapter for the erased type + private final Adapter adapter; + // entry point for adapter (Adapter mh, a...) => ... + private final MethodHandle entryPoint; + // permutation of arguments for primsAtEndType + private final int[] primsAtEndOrder; + // optional final argument list conversions (at least, invokes the target) + private final MethodHandle invoker; + // conversion which unboxes a primitive return value + private final MethodHandle returnConversion; + + /** Compute and cache information common to all collecting adapters + * that implement members of the erasure-family of the given erased type. + */ + private ToGeneric(MethodType entryType) { + assert(entryType.erase() == entryType); // for now + // incoming call will first "forget" all reference types except Object + this.entryType = entryType; + MethodHandle invoker0 = MethodHandles.exactInvoker(entryType.generic()); + MethodType rawEntryTypeInit; + Adapter ad = findAdapter(rawEntryTypeInit = entryType); + if (ad != null) { + // Immediate hit to exactly the adapter we want, + // with no monkeying around with primitive types. + this.returnConversion = computeReturnConversion(entryType, rawEntryTypeInit, false); + this.rawEntryType = rawEntryTypeInit; + this.adapter = ad; + this.entryPoint = ad.prototypeEntryPoint(); + this.primsAtEndOrder = null; + this.invoker = invoker0; + return; + } + + // next, it will reorder primitives after references + MethodType primsAtEnd = MethodTypeImpl.of(entryType).primsAtEnd(); + // at the same time, it will "forget" all primitive types except int/long + this.primsAtEndOrder = MethodTypeImpl.primsAtEndOrder(entryType); + if (primsAtEndOrder != null) { + // reordering is required; build on top of a simpler ToGeneric + ToGeneric va2 = ToGeneric.of(primsAtEnd); + this.adapter = va2.adapter; + this.entryPoint = MethodHandleImpl.convertArguments(Access.TOKEN, + va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder); + // example: for entryType of (int,Object,Object), the reordered + // type is (Object,Object,int) and the order is {1,2,0}, + // and putPAE is (mh,int0,obj1,obj2) => mh.invoke(obj1,obj2,int0) + if (true) throw new UnsupportedOperationException("NYI"); + return; + } + + // after any needed argument reordering, it will reinterpret + // primitive arguments according to their "raw" types int/long + MethodType intsAtEnd = MethodTypeImpl.of(primsAtEnd).primsAsInts(); + ad = findAdapter(rawEntryTypeInit = intsAtEnd); + if (ad == null) { + // Perhaps the adapter is available only for longs. + // If so, we can use it, but there will have to be a little + // more stack motion on each call. + MethodType longsAtEnd = MethodTypeImpl.of(primsAtEnd).primsAsLongs(); + ad = findAdapter(rawEntryTypeInit = longsAtEnd); + if (ad == null) { + // If there is no statically compiled adapter, + // build one by means of dynamic bytecode generation. + ad = buildAdapterFromBytecodes(rawEntryTypeInit = intsAtEnd); + } + } + MethodHandle rawEntryPoint = ad.prototypeEntryPoint(); + MethodType tepType = entryType.insertParameterType(0, ad.getClass()); + this.entryPoint = + AdapterMethodHandle.makeRawRetypeOnly(Access.TOKEN, tepType, rawEntryPoint); + if (this.entryPoint == null) + throw new UnsupportedOperationException("cannot retype to "+entryType + +" from "+rawEntryPoint.type().dropParameterType(0)); + this.returnConversion = computeReturnConversion(entryType, rawEntryTypeInit, false); + this.rawEntryType = rawEntryTypeInit; + this.adapter = ad; + this.invoker = makeRawArgumentFilter(invoker0, + rawEntryPoint.type().dropParameterType(0), entryType); + } + + /** A generic argument list will be created by a call of type 'raw'. + * The values need to be reboxed for to match 'cooked'. + * Do this on the fly. + */ + // TO DO: Use a generic argument converter in a different file + static MethodHandle makeRawArgumentFilter(MethodHandle invoker, + MethodType raw, MethodType cooked) { + MethodHandle filteredInvoker = null; + for (int i = 0, nargs = raw.parameterCount(); i < nargs; i++) { + Class<?> src = raw.parameterType(i); + Class<?> dst = cooked.parameterType(i); + if (src == dst) continue; + assert(src.isPrimitive() && dst.isPrimitive()); + if (filteredInvoker == null) { + filteredInvoker = + AdapterMethodHandle.makeCheckCast(Access.TOKEN, + invoker.type().generic(), invoker, 0, MethodHandle.class); + if (filteredInvoker == null) throw new UnsupportedOperationException("NYI"); + } + MethodHandle reboxer = ValueConversions.rebox(dst, false); + FilterGeneric gen = new FilterGeneric(filteredInvoker.type(), (short)(1+i), (short)1, 'R'); + filteredInvoker = gen.makeInstance(reboxer, filteredInvoker); + } + if (filteredInvoker == null) return invoker; + return AdapterMethodHandle.makeRetypeOnly(Access.TOKEN, invoker.type(), filteredInvoker); + } + + /** + * Caller will be expecting a result from a call to {@code type}, + * while the internal adapter entry point is rawEntryType. + * Also, the internal target method will be returning a boxed value, + * as an untyped object. + * <p> + * Produce a value converter which will be typed to convert from + * {@code Object} to the return value of {@code rawEntryType}, and will + * in fact ensure that the value is compatible with the return type of + * {@code type}. + */ + private static MethodHandle computeReturnConversion( + MethodType type, MethodType rawEntryType, boolean mustCast) { + Class<?> tret = type.returnType(); + Class<?> rret = rawEntryType.returnType(); + if (mustCast || !tret.isPrimitive()) { + assert(!tret.isPrimitive()); + assert(!rret.isPrimitive()); + if (rret == Object.class && !mustCast) + return null; + return ValueConversions.cast(tret, false); + } else if (tret == rret) { + return ValueConversions.unbox(tret, false); + } else { + assert(rret.isPrimitive()); + assert(tret == double.class ? rret == long.class : rret == int.class); + return ValueConversions.unboxRaw(tret, false); + } + } + + Adapter makeInstance(MethodType type, MethodHandle genericTarget) { + genericTarget.getClass(); // check for NPE + MethodHandle convert = returnConversion; + if (primsAtEndOrder != null) + // reorder arguments passed to genericTarget, if primsAtEndOrder + throw new UnsupportedOperationException("NYI"); + if (type == entryType) { + if (convert == null) convert = ValueConversions.identity(); + return adapter.makeInstance(entryPoint, invoker, convert, genericTarget); + } + // my erased-type is not exactly the same as the desired type + assert(type.erase() == entryType); // else we are busted + if (convert == null) + convert = computeReturnConversion(type, rawEntryType, true); + // retype erased reference arguments (the cast makes it safe to do this) + MethodType tepType = type.insertParameterType(0, adapter.getClass()); + MethodHandle typedEntryPoint = + AdapterMethodHandle.makeRawRetypeOnly(Access.TOKEN, tepType, entryPoint); + return adapter.makeInstance(typedEntryPoint, invoker, convert, genericTarget); + } + + /** Build an adapter of the given type, which invokes genericTarget + * on the incoming arguments, after boxing as necessary. + * The return value is unboxed if necessary. + * @param type the required type of the + * @param genericTarget the target, which must accept and return only Object values + * @return an adapter method handle + */ + public static MethodHandle make(MethodType type, MethodHandle genericTarget) { + MethodType gtype = genericTarget.type(); + if (type.generic() != gtype) + throw new IllegalArgumentException(); + if (type == gtype) return genericTarget; + return ToGeneric.of(type).makeInstance(type, genericTarget); + } + + /** Return the adapter information for this type's erasure. */ + static ToGeneric of(MethodType type) { + MethodTypeImpl form = MethodTypeImpl.of(type); + ToGeneric toGen = form.toGeneric; + if (toGen == null) + form.toGeneric = toGen = new ToGeneric(form.erasedType()); + return toGen; + } + + public String toString() { + return "ToGeneric"+entryType + +(primsAtEndOrder!=null?"[reorder]":""); + } + + /* Create an adapter for the given incoming call type. */ + static Adapter findAdapter(MethodType entryPointType) { + MethodTypeImpl form = MethodTypeImpl.of(entryPointType); + Class<?> rtype = entryPointType.returnType(); + int argc = form.parameterCount(); + int lac = form.longPrimitiveParameterCount(); + int iac = form.primitiveParameterCount() - lac; + String intsAndLongs = (iac > 0 ? "I"+iac : "")+(lac > 0 ? "J"+lac : ""); + String rawReturn = String.valueOf(Wrapper.forPrimitiveType(rtype).basicTypeChar()); + String iname0 = "invoke_"+rawReturn; + String iname1 = "invoke"; + String[] inames = { iname0, iname1 }; + String cname0 = rawReturn + argc; + String cname1 = "A" + argc; + String[] cnames = { cname1, cname1+intsAndLongs, cname0, cname0+intsAndLongs }; + // e.g., D5I2, D5, L5I2, L5 + for (String cname : cnames) { + Class<? extends Adapter> acls = Adapter.findSubClass(cname); + if (acls == null) continue; + // see if it has the required invoke method + for (String iname : inames) { + MethodHandle entryPoint = null; + try { + entryPoint = MethodHandleImpl.IMPL_LOOKUP. + findSpecial(acls, iname, entryPointType, acls); + } catch (NoAccessException ex) { + } + if (entryPoint == null) continue; + Constructor<? extends Adapter> ctor = null; + try { + // Prototype builder: + ctor = acls.getDeclaredConstructor(MethodHandle.class); + } catch (NoSuchMethodException ex) { + } catch (SecurityException ex) { + } + if (ctor == null) continue; + try { + return ctor.newInstance(entryPoint); + } catch (IllegalArgumentException ex) { + } catch (InvocationTargetException ex) { + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + } + } + return null; + } + + static Adapter buildAdapterFromBytecodes(MethodType entryPointType) { + throw new UnsupportedOperationException("NYI"); + } + + /** + * The invoke method takes some particular but unconstrained spread + * of raw argument types, and returns a raw return type (in L/I/J/F/D). + * Internally, it converts the incoming arguments uniformly into objects. + * This series of objects is then passed to the {@code target} method, + * which returns a result object. This result is finally converted, + * via another method handle {@code convert}, which is responsible for + * converting the object result into the raw return value. + */ + static abstract class Adapter extends JavaMethodHandle { + /* + * class X<<R,A...>> extends Adapter { + * Object...=>Object target; + * Object=>R convert; + * R invoke(A... a...) = convert(invoker(target, a...))) + * } + */ + protected final MethodHandle invoker; // (MH, Object...) -> Object + protected final MethodHandle target; // Object... -> Object + protected final MethodHandle convert; // Object -> R + + protected boolean isPrototype() { return target == null; } + /* Prototype constructor. */ + protected Adapter(MethodHandle entryPoint) { + super(entryPoint); + this.invoker = null; + this.convert = entryPoint; + this.target = null; + assert(isPrototype()); + } + protected MethodHandle prototypeEntryPoint() { + if (!isPrototype()) throw new InternalError(); + return convert; + } + + protected Adapter(MethodHandle entryPoint, MethodHandle invoker, MethodHandle convert, MethodHandle target) { + super(entryPoint); + this.invoker = invoker; + this.convert = convert; + this.target = target; + } + + /** Make a copy of self, with new fields. */ + protected abstract Adapter makeInstance(MethodHandle entryPoint, + MethodHandle invoker, MethodHandle convert, MethodHandle target); + // { return new ThisType(entryPoint, convert, target); } + + // Code to run when the arguments (<= 4) have all been boxed. + protected Object target() { return invoker.<Object>invoke(target); } + protected Object target(Object a0) { return invoker.<Object>invoke(target, a0); } + protected Object target(Object a0, Object a1) + { return invoker.<Object>invoke(target, a0, a1); } + protected Object target(Object a0, Object a1, Object a2) + { return invoker.<Object>invoke(target, a0, a1, a2); } + protected Object target(Object a0, Object a1, Object a2, Object a3) + { return invoker.<Object>invoke(target, a0, a1, a2, a3); } + /* + protected Object target_0(Object... av) { return invoker.<Object>invoke(target, av); } + protected Object target_1(Object a0, Object... av) + { return invoker.<Object>invoke(target, a0, (Object)av); } + protected Object target_2(Object a0, Object a1, Object... av) + { return invoker.<Object>invoke(target, a0, a1, (Object)av); } + protected Object target_3(Object a0, Object a1, Object a2, Object... av) + { return invoker.<Object>invoke(target, a0, a1, a2, (Object)av); } + protected Object target_4(Object a0, Object a1, Object a2, Object a3, Object... av) + { return invoker.<Object>invoke(target, a0, a1, a2, a3, (Object)av); } + // */ + // (For more than 4 arguments, generate the code in the adapter itself.) + + // Code to run when the generic target has finished and produced a value. + protected Object return_L(Object res) { return convert.<Object>invoke(res); } + protected int return_I(Object res) { return convert.<int >invoke(res); } + protected long return_J(Object res) { return convert.<long >invoke(res); } + protected float return_F(Object res) { return convert.<float >invoke(res); } + protected double return_D(Object res) { return convert.<double>invoke(res); } + + static private final String CLASS_PREFIX; // "sun.dyn.ToGeneric$" + static { + String aname = Adapter.class.getName(); + String sname = Adapter.class.getSimpleName(); + if (!aname.endsWith(sname)) throw new InternalError(); + CLASS_PREFIX = aname.substring(0, aname.length() - sname.length()); + } + /** Find a sibing class of Adapter. */ + static Class<? extends Adapter> findSubClass(String name) { + String cname = Adapter.CLASS_PREFIX + name; + try { + return Class.forName(cname).asSubclass(Adapter.class); + } catch (ClassNotFoundException ex) { + return null; + } catch (ClassCastException ex) { + return null; + } + } + } + + /* generated classes follow this pattern: + static class A1 extends Adapter { + protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A1(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A1(e, i, c, t); } + protected Object target(Object a0) { return invoker.<Object>invoke(target, a0); } + protected Object targetA1(Object a0) { return target(a0); } + protected Object targetA1(int a0) { return target(a0); } + protected Object targetA1(long a0) { return target(a0); } + protected Object invoke_L(Object a0) { return return_L(targetA1(a0)); } + protected int invoke_I(Object a0) { return return_I(targetA1(a0)); } + protected long invoke_J(Object a0) { return return_J(targetA1(a0)); } + protected float invoke_F(Object a0) { return return_F(targetA1(a0)); } + protected double invoke_D(Object a0) { return return_D(targetA1(a0)); } + protected Object invoke_L(int a0) { return return_L(targetA1(a0)); } + protected int invoke_I(int a0) { return return_I(targetA1(a0)); } + protected long invoke_J(int a0) { return return_J(targetA1(a0)); } + protected float invoke_F(int a0) { return return_F(targetA1(a0)); } + protected double invoke_D(int a0) { return return_D(targetA1(a0)); } + protected Object invoke_L(long a0) { return return_L(targetA1(a0)); } + protected int invoke_I(long a0) { return return_I(targetA1(a0)); } + protected long invoke_J(long a0) { return return_J(targetA1(a0)); } + protected float invoke_F(long a0) { return return_F(targetA1(a0)); } + protected double invoke_D(long a0) { return return_D(targetA1(a0)); } + } + // */ + +/* +: SHELL; n=ToGeneric; cp -p $n.java $n.java-; sed < $n.java- > $n.java+ -e '/{{*{{/,/}}*}}/w /tmp/genclasses.java' -e '/}}*}}/q'; (cd /tmp; javac -d . genclasses.java; java -cp . genclasses) >> $n.java+; echo '}' >> $n.java+; mv $n.java+ $n.java; mv $n.java- $n.java~ +//{{{ +import java.util.*; +class genclasses { + static String[] TYPES = { "Object", "int ", "long ", "float ", "double" }; + static String[] TCHARS = { "L", "I", "J", "F", "D", "A" }; + static String[][] TEMPLATES = { { + "@for@ arity=0..3 rcat<=4 nrefs<=99 nints<=99 nlongs<=99", + "@for@ arity=4..5 rcat<=2 nrefs<=99 nints<=99 nlongs<=99", + "@for@ arity=6..10 rcat<=2 nrefs<=99 nints=0 nlongs<=99", + " //@each-cat@", + " static class @cat@ extends Adapter {", + " protected @cat@(MethodHandle entryPoint) { super(entryPoint); } // to build prototype", + " protected @cat@(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }", + " protected @cat@ makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new @cat@(e, i, c, t); }", + " protected Object target(@Ovav@) { return invoker.<Object>invoke(target, @av@); }", + " //@each-Tv@", + " protected Object target@cat@(@Tvav@) { return target(@av@); }", + " //@end-Tv@", + " //@each-Tv@", + " //@each-R@", + " protected @R@ invoke_@Rc@(@Tvav@) { return return_@Rc@(target@cat@(@av@)); }", + " //@end-R@", + " //@end-Tv@", + " }", + } }; + enum VAR { + cat, R, Rc, Tv, av, Tvav, Ovav; + public final String pattern = "@"+toString().replace('_','.')+"@"; + public String binding; + static void makeBindings(boolean topLevel, int rcat, int nrefs, int nints, int nlongs) { + int nargs = nrefs + nints + nlongs; + if (topLevel) + VAR.cat.binding = catstr(ALL_RETURN_TYPES ? TYPES.length : rcat, nrefs, nints, nlongs); + VAR.R.binding = TYPES[rcat]; + VAR.Rc.binding = TCHARS[rcat]; + String[] Tv = new String[nargs]; + String[] av = new String[nargs]; + String[] Tvav = new String[nargs]; + String[] Ovav = new String[nargs]; + for (int i = 0; i < nargs; i++) { + int tcat = (i < nrefs) ? 0 : (i < nrefs + nints) ? 1 : 2; + Tv[i] = TYPES[tcat]; + av[i] = arg(i); + Tvav[i] = param(Tv[i], av[i]); + Ovav[i] = param("Object", av[i]); + } + VAR.Tv.binding = comma(Tv); + VAR.av.binding = comma(av); + VAR.Tvav.binding = comma(Tvav); + VAR.Ovav.binding = comma(Ovav); + } + static String arg(int i) { return "a"+i; } + static String param(String t, String a) { return t+" "+a; } + static String comma(String[] v) { return comma(v, ""); } + static String comma(String sep, String[] v) { + if (v.length == 0) return ""; + String res = sep+v[0]; + for (int i = 1; i < v.length; i++) res += ", "+v[i]; + return res; + } + static String transform(String string) { + for (VAR var : values()) + string = string.replaceAll(var.pattern, var.binding); + return string; + } + } + static String[] stringsIn(String[] strings, int beg, int end) { + return Arrays.copyOfRange(strings, beg, Math.min(end, strings.length)); + } + static String[] stringsBefore(String[] strings, int pos) { + return stringsIn(strings, 0, pos); + } + static String[] stringsAfter(String[] strings, int pos) { + return stringsIn(strings, pos, strings.length); + } + static int indexAfter(String[] strings, int pos, String tag) { + return Math.min(indexBefore(strings, pos, tag) + 1, strings.length); + } + static int indexBefore(String[] strings, int pos, String tag) { + for (int i = pos, end = strings.length; ; i++) { + if (i == end || strings[i].endsWith(tag)) return i; + } + } + static int MIN_ARITY, MAX_ARITY, MAX_RCAT, MAX_REFS, MAX_INTS, MAX_LONGS; + static boolean ALL_ARG_TYPES, ALL_RETURN_TYPES; + static HashSet<String> done = new HashSet<String>(); + public static void main(String... av) { + for (String[] template : TEMPLATES) { + int forLinesLimit = indexBefore(template, 0, "@each-cat@"); + String[] forLines = stringsBefore(template, forLinesLimit); + template = stringsAfter(template, forLinesLimit); + for (String forLine : forLines) + expandTemplate(forLine, template); + } + } + static void expandTemplate(String forLine, String[] template) { + String[] params = forLine.split("[^0-9]+"); + if (params[0].length() == 0) params = stringsAfter(params, 1); + System.out.println("//params="+Arrays.asList(params)); + int pcur = 0; + MIN_ARITY = Integer.valueOf(params[pcur++]); + MAX_ARITY = Integer.valueOf(params[pcur++]); + MAX_RCAT = Integer.valueOf(params[pcur++]); + MAX_REFS = Integer.valueOf(params[pcur++]); + MAX_INTS = Integer.valueOf(params[pcur++]); + MAX_LONGS = Integer.valueOf(params[pcur++]); + if (pcur != params.length) throw new RuntimeException("bad extra param: "+forLine); + if (MAX_RCAT >= TYPES.length) MAX_RCAT = TYPES.length - 1; + ALL_ARG_TYPES = (indexBefore(template, 0, "@each-Tv@") < template.length); + ALL_RETURN_TYPES = (indexBefore(template, 0, "@each-R@") < template.length); + for (int nargs = MIN_ARITY; nargs <= MAX_ARITY; nargs++) { + for (int rcat = 0; rcat <= MAX_RCAT; rcat++) { + expandTemplate(template, true, rcat, nargs, 0, 0); + if (ALL_ARG_TYPES) break; + expandTemplateForPrims(template, true, rcat, nargs, 1, 1); + if (ALL_RETURN_TYPES) break; + } + } + } + static String catstr(int rcat, int nrefs, int nints, int nlongs) { + int nargs = nrefs + nints + nlongs; + String cat = TCHARS[rcat] + nargs; + if (!ALL_ARG_TYPES) cat += (nints==0?"":"I"+nints)+(nlongs==0?"":"J"+nlongs); + return cat; + } + static void expandTemplateForPrims(String[] template, boolean topLevel, int rcat, int nargs, int minints, int minlongs) { + for (int isLong = 0; isLong <= 1; isLong++) { + for (int nprims = 1; nprims <= nargs; nprims++) { + int nrefs = nargs - nprims; + int nints = ((1-isLong) * nprims); + int nlongs = (isLong * nprims); + expandTemplate(template, topLevel, rcat, nrefs, nints, nlongs); + } + } + } + static void expandTemplate(String[] template, boolean topLevel, + int rcat, int nrefs, int nints, int nlongs) { + int nargs = nrefs + nints + nlongs; + if (nrefs > MAX_REFS || nints > MAX_INTS || nlongs > MAX_LONGS) return; + VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs); + if (topLevel && !done.add(VAR.cat.binding)) { + System.out.println(" //repeat "+VAR.cat.binding); + return; + } + for (int i = 0; i < template.length; i++) { + String line = template[i]; + if (line.endsWith("@each-cat@")) { + // ignore + } else if (line.endsWith("@each-R@")) { + int blockEnd = indexAfter(template, i, "@end-R@"); + String[] block = stringsIn(template, i+1, blockEnd-1); + for (int rcat1 = rcat; rcat1 <= MAX_RCAT; rcat1++) + expandTemplate(block, false, rcat1, nrefs, nints, nlongs); + VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs); + i = blockEnd-1; continue; + } else if (line.endsWith("@each-Tv@")) { + int blockEnd = indexAfter(template, i, "@end-Tv@"); + String[] block = stringsIn(template, i+1, blockEnd-1); + expandTemplate(block, false, rcat, nrefs, nints, nlongs); + expandTemplateForPrims(block, false, rcat, nargs, nints+1, nlongs+1); + VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs); + i = blockEnd-1; continue; + } else { + System.out.println(VAR.transform(line)); + } + } + } +} +//}}} */ +//params=[0, 3, 4, 99, 99, 99] + static class A0 extends Adapter { + protected A0(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A0(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A0 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A0(e, i, c, t); } + protected Object target() { return invoker.<Object>invoke(target); } + protected Object targetA0() { return target(); } + protected Object invoke_L() { return return_L(targetA0()); } + protected int invoke_I() { return return_I(targetA0()); } + protected long invoke_J() { return return_J(targetA0()); } + protected float invoke_F() { return return_F(targetA0()); } + protected double invoke_D() { return return_D(targetA0()); } + } + static class A1 extends Adapter { + protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A1(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A1(e, i, c, t); } + protected Object target(Object a0) { return invoker.<Object>invoke(target, a0); } + protected Object targetA1(Object a0) { return target(a0); } + protected Object targetA1(int a0) { return target(a0); } + protected Object targetA1(long a0) { return target(a0); } + protected Object invoke_L(Object a0) { return return_L(targetA1(a0)); } + protected int invoke_I(Object a0) { return return_I(targetA1(a0)); } + protected long invoke_J(Object a0) { return return_J(targetA1(a0)); } + protected float invoke_F(Object a0) { return return_F(targetA1(a0)); } + protected double invoke_D(Object a0) { return return_D(targetA1(a0)); } + protected Object invoke_L(int a0) { return return_L(targetA1(a0)); } + protected int invoke_I(int a0) { return return_I(targetA1(a0)); } + protected long invoke_J(int a0) { return return_J(targetA1(a0)); } + protected float invoke_F(int a0) { return return_F(targetA1(a0)); } + protected double invoke_D(int a0) { return return_D(targetA1(a0)); } + protected Object invoke_L(long a0) { return return_L(targetA1(a0)); } + protected int invoke_I(long a0) { return return_I(targetA1(a0)); } + protected long invoke_J(long a0) { return return_J(targetA1(a0)); } + protected float invoke_F(long a0) { return return_F(targetA1(a0)); } + protected double invoke_D(long a0) { return return_D(targetA1(a0)); } + } + static class A2 extends Adapter { + protected A2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A2(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A2(e, i, c, t); } + protected Object target(Object a0, Object a1) { return invoker.<Object>invoke(target, a0, a1); } + protected Object targetA2(Object a0, Object a1) { return target(a0, a1); } + protected Object targetA2(Object a0, int a1) { return target(a0, a1); } + protected Object targetA2(int a0, int a1) { return target(a0, a1); } + protected Object targetA2(Object a0, long a1) { return target(a0, a1); } + protected Object targetA2(long a0, long a1) { return target(a0, a1); } + protected Object invoke_L(Object a0, Object a1) { return return_L(targetA2(a0, a1)); } + protected int invoke_I(Object a0, Object a1) { return return_I(targetA2(a0, a1)); } + protected long invoke_J(Object a0, Object a1) { return return_J(targetA2(a0, a1)); } + protected float invoke_F(Object a0, Object a1) { return return_F(targetA2(a0, a1)); } + protected double invoke_D(Object a0, Object a1) { return return_D(targetA2(a0, a1)); } + protected Object invoke_L(Object a0, int a1) { return return_L(targetA2(a0, a1)); } + protected int invoke_I(Object a0, int a1) { return return_I(targetA2(a0, a1)); } + protected long invoke_J(Object a0, int a1) { return return_J(targetA2(a0, a1)); } + protected float invoke_F(Object a0, int a1) { return return_F(targetA2(a0, a1)); } + protected double invoke_D(Object a0, int a1) { return return_D(targetA2(a0, a1)); } + protected Object invoke_L(int a0, int a1) { return return_L(targetA2(a0, a1)); } + protected int invoke_I(int a0, int a1) { return return_I(targetA2(a0, a1)); } + protected long invoke_J(int a0, int a1) { return return_J(targetA2(a0, a1)); } + protected float invoke_F(int a0, int a1) { return return_F(targetA2(a0, a1)); } + protected double invoke_D(int a0, int a1) { return return_D(targetA2(a0, a1)); } + protected Object invoke_L(Object a0, long a1) { return return_L(targetA2(a0, a1)); } + protected int invoke_I(Object a0, long a1) { return return_I(targetA2(a0, a1)); } + protected long invoke_J(Object a0, long a1) { return return_J(targetA2(a0, a1)); } + protected float invoke_F(Object a0, long a1) { return return_F(targetA2(a0, a1)); } + protected double invoke_D(Object a0, long a1) { return return_D(targetA2(a0, a1)); } + protected Object invoke_L(long a0, long a1) { return return_L(targetA2(a0, a1)); } + protected int invoke_I(long a0, long a1) { return return_I(targetA2(a0, a1)); } + protected long invoke_J(long a0, long a1) { return return_J(targetA2(a0, a1)); } + protected float invoke_F(long a0, long a1) { return return_F(targetA2(a0, a1)); } + protected double invoke_D(long a0, long a1) { return return_D(targetA2(a0, a1)); } + } + static class A3 extends Adapter { + protected A3(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A3(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A3 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A3(e, i, c, t); } + protected Object target(Object a0, Object a1, Object a2) { return invoker.<Object>invoke(target, a0, a1, a2); } + protected Object targetA3(Object a0, Object a1, Object a2) { return target(a0, a1, a2); } + protected Object targetA3(Object a0, Object a1, int a2) { return target(a0, a1, a2); } + protected Object targetA3(Object a0, int a1, int a2) { return target(a0, a1, a2); } + protected Object targetA3(int a0, int a1, int a2) { return target(a0, a1, a2); } + protected Object targetA3(Object a0, Object a1, long a2) { return target(a0, a1, a2); } + protected Object targetA3(Object a0, long a1, long a2) { return target(a0, a1, a2); } + protected Object targetA3(long a0, long a1, long a2) { return target(a0, a1, a2); } + protected Object invoke_L(Object a0, Object a1, Object a2) { return return_L(targetA3(a0, a1, a2)); } + protected int invoke_I(Object a0, Object a1, Object a2) { return return_I(targetA3(a0, a1, a2)); } + protected long invoke_J(Object a0, Object a1, Object a2) { return return_J(targetA3(a0, a1, a2)); } + protected float invoke_F(Object a0, Object a1, Object a2) { return return_F(targetA3(a0, a1, a2)); } + protected double invoke_D(Object a0, Object a1, Object a2) { return return_D(targetA3(a0, a1, a2)); } + protected Object invoke_L(Object a0, Object a1, int a2) { return return_L(targetA3(a0, a1, a2)); } + protected int invoke_I(Object a0, Object a1, int a2) { return return_I(targetA3(a0, a1, a2)); } + protected long invoke_J(Object a0, Object a1, int a2) { return return_J(targetA3(a0, a1, a2)); } + protected float invoke_F(Object a0, Object a1, int a2) { return return_F(targetA3(a0, a1, a2)); } + protected double invoke_D(Object a0, Object a1, int a2) { return return_D(targetA3(a0, a1, a2)); } + protected Object invoke_L(Object a0, int a1, int a2) { return return_L(targetA3(a0, a1, a2)); } + protected int invoke_I(Object a0, int a1, int a2) { return return_I(targetA3(a0, a1, a2)); } + protected long invoke_J(Object a0, int a1, int a2) { return return_J(targetA3(a0, a1, a2)); } + protected float invoke_F(Object a0, int a1, int a2) { return return_F(targetA3(a0, a1, a2)); } + protected double invoke_D(Object a0, int a1, int a2) { return return_D(targetA3(a0, a1, a2)); } + protected Object invoke_L(int a0, int a1, int a2) { return return_L(targetA3(a0, a1, a2)); } + protected int invoke_I(int a0, int a1, int a2) { return return_I(targetA3(a0, a1, a2)); } + protected long invoke_J(int a0, int a1, int a2) { return return_J(targetA3(a0, a1, a2)); } + protected float invoke_F(int a0, int a1, int a2) { return return_F(targetA3(a0, a1, a2)); } + protected double invoke_D(int a0, int a1, int a2) { return return_D(targetA3(a0, a1, a2)); } + protected Object invoke_L(Object a0, Object a1, long a2) { return return_L(targetA3(a0, a1, a2)); } + protected int invoke_I(Object a0, Object a1, long a2) { return return_I(targetA3(a0, a1, a2)); } + protected long invoke_J(Object a0, Object a1, long a2) { return return_J(targetA3(a0, a1, a2)); } + protected float invoke_F(Object a0, Object a1, long a2) { return return_F(targetA3(a0, a1, a2)); } + protected double invoke_D(Object a0, Object a1, long a2) { return return_D(targetA3(a0, a1, a2)); } + protected Object invoke_L(Object a0, long a1, long a2) { return return_L(targetA3(a0, a1, a2)); } + protected int invoke_I(Object a0, long a1, long a2) { return return_I(targetA3(a0, a1, a2)); } + protected long invoke_J(Object a0, long a1, long a2) { return return_J(targetA3(a0, a1, a2)); } + protected float invoke_F(Object a0, long a1, long a2) { return return_F(targetA3(a0, a1, a2)); } + protected double invoke_D(Object a0, long a1, long a2) { return return_D(targetA3(a0, a1, a2)); } + protected Object invoke_L(long a0, long a1, long a2) { return return_L(targetA3(a0, a1, a2)); } + protected int invoke_I(long a0, long a1, long a2) { return return_I(targetA3(a0, a1, a2)); } + protected long invoke_J(long a0, long a1, long a2) { return return_J(targetA3(a0, a1, a2)); } + protected float invoke_F(long a0, long a1, long a2) { return return_F(targetA3(a0, a1, a2)); } + protected double invoke_D(long a0, long a1, long a2) { return return_D(targetA3(a0, a1, a2)); } + } +//params=[4, 5, 2, 99, 99, 99] + static class A4 extends Adapter { + protected A4(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A4(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A4 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A4(e, i, c, t); } + protected Object target(Object a0, Object a1, Object a2, Object a3) { return invoker.<Object>invoke(target, a0, a1, a2, a3); } + protected Object targetA4(Object a0, Object a1, Object a2, Object a3) { return target(a0, a1, a2, a3); } + protected Object targetA4(Object a0, Object a1, Object a2, int a3) { return target(a0, a1, a2, a3); } + protected Object targetA4(Object a0, Object a1, int a2, int a3) { return target(a0, a1, a2, a3); } + protected Object targetA4(Object a0, int a1, int a2, int a3) { return target(a0, a1, a2, a3); } + protected Object targetA4(int a0, int a1, int a2, int a3) { return target(a0, a1, a2, a3); } + protected Object targetA4(Object a0, Object a1, Object a2, long a3) { return target(a0, a1, a2, a3); } + protected Object targetA4(Object a0, Object a1, long a2, long a3) { return target(a0, a1, a2, a3); } + protected Object targetA4(Object a0, long a1, long a2, long a3) { return target(a0, a1, a2, a3); } + protected Object targetA4(long a0, long a1, long a2, long a3) { return target(a0, a1, a2, a3); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3) { return return_L(targetA4(a0, a1, a2, a3)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3) { return return_I(targetA4(a0, a1, a2, a3)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3) { return return_J(targetA4(a0, a1, a2, a3)); } + protected Object invoke_L(Object a0, Object a1, Object a2, int a3) { return return_L(targetA4(a0, a1, a2, a3)); } + protected int invoke_I(Object a0, Object a1, Object a2, int a3) { return return_I(targetA4(a0, a1, a2, a3)); } + protected long invoke_J(Object a0, Object a1, Object a2, int a3) { return return_J(targetA4(a0, a1, a2, a3)); } + protected Object invoke_L(Object a0, Object a1, int a2, int a3) { return return_L(targetA4(a0, a1, a2, a3)); } + protected int invoke_I(Object a0, Object a1, int a2, int a3) { return return_I(targetA4(a0, a1, a2, a3)); } + protected long invoke_J(Object a0, Object a1, int a2, int a3) { return return_J(targetA4(a0, a1, a2, a3)); } + protected Object invoke_L(Object a0, int a1, int a2, int a3) { return return_L(targetA4(a0, a1, a2, a3)); } + protected int invoke_I(Object a0, int a1, int a2, int a3) { return return_I(targetA4(a0, a1, a2, a3)); } + protected long invoke_J(Object a0, int a1, int a2, int a3) { return return_J(targetA4(a0, a1, a2, a3)); } + protected Object invoke_L(int a0, int a1, int a2, int a3) { return return_L(targetA4(a0, a1, a2, a3)); } + protected int invoke_I(int a0, int a1, int a2, int a3) { return return_I(targetA4(a0, a1, a2, a3)); } + protected long invoke_J(int a0, int a1, int a2, int a3) { return return_J(targetA4(a0, a1, a2, a3)); } + protected Object invoke_L(Object a0, Object a1, Object a2, long a3) { return return_L(targetA4(a0, a1, a2, a3)); } + protected int invoke_I(Object a0, Object a1, Object a2, long a3) { return return_I(targetA4(a0, a1, a2, a3)); } + protected long invoke_J(Object a0, Object a1, Object a2, long a3) { return return_J(targetA4(a0, a1, a2, a3)); } + protected Object invoke_L(Object a0, Object a1, long a2, long a3) { return return_L(targetA4(a0, a1, a2, a3)); } + protected int invoke_I(Object a0, Object a1, long a2, long a3) { return return_I(targetA4(a0, a1, a2, a3)); } + protected long invoke_J(Object a0, Object a1, long a2, long a3) { return return_J(targetA4(a0, a1, a2, a3)); } + protected Object invoke_L(Object a0, long a1, long a2, long a3) { return return_L(targetA4(a0, a1, a2, a3)); } + protected int invoke_I(Object a0, long a1, long a2, long a3) { return return_I(targetA4(a0, a1, a2, a3)); } + protected long invoke_J(Object a0, long a1, long a2, long a3) { return return_J(targetA4(a0, a1, a2, a3)); } + protected Object invoke_L(long a0, long a1, long a2, long a3) { return return_L(targetA4(a0, a1, a2, a3)); } + protected int invoke_I(long a0, long a1, long a2, long a3) { return return_I(targetA4(a0, a1, a2, a3)); } + protected long invoke_J(long a0, long a1, long a2, long a3) { return return_J(targetA4(a0, a1, a2, a3)); } + } + static class A5 extends Adapter { + protected A5(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A5(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A5 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A5(e, i, c, t); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4) { return invoker.<Object>invoke(target, a0, a1, a2, a3, a4); } + protected Object targetA5(Object a0, Object a1, Object a2, Object a3, Object a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(Object a0, Object a1, Object a2, Object a3, int a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(Object a0, Object a1, Object a2, int a3, int a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(Object a0, Object a1, int a2, int a3, int a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(Object a0, int a1, int a2, int a3, int a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(int a0, int a1, int a2, int a3, int a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(Object a0, Object a1, Object a2, Object a3, long a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(Object a0, Object a1, Object a2, long a3, long a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(Object a0, Object a1, long a2, long a3, long a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(Object a0, long a1, long a2, long a3, long a4) { return target(a0, a1, a2, a3, a4); } + protected Object targetA5(long a0, long a1, long a2, long a3, long a4) { return target(a0, a1, a2, a3, a4); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(Object a0, Object a1, Object a2, int a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(Object a0, Object a1, Object a2, int a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(Object a0, Object a1, Object a2, int a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(Object a0, Object a1, int a2, int a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(Object a0, Object a1, int a2, int a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(Object a0, Object a1, int a2, int a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(Object a0, int a1, int a2, int a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(Object a0, int a1, int a2, int a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(Object a0, int a1, int a2, int a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(int a0, int a1, int a2, int a3, int a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(int a0, int a1, int a2, int a3, int a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(int a0, int a1, int a2, int a3, int a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(Object a0, long a1, long a2, long a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(Object a0, long a1, long a2, long a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + protected Object invoke_L(long a0, long a1, long a2, long a3, long a4) { return return_L(targetA5(a0, a1, a2, a3, a4)); } + protected int invoke_I(long a0, long a1, long a2, long a3, long a4) { return return_I(targetA5(a0, a1, a2, a3, a4)); } + protected long invoke_J(long a0, long a1, long a2, long a3, long a4) { return return_J(targetA5(a0, a1, a2, a3, a4)); } + } +//params=[6, 10, 2, 99, 0, 99] + static class A6 extends Adapter { + protected A6(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A6(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A6 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A6(e, i, c, t); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5); } + protected Object targetA6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return target(a0, a1, a2, a3, a4, a5); } + protected Object targetA6(Object a0, Object a1, Object a2, Object a3, Object a4, long a5) { return target(a0, a1, a2, a3, a4, a5); } + protected Object targetA6(Object a0, Object a1, Object a2, Object a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); } + protected Object targetA6(Object a0, Object a1, Object a2, long a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); } + protected Object targetA6(Object a0, Object a1, long a2, long a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); } + protected Object targetA6(Object a0, long a1, long a2, long a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); } + protected Object targetA6(long a0, long a1, long a2, long a3, long a4, long a5) { return target(a0, a1, a2, a3, a4, a5); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); } + protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); } + protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); } + protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); } + protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); } + protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); } + protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); } + protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); } + protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); } + protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); } + protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5) { return return_L(targetA6(a0, a1, a2, a3, a4, a5)); } + protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5) { return return_I(targetA6(a0, a1, a2, a3, a4, a5)); } + protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5) { return return_J(targetA6(a0, a1, a2, a3, a4, a5)); } + } + static class A7 extends Adapter { + protected A7(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A7(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A7 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A7(e, i, c, t); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6); } + protected Object targetA7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return target(a0, a1, a2, a3, a4, a5, a6); } + protected Object targetA7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); } + protected Object targetA7(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); } + protected Object targetA7(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); } + protected Object targetA7(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); } + protected Object targetA7(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); } + protected Object targetA7(Object a0, long a1, long a2, long a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); } + protected Object targetA7(long a0, long a1, long a2, long a3, long a4, long a5, long a6) { return target(a0, a1, a2, a3, a4, a5, a6); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_L(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_I(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5, long a6) { return return_J(targetA7(a0, a1, a2, a3, a4, a5, a6)); } + } + static class A8 extends Adapter { + protected A8(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A8(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A8 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A8(e, i, c, t); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object targetA8(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object targetA8(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object targetA8(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object targetA8(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object targetA8(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return target(a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_L(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_I(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) { return return_J(targetA8(a0, a1, a2, a3, a4, a5, a6, a7)); } + } + static class A9 extends Adapter { + protected A9(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A9(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A9 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A9(e, i, c, t); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object targetA9(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_L(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_I(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { return return_J(targetA9(a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + } + static class A10 extends Adapter { + protected A10(MethodHandle entryPoint) { super(entryPoint); } // to build prototype + protected A10(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } + protected A10 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A10(e, i, c, t); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object targetA10(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, Object a1, Object a2, Object a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, Object a1, Object a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, Object a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(Object a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_L(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected int invoke_I(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_I(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected long invoke_J(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { return return_J(targetA10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,297 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.anon; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Anonymous class loader. Will load any valid classfile, producing + * a {@link Class} metaobject, without installing that class in the + * system dictionary. Therefore, {@link Class#forName(String)} will never + * produce a reference to an anonymous class. + * <p> + * The access permissions of the anonymous class are borrowed from + * a <em>host class</em>. The new class behaves as if it were an + * inner class of the host class. It can access the host's private + * members, if the creator of the class loader has permission to + * do so (or to create accessible reflective objects). + * <p> + * When the anonymous class is loaded, elements of its constant pool + * can be patched to new values. This provides a hook to pre-resolve + * named classes in the constant pool to other classes, including + * anonymous ones. Also, string constants can be pre-resolved to + * any reference. (The verifier treats non-string, non-class reference + * constants as plain objects.) + * <p> + * Why include the patching function? It makes some use cases much easier. + * Second, the constant pool needed some internal patching anyway, + * to anonymize the loaded class itself. Finally, if you are going + * to use this seriously, you'll want to build anonymous classes + * on top of pre-existing anonymous classes, and that requires patching. + * + * <p>%%% TO-DO: + * <ul> + * <li>needs better documentation</li> + * <li>needs more security work (for safe delegation)</li> + * <li>needs a clearer story about error processing</li> + * <li>patch member references also (use ';' as delimiter char)</li> + * <li>patch method references to (conforming) method handles</li> + * </ul> + * + * @author jrose + * @author Remi Forax + * @see <a href="http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm"> + * http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm</a> + */ + +public class AnonymousClassLoader { + final Class<?> hostClass; + + // Note: Do not refactor the calls to checkHostClass unless you + // also adjust this constant: + private static int CHC_CALLERS = 3; + + public AnonymousClassLoader() { + this.hostClass = checkHostClass(null); + } + public AnonymousClassLoader(Class<?> hostClass) { + this.hostClass = checkHostClass(hostClass); + } + + private static Class<?> getTopLevelClass(Class<?> clazz) { + for(Class<?> outer = clazz.getDeclaringClass(); outer != null; + outer = outer.getDeclaringClass()) { + clazz = outer; + } + return clazz; + } + + private static Class<?> checkHostClass(Class<?> hostClass) { + // called only from the constructor + // does a context-sensitive check on caller class + // CC[0..3] = {Reflection, this.checkHostClass, this.<init>, caller} + Class<?> caller = sun.reflect.Reflection.getCallerClass(CHC_CALLERS); + + if (caller == null) { + // called from the JVM directly + if (hostClass == null) + return AnonymousClassLoader.class; // anything central will do + return hostClass; + } + + if (hostClass == null) + hostClass = caller; // default value is caller itself + + // anonymous class will access hostClass on behalf of caller + Class<?> callee = hostClass; + + if (caller == callee) + // caller can always nominate itself to grant caller's own access rights + return hostClass; + + // normalize caller and callee to their top-level classes: + caller = getTopLevelClass(caller); + callee = getTopLevelClass(callee); + if (caller == callee) + return caller; + + ClassLoader callerCL = caller.getClassLoader(); + if (callerCL == null) { + // caller is trusted code, so accept the proposed hostClass + return hostClass; + } + + // %%% should do something with doPrivileged, because trusted + // code should have a way to execute on behalf of + // partially-trusted clients + + // Does the caller have the right to access the private + // members of the callee? If not, raise an error. + final int ACC_PRIVATE = 2; + try { + sun.reflect.Reflection.ensureMemberAccess(caller, callee, null, ACC_PRIVATE); + } catch (IllegalAccessException ee) { + throw new IllegalArgumentException(ee); + } + + return hostClass; + } + + public Class<?> loadClass(byte[] classFile) { + if (defineAnonymousClass == null) { + // no JVM support; try to fake an approximation + try { + return fakeLoadClass(new ConstantPoolParser(classFile).createPatch()); + } catch (InvalidConstantPoolFormatException ee) { + throw new IllegalArgumentException(ee); + } + } + return loadClass(classFile, null); + } + + public Class<?> loadClass(ConstantPoolPatch classPatch) { + if (defineAnonymousClass == null) { + // no JVM support; try to fake an approximation + return fakeLoadClass(classPatch); + } + Object[] patches = classPatch.patchArray; + // Convert class names (this late in the game) + // to use slash '/' instead of dot '.'. + // Java likes dots, but the JVM likes slashes. + for (int i = 0; i < patches.length; i++) { + Object value = patches[i]; + if (value != null) { + byte tag = classPatch.getTag(i); + switch (tag) { + case ConstantPoolVisitor.CONSTANT_Class: + if (value instanceof String) { + if (patches == classPatch.patchArray) + patches = patches.clone(); + patches[i] = ((String)value).replace('.', '/'); + } + break; + case ConstantPoolVisitor.CONSTANT_Fieldref: + case ConstantPoolVisitor.CONSTANT_Methodref: + case ConstantPoolVisitor.CONSTANT_InterfaceMethodref: + case ConstantPoolVisitor.CONSTANT_NameAndType: + // When/if the JVM supports these patches, + // we'll probably need to reformat them also. + // Meanwhile, let the class loader create the error. + break; + } + } + } + return loadClass(classPatch.outer.classFile, classPatch.patchArray); + } + + private Class<?> loadClass(byte[] classFile, Object[] patchArray) { + try { + return (Class<?>) + defineAnonymousClass.invoke(unsafe, + hostClass, classFile, patchArray); + } catch (Exception ex) { + throwReflectedException(ex); + throw new RuntimeException("error loading into "+hostClass, ex); + } + } + + private static void throwReflectedException(Exception ex) { + if (ex instanceof InvocationTargetException) { + Throwable tex = ((InvocationTargetException)ex).getTargetException(); + if (tex instanceof Error) + throw (Error) tex; + ex = (Exception) tex; + } + if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + } + + private Class<?> fakeLoadClass(ConstantPoolPatch classPatch) { + // Implementation: + // 1. Make up a new name nobody has used yet. + // 2. Inspect the tail-header of the class to find the this_class index. + // 3. Patch the CONSTANT_Class for this_class to the new name. + // 4. Add other CP entries required by (e.g.) string patches. + // 5. Flatten Class constants down to their names, making sure that + // the host class loader can pick them up again accurately. + // 6. Generate the edited class file bytes. + // + // Potential limitations: + // * The class won't be truly anonymous, and may interfere with others. + // * Flattened class constants might not work, because of loader issues. + // * Pseudo-string constants will not flatten down to real strings. + // * Method handles will (of course) fail to flatten to linkage strings. + if (true) throw new UnsupportedOperationException("NYI"); + Object[] cpArray; + try { + cpArray = classPatch.getOriginalCP(); + } catch (InvalidConstantPoolFormatException ex) { + throw new RuntimeException(ex); + } + int thisClassIndex = classPatch.getParser().getThisClassIndex(); + String thisClassName = (String) cpArray[thisClassIndex]; + synchronized (AnonymousClassLoader.class) { + thisClassName = thisClassName+"\\|"+(++fakeNameCounter); + } + classPatch.putUTF8(thisClassIndex, thisClassName); + byte[] classFile = null; + return unsafe.defineClass(null, classFile, 0, classFile.length, + hostClass.getClassLoader(), + hostClass.getProtectionDomain()); + } + private static int fakeNameCounter = 99999; + + // ignore two warnings on this line: + static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + // preceding line requires that this class be on the boot class path + + static private final Method defineAnonymousClass; + static { + Method dac = null; + Class<? extends sun.misc.Unsafe> unsafeClass = unsafe.getClass(); + try { + dac = unsafeClass.getMethod("defineAnonymousClass", + Class.class, + byte[].class, + Object[].class); + } catch (Exception ee) { + dac = null; + } + defineAnonymousClass = dac; + } + + private static void noJVMSupport() { + throw new UnsupportedOperationException("no JVM support for anonymous classes"); + } + + + private static native Class<?> loadClassInternal(Class<?> hostClass, + byte[] classFile, + Object[] patchArray); + + public static byte[] readClassFile(Class<?> templateClass) throws IOException { + String templateName = templateClass.getName(); + int lastDot = templateName.lastIndexOf('.'); + java.net.URL url = templateClass.getResource(templateName.substring(lastDot+1)+".class"); + java.net.URLConnection connection = url.openConnection(); + int contentLength = connection.getContentLength(); + if (contentLength < 0) + throw new IOException("invalid content length "+contentLength); + + byte[] classFile = new byte[contentLength]; + InputStream tcs = connection.getInputStream(); + for (int fill = 0, nr; fill < classFile.length; fill += nr) { + nr = tcs.read(classFile, fill, classFile.length - fill); + if (nr < 0) + throw new IOException("premature end of file"); + } + return classFile; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/anon/ConstantPoolParser.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,368 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.anon; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; + +import static sun.dyn.anon.ConstantPoolVisitor.*; + +/** A constant pool parser. + */ +public class ConstantPoolParser { + final byte[] classFile; + final byte[] tags; + final char[] firstHeader; // maghi, maglo, minor, major, cplen + + // these are filled in on first parse: + int endOffset; + char[] secondHeader; // flags, this_class, super_class, intlen + + // used to decode UTF8 array + private char[] charArray = new char[80]; + + /** Creates a constant pool parser. + * @param classFile an array of bytes containing a class. + * @throws InvalidConstantPoolFormatException if the header of the class has errors. + */ + public ConstantPoolParser(byte[] classFile) throws InvalidConstantPoolFormatException { + this.classFile = classFile; + this.firstHeader = parseHeader(classFile); + this.tags = new byte[firstHeader[4]]; + } + + /** Create a constant pool parser by loading the bytecodes of the + * class taken as argument. + * + * @param templateClass the class to parse. + * + * @throws IOException raised if an I/O occurs when loading + * the bytecode of the template class. + * @throws InvalidConstantPoolFormatException if the header of the class has errors. + * + * @see #ConstantPoolParser(byte[]) + * @see AnonymousClassLoader#readClassFile(Class) + */ + public ConstantPoolParser(Class<?> templateClass) throws IOException, InvalidConstantPoolFormatException { + this(AnonymousClassLoader.readClassFile(templateClass)); + } + + /** Creates an empty patch to patch the class file + * used by the current parser. + * @return a new class patch. + */ + public ConstantPoolPatch createPatch() { + return new ConstantPoolPatch(this); + } + + /** Report the tag of the indicated CP entry. + * @param index + * @return one of {@link ConstantPoolVisitor#CONSTANT_Utf8}, etc. + */ + public byte getTag(int index) { + getEndOffset(); // trigger an exception if we haven't parsed yet + return tags[index]; + } + + /** Report the length of the constant pool. */ + public int getLength() { + return firstHeader[4]; + } + + /** Report the offset, within the class file, of the start of the constant pool. */ + public int getStartOffset() { + return firstHeader.length * 2; + } + + /** Report the offset, within the class file, of the end of the constant pool. */ + public int getEndOffset() { + if (endOffset == 0) + throw new IllegalStateException("class file has not yet been parsed"); + return endOffset; + } + + /** Report the CP index of this class's own name. */ + public int getThisClassIndex() { + getEndOffset(); // provoke exception if not yet parsed + return secondHeader[1]; + } + + /** Report the total size of the class file. */ + public int getTailLength() { + return classFile.length - getEndOffset(); + } + + /** Write the head (header plus constant pool) + * of the class file to the indicated stream. + */ + public void writeHead(OutputStream out) throws IOException { + out.write(classFile, 0, getEndOffset()); + } + + /** Write the head (header plus constant pool) + * of the class file to the indicated stream, + * incorporating the non-null entries of the given array + * as patches. + */ + void writePatchedHead(OutputStream out, Object[] patchArray) { + // this will be useful to partially emulate the class loader on old JVMs + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** Write the tail (everything after the constant pool) + * of the class file to the indicated stream. + */ + public void writeTail(OutputStream out) throws IOException { + out.write(classFile, getEndOffset(), getTailLength()); + } + + private static char[] parseHeader(byte[] classFile) throws InvalidConstantPoolFormatException { + char[] result = new char[5]; + ByteBuffer buffer = ByteBuffer.wrap(classFile); + for (int i = 0; i < result.length; i++) + result[i] = (char) getUnsignedShort(buffer); + int magic = result[0] << 16 | result[1] << 0; + if (magic != 0xCAFEBABE) + throw new InvalidConstantPoolFormatException("invalid magic number "+magic); + // skip major, minor version + int len = result[4]; + if (len < 1) + throw new InvalidConstantPoolFormatException("constant pool length < 1"); + return result; + } + + /** Parse the constant pool of the class + * calling a method visit* each time a constant pool entry is parsed. + * + * The order of the calls to visit* is not guaranteed to be the same + * than the order of the constant pool entry in the bytecode array. + * + * @param visitor + * @throws InvalidConstantPoolFormatException + */ + public void parse(ConstantPoolVisitor visitor) throws InvalidConstantPoolFormatException { + ByteBuffer buffer = ByteBuffer.wrap(classFile); + buffer.position(getStartOffset()); //skip header + + Object[] values = new Object[getLength()]; + try { + parseConstantPool(buffer, values, visitor); + } catch(BufferUnderflowException e) { + throw new InvalidConstantPoolFormatException(e); + } + if (endOffset == 0) { + endOffset = buffer.position(); + secondHeader = new char[4]; + for (int i = 0; i < secondHeader.length; i++) { + secondHeader[i] = (char) getUnsignedShort(buffer); + } + } + resolveConstantPool(values, visitor); + } + + private char[] getCharArray(int utfLength) { + if (utfLength <= charArray.length) + return charArray; + return charArray = new char[utfLength]; + } + + private void parseConstantPool(ByteBuffer buffer, Object[] values, ConstantPoolVisitor visitor) throws InvalidConstantPoolFormatException { + for (int i = 1; i < tags.length; ) { + byte tag = (byte) getUnsignedByte(buffer); + assert(tags[i] == 0 || tags[i] == tag); + tags[i] = tag; + switch (tag) { + case CONSTANT_Utf8: + int utfLen = getUnsignedShort(buffer); + String value = getUTF8(buffer, utfLen, getCharArray(utfLen)); + visitor.visitUTF8(i, CONSTANT_Utf8, value); + tags[i] = tag; + values[i++] = value; + break; + case CONSTANT_Integer: + visitor.visitConstantValue(i, tag, buffer.getInt()); + i++; + break; + case CONSTANT_Float: + visitor.visitConstantValue(i, tag, buffer.getFloat()); + i++; + break; + case CONSTANT_Long: + visitor.visitConstantValue(i, tag, buffer.getLong()); + i+=2; + break; + case CONSTANT_Double: + visitor.visitConstantValue(i, tag, buffer.getDouble()); + i+=2; + break; + + case CONSTANT_Class: // fall through: + case CONSTANT_String: + tags[i] = tag; + values[i++] = new int[] { getUnsignedShort(buffer) }; + break; + + case CONSTANT_Fieldref: // fall through: + case CONSTANT_Methodref: // fall through: + case CONSTANT_InterfaceMethodref: // fall through: + case CONSTANT_NameAndType: + tags[i] = tag; + values[i++] = new int[] { getUnsignedShort(buffer), getUnsignedShort(buffer) }; + break; + default: + throw new AssertionError("invalid constant "+tag); + } + } + } + + private void resolveConstantPool(Object[] values, ConstantPoolVisitor visitor) { + // clean out the int[] values, which are temporary + for (int beg = 1, end = values.length-1, beg2, end2; + beg <= end; + beg = beg2, end = end2) { + beg2 = end; end2 = beg-1; + //System.out.println("CP resolve pass: "+beg+".."+end); + for (int i = beg; i <= end; i++) { + Object value = values[i]; + if (!(value instanceof int[])) + continue; + int[] array = (int[]) value; + byte tag = tags[i]; + switch (tag) { + case CONSTANT_String: + String stringBody = (String) values[array[0]]; + visitor.visitConstantString(i, tag, stringBody, array[0]); + values[i] = null; + break; + case CONSTANT_Class: { + String className = (String) values[array[0]]; + // use the external form favored by Class.forName: + className = className.replace('/', '.'); + visitor.visitConstantString(i, tag, className, array[0]); + values[i] = className; + break; + } + case CONSTANT_NameAndType: { + String memberName = (String) values[array[0]]; + String signature = (String) values[array[1]]; + visitor.visitDescriptor(i, tag, memberName, signature, + array[0], array[1]); + values[i] = new String[] {memberName, signature}; + break; + } + case CONSTANT_Fieldref: // fall through: + case CONSTANT_Methodref: // fall through: + case CONSTANT_InterfaceMethodref: { + Object className = values[array[0]]; + Object nameAndType = values[array[1]]; + if (!(className instanceof String) || + !(nameAndType instanceof String[])) { + // one more pass is needed + if (beg2 > i) beg2 = i; + if (end2 < i) end2 = i; + continue; + } + String[] nameAndTypeArray = (String[]) nameAndType; + visitor.visitMemberRef(i, tag, + (String)className, + nameAndTypeArray[0], + nameAndTypeArray[1], + array[0], array[1]); + values[i] = null; + } + break; + default: + continue; + } + } + } + } + + private static int getUnsignedByte(ByteBuffer buffer) { + return buffer.get() & 0xFF; + } + + private static int getUnsignedShort(ByteBuffer buffer) { + int b1 = getUnsignedByte(buffer); + int b2 = getUnsignedByte(buffer); + return (b1 << 8) + (b2 << 0); + } + + private static String getUTF8(ByteBuffer buffer, int utfLen, char[] charArray) throws InvalidConstantPoolFormatException { + int utfLimit = buffer.position() + utfLen; + int index = 0; + while (buffer.position() < utfLimit) { + int c = buffer.get() & 0xff; + if (c > 127) { + buffer.position(buffer.position() - 1); + return getUTF8Extended(buffer, utfLimit, charArray, index); + } + charArray[index++] = (char)c; + } + return new String(charArray, 0, index); + } + + private static String getUTF8Extended(ByteBuffer buffer, int utfLimit, char[] charArray, int index) throws InvalidConstantPoolFormatException { + int c, c2, c3; + while (buffer.position() < utfLimit) { + c = buffer.get() & 0xff; + switch (c >> 4) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + /* 0xxxxxxx*/ + charArray[index++] = (char)c; + break; + case 12: case 13: + /* 110x xxxx 10xx xxxx*/ + c2 = buffer.get(); + if ((c2 & 0xC0) != 0x80) + throw new InvalidConstantPoolFormatException( + "malformed input around byte " + buffer.position()); + charArray[index++] = (char)(((c & 0x1F) << 6) | + (c2 & 0x3F)); + break; + case 14: + /* 1110 xxxx 10xx xxxx 10xx xxxx */ + c2 = buffer.get(); + c3 = buffer.get(); + if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) + throw new InvalidConstantPoolFormatException( + "malformed input around byte " + (buffer.position())); + charArray[index++] = (char)(((c & 0x0F) << 12) | + ((c2 & 0x3F) << 6) | + ((c3 & 0x3F) << 0)); + break; + default: + /* 10xx xxxx, 1111 xxxx */ + throw new InvalidConstantPoolFormatException( + "malformed input around byte " + buffer.position()); + } + } + // The number of chars produced may be less than utflen + return new String(charArray, 0, index); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/anon/ConstantPoolPatch.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,503 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.anon; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Map; + +import static sun.dyn.anon.ConstantPoolVisitor.*; + +/** A class and its patched constant pool. + * + * This class allow to modify (patch) a constant pool + * by changing the value of its entry. + * Entry are referenced using index that can be get + * by parsing the constant pool using + * {@link ConstantPoolParser#parse(ConstantPoolVisitor)}. + * + * @see ConstantPoolVisitor + * @see ConstantPoolParser#createPatch() + */ +public class ConstantPoolPatch { + final ConstantPoolParser outer; + final Object[] patchArray; + + ConstantPoolPatch(ConstantPoolParser outer) { + this.outer = outer; + this.patchArray = new Object[outer.getLength()]; + } + + /** Create a {@link ConstantPoolParser} and + * a {@link ConstantPoolPatch} in one step. + * Equivalent to {@code new ConstantPoolParser(classFile).createPatch()}. + * + * @param classFile an array of bytes containing a class. + * @see #ConstantPoolParser(Class) + */ + public ConstantPoolPatch(byte[] classFile) throws InvalidConstantPoolFormatException { + this(new ConstantPoolParser(classFile)); + } + + /** Create a {@link ConstantPoolParser} and + * a {@link ConstantPoolPatch} in one step. + * Equivalent to {@code new ConstantPoolParser(templateClass).createPatch()}. + * + * @param templateClass the class to parse. + * @see #ConstantPoolParser(Class) + */ + public ConstantPoolPatch(Class<?> templateClass) throws IOException, InvalidConstantPoolFormatException { + this(new ConstantPoolParser(templateClass)); + } + + + /** Creates a patch from an existing patch. + * All changes are copied from that patch. + * @param patch a patch + * + * @see ConstantPoolParser#createPatch() + */ + public ConstantPoolPatch(ConstantPoolPatch patch) { + outer = patch.outer; + patchArray = patch.patchArray.clone(); + } + + /** Which parser built this patch? */ + public ConstantPoolParser getParser() { + return outer; + } + + /** Report the tag at the given index in the constant pool. */ + public byte getTag(int index) { + return outer.getTag(index); + } + + /** Report the current patch at the given index of the constant pool. + * Null means no patch will be made. + * To observe the unpatched entry at the given index, use + * {@link #getParser()}{@code .}@link ConstantPoolParser#parse(ConstantPoolVisitor)} + */ + public Object getPatch(int index) { + Object value = patchArray[index]; + if (value == null) return null; + switch (getTag(index)) { + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + if (value instanceof String) + value = stripSemis(2, (String) value); + break; + case CONSTANT_NameAndType: + if (value instanceof String) + value = stripSemis(1, (String) value); + break; + } + return value; + } + + /** Clear all patches. */ + public void clear() { + Arrays.fill(patchArray, null); + } + + /** Clear one patch. */ + public void clear(int index) { + patchArray[index] = null; + } + + /** Produce the patches as an array. */ + public Object[] getPatches() { + return patchArray.clone(); + } + + /** Produce the original constant pool as an array. */ + public Object[] getOriginalCP() throws InvalidConstantPoolFormatException { + return getOriginalCP(0, patchArray.length, -1); + } + + /** Walk the constant pool, applying patches using the given map. + * + * @param utf8Map Utf8 strings to modify, if encountered + * @param classMap Classes (or their names) to modify, if encountered + * @param valueMap Constant values to modify, if encountered + * @param deleteUsedEntries if true, delete map entries that are used + */ + public void putPatches(final Map<String,String> utf8Map, + final Map<String,Object> classMap, + final Map<Object,Object> valueMap, + boolean deleteUsedEntries) throws InvalidConstantPoolFormatException { + final HashSet<String> usedUtf8Keys; + final HashSet<String> usedClassKeys; + final HashSet<Object> usedValueKeys; + if (deleteUsedEntries) { + usedUtf8Keys = (utf8Map == null) ? null : new HashSet<String>(); + usedClassKeys = (classMap == null) ? null : new HashSet<String>(); + usedValueKeys = (valueMap == null) ? null : new HashSet<Object>(); + } else { + usedUtf8Keys = null; + usedClassKeys = null; + usedValueKeys = null; + } + + outer.parse(new ConstantPoolVisitor() { + + @Override + public void visitUTF8(int index, byte tag, String utf8) { + putUTF8(index, utf8Map.get(utf8)); + if (usedUtf8Keys != null) usedUtf8Keys.add(utf8); + } + + @Override + public void visitConstantValue(int index, byte tag, Object value) { + putConstantValue(index, tag, valueMap.get(value)); + if (usedValueKeys != null) usedValueKeys.add(value); + } + + @Override + public void visitConstantString(int index, byte tag, String name, int nameIndex) { + if (tag == CONSTANT_Class) { + putConstantValue(index, tag, classMap.get(name)); + if (usedClassKeys != null) usedClassKeys.add(name); + } else { + assert(tag == CONSTANT_String); + visitConstantValue(index, tag, name); + } + } + }); + if (usedUtf8Keys != null) utf8Map.keySet().removeAll(usedUtf8Keys); + if (usedClassKeys != null) classMap.keySet().removeAll(usedClassKeys); + if (usedValueKeys != null) valueMap.keySet().removeAll(usedValueKeys); + } + + Object[] getOriginalCP(final int startIndex, + final int endIndex, + final int tagMask) throws InvalidConstantPoolFormatException { + final Object[] cpArray = new Object[endIndex - startIndex]; + outer.parse(new ConstantPoolVisitor() { + + void show(int index, byte tag, Object value) { + if (index < startIndex || index >= endIndex) return; + if (((1 << tag) & tagMask) == 0) return; + cpArray[index - startIndex] = value; + } + + @Override + public void visitUTF8(int index, byte tag, String utf8) { + show(index, tag, utf8); + } + + @Override + public void visitConstantValue(int index, byte tag, Object value) { + assert(tag != CONSTANT_String); + show(index, tag, value); + } + + @Override + public void visitConstantString(int index, byte tag, + String value, int j) { + show(index, tag, value); + } + + @Override + public void visitMemberRef(int index, byte tag, + String className, String memberName, + String signature, + int j, int k) { + show(index, tag, new String[]{ className, memberName, signature }); + } + + @Override + public void visitDescriptor(int index, byte tag, + String memberName, String signature, + int j, int k) { + show(index, tag, new String[]{ memberName, signature }); + } + }); + return cpArray; + } + + /** Write the head (header plus constant pool) + * of the patched class file to the indicated stream. + */ + void writeHead(OutputStream out) throws IOException { + outer.writePatchedHead(out, patchArray); + } + + /** Write the tail (everything after the constant pool) + * of the patched class file to the indicated stream. + */ + void writeTail(OutputStream out) throws IOException { + outer.writeTail(out); + } + + private void checkConstantTag(byte tag, Object value) { + if (value == null) + throw new IllegalArgumentException( + "invalid null constant value"); + if (classForTag(tag) != value.getClass()) + throw new IllegalArgumentException( + "invalid constant value" + + (tag == CONSTANT_None ? "" + : " for tag "+tagName(tag)) + + " of class "+value.getClass()); + } + + private void checkTag(int index, byte putTag) { + byte tag = outer.tags[index]; + if (tag != putTag) + throw new IllegalArgumentException( + "invalid put operation" + + " for " + tagName(putTag) + + " at index " + index + " found " + tagName(tag)); + } + + private void checkTagMask(int index, int tagBitMask) { + byte tag = outer.tags[index]; + int tagBit = ((tag & 0x1F) == tag) ? (1 << tag) : 0; + if ((tagBit & tagBitMask) == 0) + throw new IllegalArgumentException( + "invalid put operation" + + " at index " + index + " found " + tagName(tag)); + } + + private static void checkMemberName(String memberName) { + if (memberName.indexOf(';') >= 0) + throw new IllegalArgumentException("memberName " + memberName + " contains a ';'"); + } + + /** Set the entry of the constant pool indexed by index to + * a new string. + * + * @param index an index to a constant pool entry containing a + * {@link ConstantPoolVisitor#CONSTANT_Utf8} value. + * @param utf8 a string + * + * @see ConstantPoolVisitor#visitUTF8(int, byte, String) + */ + public void putUTF8(int index, String utf8) { + if (utf8 == null) { clear(index); return; } + checkTag(index, CONSTANT_Utf8); + patchArray[index] = utf8; + } + + /** Set the entry of the constant pool indexed by index to + * a new value, depending on its dynamic type. + * + * @param index an index to a constant pool entry containing a + * one of the following structures: + * {@link ConstantPoolVisitor#CONSTANT_Integer}, + * {@link ConstantPoolVisitor#CONSTANT_Float}, + * {@link ConstantPoolVisitor#CONSTANT_Long}, + * {@link ConstantPoolVisitor#CONSTANT_Double}, + * {@link ConstantPoolVisitor#CONSTANT_String}, or + * {@link ConstantPoolVisitor#CONSTANT_Class} + * @param value a boxed int, float, long or double; or a string or class object + * @throws IllegalArgumentException if the type of the constant does not + * match the constant pool entry type, + * as reported by {@link #getTag(int)} + * + * @see #putConstantValue(int, byte, Object) + * @see ConstantPoolVisitor#visitConstantValue(int, byte, Object) + * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int) + */ + public void putConstantValue(int index, Object value) { + if (value == null) { clear(index); return; } + byte tag = tagForConstant(value.getClass()); + checkConstantTag(tag, value); + checkTag(index, tag); + patchArray[index] = value; + } + + /** Set the entry of the constant pool indexed by index to + * a new value. + * + * @param index an index to a constant pool entry matching the given tag + * @param tag one of the following values: + * {@link ConstantPoolVisitor#CONSTANT_Integer}, + * {@link ConstantPoolVisitor#CONSTANT_Float}, + * {@link ConstantPoolVisitor#CONSTANT_Long}, + * {@link ConstantPoolVisitor#CONSTANT_Double}, + * {@link ConstantPoolVisitor#CONSTANT_String}, or + * {@link ConstantPoolVisitor#CONSTANT_Class} + * @param value a boxed number, string, or class object + * @throws IllegalArgumentException if the type of the constant does not + * match the constant pool entry type, or if a class name contains + * '/' or ';' + * + * @see #putConstantValue(int, Object) + * @see ConstantPoolVisitor#visitConstantValue(int, byte, Object) + * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int) + */ + public void putConstantValue(int index, byte tag, Object value) { + if (value == null) { clear(index); return; } + checkTag(index, tag); + if (tag == CONSTANT_Class && value instanceof String) { + checkClassName((String) value); + } else if (tag == CONSTANT_String) { + // the JVM accepts any object as a patch for a string + } else { + // make sure the incoming value is the right type + checkConstantTag(tag, value); + } + checkTag(index, tag); + patchArray[index] = value; + } + + /** Set the entry of the constant pool indexed by index to + * a new {@link ConstantPoolVisitor#CONSTANT_NameAndType} value. + * + * @param index an index to a constant pool entry containing a + * {@link ConstantPoolVisitor#CONSTANT_NameAndType} value. + * @param memberName a memberName + * @param signature a signature + * @throws IllegalArgumentException if memberName contains the character ';' + * + * @see ConstantPoolVisitor#visitDescriptor(int, byte, String, String, int, int) + */ + public void putDescriptor(int index, String memberName, String signature) { + checkTag(index, CONSTANT_NameAndType); + checkMemberName(memberName); + patchArray[index] = addSemis(memberName, signature); + } + + /** Set the entry of the constant pool indexed by index to + * a new {@link ConstantPoolVisitor#CONSTANT_Fieldref}, + * {@link ConstantPoolVisitor#CONSTANT_Methodref}, or + * {@link ConstantPoolVisitor#CONSTANT_InterfaceMethodref} value. + * + * @param index an index to a constant pool entry containing a member reference + * @param className a class name + * @param memberName a field or method name + * @param signature a field or method signature + * @throws IllegalArgumentException if memberName contains the character ';' + * or signature is not a correct signature + * + * @see ConstantPoolVisitor#visitMemberRef(int, byte, String, String, String, int, int) + */ + public void putMemberRef(int index, byte tag, + String className, String memberName, String signature) { + checkTagMask(tag, CONSTANT_MemberRef_MASK); + checkTag(index, tag); + checkClassName(className); + checkMemberName(memberName); + if (signature.startsWith("(") == (tag == CONSTANT_Fieldref)) + throw new IllegalArgumentException("bad signature: "+signature); + patchArray[index] = addSemis(className, memberName, signature); + } + + static private final int CONSTANT_MemberRef_MASK = + CONSTANT_Fieldref + | CONSTANT_Methodref + | CONSTANT_InterfaceMethodref; + + private static final Map<Class<?>, Byte> CONSTANT_VALUE_CLASS_TAG + = new IdentityHashMap<Class<?>, Byte>(); + private static final Class[] CONSTANT_VALUE_CLASS = new Class[16]; + static { + Object[][] values = { + {Integer.class, CONSTANT_Integer}, + {Long.class, CONSTANT_Long}, + {Float.class, CONSTANT_Float}, + {Double.class, CONSTANT_Double}, + {String.class, CONSTANT_String}, + {Class.class, CONSTANT_Class} + }; + for (Object[] value : values) { + Class<?> cls = (Class<?>)value[0]; + Byte tag = (Byte) value[1]; + CONSTANT_VALUE_CLASS_TAG.put(cls, tag); + CONSTANT_VALUE_CLASS[(byte)tag] = cls; + } + } + + static Class<?> classForTag(byte tag) { + if ((tag & 0xFF) >= CONSTANT_VALUE_CLASS.length) + return null; + return CONSTANT_VALUE_CLASS[tag]; + } + + static byte tagForConstant(Class<?> cls) { + Byte tag = CONSTANT_VALUE_CLASS_TAG.get(cls); + return (tag == null) ? CONSTANT_None : (byte)tag; + } + + private static void checkClassName(String className) { + if (className.indexOf('/') >= 0 || className.indexOf(';') >= 0) + throw new IllegalArgumentException("invalid class name " + className); + } + + static String addSemis(String name, String... names) { + StringBuilder buf = new StringBuilder(name.length() * 5); + buf.append(name); + for (String name2 : names) { + buf.append(';').append(name2); + } + String res = buf.toString(); + assert(stripSemis(names.length, res)[0].equals(name)); + assert(stripSemis(names.length, res)[1].equals(names[0])); + assert(names.length == 1 || + stripSemis(names.length, res)[2].equals(names[1])); + return res; + } + + static String[] stripSemis(int count, String string) { + String[] res = new String[count+1]; + int pos = 0; + for (int i = 0; i < count; i++) { + int pos2 = string.indexOf(';', pos); + if (pos2 < 0) pos2 = string.length(); // yuck + res[i] = string.substring(pos, pos2); + pos = pos2; + } + res[count] = string.substring(pos); + return res; + } + + public String toString() { + StringBuilder buf = new StringBuilder(this.getClass().getName()); + buf.append("{"); + Object[] origCP = null; + for (int i = 0; i < patchArray.length; i++) { + if (patchArray[i] == null) continue; + if (origCP != null) { + buf.append(", "); + } else { + try { + origCP = getOriginalCP(); + } catch (InvalidConstantPoolFormatException ee) { + origCP = new Object[0]; + } + } + Object orig = (i < origCP.length) ? origCP[i] : "?"; + buf.append(orig).append("=").append(patchArray[i]); + } + buf.append("}"); + return buf.toString(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/anon/ConstantPoolVisitor.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,192 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.anon; + +/** + * A visitor called by {@link ConstantPoolParser#parse(ConstantPoolVisitor)} + * when a constant pool entry is parsed. + * <p> + * A visit* method is called when a constant pool entry is parsed. + * The first argument is always the constant pool index. + * The second argument is always the constant pool tag, + * even for methods like {@link #visitUTF8(int, byte, String)} which only apply to one tag. + * String arguments refer to Utf8 or NameAndType entries declared elsewhere, + * and are always accompanied by the indexes of those entries. + * <p> + * The order of the calls to the visit* methods is not necessarily related + * to the order of the entries in the constant pool. + * If one entry has a reference to another entry, the latter (lower-level) + * entry will be visited first. + * <p> + * The following table shows the relation between constant pool entry + * types and the corresponding visit* methods: + * + * <table border=1 cellpadding=5 summary="constant pool visitor methods"> + * <tr><th>Tag(s)</th><th>Method</th></tr> + * <tr> + * <td>{@link #CONSTANT_Utf8}</td> + * <td>{@link #visitUTF8(int, byte, String)}</td> + * </tr><tr> + * <td>{@link #CONSTANT_Integer}, {@link #CONSTANT_Float}, + * {@link #CONSTANT_Long}, {@link #CONSTANT_Double}</td> + * <td>{@link #visitConstantValue(int, byte, Object)}</td> + * </tr><tr> + * <td>{@link #CONSTANT_String}, {@link #CONSTANT_Class}</td> + * <td>{@link #visitConstantString(int, byte, String, int)}</td> + * </tr><tr> + * <td>{@link #CONSTANT_NameAndType}</td> + * <td>{@link #visitDescriptor(int, byte, String, String, int, int)}</td> + * </tr><tr> + * <td>{@link #CONSTANT_Fieldref}, + * {@link #CONSTANT_Methodref}, + * {@link #CONSTANT_InterfaceMethodref}</td> + * <td>{@link #visitMemberRef(int, byte, String, String, String, int, int)}</td> + * </tr> + * </table> + * + * @see ConstantPoolPatch + * @author Remi Forax + * @author jrose + */ +public class ConstantPoolVisitor { + /** Called each time an UTF8 constant pool entry is found. + * @param index the constant pool index + * @param tag always {@link #CONSTANT_Utf8} + * @param utf8 string encoded in modified UTF-8 format passed as a {@code String} + * + * @see ConstantPoolPatch#putUTF8(int, String) + */ + public void visitUTF8(int index, byte tag, String utf8) { + // do nothing + } + + /** Called for each constant pool entry that encodes an integer, + * a float, a long, or a double. + * Constant strings and classes are not managed by this method but + * by {@link #visitConstantString(int, byte, String, int)}. + * + * @param index the constant pool index + * @param tag one of {@link #CONSTANT_Integer}, + * {@link #CONSTANT_Float}, + * {@link #CONSTANT_Long}, + * or {@link #CONSTANT_Double} + * @param value encoded value + * + * @see ConstantPoolPatch#putConstantValue(int, Object) + */ + public void visitConstantValue(int index, byte tag, Object value) { + // do nothing + } + + /** Called for each constant pool entry that encodes a string or a class. + * @param index the constant pool index + * @param tag one of {@link #CONSTANT_String}, + * {@link #CONSTANT_Class}, + * @param name string body or class name (using dot separator) + * @param nameIndex the index of the Utf8 string for the name + * + * @see ConstantPoolPatch#putConstantValue(int, byte, Object) + */ + public void visitConstantString(int index, byte tag, + String name, int nameIndex) { + // do nothing + } + + /** Called for each constant pool entry that encodes a name and type. + * @param index the constant pool index + * @param tag always {@link #CONSTANT_NameAndType} + * @param memberName a field or method name + * @param signature the member signature + * @param memberNameIndex index of the Utf8 string for the member name + * @param signatureIndex index of the Utf8 string for the signature + * + * @see ConstantPoolPatch#putDescriptor(int, String, String) + */ + public void visitDescriptor(int index, byte tag, + String memberName, String signature, + int memberNameIndex, int signatureIndex) { + // do nothing + } + + /** Called for each constant pool entry that encodes a field or method. + * @param index the constant pool index + * @param tag one of {@link #CONSTANT_Fieldref}, + * or {@link #CONSTANT_Methodref}, + * or {@link #CONSTANT_InterfaceMethodref} + * @param className the class name (using dot separator) + * @param memberName name of the field or method + * @param signature the field or method signature + * @param classNameIndex index of the Utf8 string for the class name + * @param descriptorIndex index of the NameAndType descriptor constant + * + * @see ConstantPoolPatch#putMemberRef(int, byte, String, String, String) + */ + public void visitMemberRef(int index, byte tag, + String className, String memberName, String signature, + int classNameIndex, int descriptorIndex) { + // do nothing + } + + public static final byte + CONSTANT_None = 0, + CONSTANT_Utf8 = 1, + //CONSTANT_Unicode = 2, /* unused */ + CONSTANT_Integer = 3, + CONSTANT_Float = 4, + CONSTANT_Long = 5, + CONSTANT_Double = 6, + CONSTANT_Class = 7, + CONSTANT_String = 8, + CONSTANT_Fieldref = 9, + CONSTANT_Methodref = 10, + CONSTANT_InterfaceMethodref = 11, + CONSTANT_NameAndType = 12; + + private static String[] TAG_NAMES = { + "Empty", + "Utf8", + null, //"Unicode", + "Integer", + "Float", + "Long", + "Double", + "Class", + "String", + "Fieldref", + "Methodref", + "InterfaceMethodref", + "NameAndType" + }; + + public static String tagName(byte tag) { + String name = null; + if ((tag & 0xFF) < TAG_NAMES.length) + name = TAG_NAMES[tag]; + if (name == null) + name = "Unknown#"+(tag&0xFF); + return name; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/anon/InvalidConstantPoolFormatException.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.anon; + +/** Exception used when there is an error in the constant pool + * format. + */ +public class InvalidConstantPoolFormatException extends Exception { + private static final long serialVersionUID=-6103888330523770949L; + + public InvalidConstantPoolFormatException(String message,Throwable cause) { + super(message,cause); + } + + public InvalidConstantPoolFormatException(String message) { + super(message); + } + + public InvalidConstantPoolFormatException(Throwable cause) { + super(cause); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/empty/Empty.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.empty; + +/** + * An empty class in an empty package. + * Used as a proxy for unprivileged code, since making access checks + * against it will only succeed against public methods in public types. + * @author jrose + */ +public class Empty { + private Empty() { throw new InternalError(); } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/package-info.java Tue May 05 23:12:47 2009 -0700 @@ -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;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/util/BytecodeName.java Tue May 05 23:12:47 2009 -0700 @@ -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; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/util/BytecodeSignature.java Tue May 05 23:12:47 2009 -0700 @@ -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 Wrapper.forBasicType(c).primitiveType(); + } + } + + public static String unparse(Class<?> type) { + StringBuilder sb = new StringBuilder(); + unparseSig(type, sb); + return sb.toString(); + } + + public static String unparse(MethodType type) { + return unparseMethod(type.returnType(), type.parameterList()); + } + + public static String unparse(Object type) { + if (type instanceof Class<?>) + return unparse((Class<?>) type); + if (type instanceof MethodType) + return unparse((MethodType) type); + return (String) type; + } + + public static String unparseMethod(Class<?> rtype, List<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 = Wrapper.forBasicType(t).basicTypeChar(); + if (c != 'L') { + sb.append(c); + } else { + boolean lsemi = (!t.isArray()); + if (lsemi) sb.append('L'); + sb.append(t.getName().replace('.', '/')); + if (lsemi) sb.append(';'); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/util/ValueConversions.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,563 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.util; + +import java.dyn.*; +import java.dyn.MethodHandles.Lookup; +import java.util.EnumMap; +import sun.dyn.Access; +import sun.dyn.AdapterMethodHandle; +import sun.dyn.MethodHandleImpl; + +public class ValueConversions { + private static final Access IMPL_TOKEN = Access.getToken(); + private static final Lookup IMPL_LOOKUP = MethodHandleImpl.getLookup(IMPL_TOKEN); + + private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) { + EnumMap<Wrapper, MethodHandle>[] caches + = (EnumMap<Wrapper, MethodHandle>[]) new EnumMap[n]; // unchecked warning expected here + for (int i = 0; i < n; i++) + caches[i] = new EnumMap<Wrapper, MethodHandle>(Wrapper.class); + return caches; + } + + /// Converting references to values. + + static int unboxInteger(Object x) { + if (x == null) return 0; // never NPE + return ((Integer) x).intValue(); + } + + static byte unboxByte(Object x) { + if (x == null) return 0; // never NPE + return ((Byte) x).byteValue(); + } + + static short unboxShort(Object x) { + if (x == null) return 0; // never NPE + return ((Short) x).shortValue(); + } + + static boolean unboxBoolean(Object x) { + if (x == null) return false; // never NPE + return ((Boolean) x).booleanValue(); + } + + static char unboxCharacter(Object x) { + if (x == null) return 0; // never NPE + return ((Character) x).charValue(); + } + + static long unboxLong(Object x) { + if (x == null) return 0; // never NPE + return ((Long) x).longValue(); + } + + static float unboxFloat(Object x) { + if (x == null) return 0; // never NPE + return ((Float) x).floatValue(); + } + + static double unboxDouble(Object x) { + if (x == null) return 0; // never NPE + return ((Double) x).doubleValue(); + } + + /// Converting references to "raw" values. + /// A raw primitive value is always an int or long. + + static int unboxByteRaw(Object x) { + return unboxByte(x); + } + + static int unboxShortRaw(Object x) { + return unboxShort(x); + } + + static int unboxBooleanRaw(Object x) { + return unboxBoolean(x) ? 1 : 0; + } + + static int unboxCharacterRaw(Object x) { + return unboxCharacter(x); + } + + static int unboxFloatRaw(Object x) { + return Float.floatToIntBits(unboxFloat(x)); + } + + static long unboxDoubleRaw(Object x) { + return Double.doubleToRawLongBits(unboxDouble(x)); + } + + private static MethodType unboxType(Wrapper wrap, boolean raw) { + return MethodType.make(rawWrapper(wrap, raw).primitiveType(), wrap.wrapperType()); + } + + private static final EnumMap<Wrapper, MethodHandle>[] + UNBOX_CONVERSIONS = newWrapperCaches(4); + + private static MethodHandle unbox(Wrapper wrap, boolean exact, boolean raw) { + EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(exact?1:0)+(raw?2:0)]; + MethodHandle mh = cache.get(wrap); + if (mh != null) { + return mh; + } + // slow path + switch (wrap) { + case OBJECT: + mh = IDENTITY; break; + case VOID: + mh = raw ? ALWAYS_ZERO : IGNORE; break; + case INT: case LONG: + // these guys don't need separate raw channels + if (raw) mh = unbox(wrap, exact, false); + break; + } + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + // look up the method + String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : ""); + MethodType type = unboxType(wrap, raw); + if (!exact) + // actually, type is wrong; the Java method takes Object + mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase()); + else + mh = retype(type, unbox(wrap, !exact, raw)); + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + (raw ? " (raw)" : "")); + } + + public static MethodHandle unbox(Wrapper type, boolean exact) { + return unbox(type, exact, false); + } + + public static MethodHandle unboxRaw(Wrapper type, boolean exact) { + return unbox(type, exact, true); + } + + public static MethodHandle unbox(Class<?> type, boolean exact) { + return unbox(Wrapper.forPrimitiveType(type), exact, false); + } + + public static MethodHandle unboxRaw(Class<?> type, boolean exact) { + return unbox(Wrapper.forPrimitiveType(type), exact, true); + } + + /// Converting primitives to references + + static Integer boxInteger(int x) { + return x; + } + + static Byte boxByte(byte x) { + return x; + } + + static Short boxShort(short x) { + return x; + } + + static Boolean boxBoolean(boolean x) { + return x; + } + + static Character boxCharacter(char x) { + return x; + } + + static Long boxLong(long x) { + return x; + } + + static Float boxFloat(float x) { + return x; + } + + static Double boxDouble(double x) { + return x; + } + + /// Converting raw primitives to references + + static Byte boxByteRaw(int x) { + return boxByte((byte)x); + } + + static Short boxShortRaw(int x) { + return boxShort((short)x); + } + + static Boolean boxBooleanRaw(int x) { + return boxBoolean(x != 0); + } + + static Character boxCharacterRaw(int x) { + return boxCharacter((char)x); + } + + static Float boxFloatRaw(int x) { + return boxFloat(Float.intBitsToFloat(x)); + } + + static Double boxDoubleRaw(long x) { + return boxDouble(Double.longBitsToDouble(x)); + } + + // a raw void value is (arbitrarily) a garbage int + static Void boxVoidRaw(int x) { + return null; + } + + private static MethodType boxType(Wrapper wrap, boolean raw) { + // be exact, since return casts are hard to compose + Class<?> boxType = wrap.wrapperType(); + return MethodType.make(boxType, rawWrapper(wrap, raw).primitiveType()); + } + + private static Wrapper rawWrapper(Wrapper wrap, boolean raw) { + if (raw) return wrap.isDoubleWord() ? Wrapper.LONG : Wrapper.INT; + return wrap; + } + + private static final EnumMap<Wrapper, MethodHandle>[] + BOX_CONVERSIONS = newWrapperCaches(4); + + private static MethodHandle box(Wrapper wrap, boolean exact, boolean raw) { + EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)+(raw?2:0)]; + MethodHandle mh = cache.get(wrap); + if (mh != null) { + return mh; + } + // slow path + switch (wrap) { + case OBJECT: + mh = IDENTITY; break; + case VOID: + if (!raw) mh = ZERO_OBJECT; + break; + case INT: case LONG: + // these guys don't need separate raw channels + if (raw) mh = box(wrap, exact, false); + break; + } + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + // look up the method + String name = "box" + wrap.simpleName() + (raw ? "Raw" : ""); + MethodType type = boxType(wrap, raw); + if (exact) + mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); + else + mh = retype(type.erase(), box(wrap, !exact, raw)); + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + throw new IllegalArgumentException("cannot find box adapter for " + wrap + (raw ? " (raw)" : "")); + } + + public static MethodHandle box(Class<?> type, boolean exact) { + return box(Wrapper.forPrimitiveType(type), exact, false); + } + + public static MethodHandle boxRaw(Class<?> type, boolean exact) { + return box(Wrapper.forPrimitiveType(type), exact, true); + } + + public static MethodHandle box(Wrapper type, boolean exact) { + return box(type, exact, false); + } + + public static MethodHandle boxRaw(Wrapper type, boolean exact) { + return box(type, exact, true); + } + + /// Kludges for when raw values get accidentally boxed. + + static Byte reboxRawByte(Object x) { + if (x instanceof Byte) return (Byte) x; + return boxByteRaw(unboxInteger(x)); + } + + static Short reboxRawShort(Object x) { + if (x instanceof Short) return (Short) x; + return boxShortRaw(unboxInteger(x)); + } + + static Boolean reboxRawBoolean(Object x) { + if (x instanceof Boolean) return (Boolean) x; + return boxBooleanRaw(unboxInteger(x)); + } + + static Character reboxRawCharacter(Object x) { + if (x instanceof Character) return (Character) x; + return boxCharacterRaw(unboxInteger(x)); + } + + static Float reboxRawFloat(Object x) { + if (x instanceof Float) return (Float) x; + return boxFloatRaw(unboxInteger(x)); + } + + static Double reboxRawDouble(Object x) { + if (x instanceof Double) return (Double) x; + return boxDoubleRaw(unboxLong(x)); + } + + private static MethodType reboxType(Wrapper wrap) { + Class<?> boxType = wrap.wrapperType(); + return MethodType.make(boxType, Object.class); + } + + private static final EnumMap<Wrapper, MethodHandle>[] + REBOX_CONVERSIONS = newWrapperCaches(2); + + public static MethodHandle rebox(Wrapper wrap, boolean exact) { + EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[exact?1:0]; + MethodHandle mh = cache.get(wrap); + if (mh != null) { + return mh; + } + // slow path + switch (wrap) { + case OBJECT: + mh = IDENTITY; break; + case VOID: + throw new IllegalArgumentException("cannot rebox a void"); + case INT: case LONG: + mh = cast(wrap.wrapperType(), exact); + break; + } + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + // look up the method + String name = "reboxRaw" + wrap.simpleName(); + MethodType type = reboxType(wrap); + if (exact) + mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); + else + mh = retype(IDENTITY.type(), rebox(wrap, !exact)); + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + throw new IllegalArgumentException("cannot find rebox adapter for " + wrap); + } + + public static MethodHandle rebox(Class<?> type, boolean exact) { + return rebox(Wrapper.forPrimitiveType(type), exact); + } + + /// Width-changing conversions between int and long. + + static long widenInt(int x) { + return x; + } + + static int narrowLong(long x) { + return (int) x; + } + + /// Constant functions + + static void ignore(Object x) { + // no value to return; this is an unbox of null + return; + } + + static void empty() { + return; + } + + static Object zeroObject() { + return null; + } + + static int zeroInteger() { + return 0; + } + + static long zeroLong() { + return 0; + } + + static float zeroFloat() { + return 0; + } + + static double zeroDouble() { + return 0; + } + + private static final EnumMap<Wrapper, MethodHandle>[] + ZERO_CONSTANT_FUNCTIONS = newWrapperCaches(1); + + public static MethodHandle zeroConstantFunction(Wrapper wrap) { + EnumMap<Wrapper, MethodHandle> cache = ZERO_CONSTANT_FUNCTIONS[0]; + MethodHandle mh = cache.get(wrap); + if (mh != null) { + return mh; + } + // slow path + MethodType type = MethodType.make(wrap.primitiveType()); + switch (wrap) { + case VOID: + mh = EMPTY; + break; + case INT: case LONG: case FLOAT: case DOUBLE: + mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type); + break; + } + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + + // use the raw method + Wrapper rawWrap = wrap.rawPrimitive(); + if (rawWrap != wrap) { + mh = retype(type, zeroConstantFunction(rawWrap)); + } + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + throw new IllegalArgumentException("cannot find zero constant for " + wrap); + } + + /// Converting references to references. + + /** + * Value-killing function. + * @param x an arbitrary reference value + * @return a null + */ + static Object alwaysNull(Object x) { + return null; + } + + /** + * Value-killing function. + * @param x an arbitrary reference value + * @return a zero + */ + static int alwaysZero(Object x) { + return 0; + } + + /** + * Identity function. + * @param x an arbitrary reference value + * @return the same value x + */ + static <T> T identity(T x) { + return x; + } + + /** + * Identity function, with reference cast. + * @param t an arbitrary reference type + * @param x an arbitrary reference value + * @return the same value x + */ + static <T,U> T castReference(Class<? extends T> t, U x) { + return t.cast(x); + } + + private static final MethodHandle IDENTITY, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY; + static { + try { + MethodType idType = MethodType.makeGeneric(1); + MethodType castType = idType.insertParameterType(0, Class.class); + MethodType alwaysZeroType = idType.changeReturnType(int.class); + MethodType ignoreType = idType.changeReturnType(void.class); + MethodType zeroObjectType = MethodType.makeGeneric(0); + IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType); + //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType); + CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType); + ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType); + ALWAYS_ZERO = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysZero", alwaysZeroType); + ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType); + IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType); + EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterType(0)); + } catch (RuntimeException ex) { + throw ex; + } + } + + private static final EnumMap<Wrapper, MethodHandle> WRAPPER_CASTS + = new EnumMap<Wrapper, MethodHandle>(Wrapper.class); + + private static final EnumMap<Wrapper, MethodHandle> EXACT_WRAPPER_CASTS + = new EnumMap<Wrapper, MethodHandle>(Wrapper.class); + + /** Return a method that casts its sole argument (an Object) to the given type + * and returns it as the given type (if exact is true), or as plain Object (if erase is true). + */ + public static MethodHandle cast(Class<?> type, boolean exact) { + if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type); + MethodHandle mh = null; + Wrapper wrap = null; + EnumMap<Wrapper, MethodHandle> cache = null; + if (Wrapper.isWrapperType(type)) { + wrap = Wrapper.forWrapperType(type); + cache = (exact ? EXACT_WRAPPER_CASTS : WRAPPER_CASTS); + mh = cache.get(wrap); + if (mh != null) return mh; + } + if (VerifyType.isNullReferenceConversion(Object.class, type)) + mh = IDENTITY; + else if (VerifyType.isNullType(type)) + mh = ALWAYS_NULL; + else + mh = MethodHandles.insertArgument(CAST_REFERENCE, 0, type); + if (exact) { + MethodType xmt = MethodType.make(type, Object.class); + mh = AdapterMethodHandle.makeRawRetypeOnly(IMPL_TOKEN, xmt, mh); + } + if (cache != null) + cache.put(wrap, mh); + return mh; + } + + public static MethodHandle identity() { + return IDENTITY; + } + + private static MethodHandle retype(MethodType type, MethodHandle mh) { + return AdapterMethodHandle.makeRetypeOnly(IMPL_TOKEN, type, mh); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/util/VerifyAccess.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,169 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.util; + +import java.dyn.LinkagePermission; +import java.lang.reflect.Modifier; +import sun.dyn.Access; + +/** + * This class centralizes information about the JVM's linkage access control. + * @author jrose + */ +public class VerifyAccess { + + private VerifyAccess() { } // cannot instantiate + + /** + * Evaluate the JVM linkage rules for access to the given method on behalf of caller. + * Return non-null if and only if the given accessing class has at least partial + * privileges to invoke the given method. The return value {@code Object.class} + * denotes unlimited privileges. + * <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 lookupClass the class for which the access check is being made + * @return null if the method is not accessible, else a receiver type constraint, else {@code Object.class} + */ + public static Class<?> isAccessible(Class<?> defc, int mods, + boolean doDispatch, Class<?> lookupClass) { + if (!isAccessible(defc, lookupClass)) + return null; + Class<?> constraint = Object.class; + if (!doDispatch && !Modifier.isStatic(mods)) { + constraint = lookupClass; + } + if (Modifier.isPublic(mods)) + return constraint; + if (Modifier.isPrivate(mods)) + return isSamePackageMember(defc, lookupClass) ? constraint : null; + if (isSamePackage(defc, lookupClass)) + return constraint; + if (Modifier.isProtected(mods) && defc.isAssignableFrom(lookupClass)) + return constraint; + // else it is private or package scoped, and not close enough + return null; + } + + /** + * Evaluate the JVM linkage rules for access to the given class on behalf of caller. + */ + public static boolean isAccessible(Class<?> refc, Class<?> lookupClass) { + int mods = refc.getModifiers(); + if (Modifier.isPublic(mods)) + return true; + if (isSamePackage(lookupClass, refc)) + return true; + return false; + } + + /** + * Test if two classes have the same class loader and package qualifier. + * @param class1 + * @param class2 + * @return whether they are in the same package + */ + public static boolean isSamePackage(Class<?> class1, Class<?> class2) { + if (class1 == class2) + return true; + if (loadersAreRelated(class1.getClassLoader(), class2.getClassLoader())) + return false; + String name1 = class1.getName(), name2 = class2.getName(); + int dot = name1.lastIndexOf('.'); + if (dot != name2.lastIndexOf('.')) + return false; + for (int i = 0; i < dot; i++) { + if (name1.charAt(i) != name2.charAt(i)) + return false; + } + return true; + } + + /** + * Test if two classes are defined as part of the same package member (top-level class). + * If this is true, they can share private access with each other. + * @param class1 + * @param class2 + * @return whether they are identical or nested together + */ + public static boolean isSamePackageMember(Class<?> class1, Class<?> class2) { + if (class1 == class2) + return true; + if (!isSamePackage(class1, class2)) + return false; + if (getOutermostEnclosingClass(class1) != getOutermostEnclosingClass(class2)) + return false; + return true; + } + + private static Class<?> getOutermostEnclosingClass(Class<?> c) { + Class<?> pkgmem = c; + for (Class<?> enc = c; (enc = enc.getEnclosingClass()) != null; ) + pkgmem = enc; + return pkgmem; + } + + private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2) { + if (loader1 == loader2 || loader1 == null || loader2 == null) { + return true; + } + for (ClassLoader scan1 = loader1; + scan1 != null; scan1 = scan1.getParent()) { + if (scan1 == loader2) return true; + } + for (ClassLoader scan2 = loader2; + scan2 != null; scan2 = scan2.getParent()) { + if (scan2 == loader1) return true; + } + return false; + } + + /** + * Ensure the requesting class have privileges to perform invokedynamic + * linkage operations on subjectClass. True if requestingClass is + * Access.class (meaning the request originates from the JVM) or if the + * classes are in the same package and have consistent class loaders. + * (The subject class loader must be identical with or be a child of + * the requesting class loader.) + * @param requestingClass + * @param subjectClass + */ + public static void checkBootstrapPrivilege(Class requestingClass, Class subjectClass, + String permissionName) { + if (requestingClass == Access.class) return; + if (requestingClass == subjectClass) return; + SecurityManager security = System.getSecurityManager(); + if (security == null) return; // open season + if (isSamePackage(requestingClass, subjectClass)) return; + security.checkPermission(new LinkagePermission(permissionName, requestingClass)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/util/VerifyType.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,219 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.util; + +import java.dyn.MethodType; + +/** + * This class centralizes information about the JVM verifier + * and its requirements about type correctness. + * @author jrose + */ +public class VerifyType { + + private VerifyType() { } // cannot instantiate + + /** + * True if a value can be stacked as the source type and unstacked as the + * destination type, without violating the JVM's type consistency. + * + * @param call the type of a stacked value + * @param recv the type by which we'd like to treat it + * @return whether the retyping can be done without motion or reformatting + */ + public static boolean isNullConversion(Class<?> src, Class<?> dst) { + if (src == dst) return true; + // Verifier allows any interface to be treated as Object: + if (dst.isInterface()) dst = Object.class; + if (src.isInterface()) src = Object.class; + if (src == dst) return true; // check again + if (dst == void.class) return true; // drop any return value + if (isNullType(src)) return !dst.isPrimitive(); + if (!src.isPrimitive()) return dst.isAssignableFrom(src); + // Verifier allows an int to carry byte, short, char, or even boolean: + if (dst == int.class) return Wrapper.forPrimitiveType(src).isSubwordOrInt(); + return false; + } + + /** + * Specialization of isNullConversion to reference types. + + * @param call the type of a stacked value + * @param recv the reference type by which we'd like to treat it + * @return whether the retyping can be done without a cast + */ + public static boolean isNullReferenceConversion(Class<?> src, Class<?> dst) { + assert(!dst.isPrimitive()); + if (dst.isInterface()) return true; // verifier allows this + if (isNullType(src)) return true; + return dst.isAssignableFrom(src); + } + + /** + * Is the given type either java.lang.Void or java.lang.Null? + * These types serve as markers for bare nulls and therefore + * may be promoted to any type. This is secure, since + */ + public static boolean isNullType(Class<?> type) { + if (type == null) return false; + return type == NULL_CLASS_1 || type == NULL_CLASS_2; + } + private static final Class<?> NULL_CLASS_1, NULL_CLASS_2; + static { + Class<?> nullClass1 = null, nullClass2 = null; + try { + nullClass1 = Class.forName("java.lang.Null"); + } catch (ClassNotFoundException ex) { + // OK, we'll cope + } + NULL_CLASS_1 = nullClass1; + + // This one may also be used as a null type. + // TO DO: Decide if we really want to legitimize it here. + // Probably we do, unless java.lang.Null really makes it into Java 7 + nullClass2 = Void.class; + NULL_CLASS_2 = nullClass2; + } + + /** + * True if a method handle can receive a call under a slightly different + * method type, without moving or reformatting any stack elements. + * + * @param call the type of call being made + * @param recv the type of the method handle receiving the call + * @return whether the retyping can be done without motion or reformatting + */ + public static boolean isNullConversion(MethodType call, MethodType recv) { + if (call == recv) return true; + int len = call.parameterCount(); + if (len != recv.parameterCount()) return false; + for (int i = 0; i < len; i++) + if (!isNullConversion(call.parameterType(i), recv.parameterType(i))) + return false; + return isNullConversion(recv.returnType(), call.returnType()); + } + + //TO DO: isRawConversion + + /** + * Determine if the JVM verifier allows a value of type call to be + * passed to a formal parameter (or return variable) of type recv. + * Returns 1 if the verifier allows the types to match without conversion. + * Returns -1 if the types can be made to match by a JVM-supported adapter. + * Cases supported are: + * <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. + // This is a property of the implementation, which links + // return values via a register rather than via a stack push. + // This makes it possible to ignore cleanly. + return 1; + if (src == void.class) + return 0; // void-to-something? + if (!src.isPrimitive()) + // Cannot pass a reference to any primitive type (exc. void). + return 0; + Wrapper sw = Wrapper.forPrimitiveType(src); + Wrapper dw = Wrapper.forPrimitiveType(dst); + if (sw.isSubwordOrInt() && dw.isSubwordOrInt()) { + if (sw.bitWidth() >= dw.bitWidth()) + return -1; // truncation may be required + if (!dw.isSigned() && sw.isSigned()) + return -1; // sign elimination may be required + } + if (src == float.class || dst == float.class) { + if (src == double.class || dst == double.class) + return -1; // floating conversion may be required + else + return 0; // other primitive conversions NYI + } else { + // all fixed-point conversions are supported + return 0; + } + } else if (src.isPrimitive()) { + // Cannot pass a primitive to any reference type. + // (Maybe allow null.class?) + return 0; + } + + // Handle reference types in the rest of the block: + + // The verifier treats interfaces exactly like Object. + if (isNullReferenceConversion(src, dst)) + // pass any reference to object or an arb. interface + return 1; + // else it's a definite "maybe" (cast is required) + return -1; + } + + public static int canPassRaw(Class<?> src, Class<?> dst) { + if (dst.isPrimitive()) { + if (dst == void.class) + // As above, return anything to a caller expecting void. + return 1; + if (src == void.class) + // Special permission for raw conversions: allow a void + // to be captured as a garbage int. + // Caller promises that the actual value will be disregarded. + return dst == int.class ? 1 : 0; + if (!src.isPrimitive()) + return 0; + Wrapper sw = Wrapper.forPrimitiveType(src); + Wrapper dw = Wrapper.forPrimitiveType(dst); + if (sw.stackSlots() == dw.stackSlots()) + return 1; // can do a reinterpret-cast on a stacked primitive + if (sw.isSubwordOrInt() && dw == Wrapper.VOID) + return 1; // can drop an outgoing int value + return 0; + } else if (src.isPrimitive()) { + return 0; + } + + // Both references. + if (isNullReferenceConversion(src, dst)) + return 1; + return -1; + } + + public static boolean isSpreadArgType(Class<?> spreadArg) { + return spreadArg.isArray(); + } + public static Class<?> spreadArgElementType(Class<?> spreadArg, int i) { + return spreadArg.getComponentType(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/util/Wrapper.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,467 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.dyn.util; + +public enum Wrapper { + INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)), + LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)), + BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)), + SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)), + CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)), + BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)), + FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)), + DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)), + VOID(Void.class, void.class, 'V', null, Format.other(0)), + //NULL(Null.class, null.class, 'N', null, Format.other(1)), + OBJECT(Object.class, Object.class, 'L', null, Format.other(1)), + ; + + private final Class<?> wrapperType; + private final Class<?> primitiveType; + private final char basicTypeChar; + private final Object zero; + private final int format; + private final String simpleName; + + private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, int format) { + this.wrapperType = wtype; + this.primitiveType = ptype; + this.basicTypeChar = tchar; + this.zero = zero; + this.format = format; + this.simpleName = wtype.getSimpleName(); + } + + private static abstract class Format { + static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12; + static final int + SIGNED = (-1) << KIND_SHIFT, + UNSIGNED = 0 << KIND_SHIFT, + FLOATING = 1 << KIND_SHIFT; + static final int + SLOT_MASK = ((1<<(SIZE_SHIFT-SLOT_SHIFT))-1), + SIZE_MASK = ((1<<(KIND_SHIFT-SIZE_SHIFT))-1); + static int format(int kind, int size, int slots) { + assert(((kind >> KIND_SHIFT) << KIND_SHIFT) == kind); + assert((size & (size-1)) == 0); // power of two + assert((kind == SIGNED) ? (size > 0) : + (kind == UNSIGNED) ? (size > 0) : + (kind == FLOATING) ? (size == 32 || size == 64) : + false); + assert((slots == 2) ? (size == 64) : + (slots == 1) ? (size <= 32) : + false); + return kind | (size << SIZE_SHIFT) | (slots << SLOT_SHIFT); + } + static int + INT = SIGNED | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT), + BOOLEAN = UNSIGNED | (1 << SIZE_SHIFT) | (1 << SLOT_SHIFT), + FLOAT = FLOATING | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT), + VOID = UNSIGNED | (0 << SIZE_SHIFT) | (0 << SLOT_SHIFT), + NUM_MASK = (-1) << SIZE_SHIFT; + static int signed(int size) { return format(SIGNED, size, (size > 32 ? 2 : 1)); } + static int unsigned(int size) { return format(UNSIGNED, size, (size > 32 ? 2 : 1)); } + static int floating(int size) { return format(FLOATING, size, (size > 32 ? 2 : 1)); } + static int other(int slots) { return slots << SLOT_SHIFT; } + } + + /// format queries: + + /** How many bits are in the wrapped value? Returns 0 for OBJECT or VOID. */ + public int bitWidth() { return (format >> Format.SIZE_SHIFT) & Format.SIZE_MASK; } + /** How many JVM stack slots occupied by the wrapped value? Returns 0 for VOID. */ + public int stackSlots() { return (format >> Format.SLOT_SHIFT) & Format.SLOT_MASK; } + /** Does the wrapped value occupy a single JVM stack slot? */ + public boolean isSingleWord() { return (format & (1 << Format.SLOT_SHIFT)) != 0; } + /** Does the wrapped value occupy two JVM stack slots? */ + public boolean isDoubleWord() { return (format & (2 << Format.SLOT_SHIFT)) != 0; } + /** Is the wrapped type numeric (not void or object)? */ + public boolean isNumeric() { return (format & Format.NUM_MASK) != 0; } + /** Is the wrapped type a primitive other than float, double, or void? */ + public boolean isIntegral() { return isNumeric() && format < Format.FLOAT; } + /** Is the wrapped type one of int, boolean, byte, char, or short? */ + public boolean isSubwordOrInt() { return isIntegral() && isSingleWord(); } + /* Is the wrapped value a signed integral type (one of byte, short, int, or long)? */ + public boolean isSigned() { return format < Format.VOID; } + /* Is the wrapped value an unsigned integral type (one of boolean or char)? */ + public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; } + /** Is the wrapped type either float or double? */ + public boolean isFloating() { return format >= Format.FLOAT; } + + /** Produce a zero value for the given wrapper type. + * This will be a numeric zero for a number or character, + * false for a boolean, and null for a reference or void. + * The common thread is that this is what is contained + * in a default-initialized variable of the given primitive + * type. (For void, it is what a reflective method returns + * instead of no value at all.) + */ + public Object zero() { return zero; } + + /** Produce a zero value for the given wrapper type T. + * The optinoal argument must a type compatible with this wrapper. + * Equivalent to {@code this.cast(this.zero(), type)}. + */ + public <T> T zero(Class<T> type) { return cast(zero, type); } + +// /** Produce a wrapper for the given wrapper or primitive type. */ +// public static Wrapper valueOf(Class<?> type) { +// if (isPrimitiveType(type)) +// return forPrimitiveType(type); +// else +// return forWrapperType(type); +// } + + /** Return the wrapper that wraps values of the given type. + * The type may be {@code Object}, meaning the {@code OBJECT} wrapper. + * Otherwise, the type must be a primitive. + * @throws IllegalArgumentException for unexpected types + */ + public static Wrapper forPrimitiveType(Class<?> type) { + Wrapper w = FROM_PRIM[hashPrim(type)]; + if (w != null && w.primitiveType == type) { + return w; + } + if (type.isPrimitive()) + throw new InternalError(); // redo hash function + throw newIllegalArgumentException("not primitive: "+type); + } + + /** Return the wrapper that wraps values into the given wrapper type. + * If it is {@code Object} or an interface, return {@code OBJECT}. + * Otherwise, it must be a wrapper type. + * The type must not be a primitive type. + * @throws IllegalArgumentException for unexpected types + */ + public static Wrapper forWrapperType(Class<?> type) { + Wrapper w = findWrapperType(type); + if (w != null) return w; + for (Wrapper x : values()) + if (w.wrapperType == type) + throw new InternalError(); // redo hash function + throw newIllegalArgumentException("not wrapper: "+type); + } + + static Wrapper findWrapperType(Class<?> type) { + Wrapper w = FROM_WRAP[hashWrap(type)]; + if (w != null && w.wrapperType == type) { + return w; + } + if (type.isInterface()) + return OBJECT; + return null; + } + + /** Return the wrapper that corresponds to the given bytecode + * signature character. Return {@code OBJECT} for the character 'L'. + * @throws IllegalArgumentException for any non-signature character or {@code '['}. + */ + public static Wrapper forBasicType(char type) { + Wrapper w = FROM_CHAR[hashChar(type)]; + if (w != null && w.basicTypeChar == type) { + return w; + } + for (Wrapper x : values()) + if (w.basicTypeChar == type) + throw new InternalError(); // redo hash function + throw newIllegalArgumentException("not basic type char: "+type); + } + + /** Return the wrapper for the given type, if it is + * a primitive type, else return {@code OBJECT}. + */ + public static Wrapper forBasicType(Class<?> type) { + if (type.isPrimitive()) + return forPrimitiveType(type); + return OBJECT; // any reference, including wrappers or arrays + } + + // Note on perfect hashes: + // for signature chars c, do (c + (c >> 1)) % 16 + // for primitive type names n, do (n[0] + n[2]) % 16 + // The type name hash works for both primitive and wrapper names. + // You can add "java/lang/Object" to the primitive names. + // But you add the wrapper name Object, use (n[2] + (3*n[1])) % 16. + private static final Wrapper[] FROM_PRIM = new Wrapper[16]; + private static final Wrapper[] FROM_WRAP = new Wrapper[16]; + private static final Wrapper[] FROM_CHAR = new Wrapper[16]; + private static int hashPrim(Class<?> x) { + String xn = x.getName(); + if (xn.length() < 3) return 0; + return (xn.charAt(0) + xn.charAt(2)) % 16; + } + private static int hashWrap(Class<?> x) { + String xn = x.getName(); + final int offset = 10; assert(offset == "java.lang.".length()); + if (xn.length() < offset+3) return 0; + return (3*xn.charAt(offset+1) + xn.charAt(offset+2)) % 16; + } + private static int hashChar(char x) { + return (x + (x >> 1)) % 16; + } + static { + for (Wrapper w : values()) { + int pi = hashPrim(w.primitiveType); + int wi = hashWrap(w.wrapperType); + int ci = hashChar(w.basicTypeChar); + assert(FROM_PRIM[pi] == null); + assert(FROM_WRAP[wi] == null); + assert(FROM_CHAR[ci] == null); + FROM_PRIM[pi] = w; + FROM_WRAP[wi] = w; + FROM_CHAR[ci] = w; + } + //assert(jdk.sun.dyn.util.WrapperTest.test(false)); + } + + /** What is the primitive type wrapped by this wrapper? */ + public Class<?> primitiveType() { return primitiveType; } + + /** What is the wrapper type for this wrapper? */ + public Class<?> wrapperType() { return wrapperType; } + + /** What is the wrapper type for this wrapper? + * The example type must be the wrapper type, + * or the corresponding primitive type. + * The resulting class type has the same type parameter. + */ + public <T> Class<T> wrapperType(Class<T> exampleType) { + if (exampleType == wrapperType) { + return exampleType; + } else if (exampleType == primitiveType || + wrapperType == Object.class || + exampleType.isInterface()) { + return forceType(wrapperType, exampleType); + } + throw new ClassCastException(exampleType + " not <:" + wrapperType); + } + + /** If {@code type} is a primitive type, return the corresponding + * wrapper type, else return {@code type} unchanged. + */ + public static <T> Class<T> asWrapperType(Class<T> type) { + if (type.isPrimitive()) { + return forPrimitiveType(type).wrapperType(type); + } + return type; + } + + /** If {@code type} is a wrapper type, return the corresponding + * primitive type, else return {@code type} unchanged. + */ + public static <T> Class<T> asPrimitiveType(Class<T> type) { + Wrapper w = findWrapperType(type); + if (w != null) { + return forceType(w.primitiveType(), type); + } + return type; + } + + /** Query: Is the given type a wrapper, such as {@code Integer} or {@code Void}? */ + public static boolean isWrapperType(Class<?> type) { + return findWrapperType(type) != null; + } + + /** Query: Is the given type a primitive, such as {@code int} or {@code void}? */ + public static boolean isPrimitiveType(Class<?> type) { + return type.isPrimitive(); + } + + /** What is the bytecode signature character for this wrapper's + * primitive type? + */ + public char basicTypeChar() { return basicTypeChar; } + + /** What is the simple name of the wrapper type? + */ + public String simpleName() { return simpleName; } + +// /** Wrap a value in the given type, which may be either a primitive or wrapper type. +// * Performs standard primitive conversions, including truncation and float conversions. +// */ +// public static <T> T wrap(Object x, Class<T> type) { +// return Wrapper.valueOf(type).cast(x, type); +// } + + /** Cast a wrapped value to the given type, which may be either a primitive or wrapper type. + * Performs standard primitive conversions, including truncation and float conversions. + * The given type must be compatible with this wrapper. That is, it must either + * be the wrapper type (or a subtype, in the case of {@code OBJECT} or else + * it must be the wrapper's primitive type. + * @throws ClassCastException if the given type is not compatible with this wrapper + */ + public <T> T cast(Object x, Class<T> type) { + Class<T> wtype = wrapperType(type); + if (wtype.isInstance(x)) + return wtype.cast(x); + return wtype.cast(wrap(x)); + } + + /** Cast a reference type to another reference type. + * If the target type is an interface, perform no runtime check. + * (This loophole is safe, and is allowed by the JVM verifier.) + * If the target type is a primitive, change it to a wrapper. + */ + static <T> Class<T> forceType(Class<?> type, Class<T> exampleType) { + assert(type == exampleType || + type == asWrapperType(exampleType) || + type == Object.class && exampleType.isInterface()); + Class<T> result = (Class<T>) type; // unchecked warning is expected here + return result; + } + + /** Wrap a value in this wrapper's type. + * Performs standard primitive conversions, including truncation and float conversions. + * Performs returns the unchanged reference for {@code OBJECT}. + * Returns null for {@code VOID}. + * Returns a zero value for a null input. + * @throws ClassCastException if this wrapper is numeric and the operand + * is not a number, character, boolean, or null + */ + public Object wrap(Object x) { + // do non-numeric wrappers first + switch (basicTypeChar) { + case 'L': return x; + case 'V': return null; + } + Number xn = numberValue(x); + switch (basicTypeChar) { + case 'I': return Integer.valueOf(xn.intValue()); + case 'J': return Long.valueOf(xn.longValue()); + case 'F': return Float.valueOf(xn.floatValue()); + case 'D': return Double.valueOf(xn.doubleValue()); + case 'S': return Short.valueOf((short) xn.intValue()); + case 'B': return Byte.valueOf((byte) xn.intValue()); + case 'C': return Character.valueOf((char) xn.intValue()); + case 'Z': return Boolean.valueOf(boolValue(xn.longValue())); + } + throw new InternalError("bad wrapper"); + } + + /** Wrap a value (an int or smaller value) in this wrapper's type. + * Performs standard primitive conversions, including truncation and float conversions. + * Produces an {@code Integer} for {@code OBJECT}, although the exact type + * of the operand is not known. + * Returns null for {@code VOID}. + */ + public Object wrap(int x) { + if (basicTypeChar == 'L') return (Integer)x; + switch (basicTypeChar) { + case 'L': throw newIllegalArgumentException("cannot wrap to object type"); + case 'V': return null; + case 'I': return Integer.valueOf((int)x); + case 'J': return Long.valueOf(x); + case 'F': return Float.valueOf(x); + case 'D': return Double.valueOf(x); + case 'S': return Short.valueOf((short) x); + case 'B': return Byte.valueOf((byte) x); + case 'C': return Character.valueOf((char) x); + case 'Z': return Boolean.valueOf(boolValue(x)); + } + throw new InternalError("bad wrapper"); + } + + /** Wrap a value (a long or smaller value) in this wrapper's type. + * Does not perform floating point conversion. + * Produces a {@code Long} for {@code OBJECT}, although the exact type + * of the operand is not known. + * Returns null for {@code VOID}. + */ + public Object wrapRaw(long x) { + switch (basicTypeChar) { + case 'F': return Float.valueOf(Float.intBitsToFloat((int)x)); + case 'D': return Double.valueOf(Double.longBitsToDouble(x)); + case 'L': // same as 'J': + case 'J': return (Long) x; + } + // Other wrapping operations are just the same, given that the + // operand is already promoted to an int. + return wrap((int)x); + } + + /** Produce bitwise value which encodes the given wrapped value. + * Does not perform floating point conversion. + * Returns zero for {@code VOID}. + */ + public long unwrapRaw(Object x) { + switch (basicTypeChar) { + case 'F': return Float.floatToRawIntBits((Float) x); + case 'D': return Double.doubleToRawLongBits((Double) x); + + case 'L': throw newIllegalArgumentException("cannot unwrap from sobject type"); + case 'V': return 0; + case 'I': return (int)(Integer) x; + case 'J': return (long)(Long) x; + case 'S': return (short)(Short) x; + case 'B': return (byte)(Byte) x; + case 'C': return (char)(Character) x; + case 'Z': return (boolean)(Boolean) x ? 1 : 0; + } + throw new InternalError("bad wrapper"); + } + + /** Report what primitive type holds this guy's raw value. */ + public Class<?> rawPrimitiveType() { + return rawPrimitive().primitiveType(); + } + + /** Report, as a wrapper, what primitive type holds this guy's raw value. + * Returns self for INT, LONG, OBJECT; returns LONG for DOUBLE, + * else returns INT. + */ + public Wrapper rawPrimitive() { + switch (basicTypeChar) { + case 'S': case 'B': + case 'C': case 'Z': + case 'V': + case 'F': + return INT; + case 'D': + return LONG; + } + return this; + } + + private static Number numberValue(Object x) { + if (x instanceof Number) return (Number)x; + if (x instanceof Character) return (int)(Character)x; + if (x instanceof Boolean) return (Boolean)x ? 1 : 0; + // Remaining allowed case of void: Must be a null reference. + return (Number)x; + } + + private static boolean boolValue(long bits) { + //bits &= 1; // simple 31-bit zero extension + return (bits != 0); + } + + private static RuntimeException newIllegalArgumentException(String message, Object x) { + return newIllegalArgumentException(message + x); + } + private static RuntimeException newIllegalArgumentException(String message) { + return new IllegalArgumentException(message); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/util/package-info.java Tue May 05 23:12:47 2009 -0700 @@ -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;
--- a/src/share/classes/sun/misc/JavaLangAccess.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/sun/misc/JavaLangAccess.java Tue May 05 23:12:47 2009 -0700 @@ -55,6 +55,22 @@ /** Set thread's blocker field. */ void blockedOn(Thread t, Interruptible b); - /** register shutdown hook */ - void registerShutdownHook(int slot, Runnable r); + /** + * Registers a shutdown hook. + * + * It is expected that this method with registerShutdownInProgress=true + * is only used to register DeleteOnExitHook since the first file + * may be added to the delete on exit list by the application shutdown + * hooks. + * + * @params slot the slot in the shutdown hook array, whose element + * will be invoked in order during shutdown + * @params registerShutdownInProgress true to allow the hook + * to be registered even if the shutdown is in progress. + * @params hook the hook to be registered + * + * @throw IllegalStateException if shutdown is in progress and + * the slot is not valid to register. + */ + void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook); }
--- a/src/share/classes/sun/misc/Unsafe.java Tue May 05 09:09:24 2009 -0700 +++ b/src/share/classes/sun/misc/Unsafe.java Tue May 05 23:12:47 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -811,6 +811,25 @@ public native Class defineClass(String name, byte[] b, int off, int len); + /** + * Define a class but do not make it known to the class loader or system dictionary. + * <p> + * For each CP entry, the corresponding CP patch must either be null or have + * the a format that matches its tag: + * <ul> + * <li>Integer, Long, Float, Double: the corresponding wrapper object type from java.lang + * <li>Utf8: a string (must have suitable syntax if used as signature or name) + * <li>Class: any java.lang.Class object + * <li>String: any object (not just a java.lang.String) + * <li>InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments + * </ul> + * @params hostClass context for linkage, access control, protection domain, and class loader + * @params data bytes of a class file + * @params cpPatches where non-null entries exist, they replace corresponding CP entries in data + */ + public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches); + + /** Allocate an instance but do not run any constructor. Initializes the class if it has not yet been. */ public native Object allocateInstance(Class cls)
--- a/src/share/javavm/export/classfile_constants.h Tue May 05 09:09:24 2009 -0700 +++ b/src/share/javavm/export/classfile_constants.h Tue May 05 23:12:47 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2004-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -306,7 +306,7 @@ 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 @@ 3, /* invokespecial */ \ 3, /* invokestatic */ \ 5, /* invokeinterface */ \ - 0, /* xxxunusedxxx */ \ + 5, /* invokedynamic */ \ 3, /* new */ \ 2, /* newarray */ \ 3, /* anewarray */ \
--- a/src/share/native/common/check_code.c Tue May 05 09:09:24 2009 -0700 +++ b/src/share/native/common/check_code.c Tue May 05 23:12:47 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1223,16 +1223,20 @@ case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial: case JVM_OPC_invokestatic: + case JVM_OPC_invokedynamic: case JVM_OPC_invokeinterface: { /* Make sure the constant pool item is the right type. */ int key = (code[offset + 1] << 8) + code[offset + 2]; const char *methodname; jclass cb = context->class; fullinfo_type clazz_info; - int is_constructor, is_internal; + int is_constructor, is_internal, is_invokedynamic; int kind = (opcode == JVM_OPC_invokeinterface ? 1 << JVM_CONSTANT_InterfaceMethodref + : opcode == JVM_OPC_invokedynamic + ? 1 << JVM_CONSTANT_NameAndType : 1 << JVM_CONSTANT_Methodref); + is_invokedynamic = opcode == JVM_OPC_invokedynamic; /* Make sure the constant pool item is the right type. */ verify_constant_pool_type(context, key, kind); methodname = JVM_GetCPMethodNameUTF(env, cb, key); @@ -1241,8 +1245,11 @@ is_internal = methodname[0] == '<'; pop_and_free(context); - clazz_info = cp_index_to_class_fullinfo(context, key, - JVM_CONSTANT_Methodref); + if (is_invokedynamic) + clazz_info = context->object_info; // anything will do + else + clazz_info = cp_index_to_class_fullinfo(context, key, + JVM_CONSTANT_Methodref); this_idata->operand.i = key; this_idata->operand2.fi = clazz_info; if (is_constructor) { @@ -1304,6 +1311,11 @@ "Fourth operand byte of invokeinterface must be zero"); } pop_and_free(context); + } else if (opcode == JVM_OPC_invokedynamic) { + if (code[offset + 3] != 0 || code[offset + 4] != 0) { + CCerror(context, + "Third and fourth operand bytes of invokedynamic must be zero"); + } } else if (opcode == JVM_OPC_invokevirtual || opcode == JVM_OPC_invokespecial) set_protected(context, inumber, key, opcode); @@ -1990,6 +2002,7 @@ 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 +2018,8 @@ print_formatted_methodname(context, operand); } #endif - if (opcode != JVM_OPC_invokestatic) + if (opcode != JVM_OPC_invokestatic && + opcode != JVM_OPC_invokedynamic) /* First, push the object */ *ip++ = (opcode == JVM_OPC_invokeinit ? '@' : 'A'); for (p = signature + 1; *p != JVM_SIGNATURE_ENDFUNC; ) { @@ -2290,6 +2304,7 @@ case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial: case JVM_OPC_invokeinit: + case JVM_OPC_invokedynamic: case JVM_OPC_invokeinterface: case JVM_OPC_invokestatic: { int operand = this_idata->operand.i; const char *signature = @@ -2299,7 +2314,8 @@ int item; const char *p; check_and_push(context, signature, VM_STRING_UTF); - if (opcode == JVM_OPC_invokestatic) { + if (opcode == JVM_OPC_invokestatic || + opcode == JVM_OPC_invokedynamic) { item = 0; } else if (opcode == JVM_OPC_invokeinit) { fullinfo_type init_type = this_idata->operand2.fi; @@ -2680,6 +2696,7 @@ 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;
--- a/src/share/native/common/opcodes.in_out Tue May 05 09:09:24 2009 -0700 +++ b/src/share/native/common/opcodes.in_out Tue May 05 23:12:47 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -210,7 +210,7 @@ {"?", "?"}, /* invokespecial */ {"?", "?"}, /* invokestatic */ {"?", "?"}, /* invokeinterface */ - {"?", "?"}, /* xxxunusedxxx */ + {"?", "?"}, /* invokedynamic */ {"", "A"}, /* new */ {"I", "A"}, /* newarray */ {"I", "A"}, /* anewarray */
--- a/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java Tue May 05 09:09:24 2009 -0700 +++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java Tue May 05 23:12:47 2009 -0700 @@ -475,49 +475,40 @@ // get an OVERLAPPED structure (from the cache or allocate) overlapped = ioCache.add(result); - // synchronize on result to allow this thread handle the case - // where the read completes immediately. - synchronized (result) { - int n = read0(handle, numBufs, readBufferArray, overlapped); - if (n == IOStatus.UNAVAILABLE) { - // I/O is pending - pending = true; - return; + // initiate read + int n = read0(handle, numBufs, readBufferArray, overlapped); + if (n == IOStatus.UNAVAILABLE) { + // I/O is pending + pending = true; + return; + } + if (n == IOStatus.EOF) { + // input shutdown + enableReading(); + if (scatteringRead) { + result.setResult((V)Long.valueOf(-1L)); + } else { + result.setResult((V)Integer.valueOf(-1)); } - // read completed immediately: - // 1. update buffer position - // 2. reset read flag - // 3. release waiters - if (n == 0) { - n = -1; - } else { - updateBuffers(n); - } - enableReading(); - - if (scatteringRead) { - result.setResult((V)Long.valueOf(n)); - } else { - result.setResult((V)Integer.valueOf(n)); - } + } else { + throw new InternalError("Read completed immediately"); } } catch (Throwable x) { - // failed to initiate read: - // 1. reset read flag - // 2. free resources - // 3. release waiters + // failed to initiate read + // reset read flag before releasing waiters enableReading(); - if (overlapped != 0L) - ioCache.remove(overlapped); if (x instanceof ClosedChannelException) x = new AsynchronousCloseException(); if (!(x instanceof IOException)) x = new IOException(x); result.setFailure(x); } finally { - if (prepared && !pending) { - // return direct buffer(s) to cache if substituted - releaseBuffers(); + // release resources if I/O not pending + if (!pending) { + if (overlapped != 0L) + ioCache.remove(overlapped); + if (prepared) + releaseBuffers(); } end(); } @@ -721,7 +712,6 @@ @Override @SuppressWarnings("unchecked") public void run() { - int n = -1; long overlapped = 0L; boolean prepared = false; boolean pending = false; @@ -736,56 +726,34 @@ // get an OVERLAPPED structure (from the cache or allocate) overlapped = ioCache.add(result); - - // synchronize on result to allow this thread handle the case - // where the read completes immediately. - synchronized (result) { - n = write0(handle, numBufs, writeBufferArray, overlapped); - if (n == IOStatus.UNAVAILABLE) { - // I/O is pending - pending = true; - return; - } - - enableWriting(); - - if (n == IOStatus.EOF) { - // special case for shutdown output - shutdown = true; - throw new ClosedChannelException(); - } - - // write completed immediately: - // 1. enable writing - // 2. update buffer position - // 3. release waiters - updateBuffers(n); - - // result is a Long or Integer - if (gatheringWrite) { - result.setResult((V)Long.valueOf(n)); - } else { - result.setResult((V)Integer.valueOf(n)); - } + int n = write0(handle, numBufs, writeBufferArray, overlapped); + if (n == IOStatus.UNAVAILABLE) { + // I/O is pending + pending = true; + return; } + if (n == IOStatus.EOF) { + // special case for shutdown output + shutdown = true; + throw new ClosedChannelException(); + } + // write completed immediately + throw new InternalError("Write completed immediately"); } catch (Throwable x) { + // write failed. Enable writing before releasing waiters. enableWriting(); - - // failed to initiate read: if (!shutdown && (x instanceof ClosedChannelException)) x = new AsynchronousCloseException(); if (!(x instanceof IOException)) x = new IOException(x); result.setFailure(x); - - // release resources - if (overlapped != 0L) - ioCache.remove(overlapped); - } finally { - if (prepared && !pending) { - // return direct buffer(s) to cache if substituted - releaseBuffers(); + // release resources if I/O not pending + if (!pending) { + if (overlapped != 0L) + ioCache.remove(overlapped); + if (prepared) + releaseBuffers(); } end(); }
--- a/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c Tue May 05 09:09:24 2009 -0700 +++ b/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c Tue May 05 23:12:47 2009 -0700 @@ -157,14 +157,13 @@ WSABUF* lpWsaBuf = (WSABUF*) jlong_to_ptr(address); OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov); BOOL res; - DWORD nread = 0; DWORD flags = 0; ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED)); res = WSARecv(s, lpWsaBuf, (DWORD)count, - &nread, + NULL, &flags, lpOverlapped, NULL); @@ -175,17 +174,12 @@ return IOS_UNAVAILABLE; } if (error == WSAESHUTDOWN) { - return 0; // input shutdown + return IOS_EOF; // input shutdown } JNU_ThrowIOExceptionWithLastError(env, "WSARecv failed"); return IOS_THROWN; } - if (nread == 0) { - // Handle graceful close or bytes not yet available cases - // via completion port notification. - return IOS_UNAVAILABLE; - } - return (jint)nread; + return IOS_UNAVAILABLE; } JNIEXPORT jint JNICALL @@ -196,13 +190,12 @@ WSABUF* lpWsaBuf = (WSABUF*) jlong_to_ptr(address); OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov); BOOL res; - DWORD nwritten; ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED)); res = WSASend(s, lpWsaBuf, (DWORD)count, - &nwritten, + NULL, 0, lpOverlapped, NULL); @@ -218,5 +211,5 @@ JNU_ThrowIOExceptionWithLastError(env, "WSASend failed"); return IOS_THROWN; } - return (jint)nwritten; + return IOS_UNAVAILABLE; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/Runtime/shutdown/ShutdownHooks.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,69 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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. + */ + +/* + * @bug 6829503 + * @summary 1) Test Console and DeleteOnExitHook can be initialized + * while shutdown is in progress + * 2) Test if files that are added by the application shutdown + * hook are deleted on exit during shutdown + */ +import java.io.*; +public class ShutdownHooks { + private static File file; + public static void main(String[] args) throws Exception { + if (args.length != 2) { + throw new IllegalArgumentException("Usage: ShutdownHooks <dir> <filename>"); + } + + // Add a shutdown hook + Runtime.getRuntime().addShutdownHook(new Cleaner()); + + File dir = new File(args[0]); + file = new File(dir, args[1]); + // write to file + System.out.println("writing to "+ file); + PrintWriter pw = new PrintWriter(file); + pw.println("Shutdown begins"); + pw.close(); + } + + public static class Cleaner extends Thread { + public void run() { + // register the Console's shutdown hook while the application + // shutdown hook is running + Console cons = System.console(); + // register the DeleteOnExitHook while the application + // shutdown hook is running + file.deleteOnExit(); + try { + PrintWriter pw = new PrintWriter(file); + pw.println("file is being deleted"); + pw.close(); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/Runtime/shutdown/ShutdownHooks.sh Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,57 @@ +#!/bin/sh + +# +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# 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. +# + + +# @test +# @bug 6829503 +# @summary 1) Test Console and DeleteOnExitHook can be initialized +# while shutdown is in progress +# 2) Test if files that are added by the application shutdown +# hook are deleted on exit during shutdown +# +# @build ShutdownHooks +# @run shell ShutdownHooks.sh + +if [ "${TESTJAVA}" = "" ] +then + echo "TESTJAVA not set. Test cannot execute. Failed." + exit 1 +fi + +FILENAME=fileToBeDeleted +rm -f ${TESTCLASSES}/${FILENAME} + +# create the file to be deleted on exit +echo "testing shutdown" > ${TESTCLASSES}/${FILENAME} + +${TESTJAVA}/bin/java ${TESTVMOPTS} -classpath ${TESTCLASSES} ShutdownHooks ${TESTCLASSES} $FILENAME +if [ $? != 0 ] ; then + echo "Test Failed"; exit 1 +fi + +if [ -f ${TESTCLASSES}/${FILENAME} ]; then + echo "Test Failed: ${TESTCLASSES}/${FILENAME} not deleted"; exit 2 +fi +echo "ShutdownHooks test passed.";
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/nio/channels/AsynchronousSocketChannel/StressLoopback.java Tue May 05 23:12:47 2009 -0700 @@ -0,0 +1,183 @@ +/* + * 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. + * + * 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. + */ + +/* @test + * @bug 6834246 + * @summary Stress test connections through the loopback interface + */ + +import java.nio.ByteBuffer; +import java.net.*; +import java.nio.channels.*; +import java.util.Random; +import java.io.IOException; + +public class StressLoopback { + static final Random rand = new Random(); + + public static void main(String[] args) throws Exception { + // setup listener + AsynchronousServerSocketChannel listener = + AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + int port =((InetSocketAddress)(listener.getLocalAddress())).getPort(); + InetAddress lh = InetAddress.getLocalHost(); + SocketAddress remote = new InetSocketAddress(lh, port); + + // create sources and sinks + int count = 2 + rand.nextInt(9); + Source[] source = new Source[count]; + Sink[] sink = new Sink[count]; + for (int i=0; i<count; i++) { + AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + ch.connect(remote).get(); + source[i] = new Source(ch); + sink[i] = new Sink(listener.accept().get()); + } + + // start the sinks and sources + for (int i=0; i<count; i++) { + sink[i].start(); + source[i].start(); + } + + // let the test run for a while + Thread.sleep(20*1000); + + // wait until everyone is done + boolean failed = false; + long total = 0L; + for (int i=0; i<count; i++) { + long nwrote = source[i].finish(); + long nread = sink[i].finish(); + if (nread != nwrote) + failed = true; + System.out.format("%d -> %d (%s)\n", + nwrote, nread, (failed) ? "FAIL" : "PASS"); + total += nwrote; + } + if (failed) + throw new RuntimeException("Test failed - see log for details"); + System.out.format("Total sent %d MB\n", total / (1024L * 1024L)); + } + + /** + * Writes bytes to a channel until "done". When done the channel is closed. + */ + static class Source { + private final AsynchronousByteChannel channel; + private final ByteBuffer sentBuffer; + private volatile long bytesSent; + private volatile boolean finished; + + Source(AsynchronousByteChannel channel) { + this.channel = channel; + int size = 1024 + rand.nextInt(10000); + this.sentBuffer = (rand.nextBoolean()) ? + ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size); + } + + void start() { + sentBuffer.position(0); + sentBuffer.limit(sentBuffer.capacity()); + channel.write(sentBuffer, null, new CompletionHandler<Integer,Void> () { + public void completed(Integer nwrote, Void att) { + bytesSent += nwrote; + if (finished) { + closeUnchecked(channel); + } else { + sentBuffer.position(0); + sentBuffer.limit(sentBuffer.capacity()); + channel.write(sentBuffer, null, this); + } + } + public void failed(Throwable exc, Void att) { + exc.printStackTrace(); + closeUnchecked(channel); + } + public void cancelled(Void att) { + } + }); + } + + long finish() { + finished = true; + waitUntilClosed(channel); + return bytesSent; + } + } + + /** + * Read bytes from a channel until EOF is received. + */ + static class Sink { + private final AsynchronousByteChannel channel; + private final ByteBuffer readBuffer; + private volatile long bytesRead; + + Sink(AsynchronousByteChannel channel) { + this.channel = channel; + int size = 1024 + rand.nextInt(10000); + this.readBuffer = (rand.nextBoolean()) ? + ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size); + } + + void start() { + channel.read(readBuffer, null, new CompletionHandler<Integer,Void> () { + public void completed(Integer nread, Void att) { + if (nread < 0) { + closeUnchecked(channel); + } else { + bytesRead += nread; + readBuffer.clear(); + channel.read(readBuffer, null, this); + } + } + public void failed(Throwable exc, Void att) { + exc.printStackTrace(); + closeUnchecked(channel); + } + public void cancelled(Void att) { + } + }); + } + + long finish() { + waitUntilClosed(channel); + return bytesRead; + } + } + + static void waitUntilClosed(Channel c) { + while (c.isOpen()) { + try { + Thread.sleep(100); + } catch (InterruptedException ignore) { } + } + } + + static void closeUnchecked(Channel c) { + try { + c.close(); + } catch (IOException ignore) { } + } +}