diff --git a/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java b/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java index 288cd2d2a5b..91960b2d73f 100644 --- a/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java +++ b/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java @@ -861,4 +861,18 @@ import jdk.internal.org.objectweb.asm.Type; * All subclasses must provide such a value describing their type signature. */ static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY; + + private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[5]; + private static SpeciesData checkCache(int size, String types) { + int idx = size - 1; + SpeciesData data = SPECIES_DATA_CACHE[idx]; + if (data != null) return data; + SPECIES_DATA_CACHE[idx] = data = getSpeciesData(types); + return data; + } + static SpeciesData speciesData_L() { return checkCache(1, "L"); } + static SpeciesData speciesData_LL() { return checkCache(2, "LL"); } + static SpeciesData speciesData_LLL() { return checkCache(3, "LLL"); } + static SpeciesData speciesData_LLLL() { return checkCache(4, "LLLL"); } + static SpeciesData speciesData_LLLLL() { return checkCache(5, "LLLLL"); } } diff --git a/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index bb873b10c5a..8ce18cdc062 100644 --- a/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -27,7 +27,6 @@ package java.lang.invoke; import sun.invoke.util.VerifyAccess; import java.lang.invoke.LambdaForm.Name; -import java.lang.invoke.MethodHandles.Lookup; import sun.invoke.util.Wrapper; @@ -39,8 +38,6 @@ import jdk.internal.org.objectweb.asm.*; import java.lang.reflect.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; -import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; -import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyType; /** @@ -51,7 +48,6 @@ import sun.invoke.util.VerifyType; class InvokerBytecodeGenerator { /** Define class names for convenience. */ private static final String MH = "java/lang/invoke/MethodHandle"; - private static final String BMH = "java/lang/invoke/BoundMethodHandle"; private static final String LF = "java/lang/invoke/LambdaForm"; private static final String LFN = "java/lang/invoke/LambdaForm$Name"; private static final String CLS = "java/lang/Class"; @@ -511,17 +507,22 @@ class InvokerBytecodeGenerator { Name name = lambdaForm.names[i]; MemberName member = name.function.member(); - if (isSelectAlternative(member)) { - // selectAlternative idiom - // FIXME: make sure this idiom is really present! + if (isSelectAlternative(i)) { emitSelectAlternative(name, lambdaForm.names[i + 1]); i++; // skip MH.invokeBasic of the selectAlternative result + } else if (isGuardWithCatch(i)) { + emitGuardWithCatch(i); + i = i+2; // Jump to the end of GWC idiom } else if (isStaticallyInvocable(member)) { emitStaticInvoke(member, name); } else { emitInvoke(name); } + // Update cached form name's info in case an intrinsic spanning multiple names was encountered. + name = lambdaForm.names[i]; + member = name.function.member(); + // store the result from evaluating to the target name in a local if required // (if this is the last value, i.e., the one that is going to be returned, // avoid store/load/return and just return) @@ -674,12 +675,66 @@ class InvokerBytecodeGenerator { } /** - * Check if MemberName is a call to MethodHandleImpl.selectAlternative. + * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}. */ - private boolean isSelectAlternative(MemberName member) { + private boolean memberRefersTo(MemberName member, Class declaringClass, String name) { return member != null && - member.getDeclaringClass() == MethodHandleImpl.class && - member.getName().equals("selectAlternative"); + member.getDeclaringClass() == declaringClass && + member.getName().equals(name); + } + private boolean nameRefersTo(Name name, Class declaringClass, String methodName) { + return name.function != null && + memberRefersTo(name.function.member(), declaringClass, methodName); + } + + /** + * Check if MemberName is a call to MethodHandle.invokeBasic. + */ + private boolean isInvokeBasic(Name name) { + if (name.function == null) + return false; + if (name.arguments.length < 1) + return false; // must have MH argument + MemberName member = name.function.member(); + return memberRefersTo(member, MethodHandle.class, "invokeBasic") && + !member.isPublic() && !member.isStatic(); + } + + /** + * Check if i-th name is a call to MethodHandleImpl.selectAlternative. + */ + private boolean isSelectAlternative(int pos) { + // selectAlternative idiom: + // t_{n}:L=MethodHandleImpl.selectAlternative(...) + // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...) + if (pos+1 >= lambdaForm.names.length) return false; + Name name0 = lambdaForm.names[pos]; + Name name1 = lambdaForm.names[pos+1]; + return nameRefersTo(name0, MethodHandleImpl.class, "selectAlternative") && + isInvokeBasic(name1) && + name1.lastUseIndex(name0) == 0 && // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...) + lambdaForm.lastUseIndex(name0) == pos+1; // t_{n} is local: used only in t_{n+1} + } + + /** + * Check if i-th name is a start of GuardWithCatch idiom. + */ + private boolean isGuardWithCatch(int pos) { + // GuardWithCatch idiom: + // t_{n}:L=MethodHandle.invokeBasic(...) + // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n}); + // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1}) + if (pos+2 >= lambdaForm.names.length) return false; + Name name0 = lambdaForm.names[pos]; + Name name1 = lambdaForm.names[pos+1]; + Name name2 = lambdaForm.names[pos+2]; + return nameRefersTo(name1, MethodHandleImpl.class, "guardWithCatch") && + isInvokeBasic(name0) && + isInvokeBasic(name2) && + name1.lastUseIndex(name0) == 3 && // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n}); + lambdaForm.lastUseIndex(name0) == pos+1 && // t_{n} is local: used only in t_{n+1} + name2.lastUseIndex(name1) == 1 && // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1}) + lambdaForm.lastUseIndex(name1) == pos+2; // t_{n+1} is local: used only in t_{n+2} } /** @@ -694,8 +749,6 @@ class InvokerBytecodeGenerator { * } */ private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { - MethodType type = selectAlternativeName.function.methodType(); - Name receiver = (Name) invokeBasicName.arguments[0]; Label L_fallback = new Label(); @@ -709,7 +762,6 @@ class InvokerBytecodeGenerator { mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback); // invoke selectAlternativeName.arguments[1] - MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1]; emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitInvoke(invokeBasicName); @@ -721,7 +773,6 @@ class InvokerBytecodeGenerator { mv.visitLabel(L_fallback); // invoke selectAlternativeName.arguments[2] - MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2]; emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitInvoke(invokeBasicName); @@ -730,6 +781,85 @@ class InvokerBytecodeGenerator { mv.visitLabel(L_done); } + /** + * Emit bytecode for the guardWithCatch idiom. + * + * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch): + *
{@code
+      *  guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{
+      *    t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L);
+      *    t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L);
+      *   t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I}
+      * }
+ * + * It is compiled into bytecode equivalent of the following code: + *
{@code
+      *  try {
+      *      return a1.invokeBasic(a6, a7);
+      *  } catch (Throwable e) {
+      *      if (!a2.isInstance(e)) throw e;
+      *      return a3.invokeBasic(ex, a6, a7);
+      *  }}
+      */
+    private void emitGuardWithCatch(int pos) {
+        Name args    = lambdaForm.names[pos];
+        Name invoker = lambdaForm.names[pos+1];
+        Name result  = lambdaForm.names[pos+2];
+
+        Label L_startBlock = new Label();
+        Label L_endBlock = new Label();
+        Label L_handler = new Label();
+        Label L_done = new Label();
+
+        Class returnType = result.function.resolvedHandle.type().returnType();
+        MethodType type = args.function.resolvedHandle.type()
+                              .dropParameterTypes(0,1)
+                              .changeReturnType(returnType);
+
+        mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable");
+
+        // Normal case
+        mv.visitLabel(L_startBlock);
+        // load target
+        emitPushArgument(invoker, 0);
+        emitPushArguments(args, 1); // skip 1st argument: method handle
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
+        mv.visitLabel(L_endBlock);
+        mv.visitJumpInsn(Opcodes.GOTO, L_done);
+
+        // Exceptional case
+        mv.visitLabel(L_handler);
+
+        // Check exception's type
+        mv.visitInsn(Opcodes.DUP);
+        // load exception class
+        emitPushArgument(invoker, 1);
+        mv.visitInsn(Opcodes.SWAP);
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z", false);
+        Label L_rethrow = new Label();
+        mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow);
+
+        // Invoke catcher
+        // load catcher
+        emitPushArgument(invoker, 2);
+        mv.visitInsn(Opcodes.SWAP);
+        emitPushArguments(args, 1); // skip 1st argument: method handle
+        MethodType catcherType = type.insertParameterTypes(0, Throwable.class);
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString(), false);
+        mv.visitJumpInsn(Opcodes.GOTO, L_done);
+
+        mv.visitLabel(L_rethrow);
+        mv.visitInsn(Opcodes.ATHROW);
+
+        mv.visitLabel(L_done);
+    }
+
+    private void emitPushArguments(Name args, int start) {
+        for (int i = start; i < args.arguments.length; i++) {
+            emitPushArgument(args, i);
+        }
+    }
+
     private void emitPushArgument(Name name, int paramIndex) {
         Object arg = name.arguments[paramIndex];
         char ptype = name.function.parameterType(paramIndex);
diff --git a/jdk/src/share/classes/java/lang/invoke/LambdaForm.java b/jdk/src/share/classes/java/lang/invoke/LambdaForm.java
index c3ab19f3ce5..a5e40ed5211 100644
--- a/jdk/src/share/classes/java/lang/invoke/LambdaForm.java
+++ b/jdk/src/share/classes/java/lang/invoke/LambdaForm.java
@@ -1465,6 +1465,33 @@ class LambdaForm {
             return false;
         }
 
+        /** Return the index of the last occurrence of n in the argument array.
+         *  Return -1 if the name is not used.
+         */
+        int lastUseIndex(Name n) {
+            if (arguments == null)  return -1;
+            for (int i = arguments.length; --i >= 0; ) {
+                if (arguments[i] == n)  return i;
+            }
+            return -1;
+        }
+
+        /** Return the number of occurrences of n in the argument array.
+         *  Return 0 if the name is not used.
+         */
+        int useCount(Name n) {
+            if (arguments == null)  return 0;
+            int count = 0;
+            for (int i = arguments.length; --i >= 0; ) {
+                if (arguments[i] == n)  ++count;
+            }
+            return count;
+        }
+
+        boolean contains(Name n) {
+            return this == n || lastUseIndex(n) >= 0;
+        }
+
         public boolean equals(Name that) {
             if (this == that)  return true;
             if (isParam())
@@ -1488,6 +1515,35 @@ class LambdaForm {
         }
     }
 
+    /** Return the index of the last name which contains n as an argument.
+     *  Return -1 if the name is not used.  Return names.length if it is the return value.
+     */
+    int lastUseIndex(Name n) {
+        int ni = n.index, nmax = names.length;
+        assert(names[ni] == n);
+        if (result == ni)  return nmax;  // live all the way beyond the end
+        for (int i = nmax; --i > ni; ) {
+            if (names[i].lastUseIndex(n) >= 0)
+                return i;
+        }
+        return -1;
+    }
+
+    /** Return the number of times n is used as an argument or return value. */
+    int useCount(Name n) {
+        int ni = n.index, nmax = names.length;
+        int end = lastUseIndex(n);
+        if (end < 0)  return 0;
+        int count = 0;
+        if (end == nmax) { count++; end--; }
+        int beg = n.index() + 1;
+        if (beg < arity)  beg = arity;
+        for (int i = beg; i <= end; i++) {
+            count += names[i].useCount(n);
+        }
+        return count;
+    }
+
     static Name argument(int which, char type) {
         int tn = ALL_TYPES.indexOf(type);
         if (tn < 0 || which >= INTERNED_ARGUMENT_LIMIT)
diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java
index eca236ce308..872ecddcdf8 100644
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java
@@ -27,7 +27,6 @@ package java.lang.invoke;
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import sun.invoke.empty.Empty;
@@ -482,12 +481,26 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
      * Factored in an inner class to delay initialization until first usage.
      */
     private static class Lazy {
+        private static final Class MHI = MethodHandleImpl.class;
+
         static final NamedFunction NF_checkSpreadArgument;
+        static final NamedFunction NF_guardWithCatch;
+        static final NamedFunction NF_selectAlternative;
+        static final NamedFunction NF_throwException;
+
         static {
             try {
-                NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
-                        .getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
+                NF_checkSpreadArgument = new NamedFunction(MHI.getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
+                NF_guardWithCatch      = new NamedFunction(MHI.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class,
+                                                                                 MethodHandle.class, Object[].class));
+                NF_selectAlternative   = new NamedFunction(MHI.getDeclaredMethod("selectAlternative", boolean.class, MethodHandle.class,
+                                                                                 MethodHandle.class));
+                NF_throwException      = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class));
+
                 NF_checkSpreadArgument.resolve();
+                NF_guardWithCatch.resolve();
+                NF_selectAlternative.resolve();
+                NF_throwException.resolve();
             } catch (ReflectiveOperationException ex) {
                 throw newInternalError(ex);
             }
@@ -548,24 +561,12 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
         return SimpleMethodHandle.make(srcType, form);
     }
 
+    @LambdaForm.Hidden
     static
     MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) {
         return testResult ? target : fallback;
     }
 
