提交 da6198b8 编写于 作者: J jrose

6980096: JSR 292 reflective lookup should throw checked exceptions

Summary: Make NoAccessException be a checked exception.  Also remove JavaMethodHandle.
Reviewed-by: twisti
上级 dd532511
...@@ -273,15 +273,19 @@ public class CallSite ...@@ -273,15 +273,19 @@ public class CallSite
public final MethodHandle dynamicInvoker() { public final MethodHandle dynamicInvoker() {
if (this instanceof ConstantCallSite) if (this instanceof ConstantCallSite)
return getTarget(); // will not change dynamically return getTarget(); // will not change dynamically
MethodHandle getCSTarget = GET_TARGET; MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this);
if (getCSTarget == null)
GET_TARGET = getCSTarget = MethodHandles.Lookup.IMPL_LOOKUP.
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, getCSTarget, this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type()); MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget); return MethodHandles.foldArguments(invoker, getTarget);
} }
private static MethodHandle GET_TARGET = null; // link this lazily, not eagerly private static final MethodHandle GET_TARGET;
static {
try {
GET_TARGET = MethodHandles.Lookup.IMPL_LOOKUP.
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
} catch (NoAccessException ignore) {
throw new InternalError();
}
}
/** Implementation of {@link MethodHandleProvider} which returns {@code this.dynamicInvoker()}. */ /** Implementation of {@link MethodHandleProvider} which returns {@code this.dynamicInvoker()}. */
public final MethodHandle asMethodHandle() { return dynamicInvoker(); } public final MethodHandle asMethodHandle() { return dynamicInvoker(); }
......
...@@ -81,6 +81,14 @@ public class MethodHandles { ...@@ -81,6 +81,14 @@ public class MethodHandles {
* Return a {@link Lookup lookup object} which is trusted minimally. * Return a {@link Lookup lookup object} which is trusted minimally.
* It can only be used to create method handles to * It can only be used to create method handles to
* publicly accessible fields and methods. * publicly accessible fields and methods.
* <p>
* As a matter of pure convention, the {@linkplain Lookup#lookupClass lookup class}
* of this lookup object will be {@link java.lang.Object}.
* <p>
* The lookup class can be changed to any other class {@code C} using an expression of the form
* {@linkplain Lookup#in <code>publicLookup().in(C.class)</code>}.
* Since all classes have equal access to public names,
* such a change would confer no new access rights.
*/ */
public static Lookup publicLookup() { public static Lookup publicLookup() {
return Lookup.PUBLIC_LOOKUP; return Lookup.PUBLIC_LOOKUP;
...@@ -90,9 +98,10 @@ public class MethodHandles { ...@@ -90,9 +98,10 @@ public class MethodHandles {
* A <em>lookup object</em> is a factory for creating method handles, * A <em>lookup object</em> is a factory for creating method handles,
* when the creation requires access checking. * when the creation requires access checking.
* Method handles do not perform * Method handles do not perform
* access checks when they are called; this is a major difference * access checks when they are called, but rather when they are created.
* (This is a major difference
* from reflective {@link Method}, which performs access checking * from reflective {@link Method}, which performs access checking
* against every caller, on every call. * against every caller, on every call.)
* Therefore, method handle access * Therefore, method handle access
* restrictions must be enforced when a method handle is created. * restrictions must be enforced when a method handle is created.
* The caller class against which those restrictions are enforced * The caller class against which those restrictions are enforced
...@@ -107,7 +116,7 @@ public class MethodHandles { ...@@ -107,7 +116,7 @@ public class MethodHandles {
* It may then use this factory to create method handles on * It may then use this factory to create method handles on
* all of its methods, including private ones. * all of its methods, including private ones.
* It may also delegate the lookup (e.g., to a metaobject protocol) * It may also delegate the lookup (e.g., to a metaobject protocol)
* by passing the {@code Lookup} object to other code. * by passing the lookup object to other code.
* If this other code creates method handles, they will be access * If this other code creates method handles, they will be access
* checked against the original lookup class, and not with any higher * checked against the original lookup class, and not with any higher
* privileges. * privileges.
...@@ -125,23 +134,28 @@ public class MethodHandles { ...@@ -125,23 +134,28 @@ public class MethodHandles {
* It can also fail if a security manager is installed and refuses * It can also fail if a security manager is installed and refuses
* access. In any of these cases, an exception will be * access. In any of these cases, an exception will be
* thrown from the attempted lookup. * thrown from the attempted lookup.
* <p>
* In general, the conditions under which a method handle may be * In general, the conditions under which a method handle may be
* created for a method {@code M} are exactly as restrictive as the conditions * created for a method {@code M} are exactly as restrictive as the conditions
* under which the lookup class could have compiled a call to {@code M}. * under which the lookup class could have compiled a call to {@code M}.
* At least some of these error conditions are likely to be * This rule is applied even if the Java compiler might have created
* represented by checked exceptions in the final version of this API. * an wrapper method to access a private method of another class
* in the same top-level declaration.
* For example, a lookup object created for a nested class {@code C.D}
* can access private members within other related classes such as
* {@code C}, {@code C.D.E}, or {@code C.B}.
*/ */
public static final public static final
class Lookup { class Lookup {
/** The class on behalf of whom the lookup is being performed. */ /** The class on behalf of whom the lookup is being performed. */
private final Class<?> lookupClass; private final Class<?> lookupClass;
/** The allowed sorts of members which may be looked up (public, etc.), with STRICT for package. */ /** The allowed sorts of members which may be looked up (public, etc.), with STATIC for package. */
private final int allowedModes; private final int allowedModes;
private static final int private static final int
PUBLIC = Modifier.PUBLIC, PUBLIC = Modifier.PUBLIC,
PACKAGE = Modifier.STRICT, PACKAGE = Modifier.STATIC,
PROTECTED = Modifier.PROTECTED, PROTECTED = Modifier.PROTECTED,
PRIVATE = Modifier.PRIVATE, PRIVATE = Modifier.PRIVATE,
ALL_MODES = (PUBLIC | PACKAGE | PROTECTED | PRIVATE), ALL_MODES = (PUBLIC | PACKAGE | PROTECTED | PRIVATE),
...@@ -155,8 +169,10 @@ public class MethodHandles { ...@@ -155,8 +169,10 @@ public class MethodHandles {
/** Which class is performing the lookup? It is this class against /** Which class is performing the lookup? It is this class against
* which checks are performed for visibility and access permissions. * which checks are performed for visibility and access permissions.
* <p> * <p>
* This value is null if and only if this lookup was produced * The class implies a maximum level of access permission,
* by {@link MethodHandles#publicLookup}. * but the permissions may be additionally limited by the bitmask
* {@link #lookupModes}, which controls whether non-public members
* can be accessed.
*/ */
public Class<?> lookupClass() { public Class<?> lookupClass() {
return lookupClass; return lookupClass;
...@@ -168,10 +184,15 @@ public class MethodHandles { ...@@ -168,10 +184,15 @@ public class MethodHandles {
} }
/** Which types of members can this lookup object produce? /** Which types of members can this lookup object produce?
* The result is a bit-mask of the modifier bits PUBLIC, PROTECTED, PRIVATE, and STRICT. * The result is a bit-mask of the {@link Modifier} bits
* The modifier bit STRICT stands in for the (non-existent) package protection mode. * {@linkplain Modifier#PUBLIC PUBLIC (0x01)},
* {@linkplain Modifier#PROTECTED PROTECTED (0x02)},
* {@linkplain Modifier#PRIVATE PRIVATE (0x04)},
* and {@linkplain Modifier#STATIC STATIC (0x08)}.
* The modifier bit {@code STATIC} stands in for the package protection mode,
* which does not have an explicit modifier bit.
*/ */
int lookupModes() { public int lookupModes() {
return allowedModes & ALL_MODES; return allowedModes & ALL_MODES;
} }
...@@ -621,32 +642,32 @@ public class MethodHandles { ...@@ -621,32 +642,32 @@ public class MethodHandles {
/// Helper methods, all package-private. /// Helper methods, all package-private.
MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) { MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve checkSymbolicClass(refc); // do this before attempting to resolve
int mods = (isStatic ? Modifier.STATIC : 0); int mods = (isStatic ? Modifier.STATIC : 0);
return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull()); return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
} }
MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) { MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve checkSymbolicClass(refc); // do this before attempting to resolve
int mods = (isStatic ? Modifier.STATIC : 0); int mods = (isStatic ? Modifier.STATIC : 0);
return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull()); return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
} }
MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic, MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic,
boolean searchSupers, Class<?> specialCaller) { boolean searchSupers, Class<?> specialCaller) throws NoAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve checkSymbolicClass(refc); // do this before attempting to resolve
int mods = (isStatic ? Modifier.STATIC : 0); int mods = (isStatic ? Modifier.STATIC : 0);
return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller); return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller);
} }
void checkSymbolicClass(Class<?> refc) { void checkSymbolicClass(Class<?> refc) throws NoAccessException {
Class<?> caller = lookupClassOrNull(); Class<?> caller = lookupClassOrNull();
if (caller != null && !VerifyAccess.isClassAccessible(refc, caller)) if (caller != null && !VerifyAccess.isClassAccessible(refc, caller))
throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), caller); throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), caller);
} }
void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) { void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws NoAccessException {
String message; String message;
if (m.isConstructor()) if (m.isConstructor())
message = "expected a method, not a constructor"; message = "expected a method, not a constructor";
...@@ -659,7 +680,7 @@ public class MethodHandles { ...@@ -659,7 +680,7 @@ public class MethodHandles {
throw newNoAccessException(message, m, lookupClass()); throw newNoAccessException(message, m, lookupClass());
} }
void checkAccess(Class<?> refc, MemberName m) { void checkAccess(Class<?> refc, MemberName m) throws NoAccessException {
int allowedModes = this.allowedModes; int allowedModes = this.allowedModes;
if (allowedModes == TRUSTED) return; if (allowedModes == TRUSTED) return;
int mods = m.getModifiers(); int mods = m.getModifiers();
...@@ -695,14 +716,14 @@ public class MethodHandles { ...@@ -695,14 +716,14 @@ public class MethodHandles {
return "member is private to package"; return "member is private to package";
} }
void checkSpecialCaller(Class<?> specialCaller) { void checkSpecialCaller(Class<?> specialCaller) throws NoAccessException {
if (allowedModes == TRUSTED) return; if (allowedModes == TRUSTED) return;
if (!VerifyAccess.isSamePackageMember(specialCaller, lookupClass())) if (!VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))
throw newNoAccessException("no private access for invokespecial", throw newNoAccessException("no private access for invokespecial",
new MemberName(specialCaller), lookupClass()); new MemberName(specialCaller), lookupClass());
} }
MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) { MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws NoAccessException {
// The accessing class only has the right to use a protected member // The accessing class only has the right to use a protected member
// on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc. // on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc.
if (!method.isProtected() || method.isStatic() if (!method.isProtected() || method.isStatic()
...@@ -712,7 +733,7 @@ public class MethodHandles { ...@@ -712,7 +733,7 @@ public class MethodHandles {
else else
return restrictReceiver(method, mh, lookupClass()); return restrictReceiver(method, mh, lookupClass());
} }
MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) { MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws NoAccessException {
assert(!method.isStatic()); assert(!method.isStatic());
Class<?> defc = method.getDeclaringClass(); // receiver type of mh is too wide Class<?> defc = method.getDeclaringClass(); // receiver type of mh is too wide
if (defc.isInterface() || !defc.isAssignableFrom(caller)) { if (defc.isInterface() || !defc.isAssignableFrom(caller)) {
...@@ -898,11 +919,16 @@ public class MethodHandles { ...@@ -898,11 +919,16 @@ public class MethodHandles {
* @return a method handle which always invokes the call site's target * @return a method handle which always invokes the call site's target
*/ */
public static public static
MethodHandle dynamicInvoker(CallSite site) { MethodHandle dynamicInvoker(CallSite site) throws NoAccessException {
MethodHandle getCSTarget = GET_TARGET; MethodHandle getCSTarget = GET_TARGET;
if (getCSTarget == null) if (getCSTarget == null) {
try {
GET_TARGET = getCSTarget = Lookup.IMPL_LOOKUP. GET_TARGET = getCSTarget = Lookup.IMPL_LOOKUP.
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
} catch (NoAccessException ex) {
throw new InternalError();
}
}
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, getCSTarget, site); MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, getCSTarget, site);
MethodHandle invoker = exactInvoker(site.type()); MethodHandle invoker = exactInvoker(site.type());
return foldArguments(invoker, getTarget); return foldArguments(invoker, getTarget);
...@@ -1260,17 +1286,20 @@ public class MethodHandles { ...@@ -1260,17 +1286,20 @@ public class MethodHandles {
* <p> * <p>
* <b>Example:</b> * <b>Example:</b>
* <p><blockquote><pre> * <p><blockquote><pre>
* MethodHandle cat = MethodHandles.lookup(). * import static java.dyn.MethodHandles.*;
* findVirtual(String.class, "concat", String.class, String.class); * import static java.dyn.MethodType.*;
* System.out.println(cat.&lt;String&gt;invokeExact("x", "y")); // xy * ...
* MethodHandle cat = lookup().findVirtual(String.class,
* "concat", methodType(String.class, String.class));
* System.out.println((String) cat.invokeExact("x", "y")); // xy
* MethodHandle d0 = dropArguments(cat, 0, String.class); * MethodHandle d0 = dropArguments(cat, 0, String.class);
* System.out.println(d0.&lt;String&gt;invokeExact("x", "y", "z")); // xy * System.out.println((String) d0.invokeExact("x", "y", "z")); // yz
* MethodHandle d1 = dropArguments(cat, 1, String.class); * MethodHandle d1 = dropArguments(cat, 1, String.class);
* System.out.println(d1.&lt;String&gt;invokeExact("x", "y", "z")); // xz * System.out.println((String) d1.invokeExact("x", "y", "z")); // xz
* MethodHandle d2 = dropArguments(cat, 2, String.class); * MethodHandle d2 = dropArguments(cat, 2, String.class);
* System.out.println(d2.&lt;String&gt;invokeExact("x", "y", "z")); // yz * System.out.println((String) d2.invokeExact("x", "y", "z")); // xy
* MethodHandle d12 = dropArguments(cat, 1, String.class, String.class); * MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
* System.out.println(d12.&lt;String&gt;invokeExact("w", "x", "y", "z")); // wz * System.out.println((String) d12.invokeExact("x", 12, true, "z")); // xz
* </pre></blockquote> * </pre></blockquote>
* @param target the method handle to invoke after the argument is dropped * @param target the method handle to invoke after the argument is dropped
* @param valueTypes the type(s) of the argument to drop * @param valueTypes the type(s) of the argument to drop
......
...@@ -40,24 +40,37 @@ import static sun.dyn.MemberName.newIllegalArgumentException; ...@@ -40,24 +40,37 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
* returned by a method handle, or the arguments and return type passed * returned by a method handle, or the arguments and return type passed
* and expected by a method handle caller. Method types must be properly * and expected by a method handle caller. Method types must be properly
* matched between a method handle and all its callers, * matched between a method handle and all its callers,
* and the JVM's operations enforce this matching at all times. * and the JVM's operations enforce this matching at, specifically
* during calls to {@link MethodHandle#invokeExact}
* and {@link MethodHandle#invokeGeneric}, and during execution
* of {@code invokedynamic} instructions.
* <p> * <p>
* The structure is a return type accompanied by any number of parameter types. * The structure is a return type accompanied by any number of parameter types.
* The types (primitive, void, and reference) are represented by Class objects. * The types (primitive, {@code void}, and reference) are represented by {@link Class} objects.
* (For ease of exposition, we treat {@code void} as if it were a type.
* In fact, it denotes the absence of a return type.)
* <p> * <p>
* All instances of <code>MethodType</code> are immutable. * All instances of {@code MethodType} are immutable.
* Two instances are completely interchangeable if they compare equal. * Two instances are completely interchangeable if they compare equal.
* Equality depends on pairwise correspondence of the return and parameter types and on nothing else. * Equality depends on pairwise correspondence of the return and parameter types and on nothing else.
* <p> * <p>
* This type can be created only by factory methods. * This type can be created only by factory methods.
* All factory methods may cache values, though caching is not guaranteed. * All factory methods may cache values, though caching is not guaranteed.
* <p> * <p>
* Note: Like classes and strings, method types can be represented directly * {@code MethodType} objects are sometimes derived from bytecode instructions
* as constants to be loaded by {@code ldc} bytecodes. * such as {@code invokedynamic}, specifically from the type descriptor strings associated
* with the instructions in a class file's constant pool.
* When this occurs, any classes named in the descriptor strings must be loaded.
* (But they need not be initialized.)
* This loading may occur at any time before the {@code MethodType} object is first derived.
* <p>
* Like classes and strings, method types can be represented directly
* in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
* Loading such a constant causes its component classes to be loaded as necessary.
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
public final public final
class MethodType { class MethodType implements java.lang.reflect.Type {
private final Class<?> rtype; private final Class<?> rtype;
private final Class<?>[] ptypes; private final Class<?>[] ptypes;
private MethodTypeForm form; // erased form, plus cached data about primitives private MethodTypeForm form; // erased form, plus cached data about primitives
...@@ -636,11 +649,11 @@ class MethodType { ...@@ -636,11 +649,11 @@ class MethodType {
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Find or create an instance of the given method type. * Find or create an instance of the given method type.
* Any class or interface name embedded in the signature string * Any class or interface name embedded in the descriptor string
* will be resolved by calling {@link ClassLoader#loadClass(java.lang.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). * on the given loader (or if it is null, on the system class loader).
* <p> * <p>
* Note that it is possible to build method types which cannot be * Note that it is possible to encounter method types which cannot be
* constructed by this method, because their component types are * constructed by this method, because their component types are
* not all reachable from a common class loader. * not all reachable from a common class loader.
* <p> * <p>
...@@ -662,8 +675,11 @@ class MethodType { ...@@ -662,8 +675,11 @@ class MethodType {
} }
/** /**
* Create a bytecode signature representation of the type. * Create a bytecode descriptor representation of the method type.
* Note that this is not a strict inverse of * <p>
* Note that this is not a strict inverse of {@link #fromMethodDescriptorString}.
* Two distinct classes which share a common name but have different class loaders
* will appear identical when viewed within descriptor strings.
* <p> * <p>
* This method is included for the benfit of applications that must * This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic. * generate bytecodes that process method handles and invokedynamic.
......
...@@ -36,7 +36,7 @@ package java.dyn; ...@@ -36,7 +36,7 @@ package java.dyn;
* at the time of creation. * at the time of creation.
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
public class NoAccessException extends RuntimeException { public class NoAccessException extends ReflectiveOperationException {
/** /**
* Constructs a {@code NoAccessException} with no detail message. * Constructs a {@code NoAccessException} with no detail message.
*/ */
......
...@@ -48,8 +48,6 @@ public class BoundMethodHandle extends MethodHandle { ...@@ -48,8 +48,6 @@ public class BoundMethodHandle extends MethodHandle {
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN); private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
// Constructors in this class *must* be package scoped or private. // Constructors in this class *must* be package scoped or private.
// Exception: JavaMethodHandle constructors are protected.
// (The link between JMH and BMH is temporary.)
/** Bind a direct MH to its receiver (or first ref. argument). /** Bind a direct MH to its receiver (or first ref. argument).
* The JVM will pre-dispatch the MH if it is not already static. * The JVM will pre-dispatch the MH if it is not already static.
...@@ -122,55 +120,6 @@ public class BoundMethodHandle extends MethodHandle { ...@@ -122,55 +120,6 @@ public class BoundMethodHandle extends MethodHandle {
assert(this instanceof JavaMethodHandle); assert(this instanceof JavaMethodHandle);
} }
/** Initialize the current object as a Java method handle.
*/
protected BoundMethodHandle(String entryPointName, MethodType type, boolean matchArity) {
super(Access.TOKEN, null);
MethodHandle entryPoint
= findJavaMethodHandleEntryPoint(this.getClass(),
entryPointName, type, matchArity);
MethodHandleImpl.initType(this, entryPoint.type().dropParameterTypes(0, 1));
this.argument = this; // kludge; get rid of
this.vmargslot = this.type().parameterSlotDepth(0);
initTarget(entryPoint, 0);
assert(this instanceof JavaMethodHandle);
}
private static
MethodHandle findJavaMethodHandleEntryPoint(Class<?> caller,
String name,
MethodType type,
boolean matchArity) {
if (matchArity) type.getClass(); // elicit NPE
List<MemberName> methods = IMPL_NAMES.getMethods(caller, true, name, null, caller);
MethodType foundType = null;
MemberName foundMethod = null;
for (MemberName method : methods) {
if (method.getDeclaringClass() == MethodHandle.class)
continue; // ignore methods inherited from MH class itself
MethodType mtype = method.getMethodType();
if (type != null && type.parameterCount() != mtype.parameterCount())
continue;
else if (foundType == null)
foundType = mtype;
else if (foundType != mtype)
throw newIllegalArgumentException("more than one method named "+name+" in "+caller.getName());
// discard overrides
if (foundMethod == null)
foundMethod = method;
else if (foundMethod.getDeclaringClass().isAssignableFrom(method.getDeclaringClass()))
foundMethod = method;
}
if (foundMethod == null)
throw newIllegalArgumentException("no method named "+name+" in "+caller.getName());
MethodHandle entryPoint = MethodHandleImpl.findMethod(IMPL_TOKEN, foundMethod, true, caller);
if (type != null) {
MethodType epType = type.insertParameterTypes(0, entryPoint.type().parameterType(0));
entryPoint = MethodHandles.convertArguments(entryPoint, epType);
}
return entryPoint;
}
/** Make sure the given {@code argument} can be used as {@code argnum}-th /** 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. * parameter of the given method handle {@code mh}, which must be a reference.
* <p> * <p>
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.*; import java.dyn.*;
import static sun.dyn.MemberName.uncaughtException;
/** /**
* Parts of CallSite known to the JVM. * Parts of CallSite known to the JVM.
...@@ -80,11 +81,18 @@ public class CallSiteImpl { ...@@ -80,11 +81,18 @@ public class CallSiteImpl {
// This method is private in CallSite because it touches private fields in CallSite. // This method is private in CallSite because it touches private fields in CallSite.
// These private fields (vmmethod, vmindex) are specific to the JVM. // These private fields (vmmethod, vmindex) are specific to the JVM.
private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE = private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE;
static {
try {
PRIVATE_INITIALIZE_CALL_SITE =
MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM", MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM",
MethodType.methodType(void.class, MethodType.methodType(void.class,
String.class, MethodType.class, String.class, MethodType.class,
MemberName.class, int.class)); MemberName.class, int.class));
} catch (NoAccessException ex) {
throw uncaughtException(ex);
}
}
public static void setCallSiteTarget(Access token, CallSite site, MethodHandle target) { public static void setCallSiteTarget(Access token, CallSite site, MethodHandle target) {
Access.check(token); Access.check(token);
......
...@@ -25,12 +25,8 @@ ...@@ -25,12 +25,8 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle; import java.lang.reflect.*;
import java.dyn.MethodType;
import java.dyn.NoAccessException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import static sun.dyn.MemberName.newIllegalArgumentException; import static sun.dyn.MemberName.newIllegalArgumentException;
/** /**
...@@ -119,7 +115,7 @@ class FilterGeneric { ...@@ -119,7 +115,7 @@ class FilterGeneric {
static MethodHandle make(Kind kind, int pos, MethodHandle filter, MethodHandle target) { static MethodHandle make(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
FilterGeneric fgen = of(kind, pos, filter.type(), target.type()); FilterGeneric fgen = of(kind, pos, filter.type(), target.type());
return fgen.makeInstance(kind, pos, filter, target); return fgen.makeInstance(kind, pos, filter, target).asMethodHandle();
} }
/** Return the adapter information for this target and filter type. */ /** Return the adapter information for this target and filter type. */
......
...@@ -25,9 +25,8 @@ ...@@ -25,9 +25,8 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle; import static sun.dyn.MemberName.uncaughtException;
import java.dyn.MethodType;
/** /**
* Unary function composition, useful for many small plumbing jobs. * Unary function composition, useful for many small plumbing jobs.
...@@ -51,8 +50,16 @@ public class FilterOneArgument extends JavaMethodHandle { ...@@ -51,8 +50,16 @@ public class FilterOneArgument extends JavaMethodHandle {
return target.invokeExact(filteredArgument); return target.invokeExact(filteredArgument);
} }
private static final MethodHandle INVOKE = private static final MethodHandle INVOKE;
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke", MethodType.genericMethodType(1)); static {
try {
INVOKE =
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke",
MethodType.genericMethodType(1));
} catch (NoAccessException ex) {
throw uncaughtException(ex);
}
}
protected FilterOneArgument(MethodHandle filter, MethodHandle target) { protected FilterOneArgument(MethodHandle filter, MethodHandle target) {
super(INVOKE); super(INVOKE);
......
...@@ -25,15 +25,9 @@ ...@@ -25,15 +25,9 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle; import java.lang.reflect.*;
import java.dyn.MethodHandles; import sun.dyn.util.*;
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 generic * Adapters which mediate between incoming calls which are generic
......
...@@ -25,10 +25,7 @@ ...@@ -25,10 +25,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.MethodHandle; import java.dyn.*;
import java.dyn.MethodHandles;
import java.dyn.MethodType;
/** /**
* Construction and caching of often-used invokers. * Construction and caching of often-used invokers.
...@@ -63,8 +60,11 @@ public class Invokers { ...@@ -63,8 +60,11 @@ public class Invokers {
public MethodHandle exactInvoker() { public MethodHandle exactInvoker() {
MethodHandle invoker = exactInvoker; MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker; if (invoker != null) return invoker;
try {
invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType); invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType);
if (invoker == null) throw new InternalError("JVM cannot find invoker for "+targetType); } catch (NoAccessException ex) {
throw new InternalError("JVM cannot find invoker for "+targetType);
}
assert(invokerType(targetType) == invoker.type()); assert(invokerType(targetType) == invoker.type());
exactInvoker = invoker; exactInvoker = invoker;
return invoker; return invoker;
......
...@@ -23,8 +23,9 @@ ...@@ -23,8 +23,9 @@
* questions. * questions.
*/ */
package java.dyn; package sun.dyn;
import java.dyn.*;
import sun.dyn.Access; import sun.dyn.Access;
/** /**
...@@ -168,70 +169,4 @@ public abstract class JavaMethodHandle ...@@ -168,70 +169,4 @@ public abstract class JavaMethodHandle
protected JavaMethodHandle(MethodHandle entryPoint) { protected JavaMethodHandle(MethodHandle entryPoint) {
super(entryPoint); super(entryPoint);
} }
/**
* Create a method handle whose entry point is a non-static method
* visible in the exact (most specific) class of
* the newly constructed object.
* <p>
* The method is specified by name and type, as if via this expression:
* {@code MethodHandles.lookup().findVirtual(this.getClass(), name, type)}.
* The class defining the method might be an anonymous inner class.
* <p>
* The method handle type of {@code this} (i.e, the fully constructed object)
* will be the given method handle type.
* A call to {@code this} will invoke the selected method.
* The receiver argument will be bound to {@code this} on every method
* handle invocation.
* <p>
* <i>Rationale:</i>
* Although this constructor may seem to be a mere luxury,
* it is not subsumed by the more general constructor which
* takes any {@code MethodHandle} as the entry point argument.
* In order to convert an entry point name to a method handle,
* the self-class of the object is required (in order to do
* the lookup). The self-class, in turn, is generally not
* available at the time of the constructor invocation,
* due to the rules of Java and the JVM verifier.
* One cannot call {@code this.getClass()}, because
* the value of {@code this} is inaccessible at the point
* of the constructor call. (Changing this would require
* change to the Java language, verifiers, and compilers.)
* In particular, this constructor allows {@code JavaMethodHandle}s
* to be created in combination with the anonymous inner class syntax.
* @param entryPointName the name of the entry point method
* @param type (optional) the desired type of the method handle
*/
protected JavaMethodHandle(String entryPointName, MethodType type) {
super(entryPointName, type, true);
}
/**
* Create a method handle whose entry point is a non-static method
* visible in the exact (most specific) class of
* the newly constructed object.
* <p>
* The method is specified only by name.
* There must be exactly one method of that name visible in the object class,
* either inherited or locally declared.
* (That is, the method must not be overloaded.)
* <p>
* The method handle type of {@code this} (i.e, the fully constructed object)
* will be the same as the type of the selected non-static method.
* The receiver argument will be bound to {@code this} on every method
* handle invocation.
* <p>ISSUE: This signature wildcarding feature does not correspond to
* any MethodHandles.Lookup API element. Can we eliminate it?
* Alternatively, it is useful for naming non-overloaded methods.
* Shall we make type arguments optional in the Lookup methods,
* throwing an error in cases of ambiguity?
* <p>
* For this method's rationale, see the documentation
* for {@link #JavaMethodHandle(String,MethodType)}.
* @param entryPointName the name of the entry point method
*/
protected JavaMethodHandle(String entryPointName) {
super(entryPointName, (MethodType) null, false);
}
} }
...@@ -521,6 +521,11 @@ public final class MemberName implements Member, Cloneable { ...@@ -521,6 +521,11 @@ public final class MemberName implements Member, Cloneable {
if (lookupClass != null) message += ", from " + lookupClass.getName(); if (lookupClass != null) message += ", from " + lookupClass.getName();
return new NoAccessException(message); return new NoAccessException(message);
} }
public static Error uncaughtException(Exception ex) {
Error err = new InternalError("uncaught exception");
err.initCause(ex);
return err;
}
/** Actually making a query requires an access check. */ /** Actually making a query requires an access check. */
public static Factory getFactory(Access token) { public static Factory getFactory(Access token) {
...@@ -641,7 +646,7 @@ public final class MemberName implements Member, Cloneable { ...@@ -641,7 +646,7 @@ public final class MemberName implements Member, Cloneable {
* If lookup fails or access is not permitted, a {@linkplain NoAccessException} is thrown. * If lookup fails or access is not permitted, a {@linkplain NoAccessException} is thrown.
* Otherwise a fresh copy of the given member is returned, with modifier bits filled in. * Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
*/ */
public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) { public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) throws NoAccessException {
MemberName result = resolveOrNull(m, searchSupers, lookupClass); MemberName result = resolveOrNull(m, searchSupers, lookupClass);
if (result != null) if (result != null)
return result; return result;
......
...@@ -25,11 +25,8 @@ ...@@ -25,11 +25,8 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle;
import java.dyn.MethodHandles;
import java.dyn.MethodHandles.Lookup; import java.dyn.MethodHandles.Lookup;
import java.dyn.MethodType;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import sun.dyn.util.VerifyType; import sun.dyn.util.VerifyType;
...@@ -46,6 +43,7 @@ import sun.dyn.util.Wrapper; ...@@ -46,6 +43,7 @@ import sun.dyn.util.Wrapper;
import sun.misc.Unsafe; import sun.misc.Unsafe;
import static sun.dyn.MemberName.newIllegalArgumentException; import static sun.dyn.MemberName.newIllegalArgumentException;
import static sun.dyn.MemberName.newNoAccessException; import static sun.dyn.MemberName.newNoAccessException;
import static sun.dyn.MemberName.uncaughtException;
/** /**
* Base class for method handles, containing JVM-specific fields and logic. * Base class for method handles, containing JVM-specific fields and logic.
...@@ -173,7 +171,7 @@ public abstract class MethodHandleImpl { ...@@ -173,7 +171,7 @@ public abstract class MethodHandleImpl {
*/ */
public static public static
MethodHandle findMethod(Access token, MemberName method, MethodHandle findMethod(Access token, MemberName method,
boolean doDispatch, Class<?> lookupClass) { boolean doDispatch, Class<?> lookupClass) throws NoAccessException {
Access.check(token); // only trusted calls Access.check(token); // only trusted calls
MethodType mtype = method.getMethodType(); MethodType mtype = method.getMethodType();
if (!method.isStatic()) { if (!method.isStatic()) {
...@@ -320,7 +318,7 @@ public abstract class MethodHandleImpl { ...@@ -320,7 +318,7 @@ public abstract class MethodHandleImpl {
try { try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true)); VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true));
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
throw new InternalError(""); throw uncaughtException(ex);
} }
} }
// Corresponding generic constructor types: // Corresponding generic constructor types:
...@@ -416,9 +414,7 @@ public abstract class MethodHandleImpl { ...@@ -416,9 +414,7 @@ public abstract class MethodHandleImpl {
f = c.getDeclaredField(field.getName()); f = c.getDeclaredField(field.getName());
return unsafe.staticFieldBase(f); return unsafe.staticFieldBase(f);
} catch (Exception ee) { } catch (Exception ee) {
Error e = new InternalError(); throw uncaughtException(ee);
e.initCause(ee);
throw e;
} }
} }
...@@ -473,10 +469,8 @@ public abstract class MethodHandleImpl { ...@@ -473,10 +469,8 @@ public abstract class MethodHandleImpl {
MethodHandle mh; MethodHandle mh;
try { try {
mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type); mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
} catch (NoAccessException ee) { } catch (NoAccessException ex) {
Error e = new InternalError("name,type="+name+type); throw uncaughtException(ex);
e.initCause(ee);
throw e;
} }
if (evclass != vclass || (!isStatic && ecclass != cclass)) { if (evclass != vclass || (!isStatic && ecclass != cclass)) {
MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic); MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
...@@ -543,10 +537,8 @@ public abstract class MethodHandleImpl { ...@@ -543,10 +537,8 @@ public abstract class MethodHandleImpl {
MethodHandle mh; MethodHandle mh;
try { try {
mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type); mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
} catch (NoAccessException ee) { } catch (NoAccessException ex) {
Error e = new InternalError("name,type="+name+type); throw uncaughtException(ex);
e.initCause(ee);
throw e;
} }
if (caclass != null) { if (caclass != null) {
MethodType strongType = FieldAccessor.atype(caclass, isSetter); MethodType strongType = FieldAccessor.atype(caclass, isSetter);
...@@ -1031,7 +1023,7 @@ public abstract class MethodHandleImpl { ...@@ -1031,7 +1023,7 @@ public abstract class MethodHandleImpl {
try { try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true)); VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true));
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
throw new InternalError(""); throw uncaughtException(ex);
} }
} }
} }
...@@ -1167,7 +1159,7 @@ public abstract class MethodHandleImpl { ...@@ -1167,7 +1159,7 @@ public abstract class MethodHandleImpl {
try { try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true)); VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
throw new InternalError(""); throw uncaughtException(ex);
} }
} }
} }
...@@ -1207,9 +1199,16 @@ public abstract class MethodHandleImpl { ...@@ -1207,9 +1199,16 @@ public abstract class MethodHandleImpl {
return AdapterMethodHandle.makeRetypeRaw(token, type, THROW_EXCEPTION); return AdapterMethodHandle.makeRetypeRaw(token, type, THROW_EXCEPTION);
} }
static final MethodHandle THROW_EXCEPTION static final MethodHandle THROW_EXCEPTION;
static {
try {
THROW_EXCEPTION
= IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException", = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
MethodType.methodType(Empty.class, Throwable.class)); MethodType.methodType(Empty.class, Throwable.class));
} catch (NoAccessException ex) {
throw new RuntimeException(ex);
}
}
static <T extends Throwable> Empty throwException(T t) throws T { throw t; } static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
public static String getNameString(Access token, MethodHandle target) { public static String getNameString(Access token, MethodHandle target) {
......
...@@ -25,9 +25,7 @@ ...@@ -25,9 +25,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.CallSite; import java.dyn.*;
import java.dyn.MethodHandle;
import java.dyn.MethodType;
import java.dyn.MethodHandles.Lookup; import java.dyn.MethodHandles.Lookup;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field; import java.lang.reflect.Field;
...@@ -324,6 +322,7 @@ class MethodHandleNatives { ...@@ -324,6 +322,7 @@ class MethodHandleNatives {
*/ */
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind, static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type) { Class<?> defc, String name, Object type) {
try {
Lookup lookup = IMPL_LOOKUP.in(callerClass); Lookup lookup = IMPL_LOOKUP.in(callerClass);
switch (refKind) { switch (refKind) {
case REF_getField: return lookup.findGetter( defc, name, (Class<?>) type ); case REF_getField: return lookup.findGetter( defc, name, (Class<?>) type );
...@@ -337,5 +336,10 @@ class MethodHandleNatives { ...@@ -337,5 +336,10 @@ class MethodHandleNatives {
case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type ); case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type );
} }
throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type); throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type);
} catch (NoAccessException ex) {
Error err = new IncompatibleClassChangeError();
err.initCause(ex);
throw err;
}
} }
} }
...@@ -25,11 +25,7 @@ ...@@ -25,11 +25,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
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.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
......
...@@ -25,11 +25,7 @@ ...@@ -25,11 +25,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
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.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import sun.dyn.util.ValueConversions; import sun.dyn.util.ValueConversions;
......
...@@ -34,6 +34,7 @@ import java.util.List; ...@@ -34,6 +34,7 @@ import java.util.List;
import sun.dyn.Access; import sun.dyn.Access;
import sun.dyn.AdapterMethodHandle; import sun.dyn.AdapterMethodHandle;
import sun.dyn.MethodHandleImpl; import sun.dyn.MethodHandleImpl;
import static sun.dyn.MemberName.uncaughtException;
public class ValueConversions { public class ValueConversions {
private static final Access IMPL_TOKEN = Access.getToken(); private static final Access IMPL_TOKEN = Access.getToken();
...@@ -148,11 +149,16 @@ public class ValueConversions { ...@@ -148,11 +149,16 @@ public class ValueConversions {
// look up the method // look up the method
String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : ""); String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
MethodType type = unboxType(wrap, raw); MethodType type = unboxType(wrap, raw);
if (!exact) if (!exact) {
try {
// actually, type is wrong; the Java method takes Object // actually, type is wrong; the Java method takes Object
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase()); mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
else } catch (NoAccessException ex) {
mh = null;
}
} else {
mh = retype(type, unbox(wrap, !exact, raw)); mh = retype(type, unbox(wrap, !exact, raw));
}
if (mh != null) { if (mh != null) {
cache.put(wrap, mh); cache.put(wrap, mh);
return mh; return mh;
...@@ -280,10 +286,15 @@ public class ValueConversions { ...@@ -280,10 +286,15 @@ public class ValueConversions {
// look up the method // look up the method
String name = "box" + wrap.simpleName() + (raw ? "Raw" : ""); String name = "box" + wrap.simpleName() + (raw ? "Raw" : "");
MethodType type = boxType(wrap, raw); MethodType type = boxType(wrap, raw);
if (exact) if (exact) {
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
else } catch (NoAccessException ex) {
mh = null;
}
} else {
mh = retype(type.erase(), box(wrap, !exact, raw)); mh = retype(type.erase(), box(wrap, !exact, raw));
}
if (mh != null) { if (mh != null) {
cache.put(wrap, mh); cache.put(wrap, mh);
return mh; return mh;
...@@ -394,10 +405,15 @@ public class ValueConversions { ...@@ -394,10 +405,15 @@ public class ValueConversions {
// look up the method // look up the method
String name = "reboxRaw" + wrap.simpleName(); String name = "reboxRaw" + wrap.simpleName();
MethodType type = reboxType(wrap); MethodType type = reboxType(wrap);
if (exact) if (exact) {
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
else } catch (NoAccessException ex) {
mh = null;
}
} else {
mh = retype(IDENTITY.type(), rebox(wrap, !exact)); mh = retype(IDENTITY.type(), rebox(wrap, !exact));
}
if (mh != null) { if (mh != null) {
cache.put(wrap, mh); cache.put(wrap, mh);
return mh; return mh;
...@@ -474,7 +490,11 @@ public class ValueConversions { ...@@ -474,7 +490,11 @@ public class ValueConversions {
mh = EMPTY; mh = EMPTY;
break; break;
case INT: case LONG: case FLOAT: case DOUBLE: case INT: case LONG: case FLOAT: case DOUBLE:
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type); mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
} catch (NoAccessException ex) {
mh = null;
}
break; break;
} }
if (mh != null) { if (mh != null) {
...@@ -549,8 +569,8 @@ public class ValueConversions { ...@@ -549,8 +569,8 @@ public class ValueConversions {
ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType); ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType);
IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType); IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType);
EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1)); EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1));
} catch (RuntimeException ex) { } catch (Exception ex) {
throw ex; throw uncaughtException(ex);
} }
} }
......
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @summary example code used in javadoc for java.dyn API
* @compile -XDallowTransitionalJSR292=no JavaDocExamples.java
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.JavaDocExamples
*/
/*
---- To run outside jtreg:
$ $JAVA7X_HOME/bin/javac -cp $JUNIT4_JAR -d /tmp/Classes \
$DAVINCI/sources/jdk/test/java/dyn/JavaDocExamples.java
$ $JAVA7X_HOME/bin/java -cp $JUNIT4_JAR:/tmp/Classes \
-XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles \
-Dtest.java.dyn.JavaDocExamples.verbosity=1 \
test.java.dyn.JavaDocExamples
----
*/
package test.java.dyn;
import java.dyn.*;
import static java.dyn.MethodHandles.*;
import static java.dyn.MethodType.*;
import java.lang.reflect.*;
import java.util.*;
import org.junit.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
/**
* @author jrose
*/
public class JavaDocExamples {
/** Wrapper for running the JUnit tests in this module.
* Put JUnit on the classpath!
*/
public static void main(String... ignore) {
org.junit.runner.JUnitCore.runClasses(JavaDocExamples.class);
}
// How much output?
static int verbosity = Integer.getInteger("test.java.dyn.JavaDocExamples.verbosity", 0);
{}
static final private Lookup LOOKUP = lookup();
// static final private MethodHandle CONCAT_1 = LOOKUP.findVirtual(String.class,
// "concat", methodType(String.class, String.class));
// static final private MethodHandle HASHCODE_1 = LOOKUP.findVirtual(Object.class,
// "hashCode", methodType(int.class));
// form required if NoAccessException is intercepted:
static final private MethodHandle CONCAT_2, HASHCODE_2;
static {
try {
CONCAT_2 = LOOKUP.findVirtual(String.class,
"concat", methodType(String.class, String.class));
HASHCODE_2 = LOOKUP.findVirtual(Object.class,
"hashCode", methodType(int.class));
} catch (NoAccessException ex) {
throw new RuntimeException(ex);
}
}
{}
@Test public void testFindVirtual() throws Throwable {
{}
MethodHandle CONCAT_3 = LOOKUP.findVirtual(String.class,
"concat", methodType(String.class, String.class));
MethodHandle HASHCODE_3 = LOOKUP.findVirtual(Object.class,
"hashCode", methodType(int.class));
//assertEquals("xy", (String) CONCAT_1.invokeExact("x", "y"));
assertEquals("xy", (String) CONCAT_2.<String>invokeExact("x", "y"));
assertEquals("xy", (String) CONCAT_3.<String>invokeExact("x", "y"));
//assertEquals("xy".hashCode(), (int) HASHCODE_1.<int>invokeExact((Object)"xy"));
assertEquals("xy".hashCode(), (int) HASHCODE_2.<int>invokeExact((Object)"xy"));
assertEquals("xy".hashCode(), (int) HASHCODE_3.<int>invokeExact((Object)"xy"));
{}
}
@Test public void testDropArguments() throws Throwable {
{{
{} /// JAVADOC
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
cat = cat.asType(methodType(Object.class, String.class, String.class)); /*(String)*/
assertEquals("xy", /*(String)*/ cat.invokeExact("x", "y"));
MethodHandle d0 = dropArguments(cat, 0, String.class);
assertEquals("yz", /*(String)*/ d0.invokeExact("x", "y", "z"));
MethodHandle d1 = dropArguments(cat, 1, String.class);
assertEquals("xz", /*(String)*/ d1.invokeExact("x", "y", "z"));
MethodHandle d2 = dropArguments(cat, 2, String.class);
assertEquals("xy", /*(String)*/ d2.invokeExact("x", "y", "z"));
MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
assertEquals("xz", /*(String)*/ d12.invokeExact("x", 12, true, "z"));
}}
}
static void assertEquals(Object exp, Object act) {
if (verbosity > 0)
System.out.println("result: "+act);
Assert.assertEquals(exp, act);
}
}
...@@ -449,7 +449,7 @@ public class MethodHandlesTest { ...@@ -449,7 +449,7 @@ public class MethodHandlesTest {
countTest(positive); countTest(positive);
MethodType type = MethodType.methodType(ret, params); MethodType type = MethodType.methodType(ret, params);
MethodHandle target = null; MethodHandle target = null;
RuntimeException noAccess = null; Exception noAccess = null;
try { try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.findStatic(defc, name, type); target = lookup.findStatic(defc, name, type);
...@@ -513,7 +513,7 @@ public class MethodHandlesTest { ...@@ -513,7 +513,7 @@ public class MethodHandlesTest {
String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params); MethodType type = MethodType.methodType(ret, params);
MethodHandle target = null; MethodHandle target = null;
RuntimeException noAccess = null; Exception noAccess = null;
try { try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.findVirtual(defc, methodName, type); target = lookup.findVirtual(defc, methodName, type);
...@@ -567,7 +567,7 @@ public class MethodHandlesTest { ...@@ -567,7 +567,7 @@ public class MethodHandlesTest {
countTest(positive); countTest(positive);
MethodType type = MethodType.methodType(ret, params); MethodType type = MethodType.methodType(ret, params);
MethodHandle target = null; MethodHandle target = null;
RuntimeException noAccess = null; Exception noAccess = null;
try { try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.findSpecial(defc, name, type, specialCaller); target = lookup.findSpecial(defc, name, type, specialCaller);
...@@ -623,7 +623,7 @@ public class MethodHandlesTest { ...@@ -623,7 +623,7 @@ public class MethodHandlesTest {
MethodType type = MethodType.methodType(ret, params); MethodType type = MethodType.methodType(ret, params);
Object receiver = randomArg(defc); Object receiver = randomArg(defc);
MethodHandle target = null; MethodHandle target = null;
RuntimeException noAccess = null; Exception noAccess = null;
try { try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.bind(receiver, methodName, type); target = lookup.bind(receiver, methodName, type);
...@@ -688,7 +688,7 @@ public class MethodHandlesTest { ...@@ -688,7 +688,7 @@ public class MethodHandlesTest {
MethodType type = MethodType.methodType(ret, params); MethodType type = MethodType.methodType(ret, params);
Method rmethod = null; Method rmethod = null;
MethodHandle target = null; MethodHandle target = null;
RuntimeException noAccess = null; Exception noAccess = null;
try { try {
rmethod = defc.getDeclaredMethod(name, params); rmethod = defc.getDeclaredMethod(name, params);
} catch (NoSuchMethodException ex) { } catch (NoSuchMethodException ex) {
...@@ -1088,7 +1088,11 @@ public class MethodHandlesTest { ...@@ -1088,7 +1088,11 @@ public class MethodHandlesTest {
if (rtype != Object.class) if (rtype != Object.class)
pfx = rtype.getSimpleName().substring(0, 1).toLowerCase(); pfx = rtype.getSimpleName().substring(0, 1).toLowerCase();
String name = pfx+"id"; String name = pfx+"id";
try {
return PRIVATE.findStatic(Callee.class, name, type); return PRIVATE.findStatic(Callee.class, name, type);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
} }
} }
...@@ -1818,8 +1822,13 @@ public class MethodHandlesTest { ...@@ -1818,8 +1822,13 @@ public class MethodHandlesTest {
testCastFailure("unbox/return", 11000); testCastFailure("unbox/return", 11000);
} }
static class Surprise extends JavaMethodHandle { static class Surprise implements MethodHandleProvider {
Surprise() { super("value"); } public MethodHandle asMethodHandle() {
return VALUE.bindTo(this);
}
public MethodHandle asMethodHandle(MethodType type) {
return asMethodHandle().asType(type);
}
Object value(Object x) { Object value(Object x) {
trace("value", x); trace("value", x);
if (boo != null) return boo; if (boo != null) return boo;
...@@ -1834,22 +1843,32 @@ public class MethodHandlesTest { ...@@ -1834,22 +1843,32 @@ public class MethodHandlesTest {
static Object refIdentity(Object x) { trace("ref.x", x); return x; } static Object refIdentity(Object x) { trace("ref.x", x); return x; }
static Integer boxIdentity(Integer x) { trace("box.x", x); return x; } static Integer boxIdentity(Integer x) { trace("box.x", x); return x; }
static int intIdentity(int x) { trace("int.x", x); return x; } static int intIdentity(int x) { trace("int.x", x); return x; }
static MethodHandle REF_IDENTITY = PRIVATE.findStatic( static MethodHandle VALUE, REF_IDENTITY, BOX_IDENTITY, INT_IDENTITY;
static {
try {
VALUE = PRIVATE.findVirtual(
Surprise.class, "value",
MethodType.methodType(Object.class, Object.class));
REF_IDENTITY = PRIVATE.findStatic(
Surprise.class, "refIdentity", Surprise.class, "refIdentity",
MethodType.methodType(Object.class, Object.class)); MethodType.methodType(Object.class, Object.class));
static MethodHandle BOX_IDENTITY = PRIVATE.findStatic( BOX_IDENTITY = PRIVATE.findStatic(
Surprise.class, "boxIdentity", Surprise.class, "boxIdentity",
MethodType.methodType(Integer.class, Integer.class)); MethodType.methodType(Integer.class, Integer.class));
static MethodHandle INT_IDENTITY = PRIVATE.findStatic( INT_IDENTITY = PRIVATE.findStatic(
Surprise.class, "intIdentity", Surprise.class, "intIdentity",
MethodType.methodType(int.class, int.class)); MethodType.methodType(int.class, int.class));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
} }
void testCastFailure(String mode, int okCount) throws Throwable { void testCastFailure(String mode, int okCount) throws Throwable {
countTest(false); countTest(false);
if (verbosity > 2) System.out.println("mode="+mode); if (verbosity > 2) System.out.println("mode="+mode);
Surprise boo = new Surprise(); Surprise boo = new Surprise();
MethodHandle identity = Surprise.REF_IDENTITY, surprise = boo; MethodHandle identity = Surprise.REF_IDENTITY, surprise0 = boo.asMethodHandle(), surprise = surprise0;
if (mode.endsWith("/return")) { if (mode.endsWith("/return")) {
if (mode.equals("unbox/return")) { if (mode.equals("unbox/return")) {
// fail on return to ((Integer)surprise).intValue // fail on return to ((Integer)surprise).intValue
...@@ -1875,7 +1894,7 @@ public class MethodHandlesTest { ...@@ -1875,7 +1894,7 @@ public class MethodHandlesTest {
identity = MethodHandles.filterArguments(callee, identity); identity = MethodHandles.filterArguments(callee, identity);
} }
} }
assertNotSame(mode, surprise, boo); assertNotSame(mode, surprise, surprise0);
identity = MethodHandles.convertArguments(identity, MethodType.genericMethodType(1)); identity = MethodHandles.convertArguments(identity, MethodType.genericMethodType(1));
surprise = MethodHandles.convertArguments(surprise, MethodType.genericMethodType(1)); surprise = MethodHandles.convertArguments(surprise, MethodType.genericMethodType(1));
Object x = 42; Object x = 42;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册