8008688: Make MethodHandleInfo public

A major overhaul to MethodHandleInfo and method handles in general.

Reviewed-by: vlivanov, twisti
This commit is contained in:
John Rose 2013-09-03 21:42:56 -07:00 committed by Robert Field
parent 7fc1c28757
commit 59440ee0be
13 changed files with 1437 additions and 204 deletions

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 "&lt;init&gt;"} 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);
}
}

View File

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

View File

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

View File

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

View File

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

View 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...
}
}
}
}
}

View 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";
};