mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8195650: Method references to VarHandle accessors
Reviewed-by: jrose
This commit is contained in:
parent
f44956330d
commit
14651d9e02
6 changed files with 190 additions and 13 deletions
|
@ -2709,11 +2709,11 @@ Handle SystemDictionary::link_method_handle_constant(Klass* caller,
|
||||||
java_lang_invoke_MemberName::set_flags(mname(), MethodHandles::ref_kind_to_flags(ref_kind));
|
java_lang_invoke_MemberName::set_flags(mname(), MethodHandles::ref_kind_to_flags(ref_kind));
|
||||||
|
|
||||||
if (ref_kind == JVM_REF_invokeVirtual &&
|
if (ref_kind == JVM_REF_invokeVirtual &&
|
||||||
callee->name() == vmSymbols::java_lang_invoke_MethodHandle() &&
|
MethodHandles::is_signature_polymorphic_public_name(callee, name)) {
|
||||||
(name == vmSymbols::invoke_name() || name == vmSymbols::invokeExact_name())) {
|
// Skip resolution for public signature polymorphic methods such as
|
||||||
// Skip resolution for j.l.i.MethodHandle.invoke()/invokeExact().
|
// j.l.i.MethodHandle.invoke()/invokeExact() and those on VarHandle
|
||||||
// They are public signature polymorphic methods, but require appendix argument
|
// They require appendix argument which MemberName resolution doesn't handle.
|
||||||
// which MemberName resolution doesn't handle. There's special logic on JDK side to handle them
|
// There's special logic on JDK side to handle them
|
||||||
// (see MethodHandles.linkMethodHandleConstant() and MethodHandles.findVirtualForMH()).
|
// (see MethodHandles.linkMethodHandleConstant() and MethodHandles.findVirtualForMH()).
|
||||||
} else {
|
} else {
|
||||||
MethodHandles::resolve_MemberName(mname, caller, /*speculative_resolve*/false, CHECK_(empty));
|
MethodHandles::resolve_MemberName(mname, caller, /*speculative_resolve*/false, CHECK_(empty));
|
||||||
|
|
|
@ -491,6 +491,24 @@ vmIntrinsics::ID MethodHandles::signature_polymorphic_name_id(Klass* klass, Symb
|
||||||
return vmIntrinsics::_none;
|
return vmIntrinsics::_none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if method is signature polymorphic and public
|
||||||
|
bool MethodHandles::is_signature_polymorphic_public_name(Klass* klass, Symbol* name) {
|
||||||
|
if (is_signature_polymorphic_name(klass, name)) {
|
||||||
|
InstanceKlass* iklass = InstanceKlass::cast(klass);
|
||||||
|
int me;
|
||||||
|
int ms = iklass->find_method_by_name(name, &me);
|
||||||
|
assert(ms != -1, "");
|
||||||
|
for (; ms < me; ms++) {
|
||||||
|
Method* m = iklass->methods()->at(ms);
|
||||||
|
int required = JVM_ACC_NATIVE | JVM_ACC_VARARGS | JVM_ACC_PUBLIC;
|
||||||
|
int flags = m->access_flags().as_int();
|
||||||
|
if ((flags & required) == required && ArgumentCount(m->signature()).size() == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// convert the external string or reflective type to an internal signature
|
// convert the external string or reflective type to an internal signature
|
||||||
Symbol* MethodHandles::lookup_signature(oop type_str, bool intern_if_not_found, TRAPS) {
|
Symbol* MethodHandles::lookup_signature(oop type_str, bool intern_if_not_found, TRAPS) {
|
||||||
|
@ -741,7 +759,7 @@ Handle MethodHandles::resolve_MemberName(Handle mname, Klass* caller,
|
||||||
|
|
||||||
vmIntrinsics::ID mh_invoke_id = vmIntrinsics::_none;
|
vmIntrinsics::ID mh_invoke_id = vmIntrinsics::_none;
|
||||||
if ((flags & ALL_KINDS) == IS_METHOD &&
|
if ((flags & ALL_KINDS) == IS_METHOD &&
|
||||||
(defc == SystemDictionary::MethodHandle_klass()) &&
|
(defc == SystemDictionary::MethodHandle_klass() || defc == SystemDictionary::VarHandle_klass()) &&
|
||||||
(ref_kind == JVM_REF_invokeVirtual ||
|
(ref_kind == JVM_REF_invokeVirtual ||
|
||||||
ref_kind == JVM_REF_invokeSpecial ||
|
ref_kind == JVM_REF_invokeSpecial ||
|
||||||
// static invocation mode is required for _linkToVirtual, etc.:
|
// static invocation mode is required for _linkToVirtual, etc.:
|
||||||
|
|
|
@ -143,6 +143,7 @@ class MethodHandles: AllStatic {
|
||||||
static bool is_signature_polymorphic_name(Klass* klass, Symbol* name) {
|
static bool is_signature_polymorphic_name(Klass* klass, Symbol* name) {
|
||||||
return signature_polymorphic_name_id(klass, name) != vmIntrinsics::_none;
|
return signature_polymorphic_name_id(klass, name) != vmIntrinsics::_none;
|
||||||
}
|
}
|
||||||
|
static bool is_signature_polymorphic_public_name(Klass* klass, Symbol* name);
|
||||||
|
|
||||||
static Bytecodes::Code signature_polymorphic_intrinsic_bytecode(vmIntrinsics::ID id);
|
static Bytecodes::Code signature_polymorphic_intrinsic_bytecode(vmIntrinsics::ID id);
|
||||||
|
|
||||||
|
|
|
@ -2452,12 +2452,18 @@ return mh1;
|
||||||
checkSymbolicClass(defc);
|
checkSymbolicClass(defc);
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
// Treat MethodHandle.invoke and invokeExact specially.
|
|
||||||
if (defc == MethodHandle.class && refKind == REF_invokeVirtual) {
|
if (defc == MethodHandle.class && refKind == REF_invokeVirtual) {
|
||||||
|
// Treat MethodHandle.invoke and invokeExact specially.
|
||||||
mh = findVirtualForMH(member.getName(), member.getMethodType());
|
mh = findVirtualForMH(member.getName(), member.getMethodType());
|
||||||
if (mh != null) {
|
if (mh != null) {
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
|
} else if (defc == VarHandle.class && refKind == REF_invokeVirtual) {
|
||||||
|
// Treat signature-polymorphic methods on VarHandle specially.
|
||||||
|
mh = findVirtualForVH(member.getName(), member.getMethodType());
|
||||||
|
if (mh != null) {
|
||||||
|
return mh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MemberName resolved = resolveOrFail(refKind, member);
|
MemberName resolved = resolveOrFail(refKind, member);
|
||||||
mh = getDirectMethodForConstant(refKind, defc, resolved);
|
mh = getDirectMethodForConstant(refKind, defc, resolved);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 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
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8087223
|
* @bug 8087223 8195650
|
||||||
* @summary Adding constantTag to keep method call consistent with it.
|
* @summary Adding constantTag to keep method call consistent with it.
|
||||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||||
* java.base/jdk.internal.misc
|
* java.base/jdk.internal.misc
|
||||||
|
@ -46,9 +46,9 @@ public class BadMethodHandles {
|
||||||
ClassWriter cw = new ClassWriter(0);
|
ClassWriter cw = new ClassWriter(0);
|
||||||
cw.visit(52, ACC_PUBLIC | ACC_SUPER, "BadInterfaceMethodref", null, "java/lang/Object", null);
|
cw.visit(52, ACC_PUBLIC | ACC_SUPER, "BadInterfaceMethodref", null, "java/lang/Object", null);
|
||||||
Handle handle1 =
|
Handle handle1 =
|
||||||
new Handle(Opcodes.H_INVOKEINTERFACE, "BadInterfaceMethodref", "m", "()V");
|
new Handle(Opcodes.H_INVOKEINTERFACE, "BadInterfaceMethodref", "m", "()V", true);
|
||||||
Handle handle2 =
|
Handle handle2 =
|
||||||
new Handle(Opcodes.H_INVOKEINTERFACE, "BadInterfaceMethodref", "staticM", "()V");
|
new Handle(Opcodes.H_INVOKEINTERFACE, "BadInterfaceMethodref", "staticM", "()V", true);
|
||||||
|
|
||||||
{
|
{
|
||||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||||
|
@ -137,9 +137,9 @@ public class BadMethodHandles {
|
||||||
ClassWriter cw = new ClassWriter(0);
|
ClassWriter cw = new ClassWriter(0);
|
||||||
cw.visit(52, ACC_PUBLIC | ACC_SUPER, "BadMethodref", null, "java/lang/Object", new String[]{"IBad"});
|
cw.visit(52, ACC_PUBLIC | ACC_SUPER, "BadMethodref", null, "java/lang/Object", new String[]{"IBad"});
|
||||||
Handle handle1 =
|
Handle handle1 =
|
||||||
new Handle(Opcodes.H_INVOKEINTERFACE, "BadMethodref", "m", "()V");
|
new Handle(Opcodes.H_INVOKEINTERFACE, "BadMethodref", "m", "()V", true);
|
||||||
Handle handle2 =
|
Handle handle2 =
|
||||||
new Handle(Opcodes.H_INVOKEINTERFACE, "BadMethodref", "staticM", "()V");
|
new Handle(Opcodes.H_INVOKEINTERFACE, "BadMethodref", "staticM", "()V", true);
|
||||||
|
|
||||||
{
|
{
|
||||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||||
|
@ -176,6 +176,37 @@ public class BadMethodHandles {
|
||||||
cw.visitEnd();
|
cw.visitEnd();
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static byte[] dumpInvokeBasic() {
|
||||||
|
ClassWriter cw = new ClassWriter(0);
|
||||||
|
cw.visit(52, ACC_PUBLIC | ACC_SUPER, "InvokeBasicref", null, "java/lang/Object", null);
|
||||||
|
Handle handle =
|
||||||
|
new Handle(Opcodes.H_INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeBasic", "([Ljava/lang/Object;)Ljava/lang/Object;", false);
|
||||||
|
|
||||||
|
{
|
||||||
|
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||||
|
mv.visitCode();
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(1, 1);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "runInvokeBasicM", "()V", null, null);
|
||||||
|
mv.visitCode();
|
||||||
|
mv.visitLdcInsn(handle);
|
||||||
|
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invoke", "()V", false);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(1, 1);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
cw.visitEnd();
|
||||||
|
return cw.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
static class CL extends ClassLoader {
|
static class CL extends ClassLoader {
|
||||||
@Override
|
@Override
|
||||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
|
@ -184,6 +215,7 @@ public class BadMethodHandles {
|
||||||
case "BadInterfaceMethodref": classBytes = dumpBadInterfaceMethodref(); break;
|
case "BadInterfaceMethodref": classBytes = dumpBadInterfaceMethodref(); break;
|
||||||
case "BadMethodref" : classBytes = dumpBadMethodref(); break;
|
case "BadMethodref" : classBytes = dumpBadMethodref(); break;
|
||||||
case "IBad" : classBytes = dumpIBad(); break;
|
case "IBad" : classBytes = dumpIBad(); break;
|
||||||
|
case "InvokeBasicref" : classBytes = dumpInvokeBasic(); break;
|
||||||
default : throw new ClassNotFoundException(name);
|
default : throw new ClassNotFoundException(name);
|
||||||
}
|
}
|
||||||
return defineClass(name, classBytes, 0, classBytes.length);
|
return defineClass(name, classBytes, 0, classBytes.length);
|
||||||
|
@ -200,6 +232,9 @@ public class BadMethodHandles {
|
||||||
try (FileOutputStream fos = new FileOutputStream("BadMethodref.class")) {
|
try (FileOutputStream fos = new FileOutputStream("BadMethodref.class")) {
|
||||||
fos.write(dumpBadMethodref());
|
fos.write(dumpBadMethodref());
|
||||||
}
|
}
|
||||||
|
try (FileOutputStream fos = new FileOutputStream("InvokeBasicref.class")) {
|
||||||
|
fos.write(dumpInvokeBasic());
|
||||||
|
}
|
||||||
|
|
||||||
Class<?> cls = (new CL()).loadClass("BadInterfaceMethodref");
|
Class<?> cls = (new CL()).loadClass("BadInterfaceMethodref");
|
||||||
String[] methods = {"runm", "runStaticM"};
|
String[] methods = {"runm", "runStaticM"};
|
||||||
|
@ -249,5 +284,30 @@ public class BadMethodHandles {
|
||||||
throw new Exception("BadMethodRef Failed to catch IncompatibleClassChangeError");
|
throw new Exception("BadMethodRef Failed to catch IncompatibleClassChangeError");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.out.println("Test InvokeBasicref:");
|
||||||
|
cls = (new CL()).loadClass("InvokeBasicref");
|
||||||
|
success = 0;
|
||||||
|
methods = new String[] {"runInvokeBasicM"};
|
||||||
|
for (String name : methods) {
|
||||||
|
try {
|
||||||
|
System.out.printf("invoke %s: \n", name);
|
||||||
|
cls.getMethod(name).invoke(cls.newInstance());
|
||||||
|
System.out.println("FAILED - ICCE should be thrown");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
if (e instanceof InvocationTargetException && e.getCause() != null &&
|
||||||
|
e.getCause() instanceof IncompatibleClassChangeError) {
|
||||||
|
System.out.println("PASSED - expected ICCE thrown");
|
||||||
|
success++;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
System.out.println("FAILED with wrong exception" + e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (success != methods.length) {
|
||||||
|
throw new Exception("InvokeBasicref Failed to catch IncompatibleClassChangeError");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* 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 8195650
|
||||||
|
* @summary Test linking of method references to VarHandle access methods.
|
||||||
|
* @run testng VarHandleMethodReferenceTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import java.lang.invoke.*;
|
||||||
|
|
||||||
|
public class VarHandleMethodReferenceTest {
|
||||||
|
|
||||||
|
interface R {
|
||||||
|
void apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMethodReferences() {
|
||||||
|
VarHandle vh = MethodHandles.arrayElementVarHandle(int[].class);
|
||||||
|
|
||||||
|
// The compilation of these method references will result in the
|
||||||
|
// placement of MethodHandles in the constant pool that reference
|
||||||
|
// VarHandle signature polymorphic methods.
|
||||||
|
// When those constant method handles are loaded the VarHandle invoker
|
||||||
|
// mechanism will be used where the first argument to invocation will be
|
||||||
|
// the bound VarHandle instance
|
||||||
|
|
||||||
|
// VarHandle invokers are tested by other test classes so here it
|
||||||
|
// is just necessary to check that functional objects can be successfully
|
||||||
|
// obtained, it does not matter about the signature of the functional
|
||||||
|
// interface
|
||||||
|
|
||||||
|
R r;
|
||||||
|
r = vh::get;
|
||||||
|
r = vh::set;
|
||||||
|
r = vh::getVolatile;
|
||||||
|
r = vh::setVolatile;
|
||||||
|
r = vh::getOpaque;
|
||||||
|
r = vh::setOpaque;
|
||||||
|
r = vh::getAcquire;
|
||||||
|
r = vh::setRelease;
|
||||||
|
|
||||||
|
r = vh::compareAndSet;
|
||||||
|
r = vh::compareAndExchange;
|
||||||
|
r = vh::compareAndExchangeAcquire;
|
||||||
|
r = vh::compareAndExchangeRelease;
|
||||||
|
r = vh::weakCompareAndSetPlain;
|
||||||
|
r = vh::weakCompareAndSet;
|
||||||
|
r = vh::weakCompareAndSetAcquire;
|
||||||
|
r = vh::weakCompareAndSetRelease;
|
||||||
|
|
||||||
|
r = vh::getAndSet;
|
||||||
|
r = vh::getAndSetAcquire;
|
||||||
|
r = vh::getAndSetRelease;
|
||||||
|
r = vh::getAndAdd;
|
||||||
|
r = vh::getAndAddAcquire;
|
||||||
|
r = vh::getAndAddRelease;
|
||||||
|
r = vh::getAndBitwiseOr;
|
||||||
|
r = vh::getAndBitwiseOrAcquire;
|
||||||
|
r = vh::getAndBitwiseOrRelease;
|
||||||
|
r = vh::getAndBitwiseAnd;
|
||||||
|
r = vh::getAndBitwiseAndAcquire;
|
||||||
|
r = vh::getAndBitwiseAndRelease;
|
||||||
|
r = vh::getAndBitwiseXor;
|
||||||
|
r = vh::getAndBitwiseXorAcquire;
|
||||||
|
r = vh::getAndBitwiseXorRelease;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue