8166974: invokedynamic implementation should not wrap Errors
authorpsandoz
Fri, 14 Oct 2016 14:47:27 -0700
changeset 42103 8e1fe4345fd7
parent 42102 3e0a2861efe1
child 42104 2ad365d96d02
8166974: invokedynamic implementation should not wrap Errors Reviewed-by: smarks, jrose
jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java
jdk/src/java.base/share/classes/java/lang/invoke/package-info.java
jdk/test/java/lang/invoke/8022701/InvokeSeveralWays.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java	Wed Oct 26 15:08:29 2016 +0530
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java	Fri Oct 14 14:47:27 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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
@@ -272,7 +272,7 @@
         MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
     }
 
-    // this implements the upcall from the JVM, MethodHandleNatives.makeDynamicCallSite:
+    // this implements the upcall from the JVM, MethodHandleNatives.linkCallSite:
     static CallSite makeSite(MethodHandle bootstrapMethod,
                              // Callee information:
                              String name, MethodType type,
@@ -293,59 +293,72 @@
                 Object[] argv = (Object[]) info;
                 maybeReBoxElements(argv);
                 switch (argv.length) {
-                case 0:
-                    binding = bootstrapMethod.invoke(caller, name, type);
-                    break;
-                case 1:
-                    binding = bootstrapMethod.invoke(caller, name, type,
-                                                     argv[0]);
-                    break;
-                case 2:
-                    binding = bootstrapMethod.invoke(caller, name, type,
-                                                     argv[0], argv[1]);
-                    break;
-                case 3:
-                    binding = bootstrapMethod.invoke(caller, name, type,
-                                                     argv[0], argv[1], argv[2]);
-                    break;
-                case 4:
-                    binding = bootstrapMethod.invoke(caller, name, type,
-                                                     argv[0], argv[1], argv[2], argv[3]);
-                    break;
-                case 5:
-                    binding = bootstrapMethod.invoke(caller, name, type,
-                                                     argv[0], argv[1], argv[2], argv[3], argv[4]);
-                    break;
-                case 6:
-                    binding = bootstrapMethod.invoke(caller, name, type,
-                                                     argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
-                    break;
-                default:
-                    final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
-                    if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY)
-                        throw new BootstrapMethodError("too many bootstrap method arguments");
-                    MethodType bsmType = bootstrapMethod.type();
-                    MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
-                    MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
-                    MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
-                    binding = spreader.invokeExact(typedBSM, (Object)caller, (Object)name, (Object)type, argv);
+                    case 0:
+                        binding = bootstrapMethod.invoke(caller, name, type);
+                        break;
+                    case 1:
+                        binding = bootstrapMethod.invoke(caller, name, type,
+                                                         argv[0]);
+                        break;
+                    case 2:
+                        binding = bootstrapMethod.invoke(caller, name, type,
+                                                         argv[0], argv[1]);
+                        break;
+                    case 3:
+                        binding = bootstrapMethod.invoke(caller, name, type,
+                                                         argv[0], argv[1], argv[2]);
+                        break;
+                    case 4:
+                        binding = bootstrapMethod.invoke(caller, name, type,
+                                                         argv[0], argv[1], argv[2], argv[3]);
+                        break;
+                    case 5:
+                        binding = bootstrapMethod.invoke(caller, name, type,
+                                                         argv[0], argv[1], argv[2], argv[3], argv[4]);
+                        break;
+                    case 6:
+                        binding = bootstrapMethod.invoke(caller, name, type,
+                                                         argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+                        break;
+                    default:
+                        final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
+                        if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY)
+                            throw new BootstrapMethodError("too many bootstrap method arguments");
+                        MethodType bsmType = bootstrapMethod.type();
+                        MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
+                        MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
+                        MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
+                        binding = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, (Object) type, argv);
                 }
             }
-            //System.out.println("BSM for "+name+type+" => "+binding);
             if (binding instanceof CallSite) {
                 site = (CallSite) binding;
-            }  else {
+            } else {
+                // See the "Linking Exceptions" section for the invokedynamic
+                // instruction in JVMS 6.5.
+                // Throws a runtime exception defining the cause that is then
+                // in the "catch (Throwable ex)" a few lines below wrapped in
+                // BootstrapMethodError
                 throw new ClassCastException("bootstrap method failed to produce a CallSite");
             }
-            if (!site.getTarget().type().equals(type))
+            if (!site.getTarget().type().equals(type)) {
+                // See the "Linking Exceptions" section for the invokedynamic
+                // instruction in JVMS 6.5.
+                // Throws a runtime exception defining the cause that is then
+                // in the "catch (Throwable ex)" a few lines below wrapped in
+                // BootstrapMethodError
                 throw wrongTargetType(site.getTarget(), type);
+            }
+        } catch (Error e) {
+            // Pass through an Error, including BootstrapMethodError, any other
+            // form of linkage error, such as IllegalAccessError if the bootstrap
+            // method is inaccessible, or say ThreadDeath/OutOfMemoryError
+            // See the "Linking Exceptions" section for the invokedynamic
+            // instruction in JVMS 6.5.
+            throw e;
         } catch (Throwable ex) {
-            BootstrapMethodError bex;
-            if (ex instanceof BootstrapMethodError)
-                bex = (BootstrapMethodError) ex;
-            else
-                bex = new BootstrapMethodError("call site initialization exception", ex);
-            throw bex;
+            // Wrap anything else in BootstrapMethodError
+            throw new BootstrapMethodError("call site initialization exception", ex);
         }
         return site;
     }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/package-info.java	Wed Oct 26 15:08:29 2016 +0530
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/package-info.java	Fri Oct 14 14:47:27 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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
@@ -84,20 +84,21 @@
  * </ul>
  * Invocation is as if by
  * {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}.
- * The returned result must be a {@link java.lang.invoke.CallSite CallSite} (or a subclass).
+ * The returned result must be a {@link java.lang.invoke.CallSite CallSite}
+ * (or a subclass), otherwise a
+ * {@link java.lang.BootstrapMethodError BootstrapMethodError} is thrown.
  * The type of the call site's target must be exactly equal to the type
  * derived from the dynamic call site's type descriptor and passed to
- * the bootstrap method.
- * The call site then becomes permanently linked to the dynamic call site.
+ * the bootstrap method, otherwise a {@code BootstrapMethodError} is thrown.
+ * On success the call site then becomes permanently linked to the dynamic call
+ * site.
  * <p>
- * As documented in the JVM specification, all failures arising from
- * the linkage of a dynamic call site are reported
- * by a {@link java.lang.BootstrapMethodError BootstrapMethodError},
- * which is thrown as the abnormal termination of the dynamic call
- * site execution.
- * If this happens, the same error will the thrown for all subsequent
- * attempts to execute the dynamic call site.
- *
+ * If an exception, {@code E} say, occurs when linking the call site then the
+ * linkage fails and terminates abnormally. {@code E} is rethrown if the type of
+ * {@code E} is {@code Error} or a subclass, otherwise a
+ * {@code BootstrapMethodError} that wraps {@code E} is thrown.
+ * If this happens, the same {@code Error} or subclass will the thrown for all
+ * subsequent attempts to execute the dynamic call site.
  * <h2>timing of linkage</h2>
  * A dynamic call site is linked just before its first execution.
  * The bootstrap method call implementing the linkage occurs within
--- a/jdk/test/java/lang/invoke/8022701/InvokeSeveralWays.java	Wed Oct 26 15:08:29 2016 +0530
+++ b/jdk/test/java/lang/invoke/8022701/InvokeSeveralWays.java	Fri Oct 14 14:47:27 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -37,18 +37,11 @@
             failures++;
         } catch (InvocationTargetException e) {
             Throwable c = e.getCause();
-            if (BootstrapMethodError.class.isInstance(c)) {
-                c = c.getCause();
-                if (expected.isInstance(c))
-                    System.out.println("EXPECTED: " + expected.getName() + ", "+ c);
-                else {
-                    failures++;
-                    System.out.println("FAIL: Unexpected wrapped exception " + c);
-                    e.printStackTrace(System.out);
-                }
-            } else {
+            if (expected.isInstance(c))
+                System.out.println("EXPECTED: " + expected.getName() + ", "+ c);
+            else {
                 failures++;
-                System.out.println("FAIL: Exception from MethodHandle invocation not wrapped in BootstrapMethodError " + c);
+                System.out.println("FAIL: Unexpected wrapped exception " + c);
                 e.printStackTrace(System.out);
             }
         } catch (Throwable e) {
@@ -80,19 +73,14 @@
             Invoker.invoke();
             System.out.println("FAIL: No exception throw, probably failed to load modified bytecodes for MethodSupplier");
             failures++;
-        } catch (BootstrapMethodError e) {
-            Throwable c = e.getCause();
-            if (expected.isInstance(c))
-                System.out.println("EXPECTED: " + expected.getName() + ", "+ c);
+        } catch (Throwable e) {
+            if (expected.isInstance(e))
+                System.out.println("EXPECTED: " + expected.getName() + ", "+ e);
             else {
                 failures++;
-                System.out.println("FAIL: Unexpected exception has been caught " + c);
+                System.out.println("FAIL: Unexpected exception has been caught " + e);
                 e.printStackTrace(System.out);
             }
-        } catch (Throwable e) {
-            failures++;
-            System.out.println("FAIL: Exception from MethodHandle invocation not wrapped in BootstrapMethodError " + e);
-            e.printStackTrace(System.out);
         }
         System.out.println();
         try {