--- old/src/java.base/share/classes/java/lang/invoke/BootstrapCallInfo.java 2018-09-28 11:54:20.000000000 -0700
+++ new/src/java.base/share/classes/java/lang/invoke/BootstrapCallInfo.java 2018-09-28 11:54:20.000000000 -0700
@@ -25,14 +25,22 @@
package java.lang.invoke;
+import java.lang.constant.Constable;
+import java.lang.constant.ConstantDesc;
+import java.lang.invoke.AbstractBootstrapCallInfo.*;
import java.lang.invoke.MethodHandles.Lookup;
+import java.util.List;
+import java.util.Objects;
+import sun.invoke.util.Wrapper;
+
+import static java.lang.invoke.BootstrapMethodInvoker.invokeCommon;
/**
* An interface providing full static information about a particular
* call to a
* bootstrap method of an
* dynamic call site or dynamic constant.
- * This information includes the method itself, the associated
+ * This information includes the bootstrap method itself, the associated
* name and type, and any associated static arguments.
*
* If a bootstrap method declares exactly two arguments, and is
@@ -72,40 +80,21 @@
args.add(lookup);
args.add(bsci.invocationName());
args.add(bsci.invocationType());
- MethodHandle bsm = (MethodHandle) bsci.get(0);
- List restOfArgs = bsci.asList().subList(1, bsci.size();
+ MethodHandle bsm = (MethodHandle) bsci.argument(0);
+ List restOfArgs = bsci.argumentList().subList(1, bsci.argumentCount());
// the next line eagerly resolves all remaining static arguments:
args.addAll(restOfArgs);
return bsm.invokeWithArguments(args);
}
* }
*
- *
- * In the other direction, here is a combinator which pops
- * a pull-mode bootstrap method from the beginning of a list of
- * static argument values (already resolved), reformats all of
- * the arguments into a pair of a lookup and a {@code BootstrapCallInfo},
- * and invokes the popped method. Again the callee has no way of
- * telling it was not called directly by the JVM, except that
- * all of the constant values will appear as resolved.
- * Put another way, if any constant fails to resolve, the
- * callee will not be able to catch the resulting error,
- * since the error will be thrown by the JVM before the
- * bootstrap method is entered.
- *
{@code
-static Object genericBSM(Lookup lookup, String name, Object type,
- MethodHandle bsm, Object... args)
- throws Throwable {
- ConstantGroup cons = ConstantGroup.makeConstantGroup(Arrays.asList(args));
- BootstrapCallInfo bsci = makeBootstrapCallInfo(bsm, name, type, cons);
- return bsm.invoke(lookup, bsci);
-}
- * }
+ * @param the type {@code MethodType} or {@code Class}
*
- * @since 1.10
+ * @since 12
*/
// public
-interface BootstrapCallInfo extends ConstantGroup {
+interface BootstrapCallInfo> {
+
/** Returns the bootstrap method for this call.
* @return the bootstrap method
*/
@@ -121,22 +110,342 @@
*/
T invocationType();
+ /** Returns a ConstantDesc for the method type or constant type for this call.
+ * Does not (by itself) trigger resolution of this type.
+ * @return the method type or constant type
+ */
+ ConstantDesc invocationTypeDesc();
+
+ /**
+ * Returns the number of static arguments.
+ * @return the number of static arguments
+ */
+ int argumentCount();
+
+ /**
+ * Returns the selected static argument, resolving it if necessary.
+ * Throws a linkage error if resolution proves impossible.
+ * @param index which static argument to select
+ * @return the selected static argument
+ * @throws LinkageError if the selected static argument needs resolution
+ * and cannot be resolved
+ */
+ Object argument(int index) throws LinkageError;
+
+ /**
+ * Returns the selected static argument,
+ * or the given sentinel value if there is none available.
+ * If the static argument cannot be resolved, the sentinel will be returned.
+ * If the static argument can (perhaps) be resolved, but has not yet been resolved,
+ * then the sentinel may be returned, at the implementation's discretion.
+ * To force resolution (and a possible exception), call {@link #argument(int)}.
+ * @param index which static argument to select
+ * @param ifNotPresent the sentinel value to return if the static argument is not present
+ * @return the selected static argument, if available, else the sentinel value
+ */
+ default Object argument(int index, Object ifNotPresent) {
+ if (argumentIsPresent(index)) return argument(index);
+ return ifNotPresent;
+ }
+
+ /**
+ * Returns a symbolic reference underlying the selected static argument,
+ * if it is available.
+ * @param index which static argument to select
+ * @return a symbolic reference underlying the selected static argument
+ * @throws IllegalArgumentException if the original symbolic reference is not available,
+ * and a substitute cannot be created on the fly by {@link ConstantDesc}.
+ */
+ ConstantDesc> argumentDesc(int index);
+
+ /**
+ * Returns an indication of whether a static argument may be available.
+ * If it returns {@code true}, it will always return true in the future,
+ * and a call to {@link #argument(int)} will never throw an exception.
+ *
+ * After a normal return from {@link #argument(int)} or a present
+ * value is reported from {@link #argument(int,Object)}, this method
+ * must always return true.
+ *
+ * If this method returns {@code false}, nothing in particular
+ * can be inferred, since the query only concerns the internal
+ * logic of the {@code BootstrapCallInfo} object which ensures that a
+ * successful query to a constant will always remain successful.
+ * The only way to force a permanent decision about whether
+ * a static argument is available is to call {@link #argument(int)} and
+ * be ready for an exception if the constant is unavailable.
+ * @param index which constant to select
+ * @return {@code true} if the selected static argument is known by
+ * this object to be present, {@code false} if it is known
+ * not to be present or
+ */
+ boolean argumentIsPresent(int index);
+
+
+ /// Views
+
+ /**
+ * Create a view on the static arguments as a {@link List} view.
+ * Any request for a static argument through this view will
+ * force resolution, and may therefore cause a {@code LinkageError}.
+ * @return a {@code List} view on the static arguments which will force resolution
+ */
+ default List argumentList() {
+ return new ArgList(this, 0, argumentCount());
+ }
+
+ /**
+ * Create a view on the static arguments as a {@link List} view.
+ * Any request for a static argument through this view will
+ * return the given sentinel value, if the corresponding
+ * call to {@link #argument(int,Object)} would do so.
+ * @param ifNotPresent the sentinel value to return if a static argument is not present
+ * @return a {@code List} view on the static arguments which will not force resolution
+ */
+ default List argumentList(Object ifNotPresent) {
+ return new ArgList(this, 0, argumentCount(), ifNotPresent);
+ }
+
+ /**
+ * Create a view on the symbolic references of the static arguments as a {@link List} view.
+ * @return a {@code List} view on this group's symbolic references
+ */
+ default List> argumentDescList() {
+ List syms = new ArgList(this, true, 0, argumentCount());
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ List> result = (List) syms;
+ return result;
+ }
+
+ /// Factories and helper methods
+
+ /**
+ * Invoke a bootstrap method handle with arguments obtained by resolving
+ * the sequence of constants supplied by a given bootstrap call descriptor,
+ * {@code bci}.
+ * The first argument to the method will be {@code lookup}.
+ * The second argument will be the invocation name of {@code bci}.
+ * The third argument will be the invocation type of {@code bci}.
+ * The fourth and subsequent arguments (if any) will be the resolved
+ * constants, in order, supplied by {@code bci}.
+ *
+ * @apiNote
+ * This method behaves like the following but may be more optimal:
+ *
{@code
+ * ArrayList args = new ArrayList<>();
+ * args.add(lookup);
+ * args.add(name);
+ * args.add(type);
+ * args.addAll(staticArgs);
+ * return handle.invokeWithArguments(args);
+ * }
+ *
+ * @param handle the bootstrap method handle to be invoked on the static arguments
+ * @param lookup the lookup
+ * @param name the name associated with the constant or call site being linked
+ * @param type the type associated with the constant or call site being linked
+ * @param staticArgs the static argument list
+ * @return the result of invocation
+ * @throws Throwable if an error occurs when resolving the constants from
+ * the bootstrap call descriptor or invoking the method handle
+ */
+ static Object invokeWithMetadata(MethodHandle handle,
+ MethodHandles.Lookup lookup,
+ String name,
+ TypeDescriptor type,
+ List staticArgs)
+ throws Throwable {
+ Objects.requireNonNull(lookup);
+ return invokeCommon(handle, lookup, name, type, null, staticArgs);
+ }
+
/**
- * Make a new bootstrap call descriptor with the given components.
- * @param bsm bootstrap method
- * @param name invocation name
- * @param type invocation type
- * @param constants the additional static arguments for the bootstrap method
- * @param the type of the invocation type, either {@link MethodHandle} or {@link Class}
- * @return a new bootstrap call descriptor with the given components
- */
- static BootstrapCallInfo makeBootstrapCallInfo(MethodHandle bsm,
- String name,
- T type,
- ConstantGroup constants) {
- AbstractConstantGroup.BSCIWithCache bsci = new AbstractConstantGroup.BSCIWithCache<>(bsm, name, type, constants.size());
- final Object NP = AbstractConstantGroup.BSCIWithCache.NOT_PRESENT;
- bsci.initializeCache(constants.asList(NP), NP);
- return bsci;
+ * Invoke a bootstrap method handle with arguments obtained by resolving
+ * the sequence of constants supplied by a given bootstrap call descriptor,
+ * {@code bci}.
+ * The first argument to the method will be {@code lookup}.
+ * The second argument will be the invocation name of {@code bci}.
+ * The third argument will be the invocation type of {@code bci}.
+ * The fourth and subsequent arguments (if any) will be the resolved
+ * constants, in order, supplied by {@code bci}.
+ *
+ * @apiNote
+ * This method behaves like the following but may be more optimal:
+ *
{@code
+ * ArrayList args = new ArrayList<>();
+ * args.add(lookup);
+ * args.add(name);
+ * args.add(type);
+ * args.addAll(Arrays.asList(staticArgs));
+ * return handle.invokeWithArguments(args);
+ * }
+ *
+ * @param handle the bootstrap method handle to be invoked on the static arguments
+ * @param lookup the lookup
+ * @param name the name associated with the constant or call site being linked
+ * @param type the type associated with the constant or call site being linked
+ * @param staticArgs the static argument list
+ * @return the result of invocation
+ * @throws Throwable if an error occurs when resolving the constants from
+ * the bootstrap call descriptor or invoking the method handle
+ */
+ static Object invokeWithMetadata(MethodHandle handle,
+ MethodHandles.Lookup lookup,
+ String name,
+ TypeDescriptor type,
+ Object... staticArgs)
+ throws Throwable {
+ Objects.requireNonNull(lookup);
+ Objects.requireNonNull(staticArgs);
+ return invokeCommon(handle, lookup, name, type, staticArgs, null);
+ }
+
+ /**
+ * Invoke a bootstrap method handle with arguments obtained by resolving
+ * the sequence of constants supplied by a given bootstrap call descriptor,
+ * {@code bci}.
+ * The first argument to the method will be {@code lookup}.
+ * The second argument will be the invocation name of {@code bci}.
+ * The third argument will be the invocation type of {@code bci}.
+ * The fourth and subsequent arguments (if any) will be the resolved
+ * constants, in order, supplied by {@code bci}.
+ *
+ * @apiNote
+ * This method behaves like the following but may be more optimal:
+ *
{@code
+ * ArrayList args = new ArrayList<>();
+ * args.add(lookup);
+ * args.add(bci.invocationName());
+ * args.add(bci.invocationType());
+ * args.addAll(bci.argumentList());
+ * return handle.invokeWithArguments(args);
+ * }
+ *
+ * @param handle the bootstrap method handle to be invoked with resolved
+ * constants supplied by {@code bci}
+ * @param lookup the lookup
+ * @param bsci the bootstrap call descriptor
+ * @return the result of invocation
+ * @throws Throwable if an error occurs when resolving the constants from
+ * the bootstrap call descriptor or invoking the method handle
+ */
+ static Object invokeWithMetadata(MethodHandle handle,
+ MethodHandles.Lookup lookup,
+ BootstrapCallInfo> bsci)
+ throws Throwable {
+ Objects.requireNonNull(lookup);
+ return invokeCommon(handle, lookup, bsci.invocationName(), bsci.invocationType(), AbstractBootstrapCallInfo.maybeShareArguments(bsci), bsci.argumentList());
+ }
+
+ /**
+ * Convert the result returned by a bootstrap method to the class
+ * required by the bootstrap method's {@code invocationType}.
+ *
+ * @param bsci the bootstrap call descriptor
+ * @param result the result to be converted
+ * @param the type {@code MethodType} or {@code Class}
+ * @return the converted result
+ * @throws ClassCastException if a value conversion error occurs during conversion
+ * @throws WrongMethodTypeException if a method type mismatch is detected occurs during conversion
+ */
+ static
+ >
+ Object convertResult(BootstrapCallInfo bsci, Object result) {
+ // FIXME: If invocationType cannot be resolved, some results are still valid.
+ return convertResult(bsci.invocationType(), bsci.bootstrapMethod().type().returnType(), result);
+ }
+
+ /**
+ * Convert the result returned by a bootstrap method to the class
+ * required by the bootstrap method's {@code invocationType}.
+ *
+ * @param type the method type or constant type to be obtained
+ * @param resultType the type of the result (return type of the BSM that returned the result)
+ * @param result the result to be converted
+ * @param the type {@code MethodType} or {@code Class}
+ * @return the converted result
+ * @throws ClassCastException if a value conversion error occurs during conversion
+ * @throws WrongMethodTypeException if a method type mismatch is detected occurs during conversion
+ */
+ static
+ >
+ Object convertResult(T type, Class> resultType, Object result) {
+ Class> resultClass;
+ boolean isFieldType;
+ if (type instanceof Class) {
+ isFieldType = true;
+ resultClass = (Class>) type;
+ } else {
+ isFieldType = false;
+ resultClass = null;
+ }
+ if (isFieldType && resultClass.isPrimitive()) {
+ Class> wrapperClass = Wrapper.asWrapperType(resultClass);
+ if (result.getClass() == wrapperClass) {
+ return result; // fast path
+ }
+ // Non-reference conversions are more than just plain casts.
+ // By pushing the value through a funnel of the form (R x)->x,
+ // the boxed result can be widened as needed. See MH::asType.
+ MethodHandle funnel = MethodHandles.identity(resultClass);
+ if (!MethodType.canConvert(resultType, resultClass)) {
+ // Example: Result type is Integer and resultClass is short.
+ funnel = funnel.asType(MethodType.methodType(resultClass, resultType));
+ assert(false); // should have thrown WMTE
+ }
+ try {
+ result = funnel.invoke(result);
+ } catch (ClassCastException | WrongMethodTypeException ex) {
+ throw ex;
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ // Now it is the wrapper type for resultType.
+ assert(result.getClass() == Wrapper.asWrapperType(resultClass));
+ return result;
+ } else if (isFieldType) {
+ // A reference type.
+ return resultClass.cast(result);
+ } else {
+ // Check or convert the method-like result.
+ MethodType mt = (MethodType) type; // must be either Class or MethodType
+ if (result instanceof CallSite) {
+ CallSite cs = (CallSite) result;
+ if (!cs.type().equals(mt))
+ throw CallSite.wrongTargetType(cs, mt);
+ return cs; // no conversion, just checking
+ }
+ if (result instanceof MethodHandle) {
+ // Method handles can be converted on the fly:
+ MethodHandle mh = (MethodHandle) result;
+ return mh.asType(mt);
+ }
+ throw new ClassCastException("CallSite bootstrap method failed to produce an instance of CallSite");
+ }
+ }
+
+ /**
+ * Produce a string that briefly reports the BSM, name, type,
+ * and argument list. For arguments, use a non-resolving list view,
+ * with unresolved elements being presented as asterisks.
+ * @param bsci the object to produce a string for
+ * @return {@code this.asList("*").toString()}
+ */
+ public static String toString(BootstrapCallInfo> bsci) {
+ MethodHandle bsm = bsci.bootstrapMethod();
+ MemberName mem = bsm.internalMemberName();
+ Object bsmStr = bsm;
+ if (mem != null) bsmStr = mem;
+ //bsmStr = bsm.describeConstable()
+ Object typeStr = null;
+ try {
+ typeStr = bsci.invocationType();
+ } catch (LinkageError ex) {
+ typeStr = bsci.invocationTypeDesc();
+ }
+ return (bsmStr
+ + "/" + bsci.invocationName()
+ + ":" + typeStr
+ + bsci.argumentList("*"));
}
}