--- old/src/share/vm/ci/ciEnv.cpp 2015-12-21 09:10:51.625725336 -0800 +++ new/src/share/vm/ci/ciEnv.cpp 2015-12-21 09:10:51.496718862 -0800 @@ -704,13 +704,14 @@ InstanceKlass* holder, Symbol* name, Symbol* sig, - Bytecodes::Code bc) { + Bytecodes::Code bc, + constantTag tag) { EXCEPTION_CONTEXT; KlassHandle h_accessor(THREAD, accessor); KlassHandle h_holder(THREAD, holder); LinkResolver::check_klass_accessability(h_accessor, h_holder, KILL_COMPILE_ON_FATAL_(NULL)); methodHandle dest_method; - LinkInfo link_info(h_holder, name, sig, h_accessor, /*check_access*/true); + LinkInfo link_info(h_holder, name, sig, h_accessor, LinkInfo::needs_access_check, tag); switch (bc) { case Bytecodes::_invokestatic: dest_method = @@ -796,7 +797,9 @@ if (holder_is_accessible) { // Our declared holder is loaded. InstanceKlass* lookup = declared_holder->get_instanceKlass(); - Method* m = lookup_method(accessor->get_instanceKlass(), lookup, name_sym, sig_sym, bc); + constantTag tag = cpool->tag_ref_at(index); + assert(accessor->get_instanceKlass() == cpool->pool_holder(), "not the pool holder?"); + Method* m = lookup_method(accessor->get_instanceKlass(), lookup, name_sym, sig_sym, bc, tag); if (m != NULL && (bc == Bytecodes::_invokestatic ? m->method_holder()->is_not_initialized() --- old/src/share/vm/ci/ciEnv.hpp 2015-12-21 09:10:52.177753038 -0800 +++ new/src/share/vm/ci/ciEnv.hpp 2015-12-21 09:10:52.052746765 -0800 @@ -158,7 +158,8 @@ InstanceKlass* holder, Symbol* name, Symbol* sig, - Bytecodes::Code bc); + Bytecodes::Code bc, + constantTag tag); // Get a ciObject from the object factory. Ensures uniqueness // of ciObjects. --- old/src/share/vm/ci/ciMethod.cpp 2015-12-21 09:10:52.740781293 -0800 +++ new/src/share/vm/ci/ciMethod.cpp 2015-12-21 09:10:52.606774568 -0800 @@ -787,7 +787,8 @@ Symbol* h_name = name()->get_symbol(); Symbol* h_signature = signature()->get_symbol(); - LinkInfo link_info(h_resolved, h_name, h_signature, caller_klass, check_access); + LinkInfo link_info(h_resolved, h_name, h_signature, caller_klass, + check_access ? LinkInfo::needs_access_check : LinkInfo::skip_access_check); methodHandle m; // Only do exact lookup if receiver klass has been linked. Otherwise, // the vtable has not been setup, and the LinkResolver will fail. --- old/src/share/vm/interpreter/linkResolver.cpp 2015-12-21 09:10:53.300809395 -0800 +++ new/src/share/vm/interpreter/linkResolver.cpp 2015-12-21 09:10:53.173803023 -0800 @@ -246,6 +246,7 @@ // Get name, signature, and static klass _name = pool->name_ref_at(index); _signature = pool->signature_ref_at(index); + _tag = pool->tag_ref_at(index); _current_klass = KlassHandle(THREAD, pool->pool_holder()); // Coming from the constant pool always checks access @@ -682,6 +683,15 @@ THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); } + // check tag at call is method + if (!link_info.tag().is_invalid() && !link_info.tag().is_method()) { + ResourceMark rm(THREAD); + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Resolving to non regular method %s", link_info.method_string()); + THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } + + // 2. lookup method in resolved klass and its super klasses methodHandle resolved_method = lookup_method_in_klasses(link_info, true, false, CHECK_NULL); @@ -741,6 +751,14 @@ THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); } + // check tag at call is an interface method + if (!link_info.tag().is_invalid() && !link_info.tag().is_interface_method()) { + ResourceMark rm(THREAD); + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Resolving to non interface method %s", link_info.method_string()); + THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } + // lookup method in this interface or its super, java.lang.Object // JDK8: also look for static methods methodHandle resolved_method = lookup_method_in_klasses(link_info, false, true, CHECK_NULL); @@ -918,7 +936,8 @@ resolved_klass->initialize(CHECK); // Use updated LinkInfo (to reresolve with resolved_klass as method_holder?) LinkInfo new_info(resolved_klass, link_info.name(), link_info.signature(), - link_info.current_klass(), link_info.check_access()); + link_info.current_klass(), + link_info.check_access() ? LinkInfo::needs_access_check : LinkInfo::skip_access_check); resolved_method = linktime_resolve_static_method(new_info, CHECK); } --- old/src/share/vm/interpreter/linkResolver.hpp 2015-12-21 09:10:53.858837399 -0800 +++ new/src/share/vm/interpreter/linkResolver.hpp 2015-12-21 09:10:53.734831175 -0800 @@ -135,20 +135,35 @@ KlassHandle _resolved_klass; // class that the constant pool entry points to KlassHandle _current_klass; // class that owns the constant pool bool _check_access; + constantTag _tag; public: + enum AccessCheck { + needs_access_check, + skip_access_check + }; + LinkInfo(const constantPoolHandle& pool, int index, TRAPS); + // Condensed information from other call sites within the vm. - LinkInfo(KlassHandle resolved_klass, Symbol* name, Symbol* signature, - KlassHandle current_klass, bool check_access = true) : + LinkInfo(KlassHandle resolved_klass, Symbol* name, Symbol* signature, KlassHandle current_klass, + AccessCheck check_access = needs_access_check, + constantTag tag = JVM_CONSTANT_Invalid) : _resolved_klass(resolved_klass), _name(name), _signature(signature), _current_klass(current_klass), - _check_access(check_access) {} + _check_access(check_access == needs_access_check && current_klass.not_null()), _tag(tag) {} + + // Case where we just find the method and don't check access against the current class + LinkInfo(KlassHandle resolved_klass, Symbol*name, Symbol* signature) : + _resolved_klass(resolved_klass), + _name(name), _signature(signature), _current_klass(NULL), + _check_access(false), _tag(JVM_CONSTANT_Invalid) {} // accessors Symbol* name() const { return _name; } Symbol* signature() const { return _signature; } KlassHandle resolved_klass() const { return _resolved_klass; } KlassHandle current_klass() const { return _current_klass; } + constantTag tag() const { return _tag; } bool check_access() const { return _check_access; } char* method_string() const; --- old/src/share/vm/jvmci/jvmciCompilerToVM.cpp 2015-12-21 09:10:54.397864449 -0800 +++ new/src/share/vm/jvmci/jvmciCompilerToVM.cpp 2015-12-21 09:10:54.275858327 -0800 @@ -574,7 +574,7 @@ if (holder_klass->is_interface()) { // do link-time resolution to check all access rules. - LinkInfo link_info(holder_klass, method_name, method_signature, caller_klass, true); + LinkInfo link_info(holder_klass, method_name, method_signature, caller_klass); methodHandle resolved_method = LinkResolver::linktime_resolve_interface_method_or_null(link_info); if (resolved_method.is_null() || resolved_method->is_private()) { return NULL; @@ -586,7 +586,7 @@ return JNIHandles::make_local(THREAD, result); } else { // do link-time resolution to check all access rules. - LinkInfo link_info(holder_klass, method_name, method_signature, caller_klass, true); + LinkInfo link_info(holder_klass, method_name, method_signature, caller_klass); methodHandle resolved_method = LinkResolver::linktime_resolve_virtual_method_or_null(link_info); if (resolved_method.is_null()) { return NULL; --- old/src/share/vm/jvmci/jvmciEnv.cpp 2015-12-21 09:10:54.955892452 -0800 +++ new/src/share/vm/jvmci/jvmciEnv.cpp 2015-12-21 09:10:54.831886229 -0800 @@ -282,11 +282,12 @@ instanceKlassHandle h_holder, Symbol* name, Symbol* sig, - Bytecodes::Code bc) { + Bytecodes::Code bc, + constantTag tag) { JVMCI_EXCEPTION_CONTEXT; LinkResolver::check_klass_accessability(h_accessor, h_holder, KILL_COMPILE_ON_FATAL_(NULL)); methodHandle dest_method; - LinkInfo link_info(h_holder, name, sig, h_accessor, /*check_access*/true); + LinkInfo link_info(h_holder, name, sig, h_accessor, LinkInfo::needs_access_check, tag); switch (bc) { case Bytecodes::_invokestatic: dest_method = @@ -359,7 +360,8 @@ if (holder_is_accessible) { // Our declared holder is loaded. instanceKlassHandle lookup = get_instance_klass_for_declared_method_holder(holder); - methodHandle m = lookup_method(accessor, lookup, name_sym, sig_sym, bc); + constantTag tag = cpool->tag_ref_at(index); + methodHandle m = lookup_method(accessor, lookup, name_sym, sig_sym, bc, tag); if (!m.is_null() && (bc == Bytecodes::_invokestatic ? InstanceKlass::cast(m->method_holder())->is_not_initialized() --- old/src/share/vm/jvmci/jvmciEnv.hpp 2015-12-21 09:10:55.516920606 -0800 +++ new/src/share/vm/jvmci/jvmciEnv.hpp 2015-12-21 09:10:55.393914433 -0800 @@ -125,7 +125,8 @@ instanceKlassHandle holder, Symbol* name, Symbol* sig, - Bytecodes::Code bc); + Bytecodes::Code bc, + constantTag tag); private: --- old/src/share/vm/oops/constantPool.cpp 2015-12-21 09:10:56.082949010 -0800 +++ new/src/share/vm/oops/constantPool.cpp 2015-12-21 09:10:55.953942538 -0800 @@ -417,6 +417,19 @@ return extract_high_short_from_int(ref_index); } +constantTag ConstantPool::impl_tag_ref_at(int which, bool uncached) { + int pool_index = which; + if (!uncached && cache() != NULL) { + if (ConstantPool::is_invokedynamic_index(which)) { + // Invokedynamic index is index into resolved_references + pool_index = invokedynamic_cp_cache_entry_at(which)->constant_pool_index(); + } else { + // change byte-ordering and go via cache + pool_index = remap_instruction_operand_from_cache(which); + } + } + return tag_at(pool_index); +} int ConstantPool::impl_klass_ref_index_at(int which, bool uncached) { guarantee(!ConstantPool::is_invokedynamic_index(which), @@ -672,6 +685,7 @@ int callee_index = this_cp->method_handle_klass_index_at(index); Symbol* name = this_cp->method_handle_name_ref_at(index); Symbol* signature = this_cp->method_handle_signature_ref_at(index); + constantTag m_tag = this_cp->tag_at(this_cp->method_handle_index_at(index)); if (PrintMiscellaneous) tty->print_cr("resolve JVM_CONSTANT_MethodHandle:%d [%d/%d/%d] %s.%s", ref_kind, index, this_cp->method_handle_index_at(index), @@ -680,6 +694,17 @@ { Klass* k = klass_at_impl(this_cp, callee_index, true, CHECK_NULL); callee = KlassHandle(THREAD, k); } + // Check tag consistency, relax for interface static default method. + if ((callee->is_interface() && !m_tag.is_interface_method() && + (ref_kind != JVM_REF_invokeStatic)) || + (!callee->is_interface() && m_tag.is_interface_method())) { + ResourceMark rm(THREAD); + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Inconsistent constant data for %s.%s%s at index %d", + callee->name()->as_C_string(), name->as_C_string(), signature->as_C_string(), index); + THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } + KlassHandle klass(THREAD, this_cp->pool_holder()); Handle value = SystemDictionary::link_method_handle_constant(klass, ref_kind, callee, name, signature, --- old/src/share/vm/oops/constantPool.hpp 2015-12-21 09:10:56.657977867 -0800 +++ new/src/share/vm/oops/constantPool.hpp 2015-12-21 09:10:56.532971595 -0800 @@ -664,6 +664,8 @@ int remap_instruction_operand_from_cache(int operand); // operand must be biased by CPCACHE_INDEX_TAG + constantTag tag_ref_at(int cp_cache_index) { return impl_tag_ref_at(cp_cache_index, false); } + // Lookup for entries consisting of (name_index, signature_index) int name_ref_index_at(int which_nt); // == low-order jshort of name_and_type_at(which_nt) int signature_ref_index_at(int which_nt); // == high-order jshort of name_and_type_at(which_nt) @@ -784,6 +786,7 @@ Symbol* impl_signature_ref_at(int which, bool uncached); int impl_klass_ref_index_at(int which, bool uncached); int impl_name_and_type_ref_index_at(int which, bool uncached); + constantTag impl_tag_ref_at(int which, bool uncached); // Used while constructing constant pool (only by ClassFileParser) jint klass_index_at(int which) { --- old/src/share/vm/prims/methodHandles.cpp 2015-12-21 09:10:57.225006320 -0800 +++ new/src/share/vm/prims/methodHandles.cpp 2015-12-21 09:10:57.095999848 -0800 @@ -680,7 +680,7 @@ case IS_METHOD: { CallInfo result; - LinkInfo link_info(defc, name, type, caller, caller.not_null()); + LinkInfo link_info(defc, name, type, caller); { assert(!HAS_PENDING_EXCEPTION, ""); if (ref_kind == JVM_REF_invokeStatic) { @@ -717,7 +717,7 @@ case IS_CONSTRUCTOR: { CallInfo result; - LinkInfo link_info(defc, name, type, caller, caller.not_null()); + LinkInfo link_info(defc, name, type, caller); { assert(!HAS_PENDING_EXCEPTION, ""); if (name == vmSymbols::object_initializer_name()) { @@ -738,7 +738,7 @@ fieldDescriptor result; // find_field initializes fd if found { assert(!HAS_PENDING_EXCEPTION, ""); - LinkInfo link_info(defc, name, type, caller, /*check_access*/false); + LinkInfo link_info(defc, name, type, caller, LinkInfo::skip_access_check); LinkResolver::resolve_field(result, link_info, Bytecodes::_nop, false, THREAD); if (HAS_PENDING_EXCEPTION) { return empty; --- old/src/share/vm/runtime/javaCalls.cpp 2015-12-21 09:10:57.807035529 -0800 +++ new/src/share/vm/runtime/javaCalls.cpp 2015-12-21 09:10:57.677029005 -0800 @@ -183,7 +183,7 @@ CallInfo callinfo; Handle receiver = args->receiver(); KlassHandle recvrKlass(THREAD, receiver.is_null() ? (Klass*)NULL : receiver->klass()); - LinkInfo link_info(spec_klass, name, signature, KlassHandle(), /*check_access*/false); + LinkInfo link_info(spec_klass, name, signature); LinkResolver::resolve_virtual_call( callinfo, receiver, recvrKlass, link_info, true, CHECK); methodHandle method = callinfo.selected_method(); @@ -220,7 +220,7 @@ void JavaCalls::call_special(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS) { CallInfo callinfo; - LinkInfo link_info(klass, name, signature, KlassHandle(), /*check_access*/false); + LinkInfo link_info(klass, name, signature); LinkResolver::resolve_special_call(callinfo, link_info, CHECK); methodHandle method = callinfo.selected_method(); assert(method.not_null(), "should have thrown exception"); @@ -255,7 +255,7 @@ void JavaCalls::call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS) { CallInfo callinfo; - LinkInfo link_info(klass, name, signature, KlassHandle(), /*check_access*/false); + LinkInfo link_info(klass, name, signature); LinkResolver::resolve_static_call(callinfo, link_info, true, CHECK); methodHandle method = callinfo.selected_method(); assert(method.not_null(), "should have thrown exception"); --- old/src/share/vm/runtime/reflection.cpp 2015-12-21 09:10:58.369063734 -0800 +++ new/src/share/vm/runtime/reflection.cpp 2015-12-21 09:10:58.243057409 -0800 @@ -836,7 +836,7 @@ Symbol* signature = method->signature(); Symbol* name = method->name(); LinkResolver::resolve_interface_call(info, receiver, recv_klass, - LinkInfo(klass, name, signature, KlassHandle(), false), + LinkInfo(klass, name, signature), true, CHECK_(methodHandle())); return info.selected_method(); --- /dev/null 2015-12-16 09:10:57.643494727 -0800 +++ new/test/runtime/8145148/BadMethodHandles.java 2015-12-21 09:10:58.765083606 -0800 @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015, 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 8087223 + * @summary Adding constantTag to keep method call consistent with it. + * @compile -XDignore.symbol.file BadMethodHandles.java + * @run main/othervm BadMethodHandles + */ + +import jdk.internal.org.objectweb.asm.*; +import java.io.FileOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +public class BadMethodHandles { + + static byte[] dumpBadInterfaceMethodref() { + 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"); + Handle handle2 = + new Handle(Opcodes.H_INVOKEINTERFACE, "BadInterfaceMethodref", "staticM", "()V"); + + { + 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, "m", "()V", null, null); + mv.visitCode(); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("hello from m"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false/*intf*/); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "staticM", "()V", null, null); + mv.visitCode(); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("hello from staticM"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false/*intf*/); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "runm", "()V", null, null); + mv.visitCode(); + // REF_invokeStatic + mv.visitLdcInsn(handle1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invoke", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "runStaticM", "()V", null, null); + mv.visitCode(); + // REF_invokeStatic + mv.visitLdcInsn(handle2); + 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 byte[] dumpIBad() { + ClassWriter cw = new ClassWriter(0); + cw.visit(52, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "IBad", null, "java/lang/Object", null); + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "m", "()V", null, null); + mv.visitCode(); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("hello from m"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false/*intf*/); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "staticM", "()V", null, null); + mv.visitCode(); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("hello from staticM"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false/*intf*/); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } + + static byte[] dumpBadMethodref() { + 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"); + Handle handle2 = + new Handle(Opcodes.H_INVOKEINTERFACE, "BadMethodref", "staticM", "()V"); + + { + 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, "runm", "()V", null, null); + mv.visitCode(); + // REF_invokeStatic + mv.visitLdcInsn(handle1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invoke", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "runStaticM", "()V", null, null); + mv.visitCode(); + // REF_invokeStatic + mv.visitLdcInsn(handle2); + 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 { + byte[] classBytes = null; + switch (name) { + case "BadInterfaceMethodref": classBytes = dumpBadInterfaceMethodref(); break; + case "BadMethodref" : classBytes = dumpBadMethodref(); break; + case "IBad" : classBytes = dumpIBad(); break; + default : throw new ClassNotFoundException(name); + } + return defineClass(name, classBytes, 0, classBytes.length); + } + } + + public static void main(String[] args) throws Throwable { + try (FileOutputStream fos = new FileOutputStream("BadInterfaceMethodref.class")) { + fos.write(dumpBadInterfaceMethodref()); + } + try (FileOutputStream fos = new FileOutputStream("IBad.class")) { + fos.write(dumpIBad()); + } + try (FileOutputStream fos = new FileOutputStream("BadMethodref.class")) { + fos.write(dumpBadMethodref()); + } + + Class cls = (new CL()).loadClass("BadInterfaceMethodref"); + String[] methods = {"runm", "runStaticM"}; + System.out.println("Test BadInterfaceMethodref:"); + int success = 0; + for (String name : methods) { + try { + System.out.printf("invoke %s: \n", name); + cls.getMethod(name).invoke(cls.newInstance()); + System.out.println("FAILED (no exception)"); // ICCE should be thrown + } catch (Throwable e) { + if (e instanceof InvocationTargetException && e.getCause() != null && + e.getCause() instanceof IncompatibleClassChangeError) { + System.out.println("PASSED"); + success++; + continue; + } else { + System.out.println("FAILED with exception"); + throw e; + } + } + } + if (success != methods.length) { + throw new Exception("BadInterfaceMethodRef Failed to catch IncompatibleClassChangeError"); + } + System.out.println("Test BadMethodref:"); + cls = (new CL()).loadClass("BadMethodref"); + success = 0; + for (String name : methods) { + try { + System.out.printf("invoke %s: \n", name); + cls.getMethod(name).invoke(cls.newInstance()); + System.out.println("FAILED (no exception)"); // ICCE should be thrown + } catch (Throwable e) { + if (e instanceof InvocationTargetException && e.getCause() != null && + e.getCause() instanceof IncompatibleClassChangeError) { + System.out.println("PASSED"); + success++; + continue; + } else { + System.out.println("FAILED with exception"); + throw e; + } + } + } + if (success != methods.length) { + throw new Exception("BadMethodRef Failed to catch IncompatibleClassChangeError"); + } + + } +} --- /dev/null 2015-12-16 09:10:57.643494727 -0800 +++ new/test/runtime/8145148/IntfMethod.java 2015-12-21 09:10:59.200105437 -0800 @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015, 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 8087223 + * @summary Adding constantTag to keep method call consistent with it. + * @compile -XDignore.symbol.file IntfMethod.java + * @run main/othervm IntfMethod + * @run main/othervm -Xint IntfMethod + * @run main/othervm -Xcomp IntfMethod + */ + + +import jdk.internal.org.objectweb.asm.*; +import java.io.FileOutputStream; +import java.lang.reflect.InvocationTargetException; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +public class IntfMethod { + static byte[] dumpC() { + ClassWriter cw = new ClassWriter(0); + cw.visit(52, ACC_PUBLIC | ACC_SUPER, "C", null, "java/lang/Object", new String[]{"I"}); + { + 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, "testSpecialIntf", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "I", "f1", "()V", /*itf=*/false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testStaticIntf", "()V", null, null); + mv.visitCode(); + mv.visitMethodInsn(INVOKESTATIC, "I", "f2", "()V", /*itf=*/false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testSpecialClass", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "C", "f1", "()V", /*itf=*/true); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "f2", "()V", null, null); + mv.visitCode(); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 1); + mv.visitEnd(); + } + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testStaticClass", "()V", null, null); + mv.visitCode(); + mv.visitMethodInsn(INVOKESTATIC, "C", "f2", "()V", /*itf=*/true); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } + + static byte[] dumpI() { + ClassWriter cw = new ClassWriter(0); + cw.visit(52, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "I", null, "java/lang/Object", null); + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "f1", "()V", null, null); + mv.visitCode(); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 1); + mv.visitEnd(); + } + { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "f2", "()V", null, null); + mv.visitCode(); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 1); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } + + static class CL extends ClassLoader { + @Override + protected Class findClass(String name) throws ClassNotFoundException { + byte[] classFile; + switch (name) { + case "I": classFile = dumpI(); break; + case "C": classFile = dumpC(); break; + default: + throw new ClassNotFoundException(name); + } + return defineClass(name, classFile, 0, classFile.length); + } + } + + public static void main(String[] args) throws Throwable { + Class cls = (new CL()).loadClass("C"); + try (FileOutputStream fos = new FileOutputStream("I.class")) { fos.write(dumpI()); } + try (FileOutputStream fos = new FileOutputStream("C.class")) { fos.write(dumpC()); } + + int success = 0; + for (String name : new String[] { "testSpecialIntf", "testStaticIntf", "testSpecialClass", "testStaticClass"}) { + System.out.printf("%s: ", name); + try { + cls.getMethod(name).invoke(cls.newInstance()); + System.out.println("FAILED"); + } catch (Throwable e) { + if (e instanceof InvocationTargetException && + e.getCause() != null && e.getCause() instanceof IncompatibleClassChangeError) { + System.out.println("PASSED"); + success++; + continue; + } + } + } + if (success != 4) throw new Exception("Failed to catch ICCE"); + } +}