< prev index next >

src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java

Print this page
rev 52749 : Bootstrap method consolidation
* clean up and simplify JDK support code for BSM invocation
* simplify JVM bootstrap handshake: use BootstrapCallInfo only
* remove unused JVM paths and data fields
* move bootstrap argument processing from MethodHandleNatives to ConstantPool
* remove ConstantGroup; merge argument access into BootstrapCallInfo
* adjust BSM argument access: remove copyArguments, add argumentRef API
* add metadata-free BSM modes, including symbolic arguments from CP

@@ -24,11 +24,17 @@
  */
 package java.lang.invoke;
 
 import sun.invoke.util.Wrapper;
 
+import java.lang.constant.ConstantDesc;
+import java.util.Arrays;
+
+import static java.lang.invoke.AbstractBootstrapCallInfo.maybeShareArguments;
+import static java.lang.invoke.BootstrapMethodInvoker.invokeCommon;
 import static java.lang.invoke.MethodHandleNatives.mapLookupExceptionToError;
+import static java.lang.invoke.MethodHandles.Lookup;
 import static java.util.Objects.requireNonNull;
 
 /**
  * Bootstrap methods for dynamically-computed constants.
  *

@@ -38,38 +44,10 @@
  * {@code null} value.
  *
  * @since 11
  */
 public final class ConstantBootstraps {
-    // implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
-    /*non-public*/
-    static Object makeConstant(MethodHandle bootstrapMethod,
-                               // Callee information:
-                               String name, Class<?> type,
-                               // Extra arguments for BSM, if any:
-                               Object info,
-                               // Caller information:
-                               Class<?> callerClass) {
-        // Restrict bootstrap methods to those whose first parameter is Lookup
-        // The motivation here is, in the future, to possibly support BSMs
-        // that do not accept the meta-data of lookup/name/type, thereby
-        // allowing the co-opting of existing methods to be used as BSMs as
-        // long as the static arguments can be passed as method arguments
-        MethodType mt = bootstrapMethod.type();
-        if (mt.parameterCount() < 2 ||
-            !MethodHandles.Lookup.class.isAssignableFrom(mt.parameterType(0))) {
-            throw new BootstrapMethodError(
-                    "Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod);
-        }
-
-        // BSMI.invoke handles all type checking and exception translation.
-        // If type is not a reference type, the JVM is expecting a boxed
-        // version, and will manage unboxing on the other side.
-        return BootstrapMethodInvoker.invoke(
-                type, bootstrapMethod, name, type, info, callerClass);
-    }
-
     /**
      * Returns a {@code null} object reference for the reference type specified
      * by {@code type}.
      *
      * @param lookup unused

@@ -216,16 +194,16 @@
         return getStaticFinal(lookup, name, type, declaring);
     }
 
 
     /**
-     * Returns the result of invoking a method handle with the provided
-     * arguments.
+     * Returns the result of invoking a bootstrap method handle on static
+     * arguments, but without metadata.
      * <p>
      * This method behaves as if the method handle to be invoked is the result
      * of adapting the given method handle, via {@link MethodHandle#asType}, to
-     * adjust the return type to the desired type.
+     * adjust the return type to {@code invocationType} of the bootstrap call.
      *
      * @param lookup unused
      * @param name unused
      * @param type the desired type of the value to be returned, which must be
      * compatible with the return type of the method handle

@@ -245,18 +223,166 @@
                                 MethodHandle handle, Object... args) throws Throwable {
         requireNonNull(type);
         requireNonNull(handle);
         requireNonNull(args);
 
-        if (type != handle.type().returnType()) {
-            // Adjust the return type of the handle to be invoked while
-            // preserving variable arity if present
-            handle = handle.asType(handle.type().changeReturnType(type)).
-                    withVarargs(handle.isVarargsCollector());
+        Object result = invokeCommon(handle, null, null, null, args, null);
+        return BootstrapCallInfo.convertResult(type, handle.type().returnType(), result);
+    }
+
+    /**
+     * Returns the result of invoking a bootstrap method handle on static
+     * arguments, but without metadata.
+     * <p>
+     * This method behaves as if the method handle to be invoked is the result
+     * of adapting the given method handle, via {@link MethodHandle#asType}, to
+     * adjust the return type to {@code invocationType} of the bootstrap call.
+     *
+     * @param lookup unused
+     * @param bsci the container of the bootstrap method
+     * @return the result of invoking the method handle
+     * @throws WrongMethodTypeException if the handle's method type cannot be
+     * adjusted to take the given number of arguments, or if the handle's return
+     * type cannot be adjusted to the desired type
+     * @throws ClassCastException if an argument or the result produced by
+     * invoking the handle cannot be converted by reference casting
+     * @throws Throwable anything thrown by the method handle invocation
+     */
+    // not public until BootstrapCallInfo is public
+    static Object invoke(MethodHandles.Lookup lookup, BootstrapCallInfo<?> bsci) throws Throwable {
+        MethodHandle bsm = bsci.bootstrapMethod();
+        Object result = invokeCommon(bsm,
+                null, null, null,
+                maybeShareArguments(bsci),
+                bsci.argumentList());
+        return BootstrapCallInfo.convertResult((Class<?>) bsci.invocationType(), bsm.type().returnType(), result);
+    }
+
+    /**
+     * Returns the result of invoking a bootstrap method handle on static
+     * arguments, but without metadata.  In addition, any arguments of type
+     * {@code Object} or of any type assignable to {@code ConstantDesc} will
+     * be provided symbolic references, rather than resolved values, for
+     * the corresponding static arguments.
+     * <p>
+     * Finally, any single argument of type {@code Lookup} will be passed the
+     * lookup object directly, rather than either a symbolic reference or
+     * resolved argument value.
+     * <p>
+     * This method behaves as if the method handle to be invoked is the result
+     * of adapting the given method handle, via {@link MethodHandle#asType}, to
+     * adjust the return type to {@code invocationType} of the bootstrap call.
+     *
+     * @param lookup unused
+     * @param bsci the container of the bootstrap method
+     * @return the result of invoking the method handle
+     * @throws WrongMethodTypeException if the handle's method type cannot be
+     * adjusted to take the given number of arguments, or if the handle's return
+     * type cannot be adjusted to the desired type
+     * @throws ClassCastException if an argument or the result produced by
+     * invoking the handle cannot be converted by reference casting
+     * @throws Throwable anything thrown by the method handle invocation
+     */
+    // not public until BootstrapCallInfo is public
+    static Object symbolic(MethodHandles.Lookup lookup, BootstrapCallInfo<?> bsci) throws Throwable {
+        MethodHandle bsm = bsci.bootstrapMethod();
+        MethodType bsmType = bsm.type();
+        boolean passLookup = bsmType.parameterList().contains(Lookup.class);
+        Object[] argv = new Object[bsci.argumentCount() + (passLookup ? 1 : 0)];
+        int pos = 0;
+        int maxPos = bsmType.parameterCount() - (bsm.isVarargsCollector() ? 1 : 0);
+        int bargPos = 0;
+        for (Class<?> ptype : bsmType.parameterList()) {
+            if (pos == maxPos)  break;
+            if (ptype == Lookup.class && passLookup) {
+                argv[pos++] = lookup;
+                passLookup = false;
+            } else if (isSymbolicArgType(ptype)) {
+                argv[pos++] = bsci.argumentDesc(bargPos++);
+            } else {
+                argv[pos++] = bsci.argument(bargPos++);
+            }
+        }
+        Class<?> ptype = bsmType.lastParameterType().componentType();
+        while (pos < argv.length) {
+            assert(ptype != null);
+            if (isSymbolicArgType(ptype)) {
+                argv[pos++] = bsci.argumentDesc(bargPos++);
+            } else {
+                argv[pos++] = bsci.argument(bargPos++);
+            }
+        }
+        Object result = invokeCommon(bsm,
+                null, null, null,
+                argv, null);
+        return BootstrapCallInfo.convertResult((Class<?>)bsci.invocationType(), bsmType.returnType(), result);
+    }
+
+    private static boolean isSymbolicArgType(Class<?> ptype) {
+        return (ptype == Object.class || ConstantDesc.class.isAssignableFrom(ptype));
+    }
+
+    /**
+     * Trivial method which returns its sole argument, which must be a
+     * ConstantDesc of some sort.  This method is useful as an
+     * expression-mode bootstrap method with the operation "symbolic".
+     *
+     * @param desc the value to be returned
+     * @param <T> the type of the value to be returned
+     * @return desc
+     * @see DynamicConstantDesc#ofSymbolic
+     */
+    public static <T extends ConstantDesc<?>> T constantDesc(T desc) {
+        return desc;
+    }
+
+    /**
+     * Returns the result of invoking a bootstrap method handle in
+     * expression mode.  The exact behavior of this mode depends on
+     * the {@code name} string.  If the name is {@code "invoke"},
+     * then the bootstrap method is applied directly to the arguments,
+     * as if it were the leading static argument to {@link #invoke}.
+     * If the name is {@code "symbolic"} then bootstrap arguments
+     * are extracted as unresolved symbolic references from the
+     * constant pool and returned.
+     * Other {@code name} strings are reserved for future use.
+     * <p>
+     * @apiNote
+     * This method behaves like the following:
+     * <blockquote><pre>{@code
+    String name = bsci.invocationName();
+    switch (name) {
+    case "invoke":          return invoke(lookup, bsci);
+    case "symbolic":        return symbolic(lookup, bsci);
+    }
+     * }</pre></blockquote>
+     * <p>
+     *
+     * @param lookup must be non-null, otherwise presently unused
+     * @param bsci  bootstrap call information
+     * @return the result of invoking the bootstrap method handle on appropriate arguments
+     * @throws WrongMethodTypeException if the handle's method type cannot be
+     * adjusted to take the given number of arguments, or if the handle's return
+     * type cannot be adjusted to the desired type
+     * @throws ClassCastException if an argument or the result produced by
+     * invoking the handle cannot be converted by reference casting
+     * @throws IllegalArgumentException if the {@code name} is invalid
+     * @throws Throwable anything thrown by an action selected by {@code name}
+     */
+    // not public until BootstrapCallInfo is public
+    static Object linkExpression(MethodHandles.Lookup lookup,
+                                 BootstrapCallInfo<?> bsci) throws Throwable {
+        requireNonNull(lookup);
+        requireNonNull(bsci);
+
+        String name = bsci.invocationName();
+        switch (name) {
+        case "invoke":          return invoke(lookup, bsci);
+        case "symbolic":        return symbolic(lookup, bsci);
         }
 
-        return handle.invokeWithArguments(args);
+        throw new IllegalArgumentException("invalid name for expression-mode constant: "+bsci);
     }
 
     /**
      * Finds a {@link VarHandle} for an instance field.
      *
< prev index next >