This commit is contained in:
Tim Bell 2009-05-05 23:12:47 -07:00
commit 8f52d15f97
62 changed files with 12000 additions and 149 deletions

@ -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 @@ EXCLUDE_PKGS = \
# 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.* \

@ -1,5 +1,5 @@
#
# Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
# Copyright 1995-2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -39,7 +39,7 @@ SUBDIRS += hpi version jvm redist verify fdlibm java sun_nio jli main zip
# Others
# Note: java_crw_demo java_hprof_demo are demos but must be delivered built in sdk
SUBDIRS += security npt java_crw_demo java_hprof_demo \
math awt util text applet net nio \
math awt util text applet net nio dyn \
sql rmi jar beans logging management instrument

@ -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

@ -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+"]";
}
}

@ -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
}

@ -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);
}
}

@ -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);
}
}

@ -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;
}
}

@ -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());
}
}

@ -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;
}
}

File diff suppressed because it is too large Load Diff

@ -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);
}
}

@ -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);
}
}

@ -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);
}
}

@ -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);
}
}

@ -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;

@ -503,20 +503,25 @@ public final class Console implements Flushable
// 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() {

@ -34,23 +34,31 @@ import java.io.File;
*/
class DeleteOnExitHook {
static {
sun.misc.SharedSecrets.getJavaLangAccess()
.registerShutdownHook(2 /* Shutdown hook invocation order */,
new Runnable() {
public void run() {
runHooks();
}
});
}
private static LinkedHashSet<String> files = new LinkedHashSet<String>();
static {
// 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 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);
}

@ -35,17 +35,26 @@ import java.util.*;
*/
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() {}

@ -53,6 +53,9 @@ class Shutdown {
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 @@ class Shutdown {
}
/* 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 @@ class Shutdown {
/* 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) {

@ -1171,8 +1171,8 @@ public final class System {
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);
}
});
}

@ -154,7 +154,7 @@ class ZipFile implements ZipConstants {
* @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 @@ class ZipFile implements ZipConstants {
*
* @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 @@ class ZipFile implements ZipConstants {
* 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

@ -84,7 +84,7 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
* @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

@ -108,7 +108,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
*
* @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

@ -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");
}
}

@ -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
}

@ -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() + "]";
}
}

@ -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;
}
}

@ -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);
}
}

@ -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)); }
}
// */
}

@ -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);
// }
}

@ -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)); }
}
}

@ -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;
}
}

@ -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));
// }
}

@ -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));
}
}

@ -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();
}
}

@ -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;
}
}

File diff suppressed because it is too large Load Diff

@ -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;
}
}

@ -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);
}
}

@ -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();
}
}

@ -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;
}
}

@ -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);
}
}

@ -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(); }
}

@ -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;

