# HG changeset patch # User psandoz # Date 1529962784 25200 # Node ID 9ca95539747d8b2ffa7a351bda17caab58e5f431 # Parent c545db4fc9bd61e461c3dd896e4a46c24498f3af 8195650: Method references to VarHandle accessors Reviewed-by: jrose diff -r c545db4fc9bd -r 9ca95539747d src/hotspot/share/classfile/systemDictionary.cpp --- a/src/hotspot/share/classfile/systemDictionary.cpp Mon Jun 25 14:36:16 2018 -0700 +++ b/src/hotspot/share/classfile/systemDictionary.cpp Mon Jun 25 14:39:44 2018 -0700 @@ -2709,11 +2709,11 @@ java_lang_invoke_MemberName::set_flags(mname(), MethodHandles::ref_kind_to_flags(ref_kind)); if (ref_kind == JVM_REF_invokeVirtual && - callee->name() == vmSymbols::java_lang_invoke_MethodHandle() && - (name == vmSymbols::invoke_name() || name == vmSymbols::invokeExact_name())) { - // Skip resolution for j.l.i.MethodHandle.invoke()/invokeExact(). - // They are public signature polymorphic methods, but require appendix argument - // which MemberName resolution doesn't handle. There's special logic on JDK side to handle them + MethodHandles::is_signature_polymorphic_public_name(callee, name)) { + // Skip resolution for public signature polymorphic methods such as + // j.l.i.MethodHandle.invoke()/invokeExact() and those on VarHandle + // They require appendix argument which MemberName resolution doesn't handle. + // There's special logic on JDK side to handle them // (see MethodHandles.linkMethodHandleConstant() and MethodHandles.findVirtualForMH()). } else { MethodHandles::resolve_MemberName(mname, caller, /*speculative_resolve*/false, CHECK_(empty)); diff -r c545db4fc9bd -r 9ca95539747d src/hotspot/share/prims/methodHandles.cpp --- a/src/hotspot/share/prims/methodHandles.cpp Mon Jun 25 14:36:16 2018 -0700 +++ b/src/hotspot/share/prims/methodHandles.cpp Mon Jun 25 14:39:44 2018 -0700 @@ -491,6 +491,24 @@ 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 Symbol* MethodHandles::lookup_signature(oop type_str, bool intern_if_not_found, TRAPS) { @@ -741,7 +759,7 @@ vmIntrinsics::ID mh_invoke_id = vmIntrinsics::_none; 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_invokeSpecial || // static invocation mode is required for _linkToVirtual, etc.: diff -r c545db4fc9bd -r 9ca95539747d src/hotspot/share/prims/methodHandles.hpp --- a/src/hotspot/share/prims/methodHandles.hpp Mon Jun 25 14:36:16 2018 -0700 +++ b/src/hotspot/share/prims/methodHandles.hpp Mon Jun 25 14:39:44 2018 -0700 @@ -143,6 +143,7 @@ static bool is_signature_polymorphic_name(Klass* klass, Symbol* name) { 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); diff -r c545db4fc9bd -r 9ca95539747d src/java.base/share/classes/java/lang/invoke/MethodHandles.java --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Mon Jun 25 14:36:16 2018 -0700 +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Mon Jun 25 14:39:44 2018 -0700 @@ -2452,12 +2452,18 @@ checkSymbolicClass(defc); return mh; } - // Treat MethodHandle.invoke and invokeExact specially. if (defc == MethodHandle.class && refKind == REF_invokeVirtual) { + // Treat MethodHandle.invoke and invokeExact specially. mh = findVirtualForMH(member.getName(), member.getMethodType()); if (mh != null) { 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); mh = getDirectMethodForConstant(refKind, defc, resolved); diff -r c545db4fc9bd -r 9ca95539747d test/hotspot/jtreg/runtime/ConstantPool/BadMethodHandles.java --- a/test/hotspot/jtreg/runtime/ConstantPool/BadMethodHandles.java Mon Jun 25 14:36:16 2018 -0700 +++ b/test/hotspot/jtreg/runtime/ConstantPool/BadMethodHandles.java Mon Jun 25 14:39:44 2018 -0700 @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8087223 + * @bug 8087223 8195650 * @summary Adding constantTag to keep method call consistent with it. * @modules java.base/jdk.internal.org.objectweb.asm * java.base/jdk.internal.misc @@ -46,9 +46,9 @@ ClassWriter cw = new ClassWriter(0); cw.visit(52, ACC_PUBLIC | ACC_SUPER, "BadInterfaceMethodref", null, "java/lang/Object", null); Handle handle1 = - new Handle(Opcodes.H_INVOKEINTERFACE, "BadInterfaceMethodref", "m", "()V"); + new Handle(Opcodes.H_INVOKEINTERFACE, "BadInterfaceMethodref", "m", "()V", true); 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, "", "()V", null, null); @@ -137,9 +137,9 @@ ClassWriter cw = new ClassWriter(0); cw.visit(52, ACC_PUBLIC | ACC_SUPER, "BadMethodref", null, "java/lang/Object", new String[]{"IBad"}); Handle handle1 = - new Handle(Opcodes.H_INVOKEINTERFACE, "BadMethodref", "m", "()V"); + new Handle(Opcodes.H_INVOKEINTERFACE, "BadMethodref", "m", "()V", true); 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, "", "()V", null, null); @@ -176,6 +176,37 @@ cw.visitEnd(); 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, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()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 { @Override protected Class findClass(String name) throws ClassNotFoundException { @@ -184,6 +215,7 @@ case "BadInterfaceMethodref": classBytes = dumpBadInterfaceMethodref(); break; case "BadMethodref" : classBytes = dumpBadMethodref(); break; case "IBad" : classBytes = dumpIBad(); break; + case "InvokeBasicref" : classBytes = dumpInvokeBasic(); break; default : throw new ClassNotFoundException(name); } return defineClass(name, classBytes, 0, classBytes.length); @@ -200,6 +232,9 @@ try (FileOutputStream fos = new FileOutputStream("BadMethodref.class")) { fos.write(dumpBadMethodref()); } + try (FileOutputStream fos = new FileOutputStream("InvokeBasicref.class")) { + fos.write(dumpInvokeBasic()); + } Class cls = (new CL()).loadClass("BadInterfaceMethodref"); String[] methods = {"runm", "runStaticM"}; @@ -249,5 +284,30 @@ 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"); + } } } diff -r c545db4fc9bd -r 9ca95539747d test/jdk/java/lang/invoke/VarHandles/VarHandleMethodReferenceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleMethodReferenceTest.java Mon Jun 25 14:39:44 2018 -0700 @@ -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; + } +}