# HG changeset patch # User aeriksso # Date 1431003946 -7200 # Node ID 81bed6c76a89512cacd22b417bd9e70de50029d5 # Parent 91a1be057e0a8eb6cac78bc38c32e2bf99496259 8051045: HotSpot fails to wrap Exceptions from invokedynamic in a BootstrapMethodError Reviewed-by: coleenp, dsimms diff -r 91a1be057e0a -r 81bed6c76a89 src/share/vm/interpreter/linkResolver.cpp --- a/src/share/vm/interpreter/linkResolver.cpp Thu Jun 04 23:11:44 2015 -0700 +++ b/src/share/vm/interpreter/linkResolver.cpp Thu May 07 15:05:46 2015 +0200 @@ -1592,6 +1592,26 @@ result.set_handle(resolved_method, resolved_appendix, resolved_method_type, CHECK); } +static void wrap_invokedynamic_exception(TRAPS) { + if (HAS_PENDING_EXCEPTION) { + if (TraceMethodHandles) { + tty->print_cr("invokedynamic throws BSME for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION)); + PENDING_EXCEPTION->print(); + } + if (PENDING_EXCEPTION->is_a(SystemDictionary::BootstrapMethodError_klass())) { + // throw these guys, since they are already wrapped + return; + } + if (!PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { + // intercept only LinkageErrors which might have failed to wrap + return; + } + // See the "Linking Exceptions" section for the invokedynamic instruction in the JVMS. + Handle nested_exception(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception) + } +} void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) { assert(EnableInvokeDynamic, ""); @@ -1607,7 +1627,8 @@ ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(index); if (cpce->is_f1_null()) { int pool_index = cpce->constant_pool_index(); - oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, CHECK); + oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, THREAD); + wrap_invokedynamic_exception(CHECK); assert(bsm_info != NULL, ""); // FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_InvokeDynamic. bootstrap_specifier = Handle(THREAD, bsm_info); @@ -1616,7 +1637,8 @@ methodHandle method( THREAD, cpce->f1_as_method()); Handle appendix( THREAD, cpce->appendix_if_resolved(pool)); Handle method_type(THREAD, cpce->method_type_if_resolved(pool)); - result.set_handle(method, appendix, method_type, CHECK); + result.set_handle(method, appendix, method_type, THREAD); + wrap_invokedynamic_exception(CHECK); return; } @@ -1647,25 +1669,9 @@ &resolved_appendix, &resolved_method_type, THREAD); - if (HAS_PENDING_EXCEPTION) { - if (TraceMethodHandles) { - tty->print_cr("invokedynamic throws BSME for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION)); - PENDING_EXCEPTION->print(); - } - if (PENDING_EXCEPTION->is_a(SystemDictionary::BootstrapMethodError_klass())) { - // throw these guys, since they are already wrapped - return; - } - if (!PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { - // intercept only LinkageErrors which might have failed to wrap - return; - } - // See the "Linking Exceptions" section for the invokedynamic instruction in the JVMS. - Handle nested_exception(THREAD, PENDING_EXCEPTION); - CLEAR_PENDING_EXCEPTION; - THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception) - } - result.set_handle(resolved_method, resolved_appendix, resolved_method_type, CHECK); + wrap_invokedynamic_exception(CHECK); + result.set_handle(resolved_method, resolved_appendix, resolved_method_type, THREAD); + wrap_invokedynamic_exception(CHECK); } //------------------------------------------------------------------------------------------------------------------------ diff -r 91a1be057e0a -r 81bed6c76a89 test/runtime/invokedynamic/BootstrapMethodErrorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/invokedynamic/BootstrapMethodErrorTest.java Thu May 07 15:05:46 2015 +0200 @@ -0,0 +1,115 @@ +/* + * 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 8051045 + * @summary Test that exceptions from invokedynamic are wrapped in BootstrapMethodError + * @modules java.base/jdk.internal.org.objectweb.asm + * @run main BootstrapMethodErrorTest + */ + +import java.lang.reflect.Method; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import static java.lang.invoke.MethodHandles.*; +import static java.lang.invoke.MethodType.*; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Handle; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; + +public class BootstrapMethodErrorTest extends ClassLoader implements Opcodes { + + @Override + public Class findClass(String name) throws ClassNotFoundException { + byte[] b; + try { + b = loadClassData(name); + } catch (Throwable th) { + throw new ClassNotFoundException("Loading error", th); + } + return defineClass(name, b, 0, b.length); + } + + private byte[] loadClassData(String name) throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + if (name.equals("C")) { + cw.visit(52, ACC_SUPER | ACC_PUBLIC, "C", null, "java/lang/Object", null); + { + mv = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, "m", "()V", null, null); + mv.visitCode(); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 1); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } else if (name.equals("Exec")) { + cw.visit(52, ACC_SUPER | ACC_PUBLIC, "Exec", null, "java/lang/Object", null); + { + mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "invokeRef", "()V", null, null); + mv.visitCode(); + Handle h = new Handle(H_INVOKESTATIC, "C", "m", "()V"); + mv.visitInvokeDynamicInsn("C", "()V", h); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } + return null; + } + + public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException { + new BootstrapMethodErrorTest().test(); + } + + public void test() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException { + Class.forName("C", true, this); + Class exec = Class.forName("Exec", true, this); + + try { + exec.getMethod("invokeRef").invoke(null); + } catch (Throwable e) { + Throwable c = e.getCause(); + if (c == null) { + throw new RuntimeException( + "Expected BootstrapMethodError wrapped in an InvocationTargetException but it wasn't wrapped", e); + } else if (c instanceof BootstrapMethodError) { + // Only way to pass test, all else should throw + return; + } else { + throw new RuntimeException( + "Expected BootstrapMethodError but got another Error: " + + c.getClass().getName(), + c); + } + } + throw new RuntimeException("Expected BootstrapMethodError but no Error at all was thrown"); + } +}