@ -0,0 +1,711 @@
/*
* Copyright 2007-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.dyn.util;
/**
* Utility routines for dealing with bytecode-level names.
* Includes universal mangling rules for the JVM.
*
* <h3>Avoiding Dangerous Characters </h3>
*
* <p>
* The JVM defines a very small set of characters which are illegal
* in name spellings. We will slightly extend and regularize this set
* into a group of <cite>dangerous characters</cite>.
* These characters will then be replaced, in mangled names, by escape sequences.
* In addition, accidental escape sequences must be further escaped.
* Finally, a special prefix will be applied if and only if
* the mangling would otherwise fail to begin with the escape character.
* This happens to cover the corner case of the null string,
* and also clearly marks symbols which need demangling.
* </p>
* <p>
* Dangerous characters are the union of all characters forbidden
* or otherwise restricted by the JVM specification,
* plus their mates, if they are brackets
* (<code><big><b>[</b></big></code> and <code><big><b>]</b></big></code>,
* <code><big><b>&lt;</b></big></code> and <code><big><b>&gt;</b></big></code>),
* plus, arbitrarily, the colon character <code><big><b>:</b></big></code>.
* There is no distinction between type, method, and field names.
* This makes it easier to convert between mangled names of different
* types, since they do not need to be decoded (demangled).
* </p>
* <p>
* The escape character is backslash <code><big><b>\</b></big></code>
* (also known as reverse solidus).
* This character is, until now, unheard of in bytecode names,
* but traditional in the proposed role.
*
* </p>
* <h3> Replacement Characters </h3>
*
*
* <p>
* Every escape sequence is two characters
* (in fact, two UTF8 bytes) beginning with
* the escape character and followed by a
* <cite>replacement character</cite>.
* (Since the replacement character is never a backslash,
* iterated manglings do not double in size.)
* </p>
* <p>
* Each dangerous character has some rough visual similarity
* to its corresponding replacement character.
* This makes mangled symbols easier to recognize by sight.
* </p>
* <p>
* The dangerous characters are
* <code><big><b>/</b></big></code> (forward slash, used to delimit package components),
* <code><big><b>.</b></big></code> (dot, also a package delimiter),
* <code><big><b>;</b></big></code> (semicolon, used in signatures),
* <code><big><b>$</b></big></code> (dollar, used in inner classes and synthetic members),
* <code><big><b>&lt;</b></big></code> (left angle),
* <code><big><b>&gt;</b></big></code> (right angle),
* <code><big><b>[</b></big></code> (left square bracket, used in array types),
* <code><big><b>]</b></big></code> (right square bracket, reserved in this scheme for language use),
* and <code><big><b>:</b></big></code> (colon, reserved in this scheme for language use).
* Their replacements are, respectively,
* <code><big><b>|</b></big></code> (vertical bar),
* <code><big><b>,</b></big></code> (comma),
* <code><big><b>?</b></big></code> (question mark),
* <code><big><b>%</b></big></code> (percent),
* <code><big><b>^</b></big></code> (caret),
* <code><big><b>_</b></big></code> (underscore), and
* <code><big><b>{</b></big></code> (left curly bracket),
* <code><big><b>}</b></big></code> (right curly bracket),
* <code><big><b>!</b></big></code> (exclamation mark).
* In addition, the replacement character for the escape character itself is
* <code><big><b>-</b></big></code> (hyphen),
* and the replacement character for the null prefix is
* <code><big><b>=</b></big></code> (equal sign).
* </p>
* <p>
* An escape character <code><big><b>\</b></big></code>
* followed by any of these replacement characters
* is an escape sequence, and there are no other escape sequences.
* An equal sign is only part of an escape sequence
* if it is the second character in the whole string, following a backslash.
* Two consecutive backslashes do <em>not</em> form an escape sequence.
* </p>
* <p>
* Each escape sequence replaces a so-called <cite>original character</cite>
* which is either one of the dangerous characters or the escape character.
* A null prefix replaces an initial null string, not a character.
* </p>
* <p>
* All this implies that escape sequences cannot overlap and may be
* determined all at once for a whole string. Note that a spelling
* string can contain <cite>accidental escapes</cite>, apparent escape
* sequences which must not be interpreted as manglings.
* These are disabled by replacing their leading backslash with an
* escape sequence (<code><big><b>\-</b></big></code>). To mangle a string, three logical steps
* are required, though they may be carried out in one pass:
* </p>
* <ol>
* <li>In each accidental escape, replace the backslash with an escape sequence
* (<code><big><b>\-</b></big></code>).</li>
* <li>Replace each dangerous character with an escape sequence
* (<code><big><b>\|</b></big></code> for <code><big><b>/</b></big></code>, etc.).</li>
* <li>If the first two steps introduced any change, <em>and</em>
* if the string does not already begin with a backslash, prepend a null prefix (<code><big><b>\=</b></big></code>).</li>
* </ol>
*
* To demangle a mangled string that begins with an escape,
* remove any null prefix, and then replace (in parallel)
* each escape sequence by its original character.
* <p>Spelling strings which contain accidental
* escapes <em>must</em> have them replaced, even if those
* strings do not contain dangerous characters.
* This restriction means that mangling a string always
* requires a scan of the string for escapes.
* But then, a scan would be required anyway,
* to check for dangerous characters.
*
* </p>
* <h3> Nice Properties </h3>
*
* <p>
* If a bytecode name does not contain any escape sequence,
* demangling is a no-op: The string demangles to itself.
* Such a string is called <cite>self-mangling</cite>.
* Almost all strings are self-mangling.
* In practice, to demangle almost any name &ldquo;found in nature&rdquo;,
* simply verify that it does not begin with a backslash.
* </p>
* <p>
* Mangling is a one-to-one function, while demangling
* is a many-to-one function.
* A mangled string is defined as <cite>validly mangled</cite> if
* it is in fact the unique mangling of its spelling string.
* Three examples of invalidly mangled strings are <code><big><b>\=foo</b></big></code>,
* <code><big><b>\-bar</b></big></code>, and <code><big><b>baz\!</b></big></code>, which demangle to <code><big><b>foo</b></big></code>, <code><big><b>\bar</b></big></code>, and
* <code><big><b>baz\!</b></big></code>, but then remangle to <code><big><b>foo</b></big></code>, <code><big><b>\bar</b></big></code>, and <code><big><b>\=baz\-!</b></big></code>.
* If a language back-end or runtime is using mangled names,
* it should never present an invalidly mangled bytecode
* name to the JVM. If the runtime encounters one,
* it should also report an error, since such an occurrence
* probably indicates a bug in name encoding which
* will lead to errors in linkage.
* However, this note does not propose that the JVM verifier
* detect invalidly mangled names.
* </p>
* <p>
* As a result of these rules, it is a simple matter to
* compute validly mangled substrings and concatenations
* of validly mangled strings, and (with a little care)
* these correspond to corresponding operations on their
* spelling strings.
* </p>
* <ul>
* <li>Any prefix of a validly mangled string is also validly mangled,
* although a null prefix may need to be removed.</li>
* <li>Any suffix of a validly mangled string is also validly mangled,
* although a null prefix may need to be added.</li>
* <li>Two validly mangled strings, when concatenated,
* are also validly mangled, although any null prefix
* must be removed from the second string,
* and a trailing backslash on the first string may need escaping,
* if it would participate in an accidental escape when followed
* by the first character of the second string.</li>
* </ul>
* <p>If languages that include non-Java symbol spellings use this
* mangling convention, they will enjoy the following advantages:
* </p>
* <ul>
* <li>They can interoperate via symbols they share in common.</li>
* <li>Low-level tools, such as backtrace printers, will have readable displays.</li>
* <li>Future JVM and language extensions can safely use the dangerous characters
* for structuring symbols, but will never interfere with valid spellings.</li>
* <li>Runtimes and compilers can use standard libraries for mangling and demangling.</li>
* <li>Occasional transliterations and name composition will be simple and regular,
* for classes, methods, and fields.</li>
* <li>Bytecode names will continue to be compact.
* When mangled, spellings will at most double in length, either in
* UTF8 or UTF16 format, and most will not change at all.</li>
* </ul>
*
*
* <h3> Suggestions for Human Readable Presentations </h3>
*
*
* <p>
* For human readable displays of symbols,
* it will be better to present a string-like quoted
* representation of the spelling, because JVM users
* are generally familiar with such tokens.
* We suggest using single or double quotes before and after
* mangled symbols which are not valid Java identifiers,
* with quotes, backslashes, and non-printing characters
* escaped as if for literals in the Java language.
* </p>
* <p>
* For example, an HTML-like spelling
* <code><big><b>&lt;pre&gt;</b></big></code> mangles to
* <code><big><b>\^pre\_</b></big></code> and could
* display more cleanly as
* <code><big><b>'&lt;pre&gt;'</b></big></code>,
* with the quotes included.
* Such string-like conventions are <em>not</em> suitable
* for mangled bytecode names, in part because
* dangerous characters must be eliminated, rather
* than just quoted. Otherwise internally structured
* strings like package prefixes and method signatures
* could not be reliably parsed.
* </p>
* <p>
* In such human-readable displays, invalidly mangled
* names should <em>not</em> be demangled and quoted,
* for this would be misleading. Likewise, JVM symbols
* which contain dangerous characters (like dots in field
* names or brackets in method names) should not be
* simply quoted. The bytecode names
* <code><big><b>\=phase\,1</b></big></code> and
* <code><big><b>phase.1</b></big></code> are distinct,
* and in demangled displays they should be presented as
* <code><big><b>'phase.1'</b></big></code> and something like
* <code><big><b>'phase'.1</b></big></code>, respectively.
* </p>
*
* @author John Rose
* @version 1.2, 02/06/2008
* @see http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm
*/
public class BytecodeName {
private BytecodeName() { } // static only class
/** Given a source name, produce the corresponding bytecode name.
* The source name should not be qualified, because any syntactic
* markers (dots, slashes, dollar signs, colons, etc.) will be mangled.
* @param s the source name
* @return a valid bytecode name which represents the source name
*/
public static String toBytecodeName(String s) {
String bn = mangle(s);
assert((Object)bn == s || looksMangled(bn)) : bn;
assert(s.equals(toSourceName(bn))) : s;
return bn;
}
/** Given an unqualified bytecode name, produce the corresponding source name.
* The bytecode name must not contain dangerous characters.
* In particular, it must not be qualified or segmented by colon {@code ':'}.
* @param s the bytecode name
* @return the source name, which may possibly have unsafe characters
* @throws IllegalArgumentException if the bytecode name is not {@link #isSafeBytecodeName safe}
* @see #isSafeBytecodeName(java.lang.String)
*/
public static String toSourceName(String s) {
checkSafeBytecodeName(s);
String sn = s;
if (looksMangled(s)) {
sn = demangle(s);
assert(s.equals(mangle(sn))) : s+" => "+sn+" => "+mangle(sn);
}
return sn;
}
/**
* Given a bytecode name from a classfile, separate it into
* components delimited by dangerous characters.
* Each resulting array element will be either a dangerous character,
* or else a safe bytecode name.
* (The safe name might possibly be mangled to hide further dangerous characters.)
* For example, the qualified class name {@code java/lang/String}
* will be parsed into the array {@code {"java", '/', "lang", '/', "String"}}.
* The name {@code &lt;init&gt;} will be parsed into { '&lt;', "init", '&gt;'}}
* The name {@code foo/bar$:baz} will be parsed into
* {@code {"foo", '/', "bar", '$', ':', "baz"}}.
*/
public static Object[] parseBytecodeName(String s) {
int slen = s.length();
Object[] res = null;
for (int pass = 0; pass <= 1; pass++) {
int fillp = 0;
int lasti = 0;
for (int i = 0; i <= slen; i++) {
int whichDC = -1;
if (i < slen) {
whichDC = DANGEROUS_CHARS.indexOf(s.charAt(i));
if (whichDC < DANGEROUS_CHAR_FIRST_INDEX) continue;
}
// got to end of string or next dangerous char
if (lasti < i) {
// normal component
if (pass != 0)
res[fillp] = s.substring(lasti, i);
fillp++;
lasti = i+1;
}
if (whichDC >= DANGEROUS_CHAR_FIRST_INDEX) {
if (pass != 0)
res[fillp] = DANGEROUS_CHARS_CA[whichDC];
fillp++;
}
}
if (pass != 0) break;
// between passes, build the result array
res = new String[fillp];
if (fillp <= 1) {
if (fillp != 0) res[0] = s;
break;
}
}
return res;
}
/**
* Given a series of components, create a bytecode name for a classfile.
* This is the inverse of {@link #parseBytecodeName(java.lang.String)}.
* Each component must either be an interned one-character string of
* a dangerous character, or else a safe bytecode name.
* @param components a series of name components
* @return the concatenation of all components
* @throws IllegalArgumentException if any component contains an unsafe
* character, and is not an interned one-character string
* @throws NullPointerException if any component is null
*/
public static String unparseBytecodeName(Object[] components) {
for (Object c : components) {
if (c instanceof String)
checkSafeBytecodeName((String) c); // may fail
}
return appendAll(components);
}
private static String appendAll(Object[] components) {
if (components.length <= 1) {
if (components.length == 1) {
return String.valueOf(components[0]);
}
return "";
}
int slen = 0;
for (Object c : components) {
if (c instanceof String)
slen += String.valueOf(c).length();
else
slen += 1;
}
StringBuilder sb = new StringBuilder(slen);
for (Object c : components) {
sb.append(c);
}
return sb.toString();
}
/**
* Given a bytecode name, produce the corresponding display name.
* This is the source name, plus quotes if needed.
* If the bytecode name contains dangerous characters,
* assume that they are being used as punctuation,
* and pass them through unchanged.
* @param s the original bytecode name (which may be qualified)
* @return a human-readable presentation
*/
public static String toDisplayName(String s) {
Object[] components = parseBytecodeName(s);
for (int i = 0; i < components.length; i++) {
if (!(components[i] instanceof String))
continue;
String c = (String) components[i];
// pretty up the name by demangling it
String sn = toSourceName(c);
if ((Object)sn != c || !isJavaIdent(sn)) {
components[i] = quoteDisplay(sn);
}
}
return appendAll(components);
}
private static boolean isJavaIdent(String s) {
int slen = s.length();
if (slen == 0) return false;
if (!Character.isUnicodeIdentifierStart(s.charAt(0)))
return false;
for (int i = 1; i < slen; i++) {
if (!Character.isUnicodeIdentifierPart(s.charAt(0)))
return false;
}
return true;
}
private static String quoteDisplay(String s) {
// TO DO: Replace wierd characters in s by C-style escapes.
return "'"+s.replaceAll("['\\\\]", "\\\\$0")+"'";
}
private static void checkSafeBytecodeName(String s)
throws IllegalArgumentException {
if (!isSafeBytecodeName(s)) {
throw new IllegalArgumentException(s);
}
}
/**
* Report whether a simple name is safe as a bytecode name.
* Such names are acceptable in class files as class, method, and field names.
* Additionally, they are free of "dangerous" characters, even if those
* characters are legal in some (or all) names in class files.
* @param s the proposed bytecode name
* @return true if the name is non-empty and all of its characters are safe
*/
public static boolean isSafeBytecodeName(String s) {
if (s.length() == 0) return false;
// check occurrences of each DANGEROUS char
for (char xc : DANGEROUS_CHARS_A) {
if (xc == ESCAPE_C) continue; // not really that dangerous
if (s.indexOf(xc) >= 0) return false;
}
return true;
}
/**
* Report whether a character is safe in a bytecode name.
* This is true of any unicode character except the following
* <em>dangerous characters</em>: {@code ".;:$[]<>/"}.
* @param s the proposed character
* @return true if the character is safe to use in classfiles
*/
public static boolean isSafeBytecodeChar(char c) {
return DANGEROUS_CHARS.indexOf(c) < DANGEROUS_CHAR_FIRST_INDEX;
}
private static boolean looksMangled(String s) {
return s.charAt(0) == ESCAPE_C;
}
private static String mangle(String s) {
if (s.length() == 0)
return NULL_ESCAPE;
// build this lazily, when we first need an escape:
StringBuilder sb = null;
for (int i = 0, slen = s.length(); i < slen; i++) {
char c = s.charAt(i);
boolean needEscape = false;
if (c == ESCAPE_C) {
if (i+1 < slen) {
char c1 = s.charAt(i+1);
if ((i == 0 && c1 == NULL_ESCAPE_C)
|| c1 != originalOfReplacement(c1)) {
// an accidental escape
needEscape = true;
}
}
} else {
needEscape = isDangerous(c);
}
if (!needEscape) {
if (sb != null) sb.append(c);
continue;
}
// build sb if this is the first escape
if (sb == null) {
sb = new StringBuilder(s.length()+10);
// mangled names must begin with a backslash:
if (s.charAt(0) != ESCAPE_C && i > 0)
sb.append(NULL_ESCAPE);
// append the string so far, which is unremarkable:
sb.append(s.substring(0, i));
}
// rewrite \ to \-, / to \|, etc.
sb.append(ESCAPE_C);
sb.append(replacementOf(c));
}
if (sb != null) return sb.toString();
return s;
}
private static String demangle(String s) {
// build this lazily, when we first meet an escape:
StringBuilder sb = null;
int stringStart = 0;
if (s.startsWith(NULL_ESCAPE))
stringStart = 2;
for (int i = stringStart, slen = s.length(); i < slen; i++) {
char c = s.charAt(i);
if (c == ESCAPE_C && i+1 < slen) {
// might be an escape sequence
char rc = s.charAt(i+1);
char oc = originalOfReplacement(rc);
if (oc != rc) {
// build sb if this is the first escape
if (sb == null) {
sb = new StringBuilder(s.length());
// append the string so far, which is unremarkable:
sb.append(s.substring(stringStart, i));
}
++i; // skip both characters
c = oc;
}
}
if (sb != null)
sb.append(c);
}
if (sb != null) return sb.toString();
return s.substring(stringStart);
}
static char ESCAPE_C = '\\';
// empty escape sequence to avoid a null name or illegal prefix
static char NULL_ESCAPE_C = '=';
static String NULL_ESCAPE = ESCAPE_C+""+NULL_ESCAPE_C;
static final String DANGEROUS_CHARS = "\\/.;:$[]<>"; // \\ must be first
static final String REPLACEMENT_CHARS = "-|,?!%{}^_";
static final int DANGEROUS_CHAR_FIRST_INDEX = 1; // index after \\
static char[] DANGEROUS_CHARS_A = DANGEROUS_CHARS.toCharArray();
static char[] REPLACEMENT_CHARS_A = REPLACEMENT_CHARS.toCharArray();
static final Character[] DANGEROUS_CHARS_CA;
static {
Character[] dcca = new Character[DANGEROUS_CHARS.length()];
for (int i = 0; i < dcca.length; i++)
dcca[i] = Character.valueOf(DANGEROUS_CHARS.charAt(i));
DANGEROUS_CHARS_CA = dcca;
}
static final long[] SPECIAL_BITMAP = new long[2]; // 128 bits
static {
String SPECIAL = DANGEROUS_CHARS + REPLACEMENT_CHARS;
//System.out.println("SPECIAL = "+SPECIAL);
for (char c : SPECIAL.toCharArray()) {
SPECIAL_BITMAP[c >>> 6] |= 1L << c;
}
}
static boolean isSpecial(char c) {
if ((c >>> 6) < SPECIAL_BITMAP.length)
return ((SPECIAL_BITMAP[c >>> 6] >> c) & 1) != 0;
else
return false;
}
static char replacementOf(char c) {
if (!isSpecial(c)) return c;
int i = DANGEROUS_CHARS.indexOf(c);
if (i < 0) return c;
return REPLACEMENT_CHARS.charAt(i);
}
static char originalOfReplacement(char c) {
if (!isSpecial(c)) return c;
int i = REPLACEMENT_CHARS.indexOf(c);
if (i < 0) return c;
return DANGEROUS_CHARS.charAt(i);
}
static boolean isDangerous(char c) {
if (!isSpecial(c)) return false;
return (DANGEROUS_CHARS.indexOf(c) >= DANGEROUS_CHAR_FIRST_INDEX);
}
static int indexOfDangerousChar(String s, int from) {
for (int i = from, slen = s.length(); i < slen; i++) {
if (isDangerous(s.charAt(i)))
return i;
}
return -1;
}
static int lastIndexOfDangerousChar(String s, int from) {
for (int i = Math.min(from, s.length()-1); i >= 0; i--) {
if (isDangerous(s.charAt(i)))
return i;
}
return -1;
}
// test driver
static void main(String[] av) {
// If verbose is enabled, quietly check everything.
// Otherwise, print the output for the user to check.
boolean verbose = false;
int maxlen = 0;
while (av.length > 0 && av[0].startsWith("-")) {
String flag = av[0].intern();
av = java.util.Arrays.copyOfRange(av, 1, av.length); // Java 1.6 or later
if (flag == "-" || flag == "--") break;
else if (flag == "-q")
verbose = false;
else if (flag == "-v")
verbose = true;
else if (flag.startsWith("-l"))
maxlen = Integer.valueOf(flag.substring(2));
else
throw new Error("Illegal flag argument: "+flag);
}
if (maxlen == 0)
maxlen = (verbose ? 2 : 4);
if (verbose) System.out.println("Note: maxlen = "+maxlen);
switch (av.length) {
case 0: av = new String[] {
DANGEROUS_CHARS.substring(0) +
REPLACEMENT_CHARS.substring(0, 1) +
NULL_ESCAPE + "x"
}; // and fall through:
case 1:
char[] cv = av[0].toCharArray();
av = new String[cv.length];
int avp = 0;
for (char c : cv) {
String s = String.valueOf(c);
if (c == 'x') s = "foo"; // tradition...
av[avp++] = s;
}
}
if (verbose)
System.out.println("Note: Verbose output mode enabled. Use '-q' to suppress.");
Tester t = new Tester();
t.maxlen = maxlen;
t.verbose = verbose;
t.tokens = av;
t.test("", 0);
}
static class Tester {
boolean verbose;
int maxlen;
java.util.Map<String,String> map = new java.util.HashMap<String,String>();
String[] tokens;
void test(String stringSoFar, int tokensSoFar) {
test(stringSoFar);
if (tokensSoFar <= maxlen) {
for (String token : tokens) {
if (token.length() == 0) continue; // skip empty tokens
if (stringSoFar.indexOf(token) != stringSoFar.lastIndexOf(token))
continue; // there are already two occs. of this token
if (token.charAt(0) == ESCAPE_C && token.length() == 1 && maxlen < 4)
test(stringSoFar+token, tokensSoFar); // want lots of \'s
else if (tokensSoFar < maxlen)
test(stringSoFar+token, tokensSoFar+1);
}
}
}
void test(String s) {
// for small batches, do not test the null string
if (s.length() == 0 && maxlen >=1 && maxlen <= 2) return;
String bn = testSourceName(s);
if (bn == null) return;
if (bn == s) {
//if (verbose) System.out.println(s+" == id");
} else {
if (verbose) System.out.println(s+" => "+bn+" "+toDisplayName(bn));
String bnbn = testSourceName(bn);
if (bnbn == null) return;
if (verbose) System.out.println(bn+" => "+bnbn+" "+toDisplayName(bnbn));
/*
String bn3 = testSourceName(bnbn);
if (bn3 == null) return;
if (verbose) System.out.println(bnbn+" => "+bn3);
*/
}
}
String testSourceName(String s) {
if (map.containsKey(s)) return null;
String bn = toBytecodeName(s);
map.put(s, bn);
String sn = toSourceName(bn);
if (!sn.equals(s)) {
String bad = (s+" => "+bn+" != "+sn);
if (!verbose) throw new Error("Bad mangling: "+bad);
System.out.println("*** "+bad);
return null;
}
return bn;
}
}
}

