< 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 >