8198523: Refactor BootstrapMethodInvoker to further avoid runtime type checks
authorredestad
Thu, 22 Feb 2018 11:16:43 +0100
changeset 48930 b1a5b4ad7427
parent 48929 28d8fc8cd3cd
child 48931 b25eb74ec283
8198523: Refactor BootstrapMethodInvoker to further avoid runtime type checks Reviewed-by: psandoz
src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java
src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
--- a/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java	Thu Feb 22 11:52:01 2018 +0530
+++ b/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java	Thu Feb 22 11:16:43 2018 +0100
@@ -37,6 +37,7 @@
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
 
 final class BootstrapMethodInvoker {
+
     /**
      * Factored code for invoking a bootstrap method for invokedynamic
      * or a dynamic constant.
@@ -76,14 +77,30 @@
             bootstrapMethod = null;
         }
         try {
+            // As an optimization we special case various known BSMs,
+            // such as LambdaMetafactory::metafactory and
+            // StringConcatFactory::makeConcatWithConstants.
+            //
+            // By providing static type information or even invoking
+            // exactly, we avoid emitting code to perform runtime
+            // checking.
             info = maybeReBox(info);
             if (info == null) {
                 // VM is allowed to pass up a null meaning no BSM args
-                result = bootstrapMethod.invoke(caller, name, type);
+                result = invoke(bootstrapMethod, caller, name, type);
             }
             else if (!info.getClass().isArray()) {
                 // VM is allowed to pass up a single BSM arg directly
-                result = bootstrapMethod.invoke(caller, name, type, info);
+
+                // Call to StringConcatFactory::makeConcatWithConstants
+                // with empty constant arguments?
+                if (isStringConcatFactoryBSM(bootstrapMethod.type())) {
+                    result = (CallSite)bootstrapMethod
+                            .invokeExact(caller, name, (MethodType)type,
+                                         (String)info, new Object[0]);
+                } else {
+                    result = invoke(bootstrapMethod, caller, name, type, info);
+                }
             }
             else if (info.getClass() == int[].class) {
                 // VM is allowed to pass up a pair {argc, index}
@@ -103,67 +120,52 @@
                 // VM is allowed to pass up a full array of resolved BSM args
                 Object[] argv = (Object[]) info;
                 maybeReBoxElements(argv);
-                switch (argv.length) {
-                    case 0:
-                        result = bootstrapMethod.invoke(caller, name, type);
-                        break;
-                    case 1:
-                        result = bootstrapMethod.invoke(caller, name, type,
-                                                        argv[0]);
-                        break;
-                    case 2:
-                        result = bootstrapMethod.invoke(caller, name, type,
-                                                        argv[0], argv[1]);
-                        break;
-                    case 3:
-                        // Special case the LambdaMetafactory::metafactory BSM
-                        //
-                        // By invoking exactly, we can avoid generating a number of
-                        // classes on first (and subsequent) lambda initialization,
-                        // most of which won't be shared with other invoke uses.
-                        MethodType bsmType = bootstrapMethod.type();
-                        if (isLambdaMetafactoryIndyBSM(bsmType)) {
-                            result = (CallSite)bootstrapMethod
-                                    .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
-                                                 (MethodHandle)argv[1], (MethodType)argv[2]);
-                        } else if (isLambdaMetafactoryCondyBSM(bsmType)) {
-                            result = bootstrapMethod
-                                    .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
-                                                 (MethodHandle)argv[1], (MethodType)argv[2]);
-                        } else {
-                            result = bootstrapMethod.invoke(caller, name, type,
-                                                            argv[0], argv[1], argv[2]);
-                        }
-                        break;
-                    case 4:
-                        result = bootstrapMethod.invoke(caller, name, type,
-                                                        argv[0], argv[1], argv[2], argv[3]);
-                        break;
-                    case 5:
-                        result = bootstrapMethod.invoke(caller, name, type,
-                                                        argv[0], argv[1], argv[2], argv[3], argv[4]);
-                        break;
-                    case 6:
-                        result = 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)
-                        final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
-                        if (argv.length >= MAX_SAFE_SIZE) {
-                            // to be on the safe side, use invokeWithArguments which handles jumbo lists
-                            Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
-                            newargv[0] = caller;
-                            newargv[1] = name;
-                            newargv[2] = type;
-                            System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
-                            result = bootstrapMethod.invokeWithArguments(newargv);
+
+                MethodType bsmType = bootstrapMethod.type();
+                if (isLambdaMetafactoryIndyBSM(bsmType) && argv.length == 3) {
+                    result = (CallSite)bootstrapMethod
+                            .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
+                                    (MethodHandle)argv[1], (MethodType)argv[2]);
+                } else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
+                    result = bootstrapMethod
+                            .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
+                                    (MethodHandle)argv[1], (MethodType)argv[2]);
+                } else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
+                    String recipe = (String)argv[0];
+                    Object[] shiftedArgs = Arrays.copyOfRange(argv, 1, argv.length);
+                    result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs);
+                } else {
+                    switch (argv.length) {
+                        case 0:
+                            result = invoke(bootstrapMethod, caller, name, type);
+                            break;
+                        case 1:
+                            result = invoke(bootstrapMethod, caller, name, type,
+                                            argv[0]);
                             break;
-                        }
-                        MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
-                        MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
-                        MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
-                        result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
+                        case 2:
+                            result = invoke(bootstrapMethod, caller, name, type,
+                                            argv[0], argv[1]);
+                            break;
+                        case 3:
+                            result = invoke(bootstrapMethod, caller, name, type,
+                                            argv[0], argv[1], argv[2]);
+                            break;
+                        case 4:
+                            result = invoke(bootstrapMethod, caller, name, type,
+                                            argv[0], argv[1], argv[2], argv[3]);
+                            break;
+                        case 5:
+                            result = invoke(bootstrapMethod, caller, name, type,
+                                            argv[0], argv[1], argv[2], argv[3], argv[4]);
+                            break;
+                        case 6:
+                            result = invoke(bootstrapMethod, caller, name, type,
+                                            argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+                            break;
+                        default:
+                            result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv);
+                    }
                 }
             }
             if (resultType.isPrimitive()) {
@@ -191,12 +193,114 @@
         }
     }
 
+    // If we don't provide static type information for type, we'll generate runtime
+    // checks. Let's try not to...
+
+    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
+                                 String name, Object type) throws Throwable {
+        if (type instanceof Class) {
+            return bootstrapMethod.invoke(caller, name, (Class<?>)type);
+        } else {
+            return bootstrapMethod.invoke(caller, name, (MethodType)type);
+        }
+    }
+
+    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
+                                 String name, Object type, Object arg0) throws Throwable {
+        if (type instanceof Class) {
+            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0);
+        } else {
+            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0);
+        }
+    }
+
+    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
+                                 Object type, Object arg0, Object arg1) throws Throwable {
+        if (type instanceof Class) {
+            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1);
+        } else {
+            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1);
+        }
+    }
+
+    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
+                                 Object type, Object arg0, Object arg1,
+                                 Object arg2) throws Throwable {
+        if (type instanceof Class) {
+            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2);
+        } else {
+            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2);
+        }
+    }
+
+    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
+                                 Object type, Object arg0, Object arg1,
+                                 Object arg2, Object arg3) throws Throwable {
+        if (type instanceof Class) {
+            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3);
+        } else {
+            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3);
+        }
+    }
+
+    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
+                                 String name, Object type, Object arg0, Object arg1,
+                                 Object arg2, Object arg3, Object arg4) throws Throwable {
+        if (type instanceof Class) {
+            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4);
+        } else {
+            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4);
+        }
+    }
+
+    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
+                                 String name, Object type, Object arg0, Object arg1,
+                                 Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable {
+        if (type instanceof Class) {
+            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4, arg5);
+        } else {
+            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4, arg5);
+        }
+    }
+
+    private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller,
+                                                  String name, Object type, Object[] argv) throws Throwable {
+        final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
+        final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
+        if (argv.length >= MAX_SAFE_SIZE) {
+            // to be on the safe side, use invokeWithArguments which handles jumbo lists
+            Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
+            newargv[0] = caller;
+            newargv[1] = name;
+            newargv[2] = type;
+            System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
+            return bootstrapMethod.invokeWithArguments(newargv);
+        } else {
+            MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
+            MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
+            MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
+            return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
+        }
+    }
+
     private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
 
     private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
             Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
 
+    private static final MethodType SCF_MT = MethodType.methodType(CallSite.class,
+            Lookup.class, String.class, MethodType.class, String.class, Object[].class);
+
+    /**
+     * @return true iff the BSM method type exactly matches
+     *         {@see java.lang.invoke.StringConcatFactory#makeConcatWithConstants(MethodHandles.Lookup,
+     *                 String,MethodType,String,Object...))}
+     */
+    private static boolean isStringConcatFactoryBSM(MethodType bsmType) {
+        return bsmType == SCF_MT;
+    }
+
     /**
      * @return true iff the BSM method type exactly matches
      *         {@see java.lang.invoke.LambdaMetafactory#metafactory(
--- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Thu Feb 22 11:52:01 2018 +0530
+++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Thu Feb 22 11:16:43 2018 +0100
@@ -373,6 +373,12 @@
         }
     }
 
+    // StringConcatFactory bootstrap methods are startup sensitive, and may be
+    // special cased in java.lang.invokeBootstrapMethodInvoker to ensure
+    // methods are invoked with exact type information to avoid generating
+    // code for runtime checks. Take care any changes or additions here are
+    // reflected there as appropriate.
+
     /**
      * Facilitates the creation of optimized String concatenation methods, that
      * can be used to efficiently concatenate a known number of arguments of