@ -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(';');
}
}
}

@ -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);
}
}

@ -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));
}
}

@ -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();
}
}

@ -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);
}
}

@ -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;

@ -55,6 +55,22 @@ public interface JavaLangAccess {
/** 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);
}

@ -1,5 +1,5 @@
/*
* Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -811,6 +811,25 @@ public final class Unsafe {
public native Class defineClass(String name, byte[] b, int off, int len);
/**
* Define a class but do not make it known to the class loader or system dictionary.
* <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)

@ -1,5 +1,5 @@
/*
* Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2004-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -306,7 +306,7 @@ enum {
JVM_OPC_invokespecial = 183,
JVM_OPC_invokestatic = 184,
JVM_OPC_invokeinterface = 185,
JVM_OPC_xxxunusedxxx = 186,
JVM_OPC_invokedynamic = 186,
JVM_OPC_new = 187,
JVM_OPC_newarray = 188,
JVM_OPC_anewarray = 189,
@ -515,7 +515,7 @@ enum {
3, /* invokespecial */ \
3, /* invokestatic */ \
5, /* invokeinterface */ \
0, /* xxxunusedxxx */ \
5, /* invokedynamic */ \
3, /* new */ \
2, /* newarray */ \
3, /* anewarray */ \

@ -1,5 +1,5 @@
/*
* Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -1223,16 +1223,20 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset)
case JVM_OPC_invokevirtual:
case JVM_OPC_invokespecial:
case JVM_OPC_invokestatic:
case JVM_OPC_invokedynamic:
case JVM_OPC_invokeinterface: {
/* Make sure the constant pool item is the right type. */
int key = (code[offset + 1] << 8) + code[offset + 2];
const char *methodname;
jclass cb = context->class;
fullinfo_type clazz_info;
int is_constructor, is_internal;
int is_constructor, is_internal, is_invokedynamic;
int kind = (opcode == JVM_OPC_invokeinterface
? 1 << JVM_CONSTANT_InterfaceMethodref
: opcode == JVM_OPC_invokedynamic
? 1 << JVM_CONSTANT_NameAndType
: 1 << JVM_CONSTANT_Methodref);
is_invokedynamic = opcode == JVM_OPC_invokedynamic;
/* Make sure the constant pool item is the right type. */
verify_constant_pool_type(context, key, kind);
methodname = JVM_GetCPMethodNameUTF(env, cb, key);
@ -1241,8 +1245,11 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset)
is_internal = methodname[0] == '<';
pop_and_free(context);
clazz_info = cp_index_to_class_fullinfo(context, key,
JVM_CONSTANT_Methodref);
if (is_invokedynamic)
clazz_info = context->object_info; // anything will do
else
clazz_info = cp_index_to_class_fullinfo(context, key,
JVM_CONSTANT_Methodref);
this_idata->operand.i = key;
this_idata->operand2.fi = clazz_info;
if (is_constructor) {
@ -1304,6 +1311,11 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset)
"Fourth operand byte of invokeinterface must be zero");
}
pop_and_free(context);
} else if (opcode == JVM_OPC_invokedynamic) {
if (code[offset + 3] != 0 || code[offset + 4] != 0) {
CCerror(context,
"Third and fourth operand bytes of invokedynamic must be zero");
}
} else if (opcode == JVM_OPC_invokevirtual
|| opcode == JVM_OPC_invokespecial)
set_protected(context, inumber, key, opcode);
@ -1990,6 +2002,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
case JVM_OPC_invokeinit: /* invokespecial call to <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 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac
print_formatted_methodname(context, operand);
}
#endif
if (opcode != JVM_OPC_invokestatic)
if (opcode != JVM_OPC_invokestatic &&
opcode != JVM_OPC_invokedynamic)
/* First, push the object */
*ip++ = (opcode == JVM_OPC_invokeinit ? '@' : 'A');
for (p = signature + 1; *p != JVM_SIGNATURE_ENDFUNC; ) {
@ -2290,6 +2304,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
case JVM_OPC_invokeinit:
case JVM_OPC_invokedynamic:
case JVM_OPC_invokeinterface: case JVM_OPC_invokestatic: {
int operand = this_idata->operand.i;
const char *signature =
@ -2299,7 +2314,8 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac
int item;
const char *p;
check_and_push(context, signature, VM_STRING_UTF);
if (opcode == JVM_OPC_invokestatic) {
if (opcode == JVM_OPC_invokestatic ||
opcode == JVM_OPC_invokedynamic) {
item = 0;
} else if (opcode == JVM_OPC_invokeinit) {
fullinfo_type init_type = this_idata->operand2.fi;
@ -2680,6 +2696,7 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
case JVM_OPC_invokeinit:
case JVM_OPC_invokedynamic:
case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: {
/* Look to signature to determine correct result. */
int operand = this_idata->operand.i;

@ -1,5 +1,5 @@
/*
* Copyright 1998 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -210,7 +210,7 @@ char * const opcode_in_out[][2] = {
{"?", "?"}, /* invokespecial */
{"?", "?"}, /* invokestatic */
{"?", "?"}, /* invokeinterface */
{"?", "?"}, /* xxxunusedxxx */
{"?", "?"}, /* invokedynamic */
{"", "A"}, /* new */
{"I", "A"}, /* newarray */
{"I", "A"}, /* anewarray */

@ -475,49 +475,40 @@ class WindowsAsynchronousSocketChannelImpl
// 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;
}
// read completed immediately:
// 1. update buffer position
// 2. reset read flag
// 3. release waiters
if (n == 0) {
n = -1;
} else {
updateBuffers(n);
}
// 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(n));
result.setResult((V)Long.valueOf(-1L));
} else {
result.setResult((V)Integer.valueOf(n));
result.setResult((V)Integer.valueOf(-1));
}
} 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 @@ class WindowsAsynchronousSocketChannelImpl
@Override
@SuppressWarnings("unchecked")
public void run() {
int n = -1;
long overlapped = 0L;
boolean prepared = false;
boolean pending = false;
@ -736,56 +726,34 @@ class WindowsAsynchronousSocketChannelImpl
// 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();
}

@ -157,14 +157,13 @@ Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_read0(JNIEnv* env, jclass t
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 @@ Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_read0(JNIEnv* env, jclass t
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 @@ Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_write0(JNIEnv* env, jclass
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 @@ Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_write0(JNIEnv* env, jclass
JNU_ThrowIOExceptionWithLastError(env, "WSASend failed");
return IOS_THROWN;
}
return (jint)nwritten;
return IOS_UNAVAILABLE;
}

@ -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);
}
}
}
}

@ -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.";

@ -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) { }
}
}