8008688: Make MethodHandleInfo public
A major overhaul to MethodHandleInfo and method handles in general. Reviewed-by: vlivanov, twisti
This commit is contained in:
parent
7fc1c28757
commit
59440ee0be
@ -124,7 +124,7 @@ import static sun.invoke.util.Wrapper.isWrapperType;
|
||||
this.samMethodType = samMethodType;
|
||||
|
||||
this.implMethod = implMethod;
|
||||
this.implInfo = new MethodHandleInfo(implMethod);
|
||||
this.implInfo = caller.revealDirect(implMethod);
|
||||
// @@@ Temporary work-around pending resolution of 8005119
|
||||
this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial)
|
||||
? MethodHandleInfo.REF_invokeVirtual
|
||||
|
145
jdk/src/share/classes/java/lang/invoke/InfoFromMemberName.java
Normal file
145
jdk/src/share/classes/java/lang/invoke/InfoFromMemberName.java
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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.
|
||||
*/
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.security.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.invoke.MethodHandleNatives.Constants;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/*
|
||||
* Auxiliary to MethodHandleInfo, wants to nest in MethodHandleInfo but must be non-public.
|
||||
*/
|
||||
/*non-public*/
|
||||
final
|
||||
class InfoFromMemberName implements MethodHandleInfo {
|
||||
private final MemberName member;
|
||||
private final int referenceKind;
|
||||
|
||||
InfoFromMemberName(Lookup lookup, MemberName member, byte referenceKind) {
|
||||
assert(member.isResolved() || member.isMethodHandleInvoke());
|
||||
assert(member.referenceKindIsConsistentWith(referenceKind));
|
||||
this.member = member;
|
||||
this.referenceKind = referenceKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDeclaringClass() {
|
||||
return member.getDeclaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return member.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodType getMethodType() {
|
||||
return member.getMethodOrFieldType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModifiers() {
|
||||
return member.getModifiers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReferenceKind() {
|
||||
return referenceKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MethodHandleInfo.toString(getReferenceKind(), getDeclaringClass(), getName(), getMethodType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup) {
|
||||
if (member.isMethodHandleInvoke() && !member.isVarargs()) {
|
||||
// This member is an instance of a signature-polymorphic method, which cannot be reflected
|
||||
// A method handle invoker can come in either of two forms:
|
||||
// A generic placeholder (present in the source code, and varargs)
|
||||
// and a signature-polymorphic instance (synthetic and not varargs).
|
||||
// For more information see comments on {@link MethodHandleNatives#linkMethod}.
|
||||
throw new IllegalArgumentException("cannot reflect signature polymorphic method");
|
||||
}
|
||||
Member mem = AccessController.doPrivileged(new PrivilegedAction<Member>() {
|
||||
public Member run() {
|
||||
try {
|
||||
return reflectUnchecked();
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
Class<?> defc = getDeclaringClass();
|
||||
byte refKind = (byte) getReferenceKind();
|
||||
lookup.checkAccess(refKind, defc, convertToMemberName(refKind, mem));
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
return expected.cast(mem);
|
||||
}
|
||||
|
||||
private Member reflectUnchecked() throws ReflectiveOperationException {
|
||||
byte refKind = (byte) getReferenceKind();
|
||||
Class<?> defc = getDeclaringClass();
|
||||
boolean isPublic = Modifier.isPublic(getModifiers());
|
||||
if (MethodHandleNatives.refKindIsMethod(refKind)) {
|
||||
if (isPublic)
|
||||
return defc.getMethod(getName(), getMethodType().parameterArray());
|
||||
else
|
||||
return defc.getDeclaredMethod(getName(), getMethodType().parameterArray());
|
||||
} else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
|
||||
if (isPublic)
|
||||
return defc.getConstructor(getMethodType().parameterArray());
|
||||
else
|
||||
return defc.getDeclaredConstructor(getMethodType().parameterArray());
|
||||
} else if (MethodHandleNatives.refKindIsField(refKind)) {
|
||||
if (isPublic)
|
||||
return defc.getField(getName());
|
||||
else
|
||||
return defc.getDeclaredField(getName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("referenceKind="+refKind);
|
||||
}
|
||||
}
|
||||
|
||||
private static MemberName convertToMemberName(byte refKind, Member mem) throws IllegalAccessException {
|
||||
if (mem instanceof Method) {
|
||||
boolean wantSpecial = (refKind == REF_invokeSpecial);
|
||||
return new MemberName((Method) mem, wantSpecial);
|
||||
} else if (mem instanceof Constructor) {
|
||||
return new MemberName((Constructor) mem);
|
||||
} else if (mem instanceof Field) {
|
||||
boolean isSetter = (refKind == REF_putField || refKind == REF_putStatic);
|
||||
return new MemberName((Field) mem, isSetter);
|
||||
}
|
||||
throw new InternalError(mem.getClass().getName());
|
||||
}
|
||||
}
|
@ -87,6 +87,7 @@ class Invokers {
|
||||
lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_INVOKER);
|
||||
invoker = SimpleMethodHandle.make(invokerType, lform);
|
||||
}
|
||||
invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invokeExact", mtype));
|
||||
assert(checkInvoker(invoker));
|
||||
exactInvoker = invoker;
|
||||
return invoker;
|
||||
@ -110,6 +111,7 @@ class Invokers {
|
||||
lform = invokeForm(mtype, true, MethodTypeForm.LF_GEN_INVOKER);
|
||||
invoker = SimpleMethodHandle.make(invokerType, lform);
|
||||
}
|
||||
invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invoke", mtype));
|
||||
assert(checkInvoker(invoker));
|
||||
generalInvoker = invoker;
|
||||
return invoker;
|
||||
|
@ -320,14 +320,18 @@ import java.util.Objects;
|
||||
|
||||
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
|
||||
public boolean isMethodHandleInvoke() {
|
||||
final int bits = Modifier.NATIVE | Modifier.FINAL;
|
||||
final int bits = MH_INVOKE_MODS;
|
||||
final int negs = Modifier.STATIC;
|
||||
if (testFlags(bits | negs, bits) &&
|
||||
clazz == MethodHandle.class) {
|
||||
return name.equals("invoke") || name.equals("invokeExact");
|
||||
return isMethodHandleInvokeName(name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static boolean isMethodHandleInvokeName(String name) {
|
||||
return name.equals("invoke") || name.equals("invokeExact");
|
||||
}
|
||||
private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC;
|
||||
|
||||
/** Utility method to query the modifier flags of this member. */
|
||||
public boolean isStatic() {
|
||||
@ -482,12 +486,27 @@ import java.util.Objects;
|
||||
m.getClass(); // NPE check
|
||||
// fill in vmtarget, vmindex while we have m in hand:
|
||||
MethodHandleNatives.init(this, m);
|
||||
if (clazz == null) { // MHN.init failed
|
||||
if (m.getDeclaringClass() == MethodHandle.class &&
|
||||
isMethodHandleInvokeName(m.getName())) {
|
||||
// The JVM did not reify this signature-polymorphic instance.
|
||||
// Need a special case here.
|
||||
// See comments on MethodHandleNatives.linkMethod.
|
||||
MethodType type = MethodType.methodType(m.getReturnType(), m.getParameterTypes());
|
||||
int flags = flagsMods(IS_METHOD, m.getModifiers(), REF_invokeVirtual);
|
||||
init(MethodHandle.class, m.getName(), type, flags);
|
||||
if (isMethodHandleInvoke())
|
||||
return;
|
||||
}
|
||||
throw new LinkageError(m.toString());
|
||||
}
|
||||
assert(isResolved() && this.clazz != null);
|
||||
this.name = m.getName();
|
||||
if (this.type == null)
|
||||
this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
|
||||
if (wantSpecial) {
|
||||
assert(!isAbstract()) : this;
|
||||
if (isAbstract())
|
||||
throw new AbstractMethodError(this.toString());
|
||||
if (getReferenceKind() == REF_invokeVirtual)
|
||||
changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
|
||||
else if (getReferenceKind() == REF_invokeInterface)
|
||||
@ -562,6 +581,22 @@ import java.util.Objects;
|
||||
initResolved(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a name for a signature-polymorphic invoker.
|
||||
* This is a placeholder for a signature-polymorphic instance
|
||||
* (of MH.invokeExact, etc.) that the JVM does not reify.
|
||||
* See comments on {@link MethodHandleNatives#linkMethod}.
|
||||
*/
|
||||
static MemberName makeMethodHandleInvoke(String name, MethodType type) {
|
||||
return makeMethodHandleInvoke(name, type, MH_INVOKE_MODS | SYNTHETIC);
|
||||
}
|
||||
static MemberName makeMethodHandleInvoke(String name, MethodType type, int mods) {
|
||||
MemberName mem = new MemberName(MethodHandle.class, name, type, REF_invokeVirtual);
|
||||
mem.flags |= mods; // it's not resolved, but add these modifiers anyway
|
||||
assert(mem.isMethodHandleInvoke()) : mem;
|
||||
return mem;
|
||||
}
|
||||
|
||||
// bare-bones constructor; the JVM will fill it in
|
||||
MemberName() { }
|
||||
|
||||
|
@ -1284,6 +1284,11 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
return null; // DMH returns DMH.member
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle withInternalMemberName(MemberName member) {
|
||||
return MethodHandleImpl.makeWrappedMember(this, member);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
boolean isInvokeSpecial() {
|
||||
return false; // DMH.Special returns true
|
||||
@ -1356,7 +1361,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
MethodHandle rebind() {
|
||||
// Bind 'this' into a new invoker, of the known class BMH.
|
||||
MethodType type2 = type();
|
||||
LambdaForm form2 = reinvokerForm(type2.basicType());
|
||||
LambdaForm form2 = reinvokerForm(this);
|
||||
// form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
|
||||
return BoundMethodHandle.bindSingle(type2, form2, this);
|
||||
}
|
||||
@ -1369,23 +1374,38 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
/** Create a LF which simply reinvokes a target of the given basic type.
|
||||
* The target MH must override {@link #reinvokerTarget} to provide the target.
|
||||
*/
|
||||
static LambdaForm reinvokerForm(MethodType mtype) {
|
||||
mtype = mtype.basicType();
|
||||
static LambdaForm reinvokerForm(MethodHandle target) {
|
||||
MethodType mtype = target.type().basicType();
|
||||
LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
|
||||
if (reinvoker != null) return reinvoker;
|
||||
MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
|
||||
if (mtype.parameterSlotCount() >= MethodType.MAX_MH_ARITY)
|
||||
return makeReinvokerForm(target.type(), target); // cannot cache this
|
||||
reinvoker = makeReinvokerForm(mtype, null);
|
||||
return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, reinvoker);
|
||||
}
|
||||
private static LambdaForm makeReinvokerForm(MethodType mtype, MethodHandle customTargetOrNull) {
|
||||
boolean customized = (customTargetOrNull != null);
|
||||
MethodHandle MH_invokeBasic = customized ? null : MethodHandles.basicInvoker(mtype);
|
||||
final int THIS_BMH = 0;
|
||||
final int ARG_BASE = 1;
|
||||
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
|
||||
int nameCursor = ARG_LIMIT;
|
||||
final int NEXT_MH = nameCursor++;
|
||||
final int NEXT_MH = customized ? -1 : nameCursor++;
|
||||
final int REINVOKE = nameCursor++;
|
||||
LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
|
||||
names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
|
||||
Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
|
||||
targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
|
||||
names[REINVOKE] = new LambdaForm.Name(MH_invokeBasic, targetArgs);
|
||||
return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
|
||||
Object[] targetArgs;
|
||||
MethodHandle targetMH;
|
||||
if (customized) {
|
||||
targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class);
|
||||
targetMH = customTargetOrNull;
|
||||
} else {
|
||||
names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
|
||||
targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
|
||||
targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
|
||||
targetMH = MethodHandles.basicInvoker(mtype);
|
||||
}
|
||||
names[REINVOKE] = new LambdaForm.Name(targetMH, targetArgs);
|
||||
return new LambdaForm("BMH.reinvoke", ARG_LIMIT, names);
|
||||
}
|
||||
|
||||
private static final LambdaForm.NamedFunction NF_reinvokerTarget;
|
||||
|
@ -317,7 +317,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
private MethodHandle cache;
|
||||
|
||||
AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
|
||||
super(type, reinvokerForm(type));
|
||||
super(type, reinvokerForm(target));
|
||||
this.target = target;
|
||||
this.arrayType = arrayType;
|
||||
this.cache = target.asCollector(arrayType, 0);
|
||||
@ -778,16 +778,27 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
|
||||
|
||||
static MethodHandle FAKE_METHOD_HANDLE_INVOKE;
|
||||
static
|
||||
MethodHandle fakeMethodHandleInvoke(MemberName method) {
|
||||
MethodType type = method.getInvocationType();
|
||||
assert(type.equals(MethodType.methodType(Object.class, Object[].class)));
|
||||
MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE;
|
||||
static MethodHandle[] FAKE_METHOD_HANDLE_INVOKE = new MethodHandle[2];
|
||||
static MethodHandle fakeMethodHandleInvoke(MemberName method) {
|
||||
int idx;
|
||||
assert(method.isMethodHandleInvoke());
|
||||
switch (method.getName()) {
|
||||
case "invoke": idx = 0; break;
|
||||
case "invokeExact": idx = 1; break;
|
||||
default: throw new InternalError(method.getName());
|
||||
}
|
||||
MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE[idx];
|
||||
if (mh != null) return mh;
|
||||
mh = throwException(type.insertParameterTypes(0, UnsupportedOperationException.class));
|
||||
MethodType type = MethodType.methodType(Object.class, UnsupportedOperationException.class,
|
||||
MethodHandle.class, Object[].class);
|
||||
mh = throwException(type);
|
||||
mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively invoke MethodHandle"));
|
||||
FAKE_METHOD_HANDLE_INVOKE = mh;
|
||||
if (!method.getInvocationType().equals(mh.type()))
|
||||
throw new InternalError(method.toString());
|
||||
mh = mh.withInternalMemberName(method);
|
||||
mh = mh.asVarargsCollector(Object[].class);
|
||||
assert(method.isVarargs());
|
||||
FAKE_METHOD_HANDLE_INVOKE[idx] = mh;
|
||||
return mh;
|
||||
}
|
||||
|
||||
@ -821,7 +832,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
MethodHandle vamh = prepareForInvoker(mh);
|
||||
// Cache the result of makeInjectedInvoker once per argument class.
|
||||
MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass);
|
||||
return restoreToType(bccInvoker.bindTo(vamh), mh.type());
|
||||
return restoreToType(bccInvoker.bindTo(vamh), mh.type(), mh.internalMemberName());
|
||||
}
|
||||
|
||||
private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
|
||||
@ -876,8 +887,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
|
||||
// Undo the adapter effect of prepareForInvoker:
|
||||
private static MethodHandle restoreToType(MethodHandle vamh, MethodType type) {
|
||||
return vamh.asCollector(Object[].class, type.parameterCount()).asType(type);
|
||||
private static MethodHandle restoreToType(MethodHandle vamh, MethodType type, MemberName member) {
|
||||
MethodHandle mh = vamh.asCollector(Object[].class, type.parameterCount());
|
||||
mh = mh.asType(type);
|
||||
mh = mh.withInternalMemberName(member);
|
||||
return mh;
|
||||
}
|
||||
|
||||
private static final MethodHandle MH_checkCallerClass;
|
||||
@ -939,4 +953,41 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */
|
||||
static class WrappedMember extends MethodHandle {
|
||||
private final MethodHandle target;
|
||||
private final MemberName member;
|
||||
|
||||
private WrappedMember(MethodHandle target, MethodType type, MemberName member) {
|
||||
super(type, reinvokerForm(target));
|
||||
this.target = target;
|
||||
this.member = member;
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle reinvokerTarget() {
|
||||
return target;
|
||||
}
|
||||
@Override
|
||||
MemberName internalMemberName() {
|
||||
return member;
|
||||
}
|
||||
@Override
|
||||
boolean isInvokeSpecial() {
|
||||
return target.isInvokeSpecial();
|
||||
}
|
||||
@Override
|
||||
MethodHandle viewAsType(MethodType newType) {
|
||||
return new WrappedMember(target, newType, member);
|
||||
}
|
||||
}
|
||||
|
||||
static MethodHandle makeWrappedMember(MethodHandle target, MemberName member) {
|
||||
if (member.equals(target.internalMemberName()))
|
||||
return target;
|
||||
return new WrappedMember(target, target.type(), member);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,80 +24,246 @@
|
||||
*/
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.lang.invoke.MethodHandleNatives.Constants;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* Cracking (reflecting) method handles back into their constituent symbolic parts.
|
||||
* A symbolic reference obtained by cracking a method handle into its consitutent symbolic parts.
|
||||
* To crack a direct method handle, call {@link Lookup#revealDirect Lookup.revealDirect}.
|
||||
* <p>
|
||||
* A <em>direct method handle</em> represents a method, constructor, or field without
|
||||
* any intervening argument bindings or other transformations.
|
||||
* The method, constructor, or field referred to by a direct method handle is called
|
||||
* its <em>underlying member</em>.
|
||||
* Direct method handles may be obtained in any of these ways:
|
||||
* <ul>
|
||||
* <li>By executing an {@code ldc} instruction on a {@code CONSTANT_MethodHandle} constant.
|
||||
* (See the Java Virtual Machine Specification, sections 4.4.8 and 5.4.3.)
|
||||
* <li>By calling one of the <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>,
|
||||
* such as {@link Lookup#findVirtual Lookup.findVirtual},
|
||||
* to resolve a symbolic reference into a method handle.
|
||||
* A symbolic reference consists of a class, name string, and type.
|
||||
* <li>By calling the factory method {@link Lookup#unreflect Lookup.unreflect}
|
||||
* or {@link Lookup#unreflectSpecial Lookup.unreflectSpecial}
|
||||
* to convert a {@link Method} into a method handle.
|
||||
* <li>By calling the factory method {@link Lookup#unreflectConstructor Lookup.unreflectConstructor}
|
||||
* to convert a {@link Constructor} into a method handle.
|
||||
* <li>By calling the factory method {@link Lookup#unreflectGetter Lookup.unreflectGetter}
|
||||
* or {@link Lookup#unreflectSetter Lookup.unreflectSetter}
|
||||
* to convert a {@link Field} into a method handle.
|
||||
* </ul>
|
||||
* In all of these cases, it is possible to crack the resulting direct method handle
|
||||
* to recover a symbolic reference for the underlying method, constructor, or field.
|
||||
* Cracking must be done via a {@code Lookup} object equivalent to that which created
|
||||
* the target method handle, or which has enough access permissions to recreate
|
||||
* an equivalent method handle.
|
||||
*
|
||||
* <h1><a name="refkinds"></a>Reference kinds</h1>
|
||||
* The <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>
|
||||
* correspond to all major use cases for methods, constructors, and fields.
|
||||
* These use cases may be distinguished using small integers as follows:
|
||||
* <table border=1 cellpadding=5 summary="reference kinds">
|
||||
* <tr><th>reference kind</th><th>descriptive name</th><th>scope</th><th>member</th><th>behavior</th></tr>
|
||||
* <tr>
|
||||
* <td>{@code 1}</td><td>{@code REF_getField}</td><td>{@code class}</td>
|
||||
* <td>{@code FT f;}</td><td>{@code (T) this.f;}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 2}</td><td>{@code REF_getStatic}</td><td>{@code class} or {@code interface}</td>
|
||||
* <td>{@code static}<br>{@code FT f;}</td><td>{@code (T) C.f;}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 3}</td><td>{@code REF_putField}</td><td>{@code class}</td>
|
||||
* <td>{@code FT f;}</td><td>{@code this.f = x;}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 4}</td><td>{@code REF_putStatic}</td><td>{@code class}</td>
|
||||
* <td>{@code static}<br>{@code FT f;}</td><td>{@code C.f = arg;}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 5}</td><td>{@code REF_invokeVirtual}</td><td>{@code class}</td>
|
||||
* <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 6}</td><td>{@code REF_invokeStatic}</td><td>{@code class} or {@code interface}</td>
|
||||
* <td>{@code static}<br>{@code T m(A*);}</td><td>{@code (T) C.m(arg*);}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 7}</td><td>{@code REF_invokeSpecial}</td><td>{@code class} or {@code interface}</td>
|
||||
* <td>{@code T m(A*);}</td><td>{@code (T) super.m(arg*);}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 8}</td><td>{@code REF_newInvokeSpecial}</td><td>{@code class}</td>
|
||||
* <td>{@code C(A*);}</td><td>{@code new C(arg*);}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 9}</td><td>{@code REF_invokeInterface}</td><td>{@code interface}</td>
|
||||
* <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* @since 1.8
|
||||
*/
|
||||
final class MethodHandleInfo {
|
||||
public static final int
|
||||
REF_getField = Constants.REF_getField,
|
||||
REF_getStatic = Constants.REF_getStatic,
|
||||
REF_putField = Constants.REF_putField,
|
||||
REF_putStatic = Constants.REF_putStatic,
|
||||
REF_invokeVirtual = Constants.REF_invokeVirtual,
|
||||
REF_invokeStatic = Constants.REF_invokeStatic,
|
||||
REF_invokeSpecial = Constants.REF_invokeSpecial,
|
||||
REF_newInvokeSpecial = Constants.REF_newInvokeSpecial,
|
||||
REF_invokeInterface = Constants.REF_invokeInterface;
|
||||
public
|
||||
interface MethodHandleInfo {
|
||||
/**
|
||||
* A direct method handle reference kind,
|
||||
* as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
|
||||
*/
|
||||
public static final int
|
||||
REF_getField = Constants.REF_getField,
|
||||
REF_getStatic = Constants.REF_getStatic,
|
||||
REF_putField = Constants.REF_putField,
|
||||
REF_putStatic = Constants.REF_putStatic,
|
||||
REF_invokeVirtual = Constants.REF_invokeVirtual,
|
||||
REF_invokeStatic = Constants.REF_invokeStatic,
|
||||
REF_invokeSpecial = Constants.REF_invokeSpecial,
|
||||
REF_newInvokeSpecial = Constants.REF_newInvokeSpecial,
|
||||
REF_invokeInterface = Constants.REF_invokeInterface;
|
||||
|
||||
private final Class<?> declaringClass;
|
||||
private final String name;
|
||||
private final MethodType methodType;
|
||||
private final int referenceKind;
|
||||
/**
|
||||
* Returns the reference kind of the cracked method handle, which in turn
|
||||
* determines whether the method handle's underlying member was a constructor, method, or field.
|
||||
* See the <a href="MethodHandleInfo.html#refkinds">table above</a> for definitions.
|
||||
* @return the integer code for the kind of reference used to access the underlying member
|
||||
*/
|
||||
public int getReferenceKind();
|
||||
|
||||
public MethodHandleInfo(MethodHandle mh) {
|
||||
MemberName mn = mh.internalMemberName();
|
||||
if (mn == null) throw new IllegalArgumentException("not a direct method handle");
|
||||
this.declaringClass = mn.getDeclaringClass();
|
||||
this.name = mn.getName();
|
||||
this.methodType = mn.getMethodOrFieldType();
|
||||
byte refKind = mn.getReferenceKind();
|
||||
if (refKind == REF_invokeSpecial && !mh.isInvokeSpecial())
|
||||
// Devirtualized method invocation is usually formally virtual.
|
||||
refKind = REF_invokeVirtual;
|
||||
this.referenceKind = refKind;
|
||||
}
|
||||
/**
|
||||
* Returns the class in which the cracked method handle's underlying member was defined.
|
||||
* @return the declaring class of the underlying member
|
||||
*/
|
||||
public Class<?> getDeclaringClass();
|
||||
|
||||
public Class<?> getDeclaringClass() {
|
||||
return declaringClass;
|
||||
}
|
||||
/**
|
||||
* Returns the name of the cracked method handle's underlying member.
|
||||
* This is {@code "<init>"} if the underlying member was a constructor,
|
||||
* else it is a simple method name or field name.
|
||||
* @return the simple name of the underlying member
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
/**
|
||||
* Returns the nominal type of the cracked symbolic reference, expressed as a method type.
|
||||
* If the reference is to a constructor, the return type will be {@code void}.
|
||||
* If it is to a non-static method, the method type will not mention the {@code this} parameter.
|
||||
* If it is to a field and the requested access is to read the field,
|
||||
* the method type will have no parameters and return the field type.
|
||||
* If it is to a field and the requested access is to write the field,
|
||||
* the method type will have one parameter of the field type and return {@code void}.
|
||||
* <p>
|
||||
* Note that original direct method handle may include a leading {@code this} parameter,
|
||||
* or (in the case of a constructor) will replace the {@code void} return type
|
||||
* with the constructed class.
|
||||
* The nominal type does not include any {@code this} parameter,
|
||||
* and (in the case of a constructor) will return {@code void}.
|
||||
* @return the type of the underlying member, expressed as a method type
|
||||
*/
|
||||
public MethodType getMethodType();
|
||||
|
||||
public MethodType getMethodType() {
|
||||
return methodType;
|
||||
}
|
||||
// Utility methods.
|
||||
// NOTE: class/name/type and reference kind constitute a symbolic reference
|
||||
// member and modifiers are an add-on, derived from Core Reflection (or the equivalent)
|
||||
|
||||
public int getModifiers() {
|
||||
return -1; //TODO
|
||||
}
|
||||
/**
|
||||
* Reflects the underlying member as a method, constructor, or field object.
|
||||
* If the underlying member is public, it is reflected as if by
|
||||
* {@code getMethod}, {@code getConstructor}, or {@code getField}.
|
||||
* Otherwise, it is reflected as if by
|
||||
* {@code getDeclaredMethod}, {@code getDeclaredConstructor}, or {@code getDeclaredField}.
|
||||
* The underlying member must be accessible to the given lookup object.
|
||||
* @param <T> the desired type of the result, either {@link Member} or a subtype
|
||||
* @param expected a class object representing the desired result type {@code T}
|
||||
* @param lookup the lookup object that created this MethodHandleInfo, or one with equivalent access privileges
|
||||
* @return a reference to the method, constructor, or field object
|
||||
* @exception ClassCastException if the member is not of the expected type
|
||||
* @exception NullPointerException if either argument is {@code null}
|
||||
* @exception IllegalArgumentException if the underlying member is not accessible to the given lookup object
|
||||
*/
|
||||
public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup);
|
||||
|
||||
public int getReferenceKind() {
|
||||
return referenceKind;
|
||||
}
|
||||
/**
|
||||
* Returns the access modifiers of the underlying member.
|
||||
* @return the Java language modifiers for underlying member,
|
||||
* or -1 if the member cannot be accessed
|
||||
* @see Modifier
|
||||
* @see reflectAs
|
||||
*/
|
||||
public int getModifiers();
|
||||
|
||||
static String getReferenceKindString(int referenceKind) {
|
||||
switch (referenceKind) {
|
||||
case REF_getField: return "getfield";
|
||||
case REF_getStatic: return "getstatic";
|
||||
case REF_putField: return "putfield";
|
||||
case REF_putStatic: return "putstatic";
|
||||
case REF_invokeVirtual: return "invokevirtual";
|
||||
case REF_invokeStatic: return "invokestatic";
|
||||
case REF_invokeSpecial: return "invokespecial";
|
||||
case REF_newInvokeSpecial: return "newinvokespecial";
|
||||
case REF_invokeInterface: return "invokeinterface";
|
||||
default: return "UNKNOWN_REFENCE_KIND" + "[" + referenceKind + "]";
|
||||
}
|
||||
/**
|
||||
* Determines if the underlying member was a variable arity method or constructor.
|
||||
* Such members are represented by method handles that are varargs collectors.
|
||||
* @implSpec
|
||||
* This produces a result equivalent to:
|
||||
* <pre>{@code
|
||||
* getReferenceKind() >= REF_invokeVirtual && Modifier.isTransient(getModifiers())
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* @return {@code true} if and only if the underlying member was declared with variable arity.
|
||||
*/
|
||||
// spelling derived from java.lang.reflect.Executable, not MethodHandle.isVarargsCollector
|
||||
public default boolean isVarArgs() {
|
||||
// fields are never varargs:
|
||||
if (MethodHandleNatives.refKindIsField((byte) getReferenceKind()))
|
||||
return false;
|
||||
// not in the public API: Modifier.VARARGS
|
||||
final int ACC_VARARGS = 0x00000080; // from JVMS 4.6 (Table 4.20)
|
||||
assert(ACC_VARARGS == Modifier.TRANSIENT);
|
||||
return Modifier.isTransient(getModifiers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s %s.%s:%s", getReferenceKindString(referenceKind),
|
||||
declaringClass.getName(), name, methodType);
|
||||
/**
|
||||
* Returns the descriptive name of the given reference kind,
|
||||
* as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
|
||||
* The conventional prefix "REF_" is omitted.
|
||||
* @param referenceKind an integer code for a kind of reference used to access a class member
|
||||
* @return a mixed-case string such as {@code "getField"}
|
||||
* @exception IllegalArgumentException if the argument is not a valid
|
||||
* <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
|
||||
*/
|
||||
public static String referenceKindToString(int referenceKind) {
|
||||
if (!MethodHandleNatives.refKindIsValid(referenceKind))
|
||||
throw newIllegalArgumentException("invalid reference kind", referenceKind);
|
||||
return MethodHandleNatives.refKindName((byte)referenceKind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation for a {@code MethodHandleInfo},
|
||||
* given the four parts of its symbolic reference.
|
||||
* This is defined to be of the form {@code "RK C.N:MT"}, where {@code RK} is the
|
||||
* {@linkplain #referenceKindToString reference kind string} for {@code kind},
|
||||
* {@code C} is the {@linkplain java.lang.Class#getName name} of {@code defc}
|
||||
* {@code N} is the {@code name}, and
|
||||
* {@code MT} is the {@code type}.
|
||||
* These four values may be obtained from the
|
||||
* {@linkplain #getReferenceKind reference kind},
|
||||
* {@linkplain #getDeclaringClass declaring class},
|
||||
* {@linkplain #getName member name},
|
||||
* and {@linkplain #getMethodType method type}
|
||||
* of a {@code MethodHandleInfo} object.
|
||||
*
|
||||
* @implSpec
|
||||
* This produces a result equivalent to:
|
||||
* <pre>{@code
|
||||
* String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type)
|
||||
* }</pre>
|
||||
*
|
||||
* @param kind the {@linkplain #getReferenceKind reference kind} part of the symbolic reference
|
||||
* @param defc the {@linkplain #getDeclaringClass declaring class} part of the symbolic reference
|
||||
* @param name the {@linkplain #getName member name} part of the symbolic reference
|
||||
* @param type the {@linkplain #getMethodType method type} part of the symbolic reference
|
||||
* @return a string of the form {@code "RK C.N:MT"}
|
||||
* @exception IllegalArgumentException if the first argument is not a valid
|
||||
* <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
|
||||
* @exception NullPointerException if any reference argument is {@code null}
|
||||
*/
|
||||
public static String toString(int kind, Class<?> defc, String name, MethodType type) {
|
||||
Objects.requireNonNull(name); Objects.requireNonNull(type);
|
||||
return String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type);
|
||||
}
|
||||
}
|
||||
|
@ -205,6 +205,9 @@ class MethodHandleNatives {
|
||||
static boolean refKindIsMethod(byte refKind) {
|
||||
return !refKindIsField(refKind) && (refKind != REF_newInvokeSpecial);
|
||||
}
|
||||
static boolean refKindIsConstructor(byte refKind) {
|
||||
return (refKind == REF_newInvokeSpecial);
|
||||
}
|
||||
static boolean refKindHasReceiver(byte refKind) {
|
||||
assert(refKindIsValid(refKind));
|
||||
return (refKind & 1) != 0;
|
||||
@ -313,7 +316,65 @@ class MethodHandleNatives {
|
||||
* The method assumes the following arguments on the stack:
|
||||
* 0: the method handle being invoked
|
||||
* 1-N: the arguments to the method handle invocation
|
||||
* N+1: an implicitly added type argument (the given MethodType)
|
||||
* N+1: an optional, implicitly added argument (typically the given MethodType)
|
||||
* <p>
|
||||
* The nominal method at such a call site is an instance of
|
||||
* a signature-polymorphic method (see @PolymorphicSignature).
|
||||
* Such method instances are user-visible entities which are
|
||||
* "split" from the generic placeholder method in {@code MethodHandle}.
|
||||
* (Note that the placeholder method is not identical with any of
|
||||
* its instances. If invoked reflectively, is guaranteed to throw an
|
||||
* {@code UnsupportedOperationException}.)
|
||||
* If the signature-polymorphic method instance is ever reified,
|
||||
* it appears as a "copy" of the original placeholder
|
||||
* (a native final member of {@code MethodHandle}) except
|
||||
* that its type descriptor has shape required by the instance,
|
||||
* and the method instance is <em>not</em> varargs.
|
||||
* The method instance is also marked synthetic, since the
|
||||
* method (by definition) does not appear in Java source code.
|
||||
* <p>
|
||||
* The JVM is allowed to reify this method as instance metadata.
|
||||
* For example, {@code invokeBasic} is always reified.
|
||||
* But the JVM may instead call {@code linkMethod}.
|
||||
* If the result is an * ordered pair of a {@code (method, appendix)},
|
||||
* the method gets all the arguments (0..N inclusive)
|
||||
* plus the appendix (N+1), and uses the appendix to complete the call.
|
||||
* In this way, one reusable method (called a "linker method")
|
||||
* can perform the function of any number of polymorphic instance
|
||||
* methods.
|
||||
* <p>
|
||||
* Linker methods are allowed to be weakly typed, with any or
|
||||
* all references rewritten to {@code Object} and any primitives
|
||||
* (except {@code long}/{@code float}/{@code double})
|
||||
* rewritten to {@code int}.
|
||||
* A linker method is trusted to return a strongly typed result,
|
||||
* according to the specific method type descriptor of the
|
||||
* signature-polymorphic instance it is emulating.
|
||||
* This can involve (as necessary) a dynamic check using
|
||||
* data extracted from the appendix argument.
|
||||
* <p>
|
||||
* The JVM does not inspect the appendix, other than to pass
|
||||
* it verbatim to the linker method at every call.
|
||||
* This means that the JDK runtime has wide latitude
|
||||
* for choosing the shape of each linker method and its
|
||||
* corresponding appendix.
|
||||
* Linker methods should be generated from {@code LambdaForm}s
|
||||
* so that they do not become visible on stack traces.
|
||||
* <p>
|
||||
* The {@code linkMethod} call is free to omit the appendix
|
||||
* (returning null) and instead emulate the required function
|
||||
* completely in the linker method.
|
||||
* As a corner case, if N==255, no appendix is possible.
|
||||
* In this case, the method returned must be custom-generated to
|
||||
* to perform any needed type checking.
|
||||
* <p>
|
||||
* If the JVM does not reify a method at a call site, but instead
|
||||
* calls {@code linkMethod}, the corresponding call represented
|
||||
* in the bytecodes may mention a valid method which is not
|
||||
* representable with a {@code MemberName}.
|
||||
* Therefore, use cases for {@code linkMethod} tend to correspond to
|
||||
* special cases in reflective code such as {@code findVirtual}
|
||||
* or {@code revealDirect}.
|
||||
*/
|
||||
static MemberName linkMethod(Class<?> callerClass, int refKind,
|
||||
Class<?> defc, String name, Object type,
|
||||
|
@ -26,8 +26,6 @@
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -54,6 +52,7 @@ import sun.security.util.SecurityConstants;
|
||||
* </ul>
|
||||
* <p>
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
*/
|
||||
public class MethodHandles {
|
||||
|
||||
@ -96,6 +95,38 @@ public class MethodHandles {
|
||||
return Lookup.PUBLIC_LOOKUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an unchecked "crack" of a direct method handle.
|
||||
* The result is as if the user had obtained a lookup object capable enough
|
||||
* to crack the target method handle, called
|
||||
* {@link java.lang.invoke.MethodHandles.Lookup#revealDirect Lookup.revealDirect}
|
||||
* on the target to obtain its symbolic reference, and then called
|
||||
* {@link java.lang.invoke.MethodHandleInfo#reflectAs MethodHandleInfo.reflectAs}
|
||||
* to resolve the symbolic reference to a member.
|
||||
* <p>
|
||||
* If there is a security manager, its {@code checkPermission} method
|
||||
* is called with a {@code ReflectPermission("suppressAccessChecks")} permission.
|
||||
* @param <T> the desired type of the result, either {@link Member} or a subtype
|
||||
* @param target a direct method handle to crack into symbolic reference components
|
||||
* @param expected a class object representing the desired result type {@code T}
|
||||
* @return a reference to the method, constructor, or field object
|
||||
* @exception SecurityException if the caller is not privileged to call {@code setAccessible}
|
||||
* @exception NullPointerException if either argument is {@code null}
|
||||
* @exception IllegalArgumentException if the target is not a direct method handle
|
||||
* @exception ClassCastException if the member is not of the expected type
|
||||
* @since 1.8
|
||||
*/
|
||||
public static <T extends Member> T
|
||||
reflectAs(Class<T> expected, MethodHandle target) {
|
||||
SecurityManager smgr = System.getSecurityManager();
|
||||
if (smgr != null) smgr.checkPermission(ACCESS_PERMISSION);
|
||||
Lookup lookup = Lookup.IMPL_LOOKUP; // use maximally privileged lookup
|
||||
return lookup.revealDirect(target).reflectAs(expected, lookup);
|
||||
}
|
||||
// Copied from AccessibleObject, as used by Method.setAccessible, etc.:
|
||||
static final private java.security.Permission ACCESS_PERMISSION =
|
||||
new ReflectPermission("suppressAccessChecks");
|
||||
|
||||
/**
|
||||
* A <em>lookup object</em> is a factory for creating method handles,
|
||||
* when the creation requires access checking.
|
||||
@ -647,6 +678,7 @@ public class MethodHandles {
|
||||
return invoker(type);
|
||||
if ("invokeExact".equals(name))
|
||||
return exactInvoker(type);
|
||||
assert(!MemberName.isMethodHandleInvokeName(name));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -892,6 +924,10 @@ return mh1;
|
||||
* @throws NullPointerException if the argument is null
|
||||
*/
|
||||
public MethodHandle unreflect(Method m) throws IllegalAccessException {
|
||||
if (m.getDeclaringClass() == MethodHandle.class) {
|
||||
MethodHandle mh = unreflectForMH(m);
|
||||
if (mh != null) return mh;
|
||||
}
|
||||
MemberName method = new MemberName(m);
|
||||
byte refKind = method.getReferenceKind();
|
||||
if (refKind == REF_invokeSpecial)
|
||||
@ -900,6 +936,12 @@ return mh1;
|
||||
Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this;
|
||||
return lookup.getDirectMethod(refKind, method.getDeclaringClass(), method, findBoundCallerClass(method));
|
||||
}
|
||||
private MethodHandle unreflectForMH(Method m) {
|
||||
// these names require special lookups because they throw UnsupportedOperationException
|
||||
if (MemberName.isMethodHandleInvokeName(m.getName()))
|
||||
return MethodHandleImpl.fakeMethodHandleInvoke(new MemberName(m));
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a method handle for a reflected method.
|
||||
@ -1004,6 +1046,46 @@ return mh1;
|
||||
return unreflectField(f, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cracks a direct method handle created by this lookup object or a similar one.
|
||||
* Security and access checks are performed to ensure that this lookup object
|
||||
* is capable of reproducing the target method handle.
|
||||
* This means that the cracking may fail if target is a direct method handle
|
||||
* but was created by an unrelated lookup object.
|
||||
* @param target a direct method handle to crack into symbolic reference components
|
||||
* @return a symbolic reference which can be used to reconstruct this method handle from this lookup object
|
||||
* @exception SecurityException if a security manager is present and it
|
||||
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
|
||||
* @throws IllegalArgumentException if the target is not a direct method handle or if access checking fails
|
||||
* @exception NullPointerException if the target is {@code null}
|
||||
* @since 1.8
|
||||
*/
|
||||
public MethodHandleInfo revealDirect(MethodHandle target) {
|
||||
MemberName member = target.internalMemberName();
|
||||
if (member == null || (!member.isResolved() && !member.isMethodHandleInvoke()))
|
||||
throw newIllegalArgumentException("not a direct method handle");
|
||||
Class<?> defc = member.getDeclaringClass();
|
||||
byte refKind = member.getReferenceKind();
|
||||
assert(MethodHandleNatives.refKindIsValid(refKind));
|
||||
if (refKind == REF_invokeSpecial && !target.isInvokeSpecial())
|
||||
// Devirtualized method invocation is usually formally virtual.
|
||||
// To avoid creating extra MemberName objects for this common case,
|
||||
// we encode this extra degree of freedom using MH.isInvokeSpecial.
|
||||
refKind = REF_invokeVirtual;
|
||||
if (refKind == REF_invokeVirtual && defc.isInterface())
|
||||
// Symbolic reference is through interface but resolves to Object method (toString, etc.)
|
||||
refKind = REF_invokeInterface;
|
||||
// Check SM permissions and member access before cracking.
|
||||
try {
|
||||
checkSecurityManager(defc, member);
|
||||
checkAccess(refKind, defc, member);
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
// Produce the handle to the results.
|
||||
return new InfoFromMemberName(this, member, refKind);
|
||||
}
|
||||
|
||||
/// Helper methods, all package-private.
|
||||
|
||||
MemberName resolveOrFail(byte refKind, Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
|
||||
@ -1201,12 +1283,12 @@ return mh1;
|
||||
private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
|
||||
boolean doRestrict, Class<?> callerClass) throws IllegalAccessException {
|
||||
checkMethod(refKind, refc, method);
|
||||
if (method.isMethodHandleInvoke())
|
||||
return fakeMethodHandleInvoke(method);
|
||||
assert(!method.isMethodHandleInvoke());
|
||||
|
||||
Class<?> refcAsSuper;
|
||||
if (refKind == REF_invokeSpecial &&
|
||||
refc != lookupClass() &&
|
||||
!refc.isInterface() &&
|
||||
refc != (refcAsSuper = lookupClass().getSuperclass()) &&
|
||||
refc.isAssignableFrom(lookupClass())) {
|
||||
assert(!method.getName().equals("<init>")); // not this code path
|
||||
@ -1234,9 +1316,6 @@ return mh1;
|
||||
mh = restrictReceiver(method, mh, lookupClass());
|
||||
return mh;
|
||||
}
|
||||
private MethodHandle fakeMethodHandleInvoke(MemberName method) {
|
||||
return throwException(method.getReturnType(), UnsupportedOperationException.class);
|
||||
}
|
||||
private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
|
||||
Class<?> callerClass)
|
||||
throws IllegalAccessException {
|
||||
|
@ -225,7 +225,7 @@ public final class SerializedLambda implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String implKind=MethodHandleInfo.getReferenceKindString(implMethodKind);
|
||||
String implKind=MethodHandleInfo.referenceKindToString(implMethodKind);
|
||||
return String.format("SerializedLambda[%s=%s, %s=%s.%s:%s, " +
|
||||
"%s=%s %s.%s:%s, %s=%s, %s=%d]",
|
||||
"capturingClass", capturingClass,
|
||||
|
@ -35,20 +35,9 @@ import java.util.*;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.*;
|
||||
import static java.lang.invoke.MethodType.*;
|
||||
import static java.lang.invoke.MethodHandleInfo.*;
|
||||
|
||||
public class Test7087570 {
|
||||
// XXX may remove the following constant declarations when MethodHandleInfo is made public
|
||||
private static final int
|
||||
REF_getField = 1,
|
||||
REF_getStatic = 2,
|
||||
REF_putField = 3,
|
||||
REF_putStatic = 4,
|
||||
REF_invokeVirtual = 5,
|
||||
REF_invokeStatic = 6,
|
||||
REF_invokeSpecial = 7,
|
||||
REF_newInvokeSpecial = 8,
|
||||
REF_invokeInterface = 9,
|
||||
REF_LIMIT = 10;
|
||||
|
||||
private static final TestMethodData[] TESTS = new TestMethodData[] {
|
||||
// field accessors
|
||||
@ -87,17 +76,17 @@ public class Test7087570 {
|
||||
}
|
||||
|
||||
private static void doTest(MethodHandle mh, TestMethodData testMethod) {
|
||||
Object mhi = newMethodHandleInfo(mh);
|
||||
MethodHandleInfo mhi = LOOKUP.revealDirect(mh);
|
||||
|
||||
System.out.printf("%s.%s: %s, nominal refKind: %s, actual refKind: %s\n",
|
||||
testMethod.clazz.getName(), testMethod.name, testMethod.methodType,
|
||||
REF_KIND_NAMES[testMethod.referenceKind],
|
||||
REF_KIND_NAMES[getReferenceKind(mhi)]);
|
||||
assertEquals(testMethod.name, getName(mhi));
|
||||
assertEquals(testMethod.methodType, getMethodType(mhi));
|
||||
assertEquals(testMethod.declaringClass, getDeclaringClass(mhi));
|
||||
referenceKindToString(testMethod.referenceKind),
|
||||
referenceKindToString(mhi.getReferenceKind()));
|
||||
assertEquals(testMethod.name, mhi.getName());
|
||||
assertEquals(testMethod.methodType, mhi.getMethodType());
|
||||
assertEquals(testMethod.declaringClass, mhi.getDeclaringClass());
|
||||
assertEquals(testMethod.referenceKind == REF_invokeSpecial, isInvokeSpecial(mh));
|
||||
assertRefKindEquals(testMethod.referenceKind, getReferenceKind(mhi));
|
||||
assertRefKindEquals(testMethod.referenceKind, mhi.getReferenceKind());
|
||||
}
|
||||
|
||||
private static void testWithLookup() throws Throwable {
|
||||
@ -122,50 +111,8 @@ public class Test7087570 {
|
||||
return methodType(void.class, clazz);
|
||||
}
|
||||
|
||||
private static final String[] REF_KIND_NAMES = {
|
||||
"MH::invokeBasic",
|
||||
"REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic",
|
||||
"REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial",
|
||||
"REF_newInvokeSpecial", "REF_invokeInterface"
|
||||
};
|
||||
|
||||
private static final Lookup LOOKUP = lookup();
|
||||
|
||||
// XXX may remove the following reflective logic when MethodHandleInfo is made public
|
||||
private static final MethodHandle MH_IS_INVOKESPECIAL;
|
||||
private static final MethodHandle MHI_CONSTRUCTOR;
|
||||
private static final MethodHandle MHI_GET_NAME;
|
||||
private static final MethodHandle MHI_GET_METHOD_TYPE;
|
||||
private static final MethodHandle MHI_GET_DECLARING_CLASS;
|
||||
private static final MethodHandle MHI_GET_REFERENCE_KIND;
|
||||
|
||||
static {
|
||||
try {
|
||||
// This is white box testing. Use reflection to grab private implementation bits.
|
||||
String magicName = "IMPL_LOOKUP";
|
||||
Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName);
|
||||
// This unit test will fail if a security manager is installed.
|
||||
magicLookup.setAccessible(true);
|
||||
// Forbidden fruit...
|
||||
Lookup directInvokeLookup = (Lookup) magicLookup.get(null);
|
||||
Class<?> mhiClass = Class.forName("java.lang.invoke.MethodHandleInfo", false, MethodHandle.class.getClassLoader());
|
||||
MH_IS_INVOKESPECIAL = directInvokeLookup
|
||||
.findVirtual(MethodHandle.class, "isInvokeSpecial", methodType(boolean.class));
|
||||
MHI_CONSTRUCTOR = directInvokeLookup
|
||||
.findConstructor(mhiClass, methodType(void.class, MethodHandle.class));
|
||||
MHI_GET_NAME = directInvokeLookup
|
||||
.findVirtual(mhiClass, "getName", methodType(String.class));
|
||||
MHI_GET_METHOD_TYPE = directInvokeLookup
|
||||
.findVirtual(mhiClass, "getMethodType", methodType(MethodType.class));
|
||||
MHI_GET_DECLARING_CLASS = directInvokeLookup
|
||||
.findVirtual(mhiClass, "getDeclaringClass", methodType(Class.class));
|
||||
MHI_GET_REFERENCE_KIND = directInvokeLookup
|
||||
.findVirtual(mhiClass, "getReferenceKind", methodType(int.class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestMethodData {
|
||||
final Class<?> clazz;
|
||||
final String name;
|
||||
@ -208,7 +155,9 @@ public class Test7087570 {
|
||||
return LOOKUP.findStatic(testMethod.clazz, testMethod.name, testMethod.methodType);
|
||||
case REF_invokeSpecial:
|
||||
Class<?> thisClass = LOOKUP.lookupClass();
|
||||
return LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
|
||||
MethodHandle smh = LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
|
||||
noteInvokeSpecial(smh);
|
||||
return smh;
|
||||
case REF_newInvokeSpecial:
|
||||
return LOOKUP.findConstructor(testMethod.clazz, testMethod.methodType);
|
||||
default:
|
||||
@ -238,7 +187,9 @@ public class Test7087570 {
|
||||
case REF_invokeSpecial: {
|
||||
Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
|
||||
Class<?> thisClass = LOOKUP.lookupClass();
|
||||
return LOOKUP.unreflectSpecial(m, thisClass);
|
||||
MethodHandle smh = LOOKUP.unreflectSpecial(m, thisClass);
|
||||
noteInvokeSpecial(smh);
|
||||
return smh;
|
||||
}
|
||||
case REF_newInvokeSpecial: {
|
||||
Constructor c = testMethod.clazz.getDeclaredConstructor(testMethod.methodType.parameterArray());
|
||||
@ -249,59 +200,20 @@ public class Test7087570 {
|
||||
}
|
||||
}
|
||||
|
||||
private static Object newMethodHandleInfo(MethodHandle mh) {
|
||||
try {
|
||||
return MHI_CONSTRUCTOR.invoke(mh);
|
||||
} catch (Throwable ex) {
|
||||
throw new Error(ex);
|
||||
}
|
||||
private static List<MethodHandle> specialMethodHandles = new ArrayList<>();
|
||||
private static void noteInvokeSpecial(MethodHandle mh) {
|
||||
specialMethodHandles.add(mh);
|
||||
assert(isInvokeSpecial(mh));
|
||||
}
|
||||
|
||||
private static boolean isInvokeSpecial(MethodHandle mh) {
|
||||
try {
|
||||
return (boolean) MH_IS_INVOKESPECIAL.invokeExact(mh);
|
||||
} catch (Throwable ex) {
|
||||
throw new Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getName(Object mhi) {
|
||||
try {
|
||||
return (String) MHI_GET_NAME.invoke(mhi);
|
||||
} catch (Throwable ex) {
|
||||
throw new Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodType getMethodType(Object mhi) {
|
||||
try {
|
||||
return (MethodType) MHI_GET_METHOD_TYPE.invoke(mhi);
|
||||
} catch (Throwable ex) {
|
||||
throw new Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> getDeclaringClass(Object mhi) {
|
||||
try {
|
||||
return (Class<?>) MHI_GET_DECLARING_CLASS.invoke(mhi);
|
||||
} catch (Throwable ex) {
|
||||
throw new Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getReferenceKind(Object mhi) {
|
||||
try {
|
||||
return (int) MHI_GET_REFERENCE_KIND.invoke(mhi);
|
||||
} catch (Throwable ex) {
|
||||
throw new Error(ex);
|
||||
}
|
||||
return specialMethodHandles.contains(mh);
|
||||
}
|
||||
|
||||
private static void assertRefKindEquals(int expect, int observed) {
|
||||
if (expect == observed) return;
|
||||
|
||||
String msg = "expected " + REF_KIND_NAMES[(int) expect] +
|
||||
" but observed " + REF_KIND_NAMES[(int) observed];
|
||||
String msg = "expected " + referenceKindToString(expect) +
|
||||
" but observed " + referenceKindToString(observed);
|
||||
System.out.println("FAILED: " + msg);
|
||||
throw new AssertionError(msg);
|
||||
}
|
||||
|
753
jdk/test/java/lang/invoke/RevealDirectTest.java
Normal file
753
jdk/test/java/lang/invoke/RevealDirectTest.java
Normal file
@ -0,0 +1,753 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 verify Lookup.revealDirect on a variety of input handles
|
||||
* @compile -XDignore.symbol.file RevealDirectTest.java
|
||||
* @run junit/othervm -ea -esa test.java.lang.invoke.RevealDirectTest
|
||||
*
|
||||
* @test
|
||||
* @summary verify Lookup.revealDirect on a variety of input handles, with security manager
|
||||
* @run main/othervm/policy=jtreg.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.RevealDirectTest
|
||||
*/
|
||||
|
||||
/* To run manually:
|
||||
* $ $JAVA8X_HOME/bin/javac -cp $JUNIT4_JAR -d ../../../.. -XDignore.symbol.file RevealDirectTest.java
|
||||
* $ $JAVA8X_HOME/bin/java -cp $JUNIT4_JAR:../../../.. -ea -esa org.junit.runner.JUnitCore test.java.lang.invoke.RevealDirectTest
|
||||
* $ $JAVA8X_HOME/bin/java -cp $JUNIT4_JAR:../../../.. -ea -esa -Djava.security.manager test.java.lang.invoke.RevealDirectTest
|
||||
*/
|
||||
|
||||
package test.java.lang.invoke;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.invoke.*;
|
||||
import static java.lang.invoke.MethodHandles.*;
|
||||
import static java.lang.invoke.MethodType.*;
|
||||
import static java.lang.invoke.MethodHandleInfo.*;
|
||||
import java.util.*;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.*;
|
||||
|
||||
public class RevealDirectTest {
|
||||
public static void main(String... av) throws Throwable {
|
||||
// Run the @Test methods explicitly, in case we don't want to use the JUnitCore driver.
|
||||
// This appears to be necessary when running with a security manager.
|
||||
Throwable fail = null;
|
||||
for (Method test : RevealDirectTest.class.getDeclaredMethods()) {
|
||||
if (!test.isAnnotationPresent(Test.class)) continue;
|
||||
try {
|
||||
test.invoke(new RevealDirectTest());
|
||||
} catch (Throwable ex) {
|
||||
if (ex instanceof InvocationTargetException)
|
||||
ex = ex.getCause();
|
||||
if (fail == null) fail = ex;
|
||||
System.out.println("Testcase: "+test.getName()
|
||||
+"("+test.getDeclaringClass().getName()
|
||||
+"):\tCaused an ERROR");
|
||||
System.out.println(ex);
|
||||
ex.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
if (fail != null) throw fail;
|
||||
}
|
||||
|
||||
public interface SimpleSuperInterface {
|
||||
public abstract int getInt();
|
||||
public static void printAll(String... args) {
|
||||
System.out.println(Arrays.toString(args));
|
||||
}
|
||||
public int NICE_CONSTANT = 42;
|
||||
}
|
||||
public interface SimpleInterface extends SimpleSuperInterface {
|
||||
default float getFloat() { return getInt(); }
|
||||
public static void printAll(String[] args) {
|
||||
System.out.println(Arrays.toString(args));
|
||||
}
|
||||
}
|
||||
public static class Simple implements SimpleInterface, Cloneable {
|
||||
public int intField;
|
||||
public final int finalField;
|
||||
private static String stringField;
|
||||
public int getInt() { return NICE_CONSTANT; }
|
||||
private static Number getNum() { return 804; }
|
||||
public Simple clone() {
|
||||
try {
|
||||
return (Simple) super.clone();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
Simple() { finalField = -NICE_CONSTANT; }
|
||||
private static Lookup localLookup() { return lookup(); }
|
||||
private static List<Member> members() { return getMembers(lookup().lookupClass()); };
|
||||
}
|
||||
|
||||
static boolean VERBOSE = false;
|
||||
|
||||
@Test public void testSimple() throws Throwable {
|
||||
if (VERBOSE) System.out.println("@Test testSimple");
|
||||
testOnMembers("testSimple", Simple.members(), Simple.localLookup());
|
||||
}
|
||||
@Test public void testPublicLookup() throws Throwable {
|
||||
if (VERBOSE) System.out.println("@Test testPublicLookup");
|
||||
List<Member> mems = publicOnly(Simple.members());
|
||||
Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
|
||||
testOnMembers("testPublicLookup/1", mems, pubLookup);
|
||||
// reveal using publicLookup:
|
||||
testOnMembers("testPublicLookup/2", mems, privLookup, pubLookup);
|
||||
// lookup using publicLookup, but reveal using private:
|
||||
testOnMembers("testPublicLookup/3", mems, pubLookup, privLookup);
|
||||
}
|
||||
@Test public void testPublicLookupNegative() throws Throwable {
|
||||
if (VERBOSE) System.out.println("@Test testPublicLookupNegative");
|
||||
List<Member> mems = nonPublicOnly(Simple.members());
|
||||
Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
|
||||
testOnMembersNoLookup("testPublicLookupNegative/1", mems, pubLookup);
|
||||
testOnMembersNoReveal("testPublicLookupNegative/2", mems, privLookup, pubLookup);
|
||||
testOnMembersNoReflect("testPublicLookupNegative/3", mems, privLookup, pubLookup);
|
||||
}
|
||||
@Test public void testJavaLangClass() throws Throwable {
|
||||
if (VERBOSE) System.out.println("@Test testJavaLangClass");
|
||||
List<Member> mems = callerSensitive(false, publicOnly(getMembers(Class.class)));
|
||||
mems = limit(20, mems);
|
||||
testOnMembers("testJavaLangClass", mems, Simple.localLookup());
|
||||
}
|
||||
@Test public void testCallerSensitive() throws Throwable {
|
||||
if (VERBOSE) System.out.println("@Test testCallerSensitive");
|
||||
List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
|
||||
getMembers(Method.class, "invoke"),
|
||||
getMembers(Field.class, "get", "set", "getLong"),
|
||||
getMembers(Class.class));
|
||||
mems = callerSensitive(true, publicOnly(mems));
|
||||
mems = limit(10, mems);
|
||||
testOnMembers("testCallerSensitive", mems, Simple.localLookup());
|
||||
}
|
||||
@Test public void testCallerSensitiveNegative() throws Throwable {
|
||||
if (VERBOSE) System.out.println("@Test testCallerSensitiveNegative");
|
||||
List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
|
||||
getMembers(Class.class, "forName"),
|
||||
getMembers(Method.class, "invoke"));
|
||||
mems = callerSensitive(true, publicOnly(mems));
|
||||
// CS methods cannot be looked up with publicLookup
|
||||
testOnMembersNoLookup("testCallerSensitiveNegative", mems, publicLookup());
|
||||
}
|
||||
@Test public void testMethodHandleNatives() throws Throwable {
|
||||
if (VERBOSE) System.out.println("@Test testMethodHandleNatives");
|
||||
List<Member> mems = getMembers(MethodHandle.class, "invoke", "invokeExact");
|
||||
testOnMembers("testMethodHandleNatives", mems, Simple.localLookup());
|
||||
}
|
||||
@Test public void testMethodHandleInvokes() throws Throwable {
|
||||
if (VERBOSE) System.out.println("@Test testMethodHandleInvokes");
|
||||
List<MethodType> types = new ArrayList<>();
|
||||
Class<?>[] someParamTypes = { void.class, int.class, Object.class, Object[].class };
|
||||
for (Class<?> rt : someParamTypes) {
|
||||
for (Class<?> p0 : someParamTypes) {
|
||||
if (p0 == void.class) { types.add(methodType(rt)); continue; }
|
||||
for (Class<?> p1 : someParamTypes) {
|
||||
if (p1 == void.class) { types.add(methodType(rt, p0)); continue; }
|
||||
for (Class<?> p2 : someParamTypes) {
|
||||
if (p2 == void.class) { types.add(methodType(rt, p0, p1)); continue; }
|
||||
types.add(methodType(rt, p0, p1, p2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Member> mems = union(getPolyMembers(MethodHandle.class, "invoke", types),
|
||||
getPolyMembers(MethodHandle.class, "invokeExact", types));
|
||||
testOnMembers("testMethodHandleInvokes/1", mems, Simple.localLookup());
|
||||
testOnMembers("testMethodHandleInvokes/2", mems, publicLookup());
|
||||
}
|
||||
|
||||
static List<Member> getPolyMembers(Class<?> cls, String name, List<MethodType> types) {
|
||||
assert(cls == MethodHandle.class);
|
||||
ArrayList<Member> mems = new ArrayList<>();
|
||||
for (MethodType type : types) {
|
||||
mems.add(new SignaturePolymorphicMethod(name, type));
|
||||
}
|
||||
return mems;
|
||||
}
|
||||
static List<Member> getMembers(Class<?> cls) {
|
||||
return getMembers(cls, (String[]) null);
|
||||
}
|
||||
static List<Member> getMembers(Class<?> cls, String... onlyNames) {
|
||||
List<String> names = (onlyNames == null || onlyNames.length == 0 ? null : Arrays.asList(onlyNames));
|
||||
ArrayList<Member> res = new ArrayList<>();
|
||||
for (Class<?> sup : getSupers(cls)) {
|
||||
res.addAll(getDeclaredMembers(sup, "getDeclaredFields"));
|
||||
res.addAll(getDeclaredMembers(sup, "getDeclaredMethods"));
|
||||
res.addAll(getDeclaredMembers(sup, "getDeclaredConstructors"));
|
||||
}
|
||||
res = new ArrayList<>(new LinkedHashSet<>(res));
|
||||
for (int i = 0; i < res.size(); i++) {
|
||||
Member mem = res.get(i);
|
||||
if (!canBeReached(mem, cls) ||
|
||||
res.indexOf(mem) != i ||
|
||||
mem.isSynthetic() ||
|
||||
(names != null && !names.contains(mem.getName()))
|
||||
) {
|
||||
res.remove(i--);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
static List<Class<?>> getSupers(Class<?> cls) {
|
||||
ArrayList<Class<?>> res = new ArrayList<>();
|
||||
ArrayList<Class<?>> intfs = new ArrayList<>();
|
||||
for (Class<?> sup = cls; sup != null; sup = sup.getSuperclass()) {
|
||||
res.add(sup);
|
||||
for (Class<?> intf : cls.getInterfaces()) {
|
||||
if (!intfs.contains(intf))
|
||||
intfs.add(intf);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < intfs.size(); i++) {
|
||||
for (Class<?> intf : intfs.get(i).getInterfaces()) {
|
||||
if (!intfs.contains(intf))
|
||||
intfs.add(intf);
|
||||
}
|
||||
}
|
||||
res.addAll(intfs);
|
||||
//System.out.println("getSupers => "+res);
|
||||
return res;
|
||||
}
|
||||
static boolean hasSM() {
|
||||
return (System.getSecurityManager() != null);
|
||||
}
|
||||
static List<Member> getDeclaredMembers(Class<?> cls, String accessor) {
|
||||
Member[] mems = {};
|
||||
Method getter = getMethod(Class.class, accessor);
|
||||
if (hasSM()) {
|
||||
try {
|
||||
mems = (Member[]) invokeMethod(getter, cls);
|
||||
} catch (SecurityException ex) {
|
||||
//if (VERBOSE) ex.printStackTrace();
|
||||
accessor = accessor.replace("Declared", "");
|
||||
getter = getMethod(Class.class, accessor);
|
||||
if (VERBOSE) System.out.println("replaced accessor: "+getter);
|
||||
}
|
||||
}
|
||||
if (mems.length == 0) {
|
||||
try {
|
||||
mems = (Member[]) invokeMethod(getter, cls);
|
||||
} catch (SecurityException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (VERBOSE) System.out.println(accessor+" "+cls.getName()+" => "+mems.length+" members");
|
||||
return Arrays.asList(mems);
|
||||
}
|
||||
static Method getMethod(Class<?> cls, String name) {
|
||||
try {
|
||||
return cls.getMethod(name);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
static Object invokeMethod(Method m, Object recv, Object... args) {
|
||||
try {
|
||||
return m.invoke(recv, args);
|
||||
} catch (InvocationTargetException ex) {
|
||||
Throwable ex2 = ex.getCause();
|
||||
if (ex2 instanceof RuntimeException) throw (RuntimeException) ex2;
|
||||
if (ex2 instanceof Error) throw (Error) ex2;
|
||||
throw new AssertionError(ex);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static List<Member> limit(int len, List<Member> mems) {
|
||||
if (mems.size() <= len) return mems;
|
||||
return mems.subList(0, len);
|
||||
}
|
||||
@SafeVarargs
|
||||
static List<Member> union(List<Member> mems, List<Member>... mem2s) {
|
||||
for (List<Member> mem2 : mem2s) {
|
||||
for (Member m : mem2) {
|
||||
if (!mems.contains(m))
|
||||
mems.add(m);
|
||||
}
|
||||
}
|
||||
return mems;
|
||||
}
|
||||
static List<Member> callerSensitive(boolean cond, List<Member> members) {
|
||||
for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
|
||||
Member mem = i.next();
|
||||
if (isCallerSensitive(mem) != cond)
|
||||
i.remove();
|
||||
}
|
||||
if (members.isEmpty()) throw new AssertionError("trivial result");
|
||||
return members;
|
||||
}
|
||||
static boolean isCallerSensitive(Member mem) {
|
||||
if (!(mem instanceof AnnotatedElement)) return false;
|
||||
AnnotatedElement ae = (AnnotatedElement) mem;
|
||||
if (CS_CLASS != null)
|
||||
return ae.isAnnotationPresent(sun.reflect.CallerSensitive.class);
|
||||
for (java.lang.annotation.Annotation a : ae.getDeclaredAnnotations()) {
|
||||
if (a.toString().contains(".CallerSensitive"))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static final Class<?> CS_CLASS;
|
||||
static {
|
||||
Class<?> c = null;
|
||||
try {
|
||||
c = sun.reflect.CallerSensitive.class;
|
||||
} catch (SecurityException | LinkageError ex) {
|
||||
}
|
||||
CS_CLASS = c;
|
||||
}
|
||||
static List<Member> publicOnly(List<Member> members) {
|
||||
return removeMods(members, Modifier.PUBLIC, 0);
|
||||
}
|
||||
static List<Member> nonPublicOnly(List<Member> members) {
|
||||
return removeMods(members, Modifier.PUBLIC, -1);
|
||||
}
|
||||
static List<Member> removeMods(List<Member> members, int mask, int bits) {
|
||||
int publicMods = (mask & Modifier.PUBLIC);
|
||||
members = new ArrayList<>(members);
|
||||
for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
|
||||
Member mem = i.next();
|
||||
int mods = mem.getModifiers();
|
||||
if ((publicMods & mods) != 0 &&
|
||||
(publicMods & mem.getDeclaringClass().getModifiers()) == 0)
|
||||
mods -= publicMods;
|
||||
if ((mods & mask) == (bits & mask))
|
||||
i.remove();
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
void testOnMembers(String tname, List<Member> mems, Lookup lookup, Lookup... lookups) throws Throwable {
|
||||
if (VERBOSE) System.out.println("testOnMembers "+mems);
|
||||
Lookup revLookup = (lookups.length > 0) ? lookups[0] : null;
|
||||
if (revLookup == null) revLookup = lookup;
|
||||
Lookup refLookup = (lookups.length > 1) ? lookups[1] : null;
|
||||
if (refLookup == null) refLookup = lookup;
|
||||
assert(lookups.length <= 2);
|
||||
testOnMembersImpl(tname, mems, lookup, revLookup, refLookup, NO_FAIL);
|
||||
}
|
||||
void testOnMembersNoLookup(String tname, List<Member> mems, Lookup lookup) throws Throwable {
|
||||
if (VERBOSE) System.out.println("testOnMembersNoLookup "+mems);
|
||||
testOnMembersImpl(tname, mems, lookup, null, null, FAIL_LOOKUP);
|
||||
}
|
||||
void testOnMembersNoReveal(String tname, List<Member> mems,
|
||||
Lookup lookup, Lookup negLookup) throws Throwable {
|
||||
if (VERBOSE) System.out.println("testOnMembersNoReveal "+mems);
|
||||
testOnMembersImpl(tname, mems, lookup, negLookup, null, FAIL_REVEAL);
|
||||
}
|
||||
void testOnMembersNoReflect(String tname, List<Member> mems,
|
||||
Lookup lookup, Lookup negLookup) throws Throwable {
|
||||
if (VERBOSE) System.out.println("testOnMembersNoReflect "+mems);
|
||||
testOnMembersImpl(tname, mems, lookup, lookup, negLookup, FAIL_REFLECT);
|
||||
}
|
||||
void testOnMembersImpl(String tname, List<Member> mems,
|
||||
Lookup lookup,
|
||||
Lookup revLookup,
|
||||
Lookup refLookup,
|
||||
int failureMode) throws Throwable {
|
||||
Throwable fail = null;
|
||||
int failCount = 0;
|
||||
failureModeCounts = new int[FAIL_MODE_COUNT];
|
||||
long tm0 = System.currentTimeMillis();
|
||||
for (Member mem : mems) {
|
||||
try {
|
||||
testWithMember(mem, lookup, revLookup, refLookup, failureMode);
|
||||
} catch (Throwable ex) {
|
||||
if (fail == null) fail = ex;
|
||||
if (++failCount > 10) { System.out.println("*** FAIL: too many failures"); break; }
|
||||
System.out.println("*** FAIL: "+mem+" => "+ex);
|
||||
if (VERBOSE) ex.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
long tm1 = System.currentTimeMillis();
|
||||
System.out.printf("@Test %s executed %s tests in %d ms",
|
||||
tname, testKinds(failureModeCounts), (tm1-tm0)).println();
|
||||
if (fail != null) throw fail;
|
||||
}
|
||||
static String testKinds(int[] modes) {
|
||||
int pos = modes[0], neg = -pos;
|
||||
for (int n : modes) neg += n;
|
||||
if (neg == 0) return pos + " positive";
|
||||
String negs = "";
|
||||
for (int n : modes) negs += "/"+n;
|
||||
negs = negs.replaceFirst("/"+pos+"/", "");
|
||||
negs += " negative";
|
||||
if (pos == 0) return negs;
|
||||
return pos + " positive, " + negs;
|
||||
}
|
||||
static class SignaturePolymorphicMethod implements Member { // non-reflected instance of MH.invoke*
|
||||
final String name;
|
||||
final MethodType type;
|
||||
SignaturePolymorphicMethod(String name, MethodType type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
public String toString() {
|
||||
String typeStr = type.toString();
|
||||
if (isVarArgs()) typeStr = typeStr.replaceFirst("\\[\\])$", "...)");
|
||||
return (Modifier.toString(getModifiers())
|
||||
+typeStr.substring(0, typeStr.indexOf('('))+" "
|
||||
+getDeclaringClass().getTypeName()+"."
|
||||
+getName()+typeStr.substring(typeStr.indexOf('(')));
|
||||
}
|
||||
public boolean equals(Object x) {
|
||||
return (x instanceof SignaturePolymorphicMethod && equals((SignaturePolymorphicMethod)x));
|
||||
}
|
||||
public boolean equals(SignaturePolymorphicMethod that) {
|
||||
return this.name.equals(that.name) && this.type.equals(that.type);
|
||||
}
|
||||
public int hashCode() {
|
||||
return name.hashCode() * 31 + type.hashCode();
|
||||
}
|
||||
public Class<?> getDeclaringClass() { return MethodHandle.class; }
|
||||
public String getName() { return name; }
|
||||
public MethodType getMethodType() { return type; }
|
||||
public int getModifiers() { return Modifier.PUBLIC | Modifier.FINAL | Modifier.NATIVE | SYNTHETIC; }
|
||||
public boolean isVarArgs() { return Modifier.isTransient(getModifiers()); }
|
||||
public boolean isSynthetic() { return true; }
|
||||
public Class<?> getReturnType() { return type.returnType(); }
|
||||
public Class<?>[] getParameterTypes() { return type.parameterArray(); }
|
||||
static final int SYNTHETIC = 0x00001000;
|
||||
}
|
||||
static class UnreflectResult { // a tuple
|
||||
final MethodHandle mh;
|
||||
final Throwable ex;
|
||||
final byte kind;
|
||||
final Member mem;
|
||||
final int var;
|
||||
UnreflectResult(MethodHandle mh, byte kind, Member mem, int var) {
|
||||
this.mh = mh;
|
||||
this.ex = null;
|
||||
this.kind = kind;
|
||||
this.mem = mem;
|
||||
this.var = var;
|
||||
}
|
||||
UnreflectResult(Throwable ex, byte kind, Member mem, int var) {
|
||||
this.mh = null;
|
||||
this.ex = ex;
|
||||
this.kind = kind;
|
||||
this.mem = mem;
|
||||
this.var = var;
|
||||
}
|
||||
public String toString() {
|
||||
return toInfoString()+"/v"+var;
|
||||
}
|
||||
public String toInfoString() {
|
||||
return String.format("%s %s.%s:%s", MethodHandleInfo.referenceKindToString(kind),
|
||||
mem.getDeclaringClass().getName(), name(mem), type(mem, kind));
|
||||
}
|
||||
static String name(Member mem) {
|
||||
if (mem instanceof Constructor) return "<init>";
|
||||
return mem.getName();
|
||||
}
|
||||
static MethodType type(Member mem, byte kind) {
|
||||
if (mem instanceof Field) {
|
||||
Class<?> type = ((Field)mem).getType();
|
||||
if (kind == REF_putStatic || kind == REF_putField)
|
||||
return methodType(void.class, type);
|
||||
return methodType(type);
|
||||
} else if (mem instanceof SignaturePolymorphicMethod) {
|
||||
return ((SignaturePolymorphicMethod)mem).getMethodType();
|
||||
}
|
||||
Class<?>[] params = ((Executable)mem).getParameterTypes();
|
||||
if (mem instanceof Constructor)
|
||||
return methodType(void.class, params);
|
||||
Class<?> type = ((Method)mem).getReturnType();
|
||||
return methodType(type, params);
|
||||
}
|
||||
}
|
||||
static UnreflectResult unreflectMember(Lookup lookup, Member mem, int variation) {
|
||||
byte[] refKind = {0};
|
||||
try {
|
||||
return unreflectMemberOrThrow(lookup, mem, variation, refKind);
|
||||
} catch (ReflectiveOperationException|SecurityException ex) {
|
||||
return new UnreflectResult(ex, refKind[0], mem, variation);
|
||||
}
|
||||
}
|
||||
static UnreflectResult unreflectMemberOrThrow(Lookup lookup, Member mem, int variation,
|
||||
byte[] refKind) throws ReflectiveOperationException {
|
||||
Class<?> cls = lookup.lookupClass();
|
||||
Class<?> defc = mem.getDeclaringClass();
|
||||
String name = mem.getName();
|
||||
int mods = mem.getModifiers();
|
||||
boolean isStatic = Modifier.isStatic(mods);
|
||||
MethodHandle mh = null;
|
||||
byte kind = 0;
|
||||
if (mem instanceof Method) {
|
||||
Method m = (Method) mem;
|
||||
MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
|
||||
boolean canBeSpecial = (!isStatic &&
|
||||
(lookup.lookupModes() & Modifier.PRIVATE) != 0 &&
|
||||
defc.isAssignableFrom(cls) &&
|
||||
(!defc.isInterface() || Arrays.asList(cls.getInterfaces()).contains(defc)));
|
||||
if (variation >= 2)
|
||||
kind = REF_invokeSpecial;
|
||||
else if (isStatic)
|
||||
kind = REF_invokeStatic;
|
||||
else if (defc.isInterface())
|
||||
kind = REF_invokeInterface;
|
||||
else
|
||||
kind = REF_invokeVirtual;
|
||||
refKind[0] = kind;
|
||||
switch (variation) {
|
||||
case 0:
|
||||
mh = lookup.unreflect(m);
|
||||
break;
|
||||
case 1:
|
||||
if (defc == MethodHandle.class &&
|
||||
!isStatic &&
|
||||
m.isVarArgs() &&
|
||||
Modifier.isFinal(mods) &&
|
||||
Modifier.isNative(mods)) {
|
||||
break;
|
||||
}
|
||||
if (isStatic)
|
||||
mh = lookup.findStatic(defc, name, type);
|
||||
else
|
||||
mh = lookup.findVirtual(defc, name, type);
|
||||
break;
|
||||
case 2:
|
||||
if (!canBeSpecial)
|
||||
break;
|
||||
mh = lookup.unreflectSpecial(m, lookup.lookupClass());
|
||||
break;
|
||||
case 3:
|
||||
if (!canBeSpecial)
|
||||
break;
|
||||
mh = lookup.findSpecial(defc, name, type, lookup.lookupClass());
|
||||
break;
|
||||
}
|
||||
} else if (mem instanceof SignaturePolymorphicMethod) {
|
||||
SignaturePolymorphicMethod m = (SignaturePolymorphicMethod) mem;
|
||||
MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
|
||||
kind = REF_invokeVirtual;
|
||||
refKind[0] = kind;
|
||||
switch (variation) {
|
||||
case 0:
|
||||
mh = lookup.findVirtual(defc, name, type);
|
||||
break;
|
||||
}
|
||||
} else if (mem instanceof Constructor) {
|
||||
name = "<init>"; // not used
|
||||
Constructor<?> m = (Constructor<?>) mem;
|
||||
MethodType type = methodType(void.class, m.getParameterTypes());
|
||||
kind = REF_newInvokeSpecial;
|
||||
refKind[0] = kind;
|
||||
switch (variation) {
|
||||
case 0:
|
||||
mh = lookup.unreflectConstructor(m);
|
||||
break;
|
||||
case 1:
|
||||
mh = lookup.findConstructor(defc, type);
|
||||
break;
|
||||
}
|
||||
} else if (mem instanceof Field) {
|
||||
Field m = (Field) mem;
|
||||
Class<?> type = m.getType();
|
||||
boolean canHaveSetter = !Modifier.isFinal(mods);
|
||||
if (variation >= 2)
|
||||
kind = (byte)(isStatic ? REF_putStatic : REF_putField);
|
||||
else
|
||||
kind = (byte)(isStatic ? REF_getStatic : REF_getField);
|
||||
refKind[0] = kind;
|
||||
switch (variation) {
|
||||
case 0:
|
||||
mh = lookup.unreflectGetter(m);
|
||||
break;
|
||||
case 1:
|
||||
if (isStatic)
|
||||
mh = lookup.findStaticGetter(defc, name, type);
|
||||
else
|
||||
mh = lookup.findGetter(defc, name, type);
|
||||
break;
|
||||
case 3:
|
||||
if (!canHaveSetter)
|
||||
break;
|
||||
mh = lookup.unreflectSetter(m);
|
||||
break;
|
||||
case 2:
|
||||
if (!canHaveSetter)
|
||||
break;
|
||||
if (isStatic)
|
||||
mh = lookup.findStaticSetter(defc, name, type);
|
||||
else
|
||||
mh = lookup.findSetter(defc, name, type);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.valueOf(mem));
|
||||
}
|
||||
if (mh == null)
|
||||
// ran out of valid variations; return null to caller
|
||||
return null;
|
||||
return new UnreflectResult(mh, kind, mem, variation);
|
||||
}
|
||||
static boolean canBeReached(Member mem, Class<?> cls) {
|
||||
Class<?> defc = mem.getDeclaringClass();
|
||||
String name = mem.getName();
|
||||
int mods = mem.getModifiers();
|
||||
if (mem instanceof Constructor) {
|
||||
name = "<init>"; // according to 292 spec.
|
||||
}
|
||||
if (defc == cls)
|
||||
return true;
|
||||
if (name.startsWith("<"))
|
||||
return false; // only my own constructors
|
||||
if (Modifier.isPrivate(mods))
|
||||
return false; // only my own constructors
|
||||
if (defc.getPackage() == cls.getPackage())
|
||||
return true; // package access or greater OK
|
||||
if (Modifier.isPublic(mods))
|
||||
return true; // publics always OK
|
||||
if (Modifier.isProtected(mods) && defc.isAssignableFrom(cls))
|
||||
return true; // protected OK
|
||||
return false;
|
||||
}
|
||||
static boolean consistent(UnreflectResult res, MethodHandleInfo info) {
|
||||
assert(res.mh != null);
|
||||
assertEquals(res.kind, info.getReferenceKind());
|
||||
assertEquals(res.mem.getModifiers(), info.getModifiers());
|
||||
assertEquals(res.mem.getDeclaringClass(), info.getDeclaringClass());
|
||||
String expectName = res.mem.getName();
|
||||
if (res.kind == REF_newInvokeSpecial)
|
||||
expectName = "<init>";
|
||||
assertEquals(expectName, info.getName());
|
||||
MethodType expectType = res.mh.type();
|
||||
if ((res.kind & 1) == (REF_getField & 1))
|
||||
expectType = expectType.dropParameterTypes(0, 1);
|
||||
if (res.kind == REF_newInvokeSpecial)
|
||||
expectType = expectType.changeReturnType(void.class);
|
||||
assertEquals(expectType, info.getMethodType());
|
||||
assertEquals(res.mh.isVarargsCollector(), isVarArgs(info));
|
||||
assertEquals(res.toInfoString(), info.toString());
|
||||
assertEquals(res.toInfoString(), MethodHandleInfo.toString(info.getReferenceKind(), info.getDeclaringClass(), info.getName(), info.getMethodType()));
|
||||
return true;
|
||||
}
|
||||
static boolean isVarArgs(MethodHandleInfo info) {
|
||||
return info.isVarArgs();
|
||||
}
|
||||
static boolean consistent(Member mem, Member mem2) {
|
||||
assertEquals(mem, mem2);
|
||||
return true;
|
||||
}
|
||||
static boolean consistent(MethodHandleInfo info, MethodHandleInfo info2) {
|
||||
assertEquals(info.getReferenceKind(), info2.getReferenceKind());
|
||||
assertEquals(info.getModifiers(), info2.getModifiers());
|
||||
assertEquals(info.getDeclaringClass(), info2.getDeclaringClass());
|
||||
assertEquals(info.getName(), info2.getName());
|
||||
assertEquals(info.getMethodType(), info2.getMethodType());
|
||||
assertEquals(isVarArgs(info), isVarArgs(info));
|
||||
return true;
|
||||
}
|
||||
static boolean consistent(MethodHandle mh, MethodHandle mh2) {
|
||||
assertEquals(mh.type(), mh2.type());
|
||||
assertEquals(mh.isVarargsCollector(), mh2.isVarargsCollector());
|
||||
return true;
|
||||
}
|
||||
int[] failureModeCounts;
|
||||
static final int NO_FAIL=0, FAIL_LOOKUP=1, FAIL_REVEAL=2, FAIL_REFLECT=3, FAIL_MODE_COUNT=4;
|
||||
void testWithMember(Member mem,
|
||||
Lookup lookup, // initial lookup of member => MH
|
||||
Lookup revLookup, // reveal MH => info
|
||||
Lookup refLookup, // reflect info => member
|
||||
int failureMode) throws Throwable {
|
||||
boolean expectEx1 = (failureMode == FAIL_LOOKUP); // testOnMembersNoLookup
|
||||
boolean expectEx2 = (failureMode == FAIL_REVEAL); // testOnMembersNoReveal
|
||||
boolean expectEx3 = (failureMode == FAIL_REFLECT); // testOnMembersNoReflect
|
||||
for (int variation = 0; ; variation++) {
|
||||
UnreflectResult res = unreflectMember(lookup, mem, variation);
|
||||
failureModeCounts[failureMode] += 1;
|
||||
if (variation == 0) assert(res != null);
|
||||
if (res == null) break;
|
||||
if (VERBOSE && variation == 0)
|
||||
System.out.println("from "+mem.getDeclaringClass().getSimpleName());
|
||||
MethodHandle mh = res.mh;
|
||||
Throwable ex1 = res.ex;
|
||||
if (VERBOSE) System.out.println(" "+variation+": "+res+" << "+(mh != null ? mh : ex1));
|
||||
if (expectEx1 && ex1 != null)
|
||||
continue; // this is OK; we expected that lookup to fail
|
||||
if (expectEx1)
|
||||
throw new AssertionError("unexpected lookup for negative test");
|
||||
if (ex1 != null && !expectEx1) {
|
||||
if (failureMode != NO_FAIL)
|
||||
throw new AssertionError("unexpected lookup failure for negative test", ex1);
|
||||
throw ex1;
|
||||
}
|
||||
MethodHandleInfo info;
|
||||
try {
|
||||
info = revLookup.revealDirect(mh);
|
||||
if (expectEx2) throw new AssertionError("unexpected revelation for negative test");
|
||||
} catch (Throwable ex2) {
|
||||
if (VERBOSE) System.out.println(" "+variation+": "+res+" => "+mh.getClass().getName()+" => (EX2)"+ex2);
|
||||
if (expectEx2)
|
||||
continue; // this is OK; we expected the reflect to fail
|
||||
if (failureMode != NO_FAIL)
|
||||
throw new AssertionError("unexpected revelation failure for negative test", ex2);
|
||||
throw ex2;
|
||||
}
|
||||
assert(consistent(res, info));
|
||||
Member mem2;
|
||||
try {
|
||||
mem2 = info.reflectAs(Member.class, refLookup);
|
||||
if (expectEx3) throw new AssertionError("unexpected reflection for negative test");
|
||||
assert(!(mem instanceof SignaturePolymorphicMethod));
|
||||
} catch (IllegalArgumentException ex3) {
|
||||
if (VERBOSE) System.out.println(" "+variation+": "+info+" => (EX3)"+ex3);
|
||||
if (expectEx3)
|
||||
continue; // this is OK; we expected the reflect to fail
|
||||
if (mem instanceof SignaturePolymorphicMethod)
|
||||
continue; // this is OK; we cannot reflect MH.invokeExact(a,b,c)
|
||||
if (failureMode != NO_FAIL)
|
||||
throw new AssertionError("unexpected reflection failure for negative test", ex3);
|
||||
throw ex3;
|
||||
}
|
||||
assert(consistent(mem, mem2));
|
||||
UnreflectResult res2 = unreflectMember(lookup, mem2, variation);
|
||||
MethodHandle mh2 = res2.mh;
|
||||
assert(consistent(mh, mh2));
|
||||
MethodHandleInfo info2 = lookup.revealDirect(mh2);
|
||||
assert(consistent(info, info2));
|
||||
assert(consistent(res, info2));
|
||||
Member mem3;
|
||||
if (hasSM())
|
||||
mem3 = info2.reflectAs(Member.class, lookup);
|
||||
else
|
||||
mem3 = MethodHandles.reflectAs(Member.class, mh2);
|
||||
assert(consistent(mem2, mem3));
|
||||
if (hasSM()) {
|
||||
try {
|
||||
MethodHandles.reflectAs(Member.class, mh2);
|
||||
throw new AssertionError("failed to throw on "+mem3);
|
||||
} catch (SecurityException ex3) {
|
||||
// OK...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
jdk/test/java/lang/invoke/jtreg.security.policy
Normal file
9
jdk/test/java/lang/invoke/jtreg.security.policy
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* security policy used by the test process
|
||||
* must allow file reads so that jtreg itself can run
|
||||
*/
|
||||
|
||||
grant {
|
||||
// standard test activation permissions
|
||||
permission java.io.FilePermission "*", "read";
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user