8244413: Avoid rebinds in MethodHandle.viewAsType

Reviewed-by: mchung, jrose
This commit is contained in:
Claes Redestad 2020-05-06 15:51:49 +02:00
parent 463e377053
commit 72704aaba1
4 changed files with 92 additions and 36 deletions

View File

@ -61,6 +61,20 @@ abstract class DelegatingMethodHandle extends MethodHandle {
return getTarget().internalMemberName();
}
@Override
boolean isCrackable() {
MemberName member = internalMemberName();
return member != null &&
(member.isResolved() ||
member.isMethodHandleInvoke() ||
member.isVarHandleMethodInvoke());
}
@Override
MethodHandle viewAsType(MethodType newType, boolean strict) {
return getTarget().viewAsType(newType, strict);
}
@Override
boolean isInvokeSpecial() {
return getTarget().isInvokeSpecial();

View File

@ -51,9 +51,10 @@ import static java.lang.invoke.MethodTypeForm.*;
*/
class DirectMethodHandle extends MethodHandle {
final MemberName member;
final boolean crackable;
// Constructors and factory methods in this class *must* be package scoped or private.
private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member) {
private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member, boolean crackable) {
super(mtype, form);
if (!member.isResolved()) throw new InternalError();
@ -70,6 +71,7 @@ class DirectMethodHandle extends MethodHandle {
}
this.member = member;
this.crackable = crackable;
}
// Factory methods:
@ -92,18 +94,18 @@ class DirectMethodHandle extends MethodHandle {
throw new InternalError("callerClass must not be null for REF_invokeSpecial");
}
LambdaForm lform = preparedLambdaForm(member, callerClass.isInterface());
return new Special(mtype, lform, member, callerClass);
return new Special(mtype, lform, member, true, callerClass);
}
case REF_invokeInterface: {
// for interfaces we always need the receiver typecheck,
// so we always pass 'true' to ensure we adapt if needed
// to include the REF_invokeSpecial case
LambdaForm lform = preparedLambdaForm(member, true);
return new Interface(mtype, lform, member, refc);
return new Interface(mtype, lform, member, true, refc);
}
default: {
LambdaForm lform = preparedLambdaForm(member);
return new DirectMethodHandle(mtype, lform, member);
return new DirectMethodHandle(mtype, lform, member, true);
}
}
} else {
@ -111,11 +113,11 @@ class DirectMethodHandle extends MethodHandle {
if (member.isStatic()) {
long offset = MethodHandleNatives.staticFieldOffset(member);
Object base = MethodHandleNatives.staticFieldBase(member);
return new StaticAccessor(mtype, lform, member, base, offset);
return new StaticAccessor(mtype, lform, member, true, base, offset);
} else {
long offset = MethodHandleNatives.objectFieldOffset(member);
assert(offset == (int)offset);
return new Accessor(mtype, lform, member, (int)offset);
return new Accessor(mtype, lform, member, true, (int)offset);
}
}
}
@ -139,7 +141,7 @@ class DirectMethodHandle extends MethodHandle {
LambdaForm lform = preparedLambdaForm(ctor);
MemberName init = ctor.asSpecial();
assert(init.getMethodType().returnType() == void.class);
return new Constructor(mtype, lform, ctor, init, instanceClass);
return new Constructor(mtype, lform, ctor, true, init, instanceClass);
}
@Override
@ -150,7 +152,22 @@ class DirectMethodHandle extends MethodHandle {
@Override
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
assert(this.getClass() == DirectMethodHandle.class); // must override in subclasses
return new DirectMethodHandle(mt, lf, member);
return new DirectMethodHandle(mt, lf, member, crackable);
}
@Override
MethodHandle viewAsType(MethodType newType, boolean strict) {
// No actual conversions, just a new view of the same method.
// However, we must not expose a DMH that is crackable into a
// MethodHandleInfo, so we return a cloned, uncrackable DMH
assert(viewAsTypeChecks(newType, strict));
assert(this.getClass() == DirectMethodHandle.class); // must override in subclasses
return new DirectMethodHandle(newType, form, member, false);
}
@Override
boolean isCrackable() {
return crackable;
}
@Override
@ -406,8 +423,8 @@ class DirectMethodHandle extends MethodHandle {
/** This subclass represents invokespecial instructions. */
static class Special extends DirectMethodHandle {
private final Class<?> caller;
private Special(MethodType mtype, LambdaForm form, MemberName member, Class<?> caller) {
super(mtype, form, member);
private Special(MethodType mtype, LambdaForm form, MemberName member, boolean crackable, Class<?> caller) {
super(mtype, form, member, crackable);
this.caller = caller;
}
@Override
@ -416,7 +433,12 @@ class DirectMethodHandle extends MethodHandle {
}
@Override
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
return new Special(mt, lf, member, caller);
return new Special(mt, lf, member, crackable, caller);
}
@Override
MethodHandle viewAsType(MethodType newType, boolean strict) {
assert(viewAsTypeChecks(newType, strict));
return new Special(newType, form, member, false, caller);
}
Object checkReceiver(Object recv) {
if (!caller.isInstance(recv)) {
@ -431,14 +453,19 @@ class DirectMethodHandle extends MethodHandle {
/** This subclass represents invokeinterface instructions. */
static class Interface extends DirectMethodHandle {
private final Class<?> refc;
private Interface(MethodType mtype, LambdaForm form, MemberName member, Class<?> refc) {
super(mtype, form, member);
assert refc.isInterface() : refc;
private Interface(MethodType mtype, LambdaForm form, MemberName member, boolean crackable, Class<?> refc) {
super(mtype, form, member, crackable);
assert(refc.isInterface()) : refc;
this.refc = refc;
}
@Override
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
return new Interface(mt, lf, member, refc);
return new Interface(mt, lf, member, crackable, refc);
}
@Override
MethodHandle viewAsType(MethodType newType, boolean strict) {
assert(viewAsTypeChecks(newType, strict));
return new Interface(newType, form, member, false, refc);
}
@Override
Object checkReceiver(Object recv) {
@ -456,22 +483,26 @@ class DirectMethodHandle extends MethodHandle {
throw new InternalError("Should only be invoked on a subclass");
}
/** This subclass handles constructor references. */
static class Constructor extends DirectMethodHandle {
final MemberName initMethod;
final Class<?> instanceClass;
private Constructor(MethodType mtype, LambdaForm form, MemberName constructor,
MemberName initMethod, Class<?> instanceClass) {
super(mtype, form, constructor);
boolean crackable, MemberName initMethod, Class<?> instanceClass) {
super(mtype, form, constructor, crackable);
this.initMethod = initMethod;
this.instanceClass = instanceClass;
assert(initMethod.isResolved());
}
@Override
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
return new Constructor(mt, lf, member, initMethod, instanceClass);
return new Constructor(mt, lf, member, crackable, initMethod, instanceClass);
}
@Override
MethodHandle viewAsType(MethodType newType, boolean strict) {
assert(viewAsTypeChecks(newType, strict));
return new Constructor(newType, form, member, false, initMethod, instanceClass);
}
}
@ -492,8 +523,8 @@ class DirectMethodHandle extends MethodHandle {
final Class<?> fieldType;
final int fieldOffset;
private Accessor(MethodType mtype, LambdaForm form, MemberName member,
int fieldOffset) {
super(mtype, form, member);
boolean crackable, int fieldOffset) {
super(mtype, form, member, crackable);
this.fieldType = member.getFieldType();
this.fieldOffset = fieldOffset;
}
@ -503,7 +534,12 @@ class DirectMethodHandle extends MethodHandle {
}
@Override
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
return new Accessor(mt, lf, member, fieldOffset);
return new Accessor(mt, lf, member, crackable, fieldOffset);
}
@Override
MethodHandle viewAsType(MethodType newType, boolean strict) {
assert(viewAsTypeChecks(newType, strict));
return new Accessor(newType, form, member, false, fieldOffset);
}
}
@ -535,8 +571,8 @@ class DirectMethodHandle extends MethodHandle {
private final long staticOffset;
private StaticAccessor(MethodType mtype, LambdaForm form, MemberName member,
Object staticBase, long staticOffset) {
super(mtype, form, member);
boolean crackable, Object staticBase, long staticOffset) {
super(mtype, form, member, crackable);
this.fieldType = member.getFieldType();
this.staticBase = staticBase;
this.staticOffset = staticOffset;
@ -547,7 +583,12 @@ class DirectMethodHandle extends MethodHandle {
}
@Override
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
return new StaticAccessor(mt, lf, member, staticBase, staticOffset);
return new StaticAccessor(mt, lf, member, crackable, staticBase, staticOffset);
}
@Override
MethodHandle viewAsType(MethodType newType, boolean strict) {
assert(viewAsTypeChecks(newType, strict));
return new StaticAccessor(newType, form, member, false, staticBase, staticOffset);
}
}

View File

@ -1640,12 +1640,9 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
/*non-public*/
MethodHandle viewAsType(MethodType newType, boolean strict) {
// No actual conversions, just a new view of the same method.
// Note that this operation must not produce a DirectMethodHandle,
// because retyped DMHs, like any transformed MHs,
// cannot be cracked into MethodHandleInfo.
assert viewAsTypeChecks(newType, strict);
BoundMethodHandle mh = rebind();
return mh.copyWith(newType, mh.form);
// Overridden in DMH, which has special rules
assert(viewAsTypeChecks(newType, strict));
return copyWith(newType, form);
}
/*non-public*/
@ -1693,7 +1690,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
} else {
// The following case is rare. Mask the internalMemberName by wrapping the MH in a BMH.
MethodHandle result = rebind();
assert (result.internalMemberName() == null);
assert(result.internalMemberName() == null);
return result;
}
}
@ -1703,6 +1700,11 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
return false; // DMH.Special returns true
}
/*non-public*/
boolean isCrackable() {
return false;
}
/*non-public*/
Object internalValues() {
return null;

View File

@ -3282,11 +3282,10 @@ return mh1;
* @since 1.8
*/
public MethodHandleInfo revealDirect(MethodHandle target) {
MemberName member = target.internalMemberName();
if (member == null || (!member.isResolved() &&
!member.isMethodHandleInvoke() &&
!member.isVarHandleMethodInvoke()))
if (!target.isCrackable()) {
throw newIllegalArgumentException("not a direct method handle");
}
MemberName member = target.internalMemberName();
Class<?> defc = member.getDeclaringClass();
byte refKind = member.getReferenceKind();
assert(MethodHandleNatives.refKindIsValid(refKind));