-    static MethodHandle SELECT_ALTERNATIVE;
-    static MethodHandle selectAlternative() {
-        if (SELECT_ALTERNATIVE != null)  return SELECT_ALTERNATIVE;
-        try {
-            SELECT_ALTERNATIVE
-            = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
-                    MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class));
-        } catch (ReflectiveOperationException ex) {
-            throw new RuntimeException(ex);
-        }
-        return SELECT_ALTERNATIVE;
-    }
-
     static
     MethodHandle makeGuardWithTest(MethodHandle test,
                                    MethodHandle target,
@@ -585,7 +586,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
 
         // call selectAlternative
         Object[] selectArgs = { names[arity + 1], target, fallback };
-        names[arity + 2] = new Name(MethodHandleImpl.selectAlternative(), selectArgs);
+        names[arity + 2] = new Name(Lazy.NF_selectAlternative, selectArgs);
         targetArgs[0] = names[arity + 2];
 
         // call target or fallback
@@ -595,167 +596,137 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
         return SimpleMethodHandle.make(target.type(), form);
     }
 
-    private static class GuardWithCatch {
-        private final MethodHandle target;
-        private final Class exType;
-        private final MethodHandle catcher;
-        // FIXME: Build the control flow out of foldArguments.
-        GuardWithCatch(MethodHandle target, Class exType, MethodHandle catcher) {
-            this.target = target;
-            this.exType = exType;
-            this.catcher = catcher;
-        }
-        @LambdaForm.Hidden
-        private Object invoke_V(Object... av) throws Throwable {
-            try {
-                return target.invokeExact(av);
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t, av);
-            }
-        }
-        @LambdaForm.Hidden
-        private Object invoke_L0() throws Throwable {
-            try {
-                return target.invokeExact();
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t);
-            }
-        }
-        @LambdaForm.Hidden
-        private Object invoke_L1(Object a0) throws Throwable {
-            try {
-                return target.invokeExact(a0);
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t, a0);
-            }
-        }
-        @LambdaForm.Hidden
-        private Object invoke_L2(Object a0, Object a1) throws Throwable {
-            try {
-                return target.invokeExact(a0, a1);
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t, a0, a1);
-            }
-        }
-        @LambdaForm.Hidden
-        private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
-            try {
-                return target.invokeExact(a0, a1, a2);
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t, a0, a1, a2);
-            }
-        }
-        @LambdaForm.Hidden
-        private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
-            try {
-                return target.invokeExact(a0, a1, a2, a3);
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t, a0, a1, a2, a3);
-            }
-        }
-        @LambdaForm.Hidden
-        private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
-            try {
-                return target.invokeExact(a0, a1, a2, a3, a4);
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t, a0, a1, a2, a3, a4);
-            }
-        }
-        @LambdaForm.Hidden
-        private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
-            try {
-                return target.invokeExact(a0, a1, a2, a3, a4, a5);
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5);
-            }
-        }
-        @LambdaForm.Hidden
-        private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
-            try {
-                return target.invokeExact(a0, a1, a2, a3, a4, a5, a6);
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6);
-            }
-        }
-        @LambdaForm.Hidden
-        private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
-            try {
-                return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
-            } catch (Throwable t) {
-                if (!exType.isInstance(t))  throw t;
-                return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7);
-            }
-        }
-        static MethodHandle[] makeInvokes() {
-            ArrayList invokes = new ArrayList<>();
-            MethodHandles.Lookup lookup = IMPL_LOOKUP;
-            for (;;) {
-                int nargs = invokes.size();
-                String name = "invoke_L"+nargs;
-                MethodHandle invoke = null;
-                try {
-                    invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs));
-                } catch (ReflectiveOperationException ex) {
-                }
-                if (invoke == null)  break;
-                invokes.add(invoke);
-            }
-            assert(invokes.size() == 9);  // current number of methods
-            return invokes.toArray(new MethodHandle[0]);
-        };
-        static final MethodHandle[] INVOKES = makeInvokes();
-        // For testing use this:
-        //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2);
-        static final MethodHandle VARARGS_INVOKE;
-        static {
-            try {
-                VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
-            } catch (ReflectiveOperationException ex) {
-                throw uncaughtException(ex);
-            }
-        }
-    }
+    /**
+     * The LambaForm shape for catchException combinator is the following:
+     * 
{@code
+     *  guardWithCatch=Lambda(a0:L,a1:L,a2:L)=>{
+     *    t3:L=BoundMethodHandle$Species_LLLLL.argL0(a0:L);
+     *    t4:L=BoundMethodHandle$Species_LLLLL.argL1(a0:L);
+     *    t5:L=BoundMethodHandle$Species_LLLLL.argL2(a0:L);
+     *    t6:L=BoundMethodHandle$Species_LLLLL.argL3(a0:L);
+     *    t7:L=BoundMethodHandle$Species_LLLLL.argL4(a0:L);
+     *    t8:L=MethodHandle.invokeBasic(t6:L,a1:L,a2:L);
+     *    t9:L=MethodHandleImpl.guardWithCatch(t3:L,t4:L,t5:L,t8:L);
+     *   t10:I=MethodHandle.invokeBasic(t7:L,t9:L);t10:I}
+     * }
+ * + * argL0 and argL2 are target and catcher method handles. argL1 is exception class. + * argL3 and argL4 are auxiliary method handles: argL3 boxes arguments and wraps them into Object[] + * (ValueConversions.array()) and argL4 unboxes result if necessary (ValueConversions.unbox()). + * + * Having t8 and t10 passed outside and not hardcoded into a lambda form allows to share lambda forms + * among catchException combinators with the same basic type. + */ + private static LambdaForm makeGuardWithCatchForm(MethodType basicType) { + MethodType lambdaType = basicType.invokerType(); + LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_GWC); + if (lform != null) { + return lform; + } + final int THIS_MH = 0; // the BMH_LLLLL + final int ARG_BASE = 1; // start of incoming arguments + final int ARG_LIMIT = ARG_BASE + basicType.parameterCount(); + + int nameCursor = ARG_LIMIT; + final int GET_TARGET = nameCursor++; + final int GET_CLASS = nameCursor++; + final int GET_CATCHER = nameCursor++; + final int GET_COLLECT_ARGS = nameCursor++; + final int GET_UNBOX_RESULT = nameCursor++; + final int BOXED_ARGS = nameCursor++; + final int TRY_CATCH = nameCursor++; + final int UNBOX_RESULT = nameCursor++; + + Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); + + BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); + names[GET_TARGET] = new Name(data.getterFunction(0), names[THIS_MH]); + names[GET_CLASS] = new Name(data.getterFunction(1), names[THIS_MH]); + names[GET_CATCHER] = new Name(data.getterFunction(2), names[THIS_MH]); + names[GET_COLLECT_ARGS] = new Name(data.getterFunction(3), names[THIS_MH]); + names[GET_UNBOX_RESULT] = new Name(data.getterFunction(4), names[THIS_MH]); + + // FIXME: rework argument boxing/result unboxing logic for LF interpretation + + // t_{i}:L=MethodHandle.invokeBasic(collectArgs:L,a1:L,...); + MethodType collectArgsType = basicType.changeReturnType(Object.class); + MethodHandle invokeBasic = MethodHandles.basicInvoker(collectArgsType); + Object[] args = new Object[invokeBasic.type().parameterCount()]; + args[0] = names[GET_COLLECT_ARGS]; + System.arraycopy(names, ARG_BASE, args, 1, ARG_LIMIT-ARG_BASE); + names[BOXED_ARGS] = new Name(new NamedFunction(invokeBasic), args); + + // t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L); + Object[] gwcArgs = new Object[] {names[GET_TARGET], names[GET_CLASS], names[GET_CATCHER], names[BOXED_ARGS]}; + names[TRY_CATCH] = new Name(Lazy.NF_guardWithCatch, gwcArgs); + + // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L); + MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class)); + Object[] unboxArgs = new Object[] {names[GET_UNBOX_RESULT], names[TRY_CATCH]}; + names[UNBOX_RESULT] = new Name(new NamedFunction(invokeBasicUnbox), unboxArgs); + + lform = new LambdaForm("guardWithCatch", lambdaType.parameterCount(), names); + + basicType.form().setCachedLambdaForm(MethodTypeForm.LF_GWC, lform); + return lform; + } static MethodHandle makeGuardWithCatch(MethodHandle target, Class exType, MethodHandle catcher) { MethodType type = target.type(); - MethodType ctype = catcher.type(); - int nargs = type.parameterCount(); - if (nargs < GuardWithCatch.INVOKES.length) { - MethodType gtype = type.generic(); - MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); - // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0) - MethodHandle gtarget = makePairwiseConvert(target, gtype, 2); - MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2); - GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher); - if (gtarget == null || gcatcher == null) throw new InternalError(); - MethodHandle ginvoker = GuardWithCatch.INVOKES[nargs].bindReceiver(gguard); - return makePairwiseConvert(ginvoker, type, 2); + LambdaForm form = makeGuardWithCatchForm(type.basicType()); + + // Prepare auxiliary method handles used during LambdaForm interpreation. + // Box arguments and wrap them into Object[]: ValueConversions.array(). + MethodType varargsType = type.changeReturnType(Object[].class); + MethodHandle collectArgs = ValueConversions.varargsArray(type.parameterCount()) + .asType(varargsType); + // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). + MethodHandle unboxResult; + if (type.returnType().isPrimitive()) { + unboxResult = ValueConversions.unbox(type.returnType()); } else { - target = target.asType(type.changeReturnType(Object.class)); - MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs); - MethodType catcherType = ctype.changeParameterType(0, Throwable.class) - .changeReturnType(Object.class); - catcher = catcher.asType(catcherType); - MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs); - GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher); - if (gtarget == null || gcatcher == null) throw new InternalError(); - MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard); - MethodHandle gcollect = makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false); - return makePairwiseConvert(gcollect, type, 2); + unboxResult = ValueConversions.identity(); } + + BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); + BoundMethodHandle mh; + try { + mh = (BoundMethodHandle) + data.constructor[0].invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher, + (Object) collectArgs, (Object) unboxResult); + } catch (Throwable ex) { + throw uncaughtException(ex); + } + assert(mh.type() == type); + return mh; + } + + /** + * Intrinsified during LambdaForm compilation + * (see {@link InvokerBytecodeGenerator#emitGuardWithCatch emitGuardWithCatch}). + */ + @LambdaForm.Hidden + static Object guardWithCatch(MethodHandle target, Class exType, MethodHandle catcher, + Object... av) throws Throwable { + try { + return target.invokeWithArguments(av); + } catch (Throwable t) { + if (!exType.isInstance(t)) throw t; + Object[] args = prepend(t, av); + return catcher.invokeWithArguments(args); + } + } + + /** Prepend an element {@code elem} to an {@code array}. */ + private static Object[] prepend(Object elem, Object[] array) { + Object[] newArray = new Object[array.length+1]; + newArray[0] = elem; + System.arraycopy(array, 0, newArray, 1, array.length); + return newArray; } static @@ -765,23 +736,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (arity > 1) { return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1); } - return makePairwiseConvert(throwException(), type, 2); + return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, 2); } - static MethodHandle THROW_EXCEPTION; - static MethodHandle throwException() { - MethodHandle mh = THROW_EXCEPTION; - if (mh != null) return mh; - try { - mh - = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException", - MethodType.methodType(Empty.class, Throwable.class)); - } catch (ReflectiveOperationException ex) { - throw new RuntimeException(ex); - } - THROW_EXCEPTION = mh; - return mh; - } static Empty throwException(T t) throws T { throw t; } static MethodHandle[] FAKE_METHOD_HANDLE_INVOKE = new MethodHandle[2]; diff --git a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java index 80a751cfce3..ee5e6221b11 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java @@ -76,7 +76,8 @@ final class MethodTypeForm { LF_GEN_INVOKER = 12, LF_CS_LINKER = 13, // linkToCallSite_CS LF_MH_LINKER = 14, // linkToCallSite_MH - LF_LIMIT = 15; + LF_GWC = 15, + LF_LIMIT = 16; public MethodType erasedType() { return erasedType; diff --git a/jdk/test/java/lang/invoke/MethodHandles/TestCatchException.java b/jdk/test/java/lang/invoke/MethodHandles/TestCatchException.java index 106eafc1426..8f1abfc83d8 100644 --- a/jdk/test/java/lang/invoke/MethodHandles/TestCatchException.java +++ b/jdk/test/java/lang/invoke/MethodHandles/TestCatchException.java @@ -72,10 +72,45 @@ public class TestCatchException { assertEquals(x, 17); } + + public static Object m1(Object o1, Object o2, Object o3, Object o4, Object o5, + Object o6, Object o7, Object o8, Object... tail) { + return tail; + } + + public static Object m2(Exception e, Object o1, Object o2, Object o3, Object o4, + Object o5, Object o6, Object o7, Object o8, Object... tail) { + return tail; + } + + @Test + public void testVarargsCollector() throws Throwable { + MethodType t1 = MethodType.methodType(Object.class, Object.class, Object.class, Object.class, Object.class, + Object.class, Object.class, Object.class, Object.class, Object[].class); + + MethodType t2 = t1.insertParameterTypes(0, Exception.class); + + MethodHandle target = LOOKUP.findStatic(TestCatchException.class, "m1", t1) + .asVarargsCollector(Object[].class); + + MethodHandle catcher = LOOKUP.findStatic(TestCatchException.class, "m2", t2); + + MethodHandle gwc = MethodHandles.catchException(target, Exception.class, catcher); + + Object o = new Object(); + Object[] obj1 = new Object[] { "str" }; + + Object r1 = target.invokeExact(o, o, o, o, o, o, o, o, obj1); + Object r2 = gwc.invokeExact(o, o, o, o, o, o, o, o, obj1); + assertEquals(r1, obj1); + assertEquals(r2, obj1); + } + public static void main(String[] args) throws Throwable { TestCatchException test = new TestCatchException(); test.testNoThrowPath(); test.testThrowPath(); + test.testVarargsCollector(); System.out.println("TEST PASSED"); } }