mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8200167: Validate more special case invocations
Co-authored-by: Vladimir Ivanov <vladimir.x.ivanov@oracle.com> Co-authored-by: Tobias Hartmann <tobias.hartmann@oracle.com> Reviewed-by: acorn, vlivanov, dholmes
This commit is contained in:
parent
08855df46a
commit
d05ed512ac
8 changed files with 431 additions and 38 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -648,7 +648,7 @@ void Canonicalizer::do_NewTypeArray (NewTypeArray* x) {}
|
||||||
void Canonicalizer::do_NewObjectArray (NewObjectArray* x) {}
|
void Canonicalizer::do_NewObjectArray (NewObjectArray* x) {}
|
||||||
void Canonicalizer::do_NewMultiArray (NewMultiArray* x) {}
|
void Canonicalizer::do_NewMultiArray (NewMultiArray* x) {}
|
||||||
void Canonicalizer::do_CheckCast (CheckCast* x) {
|
void Canonicalizer::do_CheckCast (CheckCast* x) {
|
||||||
if (x->klass()->is_loaded()) {
|
if (x->klass()->is_loaded() && !x->is_invokespecial_receiver_check()) {
|
||||||
Value obj = x->obj();
|
Value obj = x->obj();
|
||||||
ciType* klass = obj->exact_type();
|
ciType* klass = obj->exact_type();
|
||||||
if (klass == NULL) klass = obj->declared_type();
|
if (klass == NULL) klass = obj->declared_type();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -72,26 +72,30 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Factory methods:
|
// Factory methods:
|
||||||
static DirectMethodHandle make(byte refKind, Class<?> receiver, MemberName member) {
|
static DirectMethodHandle make(byte refKind, Class<?> refc, MemberName member, Class<?> callerClass) {
|
||||||
MethodType mtype = member.getMethodOrFieldType();
|
MethodType mtype = member.getMethodOrFieldType();
|
||||||
if (!member.isStatic()) {
|
if (!member.isStatic()) {
|
||||||
if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
|
if (!member.getDeclaringClass().isAssignableFrom(refc) || member.isConstructor())
|
||||||
throw new InternalError(member.toString());
|
throw new InternalError(member.toString());
|
||||||
mtype = mtype.insertParameterTypes(0, receiver);
|
mtype = mtype.insertParameterTypes(0, refc);
|
||||||
}
|
}
|
||||||
if (!member.isField()) {
|
if (!member.isField()) {
|
||||||
switch (refKind) {
|
switch (refKind) {
|
||||||
case REF_invokeSpecial: {
|
case REF_invokeSpecial: {
|
||||||
member = member.asSpecial();
|
member = member.asSpecial();
|
||||||
LambdaForm lform = preparedLambdaForm(member);
|
LambdaForm lform = preparedLambdaForm(member, callerClass);
|
||||||
return new Special(mtype, lform, member);
|
Class<?> checkClass = refc; // Class to use for receiver type check
|
||||||
|
if (callerClass != null) {
|
||||||
|
checkClass = callerClass; // potentially strengthen to caller class
|
||||||
|
}
|
||||||
|
return new Special(mtype, lform, member, checkClass);
|
||||||
}
|
}
|
||||||
case REF_invokeInterface: {
|
case REF_invokeInterface: {
|
||||||
LambdaForm lform = preparedLambdaForm(member);
|
LambdaForm lform = preparedLambdaForm(member, callerClass);
|
||||||
return new Interface(mtype, lform, member, receiver);
|
return new Interface(mtype, lform, member, refc);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
LambdaForm lform = preparedLambdaForm(member);
|
LambdaForm lform = preparedLambdaForm(member, callerClass);
|
||||||
return new DirectMethodHandle(mtype, lform, member);
|
return new DirectMethodHandle(mtype, lform, member);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,11 +112,11 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static DirectMethodHandle make(Class<?> receiver, MemberName member) {
|
static DirectMethodHandle make(Class<?> refc, MemberName member) {
|
||||||
byte refKind = member.getReferenceKind();
|
byte refKind = member.getReferenceKind();
|
||||||
if (refKind == REF_invokeSpecial)
|
if (refKind == REF_invokeSpecial)
|
||||||
refKind = REF_invokeVirtual;
|
refKind = REF_invokeVirtual;
|
||||||
return make(refKind, receiver, member);
|
return make(refKind, refc, member, null /* no callerClass context */);
|
||||||
}
|
}
|
||||||
static DirectMethodHandle make(MemberName member) {
|
static DirectMethodHandle make(MemberName member) {
|
||||||
if (member.isConstructor())
|
if (member.isConstructor())
|
||||||
|
@ -161,7 +165,7 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
* Cache and share this structure among all methods with
|
* Cache and share this structure among all methods with
|
||||||
* the same basicType and refKind.
|
* the same basicType and refKind.
|
||||||
*/
|
*/
|
||||||
private static LambdaForm preparedLambdaForm(MemberName m) {
|
private static LambdaForm preparedLambdaForm(MemberName m, Class<?> callerClass) {
|
||||||
assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
|
assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
|
||||||
MethodType mtype = m.getInvocationType().basicType();
|
MethodType mtype = m.getInvocationType().basicType();
|
||||||
assert(!m.isMethodHandleInvoke()) : m;
|
assert(!m.isMethodHandleInvoke()) : m;
|
||||||
|
@ -179,6 +183,9 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
preparedLambdaForm(mtype, which);
|
preparedLambdaForm(mtype, which);
|
||||||
which = LF_INVSTATIC_INIT;
|
which = LF_INVSTATIC_INIT;
|
||||||
}
|
}
|
||||||
|
if (which == LF_INVSPECIAL && callerClass != null && callerClass.isInterface()) {
|
||||||
|
which = LF_INVSPECIAL_IFC;
|
||||||
|
}
|
||||||
LambdaForm lform = preparedLambdaForm(mtype, which);
|
LambdaForm lform = preparedLambdaForm(mtype, which);
|
||||||
maybeCompile(lform, m);
|
maybeCompile(lform, m);
|
||||||
assert(lform.methodType().dropParameterTypes(0, 1)
|
assert(lform.methodType().dropParameterTypes(0, 1)
|
||||||
|
@ -187,6 +194,10 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
return lform;
|
return lform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static LambdaForm preparedLambdaForm(MemberName m) {
|
||||||
|
return preparedLambdaForm(m, null);
|
||||||
|
}
|
||||||
|
|
||||||
private static LambdaForm preparedLambdaForm(MethodType mtype, int which) {
|
private static LambdaForm preparedLambdaForm(MethodType mtype, int which) {
|
||||||
LambdaForm lform = mtype.form().cachedLambdaForm(which);
|
LambdaForm lform = mtype.form().cachedLambdaForm(which);
|
||||||
if (lform != null) return lform;
|
if (lform != null) return lform;
|
||||||
|
@ -197,13 +208,16 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
|
static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
|
||||||
boolean needsInit = (which == LF_INVSTATIC_INIT);
|
boolean needsInit = (which == LF_INVSTATIC_INIT);
|
||||||
boolean doesAlloc = (which == LF_NEWINVSPECIAL);
|
boolean doesAlloc = (which == LF_NEWINVSPECIAL);
|
||||||
boolean needsReceiverCheck = (which == LF_INVINTERFACE);
|
boolean needsReceiverCheck = (which == LF_INVINTERFACE ||
|
||||||
|
which == LF_INVSPECIAL_IFC);
|
||||||
|
|
||||||
String linkerName;
|
String linkerName;
|
||||||
LambdaForm.Kind kind;
|
LambdaForm.Kind kind;
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case LF_INVVIRTUAL: linkerName = "linkToVirtual"; kind = DIRECT_INVOKE_VIRTUAL; break;
|
case LF_INVVIRTUAL: linkerName = "linkToVirtual"; kind = DIRECT_INVOKE_VIRTUAL; break;
|
||||||
case LF_INVSTATIC: linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC; break;
|
case LF_INVSTATIC: linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC; break;
|
||||||
case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC_INIT; break;
|
case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC_INIT; break;
|
||||||
|
case LF_INVSPECIAL_IFC:linkerName = "linkToSpecial"; kind = DIRECT_INVOKE_SPECIAL_IFC; break;
|
||||||
case LF_INVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_INVOKE_SPECIAL; break;
|
case LF_INVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_INVOKE_SPECIAL; break;
|
||||||
case LF_INVINTERFACE: linkerName = "linkToInterface"; kind = DIRECT_INVOKE_INTERFACE; break;
|
case LF_INVINTERFACE: linkerName = "linkToInterface"; kind = DIRECT_INVOKE_INTERFACE; break;
|
||||||
case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_NEW_INVOKE_SPECIAL; break;
|
case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_NEW_INVOKE_SPECIAL; break;
|
||||||
|
@ -376,8 +390,10 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
|
|
||||||
/** This subclass represents invokespecial instructions. */
|
/** This subclass represents invokespecial instructions. */
|
||||||
static class Special extends DirectMethodHandle {
|
static class Special extends DirectMethodHandle {
|
||||||
private Special(MethodType mtype, LambdaForm form, MemberName member) {
|
private final Class<?> caller;
|
||||||
|
private Special(MethodType mtype, LambdaForm form, MemberName member, Class<?> caller) {
|
||||||
super(mtype, form, member);
|
super(mtype, form, member);
|
||||||
|
this.caller = caller;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
boolean isInvokeSpecial() {
|
boolean isInvokeSpecial() {
|
||||||
|
@ -385,7 +401,15 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||||
return new Special(mt, lf, member);
|
return new Special(mt, lf, member, caller);
|
||||||
|
}
|
||||||
|
Object checkReceiver(Object recv) {
|
||||||
|
if (!caller.isInstance(recv)) {
|
||||||
|
String msg = String.format("Receiver class %s is not a subclass of caller class %s",
|
||||||
|
recv.getClass().getName(), caller.getName());
|
||||||
|
throw new IncompatibleClassChangeError(msg);
|
||||||
|
}
|
||||||
|
return recv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,17 +425,23 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||||
return new Interface(mt, lf, member, refc);
|
return new Interface(mt, lf, member, refc);
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
Object checkReceiver(Object recv) {
|
Object checkReceiver(Object recv) {
|
||||||
if (!refc.isInstance(recv)) {
|
if (!refc.isInstance(recv)) {
|
||||||
String msg = String.format("Class %s does not implement the requested interface %s",
|
String msg = String.format("Receiver class %s does not implement the requested interface %s",
|
||||||
recv.getClass().getName(), refc.getName());
|
recv.getClass().getName(), refc.getName());
|
||||||
throw new IncompatibleClassChangeError(msg);
|
throw new IncompatibleClassChangeError(msg);
|
||||||
}
|
}
|
||||||
return recv;
|
return recv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Used for interface receiver type checks, by Interface and Special modes. */
|
||||||
|
Object checkReceiver(Object recv) {
|
||||||
|
throw new InternalError("Should only be invoked on a subclass");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** This subclass handles constructor references. */
|
/** This subclass handles constructor references. */
|
||||||
static class Constructor extends DirectMethodHandle {
|
static class Constructor extends DirectMethodHandle {
|
||||||
final MemberName initMethod;
|
final MemberName initMethod;
|
||||||
|
@ -823,10 +853,10 @@ class DirectMethodHandle extends MethodHandle {
|
||||||
MemberName.getFactory()
|
MemberName.getFactory()
|
||||||
.resolveOrFail(REF_getField, member, DirectMethodHandle.class, NoSuchMethodException.class));
|
.resolveOrFail(REF_getField, member, DirectMethodHandle.class, NoSuchMethodException.class));
|
||||||
case NF_checkReceiver:
|
case NF_checkReceiver:
|
||||||
member = new MemberName(Interface.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual);
|
member = new MemberName(DirectMethodHandle.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual);
|
||||||
return new NamedFunction(
|
return new NamedFunction(
|
||||||
MemberName.getFactory()
|
MemberName.getFactory()
|
||||||
.resolveOrFail(REF_invokeVirtual, member, Interface.class, NoSuchMethodException.class));
|
.resolveOrFail(REF_invokeVirtual, member, DirectMethodHandle.class, NoSuchMethodException.class));
|
||||||
default:
|
default:
|
||||||
throw newInternalError("Unknown function: " + func);
|
throw newInternalError("Unknown function: " + func);
|
||||||
}
|
}
|
||||||
|
|
|
@ -674,6 +674,7 @@ class InvokerBytecodeGenerator {
|
||||||
case DIRECT_NEW_INVOKE_SPECIAL: // fall-through
|
case DIRECT_NEW_INVOKE_SPECIAL: // fall-through
|
||||||
case DIRECT_INVOKE_INTERFACE: // fall-through
|
case DIRECT_INVOKE_INTERFACE: // fall-through
|
||||||
case DIRECT_INVOKE_SPECIAL: // fall-through
|
case DIRECT_INVOKE_SPECIAL: // fall-through
|
||||||
|
case DIRECT_INVOKE_SPECIAL_IFC: // fall-through
|
||||||
case DIRECT_INVOKE_STATIC: // fall-through
|
case DIRECT_INVOKE_STATIC: // fall-through
|
||||||
case DIRECT_INVOKE_STATIC_INIT: // fall-through
|
case DIRECT_INVOKE_STATIC_INIT: // fall-through
|
||||||
case DIRECT_INVOKE_VIRTUAL: return resolveFrom(name, invokerType, DirectMethodHandle.Holder.class);
|
case DIRECT_INVOKE_VIRTUAL: return resolveFrom(name, invokerType, DirectMethodHandle.Holder.class);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -291,6 +291,7 @@ class LambdaForm {
|
||||||
LINK_TO_CALL_SITE("linkToCallSite"),
|
LINK_TO_CALL_SITE("linkToCallSite"),
|
||||||
DIRECT_INVOKE_VIRTUAL("DMH.invokeVirtual", "invokeVirtual"),
|
DIRECT_INVOKE_VIRTUAL("DMH.invokeVirtual", "invokeVirtual"),
|
||||||
DIRECT_INVOKE_SPECIAL("DMH.invokeSpecial", "invokeSpecial"),
|
DIRECT_INVOKE_SPECIAL("DMH.invokeSpecial", "invokeSpecial"),
|
||||||
|
DIRECT_INVOKE_SPECIAL_IFC("DMH.invokeSpecialIFC", "invokeSpecialIFC"),
|
||||||
DIRECT_INVOKE_STATIC("DMH.invokeStatic", "invokeStatic"),
|
DIRECT_INVOKE_STATIC("DMH.invokeStatic", "invokeStatic"),
|
||||||
DIRECT_NEW_INVOKE_SPECIAL("DMH.newInvokeSpecial", "newInvokeSpecial"),
|
DIRECT_NEW_INVOKE_SPECIAL("DMH.newInvokeSpecial", "newInvokeSpecial"),
|
||||||
DIRECT_INVOKE_INTERFACE("DMH.invokeInterface", "invokeInterface"),
|
DIRECT_INVOKE_INTERFACE("DMH.invokeInterface", "invokeInterface"),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -2267,27 +2267,27 @@ return mh1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check access and get the requested method. */
|
/** Check access and get the requested method. */
|
||||||
private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
|
private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method, Class<?> boundCallerClass) throws IllegalAccessException {
|
||||||
final boolean doRestrict = true;
|
final boolean doRestrict = true;
|
||||||
final boolean checkSecurity = true;
|
final boolean checkSecurity = true;
|
||||||
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass);
|
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, boundCallerClass);
|
||||||
}
|
}
|
||||||
/** Check access and get the requested method, for invokespecial with no restriction on the application of narrowing rules. */
|
/** Check access and get the requested method, for invokespecial with no restriction on the application of narrowing rules. */
|
||||||
private MethodHandle getDirectMethodNoRestrictInvokeSpecial(Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
|
private MethodHandle getDirectMethodNoRestrictInvokeSpecial(Class<?> refc, MemberName method, Class<?> boundCallerClass) throws IllegalAccessException {
|
||||||
final boolean doRestrict = false;
|
final boolean doRestrict = false;
|
||||||
final boolean checkSecurity = true;
|
final boolean checkSecurity = true;
|
||||||
return getDirectMethodCommon(REF_invokeSpecial, refc, method, checkSecurity, doRestrict, callerClass);
|
return getDirectMethodCommon(REF_invokeSpecial, refc, method, checkSecurity, doRestrict, boundCallerClass);
|
||||||
}
|
}
|
||||||
/** Check access and get the requested method, eliding security manager checks. */
|
/** Check access and get the requested method, eliding security manager checks. */
|
||||||
private MethodHandle getDirectMethodNoSecurityManager(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
|
private MethodHandle getDirectMethodNoSecurityManager(byte refKind, Class<?> refc, MemberName method, Class<?> boundCallerClass) throws IllegalAccessException {
|
||||||
final boolean doRestrict = true;
|
final boolean doRestrict = true;
|
||||||
final boolean checkSecurity = false; // not needed for reflection or for linking CONSTANT_MH constants
|
final boolean checkSecurity = false; // not needed for reflection or for linking CONSTANT_MH constants
|
||||||
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass);
|
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, boundCallerClass);
|
||||||
}
|
}
|
||||||
/** Common code for all methods; do not call directly except from immediately above. */
|
/** Common code for all methods; do not call directly except from immediately above. */
|
||||||
private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
|
private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
|
||||||
boolean checkSecurity,
|
boolean checkSecurity,
|
||||||
boolean doRestrict, Class<?> callerClass) throws IllegalAccessException {
|
boolean doRestrict, Class<?> boundCallerClass) throws IllegalAccessException {
|
||||||
checkMethod(refKind, refc, method);
|
checkMethod(refKind, refc, method);
|
||||||
// Optionally check with the security manager; this isn't needed for unreflect* calls.
|
// Optionally check with the security manager; this isn't needed for unreflect* calls.
|
||||||
if (checkSecurity)
|
if (checkSecurity)
|
||||||
|
@ -2325,25 +2325,25 @@ return mh1;
|
||||||
checkMethod(refKind, refc, method);
|
checkMethod(refKind, refc, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method);
|
DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method, lookupClass());
|
||||||
MethodHandle mh = dmh;
|
MethodHandle mh = dmh;
|
||||||
// Optionally narrow the receiver argument to refc using restrictReceiver.
|
// Optionally narrow the receiver argument to lookupClass using restrictReceiver.
|
||||||
if ((doRestrict && refKind == REF_invokeSpecial) ||
|
if ((doRestrict && refKind == REF_invokeSpecial) ||
|
||||||
(MethodHandleNatives.refKindHasReceiver(refKind) && restrictProtectedReceiver(method))) {
|
(MethodHandleNatives.refKindHasReceiver(refKind) && restrictProtectedReceiver(method))) {
|
||||||
mh = restrictReceiver(method, dmh, lookupClass());
|
mh = restrictReceiver(method, dmh, lookupClass());
|
||||||
}
|
}
|
||||||
mh = maybeBindCaller(method, mh, callerClass);
|
mh = maybeBindCaller(method, mh, boundCallerClass);
|
||||||
mh = mh.setVarargs(method);
|
mh = mh.setVarargs(method);
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
|
private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
|
||||||
Class<?> callerClass)
|
Class<?> boundCallerClass)
|
||||||
throws IllegalAccessException {
|
throws IllegalAccessException {
|
||||||
if (allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method))
|
if (allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method))
|
||||||
return mh;
|
return mh;
|
||||||
Class<?> hostClass = lookupClass;
|
Class<?> hostClass = lookupClass;
|
||||||
if (!hasPrivateAccess()) // caller must have private access
|
if (!hasPrivateAccess()) // caller must have private access
|
||||||
hostClass = callerClass; // callerClass came from a security manager style stack walk
|
hostClass = boundCallerClass; // boundCallerClass came from a security manager style stack walk
|
||||||
MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);
|
MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);
|
||||||
// Note: caller will apply varargs after this step happens.
|
// Note: caller will apply varargs after this step happens.
|
||||||
return cbmh;
|
return cbmh;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -85,7 +85,8 @@ final class MethodTypeForm {
|
||||||
LF_GWT = 17, // guardWithTest
|
LF_GWT = 17, // guardWithTest
|
||||||
LF_TF = 18, // tryFinally
|
LF_TF = 18, // tryFinally
|
||||||
LF_LOOP = 19, // loop
|
LF_LOOP = 19, // loop
|
||||||
LF_LIMIT = 20;
|
LF_INVSPECIAL_IFC = 20, // DMH invokeSpecial of (private) interface method
|
||||||
|
LF_LIMIT = 21;
|
||||||
|
|
||||||
/** Return the type corresponding uniquely (1-1) to this MT-form.
|
/** Return the type corresponding uniquely (1-1) to this MT-form.
|
||||||
* It might have any primitive returns or arguments, but will have no references except Object.
|
* It might have any primitive returns or arguments, but will have no references except Object.
|
||||||
|
|
106
test/jdk/java/lang/invoke/I4Special.jcod
Normal file
106
test/jdk/java/lang/invoke/I4Special.jcod
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// invokeDirect is modified to use invokespecial instead of invokevirtual
|
||||||
|
|
||||||
|
class SpecialInterfaceCall$I4 {
|
||||||
|
0xCAFEBABE;
|
||||||
|
0; // minor version
|
||||||
|
55; // version
|
||||||
|
[] { // Constant Pool
|
||||||
|
; // first element is empty
|
||||||
|
Method #3 #13; // #1
|
||||||
|
class #15; // #2
|
||||||
|
class #16; // #3
|
||||||
|
class #17; // #4
|
||||||
|
Utf8 "invokeDirect"; // #5
|
||||||
|
Utf8 "I4"; // #6
|
||||||
|
Utf8 "InnerClasses"; // #7
|
||||||
|
Utf8 "(LSpecialInterfaceCall$I4;)V"; // #8
|
||||||
|
Utf8 "Code"; // #9
|
||||||
|
Utf8 "LineNumberTable"; // #10
|
||||||
|
Utf8 "SourceFile"; // #11
|
||||||
|
Utf8 "SpecialInterfaceCall.java"; // #12
|
||||||
|
NameAndType #19 #20; // #13
|
||||||
|
class #21; // #14
|
||||||
|
Utf8 "SpecialInterfaceCall$I4"; // #15
|
||||||
|
Utf8 "java/lang/Object"; // #16
|
||||||
|
Utf8 "SpecialInterfaceCall$I1"; // #17
|
||||||
|
Utf8 "I1"; // #18
|
||||||
|
Utf8 "toString"; // #19
|
||||||
|
Utf8 "()Ljava/lang/String;"; // #20
|
||||||
|
Utf8 "SpecialInterfaceCall"; // #21
|
||||||
|
} // Constant Pool
|
||||||
|
|
||||||
|
0x0600; // access
|
||||||
|
#2;// this_cpx
|
||||||
|
#3;// super_cpx
|
||||||
|
|
||||||
|
[] { // Interfaces
|
||||||
|
#4;
|
||||||
|
} // Interfaces
|
||||||
|
|
||||||
|
[] { // fields
|
||||||
|
} // fields
|
||||||
|
|
||||||
|
[] { // methods
|
||||||
|
{ // Member
|
||||||
|
0x0009; // access
|
||||||
|
#5; // name_cpx
|
||||||
|
#8; // sig_cpx
|
||||||
|
[] { // Attributes
|
||||||
|
Attr(#9) { // Code
|
||||||
|
1; // max_stack
|
||||||
|
2; // max_locals
|
||||||
|
Bytes[]{
|
||||||
|
// 0x2AB600014CB1;
|
||||||
|
0x2AB700014CB1; // invokespecial
|
||||||
|
};
|
||||||
|
[] { // Traps
|
||||||
|
} // end Traps
|
||||||
|
[] { // Attributes
|
||||||
|
Attr(#10) { // LineNumberTable
|
||||||
|
[] { // LineNumberTable
|
||||||
|
0 77;
|
||||||
|
5 78;
|
||||||
|
}
|
||||||
|
} // end LineNumberTable
|
||||||
|
} // Attributes
|
||||||
|
} // end Code
|
||||||
|
} // Attributes
|
||||||
|
} // Member
|
||||||
|
} // methods
|
||||||
|
|
||||||
|
[] { // Attributes
|
||||||
|
Attr(#11) { // SourceFile
|
||||||
|
#12;
|
||||||
|
} // end SourceFile
|
||||||
|
;
|
||||||
|
Attr(#7) { // InnerClasses
|
||||||
|
[] { // InnerClasses
|
||||||
|
#2 #14 #6 1544;
|
||||||
|
#4 #14 #18 1544;
|
||||||
|
}
|
||||||
|
} // end InnerClasses
|
||||||
|
} // Attributes
|
||||||
|
} // end class SpecialInterfaceCall$I4
|
254
test/jdk/java/lang/invoke/SpecialInterfaceCall.java
Normal file
254
test/jdk/java/lang/invoke/SpecialInterfaceCall.java
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, 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.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* @bug 8200167
|
||||||
|
* @summary Test direct and MethodHandle access to interface methods using invokespecial semantics
|
||||||
|
* @compile SpecialInterfaceCall.java
|
||||||
|
* @compile I4Special.jcod
|
||||||
|
* @run main/othervm -Xint SpecialInterfaceCall
|
||||||
|
* @run main/othervm -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1 SpecialInterfaceCall
|
||||||
|
* @run main/othervm -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=2 SpecialInterfaceCall
|
||||||
|
* @run main/othervm -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=3 SpecialInterfaceCall
|
||||||
|
* @run main/othervm -Xbatch -XX:-TieredCompilation SpecialInterfaceCall
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.invoke.*;
|
||||||
|
|
||||||
|
public class SpecialInterfaceCall {
|
||||||
|
interface I1 {
|
||||||
|
default void pub_m() {};
|
||||||
|
private void priv_m() {};
|
||||||
|
}
|
||||||
|
interface I2 extends I1 {
|
||||||
|
// This needs to be a public method to avoid access control issues,
|
||||||
|
// but logically we treat it as private and emulate invokespecial
|
||||||
|
// using MethodHandles.
|
||||||
|
default void pub_m() {};
|
||||||
|
|
||||||
|
private void priv_m() {};
|
||||||
|
|
||||||
|
static void invokeDirect(I2 i) {
|
||||||
|
i.priv_m(); // generates invokespecial
|
||||||
|
}
|
||||||
|
static void invokeSpecialMH(I2 i) throws Throwable {
|
||||||
|
// emulates behaviour of invokeDirect
|
||||||
|
mh_I2_priv_m_from_I2.invokeExact(i);
|
||||||
|
}
|
||||||
|
// special case of invoking an Object method via an interface
|
||||||
|
static void invokeSpecialObjectMH(I2 i) throws Throwable {
|
||||||
|
// emulates invokespecial of I1.toString on i, which resolves
|
||||||
|
// to Object.toString
|
||||||
|
String s = (String) mh_I1_toString_from_I2.invokeExact(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface I3 extends I2 {
|
||||||
|
// Must take an I3 here rather than I2 else we get
|
||||||
|
// WrongMethodTypeException: expected (I3)void but found (I2)void
|
||||||
|
// Statically the receiver type is bounded by the caller type.
|
||||||
|
static void invokeSpecialMH(I3 i) throws Throwable {
|
||||||
|
// emulates an invokespecial of ((I2)i).pub_m()
|
||||||
|
mh_I2_pub_m_from_I3.invokeExact(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This interface acts like I2 but we define a directInvoke method
|
||||||
|
// that we will rewrite the bytecode of to use invokespecial
|
||||||
|
interface I4 extends I1 {
|
||||||
|
static void invokeDirect(I4 i) {
|
||||||
|
String s = i.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete classes
|
||||||
|
static class C1 implements I1 { }
|
||||||
|
static class C2 implements I2 { }
|
||||||
|
static class C3 implements I3 { }
|
||||||
|
static class C4 implements I4 { }
|
||||||
|
|
||||||
|
// Classes that don't implement I2/I3 but do have a
|
||||||
|
// priv_m/pub_m method in their hierarchy
|
||||||
|
static class D1 implements I1 { }
|
||||||
|
static class E {
|
||||||
|
public void pub_m() {}
|
||||||
|
private void priv_m() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This MH acts like the direct invokespecial in I2.invokeDirect
|
||||||
|
static final MethodHandle mh_I2_priv_m_from_I2;
|
||||||
|
|
||||||
|
// This MH acts like an invokespecial of I2.pub_m from I3
|
||||||
|
static final MethodHandle mh_I2_pub_m_from_I3;
|
||||||
|
|
||||||
|
// This MH acts likes an invokespecial of I1.toString from I2
|
||||||
|
static final MethodHandle mh_I1_toString_from_I2;
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
MethodType mt = MethodType.methodType(void.class);
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
|
||||||
|
mh_I2_priv_m_from_I2 = lookup.findSpecial(I2.class, "priv_m", mt, I2.class);
|
||||||
|
mh_I2_pub_m_from_I3 = lookup.findSpecial(I2.class, "pub_m", mt, I3.class);
|
||||||
|
|
||||||
|
mt = MethodType.methodType(String.class);
|
||||||
|
mh_I1_toString_from_I2 = lookup.findSpecial(I1.class, "toString", mt, I2.class);
|
||||||
|
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void runPositiveTests() {
|
||||||
|
shouldNotThrow(() -> I2.invokeDirect(new C2()));
|
||||||
|
shouldNotThrow(() -> I2.invokeDirect(new C3()));
|
||||||
|
shouldNotThrow(() -> I2.invokeSpecialMH(new C2()));
|
||||||
|
shouldNotThrow(() -> I2.invokeSpecialMH(new C3()));
|
||||||
|
|
||||||
|
shouldNotThrow(() -> I3.invokeSpecialMH(new C3()));
|
||||||
|
|
||||||
|
shouldNotThrow(() -> I4.invokeDirect(new C4()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void runNegativeTests() {
|
||||||
|
System.out.println("IAE I2.invokeDirect D1");
|
||||||
|
shouldThrowIAE(() -> I2.invokeDirect(unsafeCastI2(new D1())));
|
||||||
|
System.out.println("IAE I2.invokeDirect E");
|
||||||
|
shouldThrowIAE(() -> I2.invokeDirect(unsafeCastI2(new E())));
|
||||||
|
System.out.println("ICCE I2.invokeMH D1");
|
||||||
|
shouldThrowICCE(() -> I2.invokeSpecialMH(unsafeCastI2(new D1())));
|
||||||
|
System.out.println("ICCE I2.invokeMH E");
|
||||||
|
shouldThrowICCE(() -> I2.invokeSpecialMH(unsafeCastI2(new E())));
|
||||||
|
System.out.println("ICCE I3.invokeMH D1");
|
||||||
|
shouldThrowICCE(() -> I3.invokeSpecialMH(unsafeCastI3(new D1())));
|
||||||
|
System.out.println("ICCE I3.invokeMH E");
|
||||||
|
shouldThrowICCE(() -> I3.invokeSpecialMH(unsafeCastI3(new E())));
|
||||||
|
System.out.println("ICCE I3.invokeMH C2");
|
||||||
|
shouldThrowICCE(() -> I3.invokeSpecialMH(unsafeCastI3(new C2())));
|
||||||
|
System.out.println("ICCE I4.invokeDirect C1");
|
||||||
|
shouldThrowIAE(() -> I4.invokeDirect(unsafeCastI4(new C1())));
|
||||||
|
System.out.println("ICCE I2.invokeObjectMH C1");
|
||||||
|
shouldThrowICCE(() -> I2.invokeSpecialObjectMH(unsafeCastI2(new C1())));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void warmup() {
|
||||||
|
for (int i = 0; i < 20_000; i++) {
|
||||||
|
runPositiveTests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
System.out.println("UNRESOLVED:");
|
||||||
|
runNegativeTests();
|
||||||
|
runPositiveTests();
|
||||||
|
|
||||||
|
System.out.println("RESOLVED:");
|
||||||
|
runNegativeTests();
|
||||||
|
|
||||||
|
System.out.println("WARMUP:");
|
||||||
|
warmup();
|
||||||
|
|
||||||
|
System.out.println("COMPILED:");
|
||||||
|
runNegativeTests();
|
||||||
|
runPositiveTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
static interface Test {
|
||||||
|
void run() throws Throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shouldThrowICCE(Test t) {
|
||||||
|
shouldThrow(IncompatibleClassChangeError.class,
|
||||||
|
"is not a subclass of caller class", t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shouldThrowIAE(Test t) {
|
||||||
|
shouldThrow(IllegalAccessError.class,
|
||||||
|
"must be the current class or a subtype of interface", t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shouldThrow(Class<?> expectedError, String reason, Test t) {
|
||||||
|
try {
|
||||||
|
t.run();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
if (expectedError.isInstance(e)) {
|
||||||
|
if (e.getMessage().contains(reason)) {
|
||||||
|
// passed
|
||||||
|
System.out.println("Threw expected: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new AssertionError("Wrong exception reason: expected '" + reason
|
||||||
|
+ "', got '" + e.getMessage() + "'", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = String.format("Wrong exception thrown: expected=%s; thrown=%s",
|
||||||
|
expectedError.getName(), e.getClass().getName());
|
||||||
|
throw new AssertionError(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AssertionError("No exception thrown: expected " + expectedError.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shouldNotThrow(Test t) {
|
||||||
|
try {
|
||||||
|
t.run();
|
||||||
|
// passed
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new AssertionError("Exception was thrown: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: these unsafe casts are only possible for interface types
|
||||||
|
|
||||||
|
static I2 unsafeCastI2(Object obj) {
|
||||||
|
try {
|
||||||
|
MethodHandle mh = MethodHandles.identity(Object.class);
|
||||||
|
mh = MethodHandles.explicitCastArguments(mh, mh.type().changeReturnType(I2.class));
|
||||||
|
return (I2)mh.invokeExact((Object) obj);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static I3 unsafeCastI3(Object obj) {
|
||||||
|
try {
|
||||||
|
MethodHandle mh = MethodHandles.identity(Object.class);
|
||||||
|
mh = MethodHandles.explicitCastArguments(mh, mh.type().changeReturnType(I3.class));
|
||||||
|
return (I3)mh.invokeExact((Object) obj);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static I4 unsafeCastI4(Object obj) {
|
||||||
|
try {
|
||||||
|
MethodHandle mh = MethodHandles.identity(Object.class);
|
||||||
|
mh = MethodHandles.explicitCastArguments(mh, mh.type().changeReturnType(I4.class));
|
||||||
|
return (I4)mh.invokeExact((Object) obj);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue