# HG changeset patch # User psandoz # Date 1476481621 25200 # Node ID c069e5e285cbdb2617f819d904ce7cb448a99dae # Parent 89e056fd82cc8c6376385c23dbd92b77836565b5 8166974: invokedynamic implementation should not wrap Errors Reviewed-by: dholmes, jrose diff -r 89e056fd82cc -r c069e5e285cb hotspot/src/share/vm/classfile/systemDictionary.cpp --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Thu Oct 27 09:42:45 2016 -0500 +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Fri Oct 14 14:47:01 2016 -0700 @@ -2698,7 +2698,7 @@ } assert(arg == npts, ""); - // call java.lang.invoke.MethodHandleNatives::findMethodType(Class rt, Class[] pts) -> MethodType + // call java.lang.invoke.MethodHandleNatives::findMethodHandleType(Class rt, Class[] pts) -> MethodType JavaCallArguments args(Handle(THREAD, rt())); args.push_oop(pts()); JavaValue result(T_OBJECT); diff -r 89e056fd82cc -r c069e5e285cb hotspot/src/share/vm/interpreter/linkResolver.cpp --- a/hotspot/src/share/vm/interpreter/linkResolver.cpp Thu Oct 27 09:42:45 2016 -0500 +++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp Fri Oct 14 14:47:01 2016 -0700 @@ -1627,19 +1627,23 @@ static void wrap_invokedynamic_exception(TRAPS) { if (HAS_PENDING_EXCEPTION) { + // See the "Linking Exceptions" section for the invokedynamic instruction + // in JVMS 6.5. + if (PENDING_EXCEPTION->is_a(SystemDictionary::Error_klass())) { + // Pass through an Error, including BootstrapMethodError, any other form + // of linkage error, or say ThreadDeath/OutOfMemoryError + if (TraceMethodHandles) { + tty->print_cr("invokedynamic passes through an Error for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION)); + PENDING_EXCEPTION->print(); + } + return; + } + + // Otherwise wrap the exception in a BootstrapMethodError 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) diff -r 89e056fd82cc -r c069e5e285cb hotspot/test/runtime/ConstantPool/TestMethodHandleConstant.java --- a/hotspot/test/runtime/ConstantPool/TestMethodHandleConstant.java Thu Oct 27 09:42:45 2016 -0500 +++ b/hotspot/test/runtime/ConstantPool/TestMethodHandleConstant.java Fri Oct 14 14:47:01 2016 -0700 @@ -23,7 +23,7 @@ /* * @test - * @bug 8159470 + * @bug 8159470 8166974 * @summary Test that MethodHandle constants are checked * @modules java.base/jdk.internal.misc * @compile WithConfiguration.jcod @@ -33,12 +33,13 @@ public static void main(String[] args) { try { - // This interface has bad constant pool entry for MethodHandle -> Method - String URI_DEFAULT - = WithConfiguration.autoDetect().getLocation(); - throw new RuntimeException("FAILED, ICCE not thrown"); - } catch (BootstrapMethodError icce) { - System.out.println("PASSED, expecting ICCE" + icce.getMessage()); + // This interface has bad constant pool entry for MethodHandle -> Method + String URI_DEFAULT + = WithConfiguration.autoDetect().getLocation(); + throw new RuntimeException("FAILED, IncompatibleClassChangeError not thrown"); + } + catch (IncompatibleClassChangeError icce) { + System.out.println("PASSED, expecting IncompatibleClassChangeError" + icce.getMessage()); } } } diff -r 89e056fd82cc -r c069e5e285cb hotspot/test/runtime/invokedynamic/BootstrapMethodErrorTest.java --- a/hotspot/test/runtime/invokedynamic/BootstrapMethodErrorTest.java Thu Oct 27 09:42:45 2016 -0500 +++ b/hotspot/test/runtime/invokedynamic/BootstrapMethodErrorTest.java Fri Oct 14 14:47:01 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, 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,93 +23,296 @@ /* * @test - * @bug 8051045 - * @summary Test that exceptions from invokedynamic are wrapped in BootstrapMethodError + * @bug 8051045 8166974 + * @summary Test exceptions from invokedynamic and the bootstrap method * @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 { +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public class BootstrapMethodErrorTest { + + static abstract class IndyClassloader extends ClassLoader implements Opcodes { + + public IndyClassloader() { + super(BootstrapMethodErrorTest.class.getClassLoader()); + } + + @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); + } + + static final String BOOTSTRAP_METHOD_CLASS_NAME = "C"; + + static final String BOOTSTRAP_METHOD_NAME = "bsm"; + + static final String INDY_CALLER_CLASS_NAME = "Exec"; + + static final String BOOTSTRAP_METHOD_DESC = MethodType.methodType( + Object.class, MethodHandles.Lookup.class, String.class, MethodType.class). + toMethodDescriptorString(); + + private byte[] loadClassData(String name) throws Exception { + ClassWriter cw = new ClassWriter( + ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + if (name.equals(BOOTSTRAP_METHOD_CLASS_NAME)) { + defineIndyBootstrapMethodClass(cw); + return cw.toByteArray(); + } + else if (name.equals("Exec")) { + defineIndyCallingClass(cw); + return cw.toByteArray(); + } + return null; + } + + void defineIndyCallingClass(ClassWriter cw) { + cw.visit(52, ACC_SUPER | ACC_PUBLIC, INDY_CALLER_CLASS_NAME, null, "java/lang/Object", null); + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "invoke", "()V", null, null); + mv.visitCode(); + Handle h = new Handle(H_INVOKESTATIC, + BOOTSTRAP_METHOD_CLASS_NAME, BOOTSTRAP_METHOD_NAME, + BOOTSTRAP_METHOD_DESC, false); + mv.visitInvokeDynamicInsn(BOOTSTRAP_METHOD_CLASS_NAME, "()V", h); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + cw.visitEnd(); + } - @Override - public Class findClass(String name) throws ClassNotFoundException { - byte[] b; - try { - b = loadClassData(name); - } catch (Throwable th) { - throw new ClassNotFoundException("Loading error", th); + void defineIndyBootstrapMethodClass(ClassWriter cw) { + cw.visit(52, ACC_SUPER | ACC_PUBLIC, + BOOTSTRAP_METHOD_CLASS_NAME, null, "java/lang/Object", null); + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, + BOOTSTRAP_METHOD_NAME, BOOTSTRAP_METHOD_DESC, null, null); + mv.visitCode(); + defineIndyBootstrapMethodBody(mv); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + void defineIndyBootstrapMethodBody(MethodVisitor mv) { + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + } + + void invoke() throws Exception { + Class.forName(BOOTSTRAP_METHOD_CLASS_NAME, true, this); + Class exec = Class.forName(INDY_CALLER_CLASS_NAME, true, this); + exec.getMethod("invoke").invoke(null); + } + + void test() throws Exception { + Class.forName(BOOTSTRAP_METHOD_CLASS_NAME, true, this); + Class exec = Class.forName(INDY_CALLER_CLASS_NAME, true, this); + try { + exec.getMethod("invoke").invoke(null); + throw new RuntimeException("Expected InvocationTargetException but no exception at all was thrown"); + } catch (InvocationTargetException e) { + Throwable t = e.getCause(); + for (Class etc : expectedThrowableClasses()) { + if (!etc.isInstance(t)) { + throw new RuntimeException( + "Expected " + etc.getName() + " but got another exception: " + + t.getClass().getName(), + t); + } + t = t.getCause(); + } + } + } + + abstract List> expectedThrowableClasses(); } - return defineClass(name, b, 0, b.length); - } + + // Methods called by a bootstrap method - private byte[] loadClassData(String name) throws Exception { - ClassWriter cw = new ClassWriter(0); - MethodVisitor mv; + public static CallSite getCallSite() { + try { + MethodHandle mh = MethodHandles.lookup().findStatic( + BootstrapMethodErrorTest.class, + "target", + MethodType.methodType(Object.class, Object.class)); + return new ConstantCallSite(mh); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + public static Object target(Object o) { + return null; + } + + static class TestThrowable extends Throwable {} + public static void throwsTestThrowable() throws Throwable { + throw new TestThrowable(); + } + + static class TestError extends Error {} + public static void throwsTestError() { + throw new TestError(); + } - 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(); + static class TestRuntimeException extends RuntimeException {} + public static void throwsTestRuntimeException() { + throw new TestRuntimeException(); + } + + static class TestCheckedException extends Exception {} + public static void throwsTestCheckedException() throws TestCheckedException { + throw new TestCheckedException(); + } + + + // Test classes + + static class InaccessibleBootstrapMethod extends IndyClassloader { + + void defineIndyBootstrapMethodClass(ClassWriter cw) { + cw.visit(52, ACC_SUPER | ACC_PUBLIC, + BOOTSTRAP_METHOD_CLASS_NAME, null, "java/lang/Object", null); + // Bootstrap method is declared to be private + MethodVisitor mv = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, + BOOTSTRAP_METHOD_NAME, BOOTSTRAP_METHOD_DESC, null, null); + mv.visitCode(); + defineIndyBootstrapMethodBody(mv); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + @Override + List> expectedThrowableClasses() { + return List.of(IllegalAccessError.class); + } } - return null; - } + + static class BootstrapMethodDoesNotReturnCallSite extends IndyClassloader { + + void defineIndyBootstrapMethodBody(MethodVisitor mv) { + // return null from the bootstrap method, + // which cannot be cast to CallSite + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + } + + @Override + List> expectedThrowableClasses() { + return List.of(BootstrapMethodError.class, ClassCastException.class); + } + } + + static class BootstrapMethodCallSiteHasWrongTarget extends IndyClassloader { + + @Override + void defineIndyBootstrapMethodBody(MethodVisitor mv) { + // Invoke the method BootstrapMethodErrorTest.getCallSite to obtain + // a CallSite instance whose target is different from that of + // the indy call site + mv.visitMethodInsn(INVOKESTATIC, "BootstrapMethodErrorTest", + "getCallSite", "()Ljava/lang/invoke/CallSite;", false); + mv.visitInsn(ARETURN); + } + + @Override + List> expectedThrowableClasses() { + return List.of(BootstrapMethodError.class, WrongMethodTypeException.class); + } + } + + abstract static class BootstrapMethodThrows extends IndyClassloader { + final String methodName; - public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException { - new BootstrapMethodErrorTest().test(); - } + public BootstrapMethodThrows(Class t) { + this.methodName = "throws" + t.getSimpleName(); + } + + @Override + void defineIndyBootstrapMethodBody(MethodVisitor mv) { + // Invoke the method whose name is methodName which will throw + // an exception + mv.visitMethodInsn(INVOKESTATIC, "BootstrapMethodErrorTest", + methodName, "()V", false); + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + } + } - public void test() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException { - Class.forName("C", true, this); - Class exec = Class.forName("Exec", true, this); + static class BootstrapMethodThrowsThrowable extends BootstrapMethodThrows { + + public BootstrapMethodThrowsThrowable() { + super(TestThrowable.class); + } + + @Override + List> expectedThrowableClasses() { + return List.of(BootstrapMethodError.class, TestThrowable.class); + } + } + + static class BootstrapMethodThrowsError extends BootstrapMethodThrows { + + public BootstrapMethodThrowsError() { + super(TestError.class); + } - 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); - } + @Override + List> expectedThrowableClasses() { + return List.of(TestError.class); + } + } + + static class BootstrapMethodThrowsRuntimeException extends BootstrapMethodThrows { + + public BootstrapMethodThrowsRuntimeException() { + super(TestRuntimeException.class); + } + + @Override + List> expectedThrowableClasses() { + return List.of(BootstrapMethodError.class, TestRuntimeException.class); + } } - throw new RuntimeException("Expected BootstrapMethodError but no Error at all was thrown"); - } + + static class BootstrapMethodThrowsCheckedException extends BootstrapMethodThrows { + + public BootstrapMethodThrowsCheckedException() { + super(TestCheckedException.class); + } + + @Override + List> expectedThrowableClasses() { + return List.of(BootstrapMethodError.class, TestCheckedException.class); + } + } + + + public static void main(String[] args) throws Exception { + new InaccessibleBootstrapMethod().test(); + new BootstrapMethodDoesNotReturnCallSite().test(); + new BootstrapMethodCallSiteHasWrongTarget().test(); + new BootstrapMethodThrowsThrowable().test(); + new BootstrapMethodThrowsError().test(); + new BootstrapMethodThrowsRuntimeException().test(); + new BootstrapMethodThrowsCheckedException().test(); + } }