< prev index next >
src/java.base/share/classes/java/lang/invoke/BootstrapCallInfo.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
*** 23,40 ****
* questions.
*/
package java.lang.invoke;
import java.lang.invoke.MethodHandles.Lookup;
/**
* An interface providing full static information about a particular
* call to a
* <a href="package-summary.html#bsm">bootstrap method</a> of an
* dynamic call site or dynamic constant.
! * This information includes the method itself, the associated
* name and type, and any associated static arguments.
* <p>
* If a bootstrap method declares exactly two arguments, and is
* not of variable arity, then it is fed only two arguments by
* the JVM, the {@linkplain Lookup lookup object} and an instance
--- 23,48 ----
* questions.
*/
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
* <a href="package-summary.html#bsm">bootstrap method</a> of an
* dynamic call site or dynamic constant.
! * This information includes the bootstrap method itself, the associated
* name and type, and any associated static arguments.
* <p>
* If a bootstrap method declares exactly two arguments, and is
* not of variable arity, then it is fed only two arguments by
* the JVM, the {@linkplain Lookup lookup object} and an instance
*** 70,113 ****
throws Throwable {
ArrayList<Object> args = new ArrayList<>();
args.add(lookup);
args.add(bsci.invocationName());
args.add(bsci.invocationType());
! MethodHandle bsm = (MethodHandle) bsci.get(0);
! List<Object> restOfArgs = bsci.asList().subList(1, bsci.size();
// the next line eagerly resolves all remaining static arguments:
args.addAll(restOfArgs);
return bsm.invokeWithArguments(args);
}
* }</pre></blockquote>
*
! * <p>
! * 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.
! * <blockquote><pre>{@code
! static Object genericBSM(Lookup lookup, String name, Object type,
! MethodHandle bsm, Object... args)
! throws Throwable {
! ConstantGroup cons = ConstantGroup.makeConstantGroup(Arrays.asList(args));
! BootstrapCallInfo<Object> bsci = makeBootstrapCallInfo(bsm, name, type, cons);
! return bsm.invoke(lookup, bsci);
! }
! * }</pre></blockquote>
*
! * @since 1.10
*/
// public
! interface BootstrapCallInfo<T> extends ConstantGroup {
/** Returns the bootstrap method for this call.
* @return the bootstrap method
*/
MethodHandle bootstrapMethod();
--- 78,102 ----
throws Throwable {
ArrayList<Object> args = new ArrayList<>();
args.add(lookup);
args.add(bsci.invocationName());
args.add(bsci.invocationType());
! MethodHandle bsm = (MethodHandle) bsci.argument(0);
! List<Object> restOfArgs = bsci.argumentList().subList(1, bsci.argumentCount());
// the next line eagerly resolves all remaining static arguments:
args.addAll(restOfArgs);
return bsm.invokeWithArguments(args);
}
* }</pre></blockquote>
*
! * @param <T> the type {@code MethodType} or {@code Class}
*
! * @since 12
*/
// public
! interface BootstrapCallInfo<T extends TypeDescriptor & Constable<T>> {
!
/** Returns the bootstrap method for this call.
* @return the bootstrap method
*/
MethodHandle bootstrapMethod();
*** 119,142 ****
/** Returns the method type or constant type for this call.
* @return the method type or constant type
*/
T invocationType();
/**
! * 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 <T> the type of the invocation type, either {@link MethodHandle} or {@link Class}
! * @return a new bootstrap call descriptor with the given components
*/
! static <T> BootstrapCallInfo<T> makeBootstrapCallInfo(MethodHandle bsm,
String name,
! T type,
! ConstantGroup constants) {
! AbstractConstantGroup.BSCIWithCache<T> bsci = new AbstractConstantGroup.BSCIWithCache<>(bsm, name, type, constants.size());
! final Object NP = AbstractConstantGroup.BSCIWithCache.NOT_PRESENT;
! bsci.initializeCache(constants.asList(NP), NP);
! return bsci;
}
}
--- 108,451 ----
/** Returns the method type or constant type for this call.
* @return the method type or constant type
*/
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<T> 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 <em>may</em> 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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<Object> 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<Object> 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<ConstantDesc<?>> argumentDescList() {
+ List<Object> syms = new ArgList(this, true, 0, argumentCount());
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ List<ConstantDesc<?>> 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}.
+ * <p>
+ * @apiNote
+ * This method behaves like the following but may be more optimal:
+ * <blockquote><pre>{@code
+ * ArrayList<Object> args = new ArrayList<>();
+ * args.add(lookup);
+ * args.add(name);
+ * args.add(type);
+ * args.addAll(staticArgs);
+ * return handle.invokeWithArguments(args);
+ * }</pre></blockquote>
+ *
+ * @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<Object> staticArgs)
+ throws Throwable {
+ Objects.requireNonNull(lookup);
+ return invokeCommon(handle, lookup, name, type, null, staticArgs);
+ }
+
/**
! * 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}.
! * <p>
! * @apiNote
! * This method behaves like the following but may be more optimal:
! * <blockquote><pre>{@code
! * ArrayList<Object> args = new ArrayList<>();
! * args.add(lookup);
! * args.add(name);
! * args.add(type);
! * args.addAll(Arrays.asList(staticArgs));
! * return handle.invokeWithArguments(args);
! * }</pre></blockquote>
! *
! * @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}.
! * <p>
! * @apiNote
! * This method behaves like the following but may be more optimal:
! * <blockquote><pre>{@code
! * ArrayList<Object> args = new ArrayList<>();
! * args.add(lookup);
! * args.add(bci.invocationName());
! * args.add(bci.invocationType());
! * args.addAll(bci.argumentList());
! * return handle.invokeWithArguments(args);
! * }</pre></blockquote>
! *
! * @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 <T> 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
! <T extends TypeDescriptor & Constable<T>>
! Object convertResult(BootstrapCallInfo<T> 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 <T> 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
! <T extends TypeDescriptor & Constable<T>>
! 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("*"));
}
}
< prev index next >