/* * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * 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 * bootstrap method 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. *

* 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 * of {@code BootstrapCallInfo} which supplies the rest of the * information about the call. *

* The API for accessing the static arguments allows the bootstrap * method to reorder the resolution (in the constant pool) of the * static arguments, and to catch errors resulting from the resolution. * This mode of evaluation pulls bootstrap parameters from * the JVM under control of the bootstrap method, as opposed to * the JVM pushing parameters to a bootstrap method * by resolving them all before the bootstrap method is called. * @apiNote *

* The {@linkplain Lookup lookup object} is not included in this * bundle of information, so as not to obscure the access control * logic of the program. * In cases where there are many thousands of parameters, it may * be preferable to pull their resolved values, either singly or in * batches, rather than wait until all of them have been resolved * before a constant or call site can be used. *

* A push mode bootstrap method can be adapted to a pull mode * bootstrap method, and vice versa. For example, this generic * adapter pops a push-mode bootstrap method from the beginning * of the static argument list, eagerly resolves all the remaining * static arguments, and invokes the popped method in push mode. * The callee has no way of telling that it was not called directly * from the JVM. *

{@code
static Object genericBSM(Lookup lookup, BootstrapCallInfo bsci)
    throws Throwable {
  ArrayList args = new ArrayList<>();
  args.add(lookup);
  args.add(bsci.invocationName());
  args.add(bsci.invocationType());
  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);
}
 * }
 *
 * @param  the type {@code MethodType} or {@code Class}
 *
 * @since 12
 */
// public
interface BootstrapCallInfo> {

    /** Returns the bootstrap method for this call.
     * @return the bootstrap method
     */
    MethodHandle bootstrapMethod();

    /** Returns the method name or constant name for this call.
     * @return the method name or constant name
     */
    String invocationName();

    /** 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 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);
    }

    /**
     * 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("*"));
    }
}