1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.invoke;
  27 
  28 import java.lang.invoke.MethodHandles.Lookup;
  29 import java.util.Arrays;
  30 import java.util.List;
  31 
  32 /**
  33  * An interface providing full static information about a particular
  34  * call to a
  35  * <a href="package-summary.html#bsm">bootstrap method</a> of an
  36  * dynamic call site or dynamic constant.
  37  * This information includes the method itself, the associated
  38  * name and type, and any associated static arguments.
  39  * <p>
  40  * If a bootstrap method declares exactly two arguments, and is
  41  * not of variable arity, then it is fed only two arguments by
  42  * the JVM, the {@linkplain Lookup lookup object} and an instance
  43  * of {@code BootstrapCallInfo} which supplies the rest of the
  44  * information about the call.
  45  * <p>
  46  * The API for accessing the static arguments allows the bootstrap
  47  * method to reorder the resolution (in the constant pool) of the
  48  * static arguments, and to catch errors resulting from the resolution.
  49  * This mode of evaluation <em>pulls</em> bootstrap parameters from
  50  * the JVM under control of the bootstrap method, as opposed to
  51  * the JVM <em>pushing</em> parameters to a bootstrap method
  52  * by resolving them all before the bootstrap method is called.
  53  * @apiNote
  54  * <p>
  55  * The {@linkplain Lookup lookup object} is <em>not</em> included in this
  56  * bundle of information, so as not to obscure the access control
  57  * logic of the program.
  58  * In cases where there are many thousands of parameters, it may
  59  * be preferable to pull their resolved values, either singly or in
  60  * batches, rather than wait until all of them have been resolved
  61  * before a constant or call site can be used.
  62  * <p>
  63  * A push mode bootstrap method can be adapted to a pull mode
  64  * bootstrap method, and vice versa.  For example, this generic
  65  * adapter pops a push-mode bootstrap method from the beginning
  66  * of the static argument list, eagerly resolves all the remaining
  67  * static arguments, and invokes the popped method in push mode.
  68  * The callee has no way of telling that it was not called directly
  69  * from the JVM.
  70  * <blockquote><pre>{@code
  71 static Object genericBSM(Lookup lookup, BootstrapCallInfo<Object> bsci)
  72     throws Throwable {
  73   ArrayList<Object> args = new ArrayList<>();
  74   args.add(lookup);
  75   args.add(bsci.invocationName());
  76   args.add(bsci.invocationType());
  77   MethodHandle bsm = (MethodHandle) bsci.get(0);
  78   List<Object> restOfArgs = bsci.asList().subList(1, bsci.size());
  79   // the next line eagerly resolves all remaining static arguments:
  80   args.addAll(restOfArgs);
  81   return bsm.invokeWithArguments(args);
  82 }
  83  * }</pre></blockquote>
  84  *
  85  * <p>
  86  * In the other direction, here is a combinator which pops
  87  * a pull-mode bootstrap method from the beginning of a list of
  88  * static argument values (already resolved), reformats all of
  89  * the arguments into a pair of a lookup and a {@code BootstrapCallInfo},
  90  * and invokes the popped method.  Again the callee has no way of
  91  * telling it was not called directly by the JVM, except that
  92  * all of the constant values will appear as resolved.
  93  * Put another way, if any constant fails to resolve, the
  94  * callee will not be able to catch the resulting error,
  95  * since the error will be thrown by the JVM before the
  96  * bootstrap method is entered.
  97  * <blockquote><pre>{@code
  98 static Object genericBSM(Lookup lookup, String name, Object type,
  99                          MethodHandle bsm, Object... args)
 100     throws Throwable {
 101   BootstrapCallInfo<Object> bsci = makeBootstrapCallInfo(bsm, name, type, args);

 102   return bsm.invoke(lookup, bsci);
 103 }
 104  * }</pre></blockquote>
 105  *
 106  * @since 11
 107  */
 108 public
 109 interface BootstrapCallInfo<T> extends ConstantGroup {
 110 
 111     /// Access
 112 
 113     /**
 114      * Returns the number of static arguments.
 115      * @return the number of static arguments
 116      */
 117     int size();
 118 
 119     /**
 120      * Returns the selected static argument, resolving it if necessary.
 121      * Throws a linkage error if resolution proves impossible.
 122      * @param index which static argument to select
 123      * @return the selected static argument
 124      * @throws LinkageError if the selected static argument needs resolution
 125      * and cannot be resolved
 126      */
 127     Object get(int index) throws LinkageError;
 128 
 129     /**
 130      * Returns the selected static argument,
 131      * or the given sentinel value if there is none available.
 132      * If the static argument cannot be resolved, the sentinel will be returned.
 133      * If the static argument can (perhaps) be resolved, but has not yet been resolved,
 134      * then the sentinel <em>may</em> be returned, at the implementation's discretion.
 135      * To force resolution (and a possible exception), call {@link #get(int)}.
 136      * @param index the selected constant
 137      * @param ifNotPresent the sentinel value to return if the static argument is not present
 138      * @return the selected static argument, if available, else the sentinel value
 139      */
 140     Object get(int index, Object ifNotPresent);
 141 
 142     /**
 143      * Returns an indication of whether a static argument may be available.
 144      * If it returns {@code true}, it will always return true in the future,
 145      * and a call to {@link #get(int)} will never throw an exception.
 146      * <p>
 147      * After a normal return from {@link #get(int)} or a present
 148      * value is reported from {@link #get(int,Object)}, this method
 149      * must always return true.
 150      * <p>
 151      * If this method returns {@code false}, nothing in particular
 152      * can be inferred, since the query only concerns the internal
 153      * logic of the {@code BootstrapCallInfo} object which ensures that a
 154      * successful query to a constant will always remain successful.
 155      * The only way to force a permanent decision about whether
 156      * a static argument is available is to call {@link #get(int)} and
 157      * be ready for an exception if the constant is unavailable.
 158      * @param index the selected constant
 159      * @return {@code true} if the selected static argument is known by
 160      *     this object to be present, {@code false} if it is known
 161      *     not to be present or
 162      */
 163     boolean isPresent(int index);
 164 
 165 
 166     /// Views
 167 
 168     /**
 169      * Create a view on the static arguments as a {@link List} view.
 170      * Any request for a static argument through this view will
 171      * force resolution.
 172      * @return a {@code List} view on the static arguments which will force resolution
 173      */
 174     default List<Object> asList() {
 175         return new AbstractConstantGroup.AsList(this, 0, size());
 176     }
 177 
 178     /**
 179      * Create a view on the static argument as a {@link List} view.
 180      * Any request for a static argument through this view will
 181      * return the given sentinel value, if the corresponding
 182      * call to {@link #get(int,Object)} would do so.
 183      * @param ifNotPresent the sentinel value to return if a static argument is not present
 184      * @return a {@code List} view on the static arguments which will not force resolution
 185      */
 186     default List<Object> asList(Object ifNotPresent) {
 187         return new AbstractConstantGroup.AsList(this, 0, size(), ifNotPresent);
 188     }
 189 
 190 
 191     /// Bulk operations
 192 
 193     /**
 194      * Copy a sequence of static arguments into a given buffer.
 195      * This is equivalent to {@code end-offset} separate calls to {@code get},
 196      * for each index in the range from {@code offset} up to but not including {@code end}.
 197      * For the first static argument that cannot be resolved,
 198      * a {@code LinkageError} is thrown, but only after
 199      * preceding static arguments have been stored.
 200      * @param start index of first static argument to retrieve
 201      * @param end limiting index of static arguments to retrieve
 202      * @param buf array to receive the requested static arguments
 203      * @param pos position in the array to offset storing the static arguments
 204      * @return the limiting index, {@code end}
 205      * @throws LinkageError if a static argument cannot be resolved
 206      */
 207     default int copyArguments(int start, int end,
 208                               Object[] buf, int pos)
 209             throws LinkageError
 210     {
 211         int bufBase = pos - start;  // buf[bufBase + i] = get(i)
 212         for (int i = start; i < end; i++) {
 213             buf[bufBase + i] = get(i);
 214         }
 215         return end;
 216     }
 217 
 218     /**
 219      * Copy a sequence of static arguments into a given buffer.
 220      * This is equivalent to {@code end-offset} separate calls to {@code get},
 221      * for each index in the range from {@code offset} up to but not including {@code end}.
 222      * Any static arguments that cannot be resolved are replaced by the
 223      * given sentinel value.
 224      * @param start index of first static argument to retrieve
 225      * @param end limiting index of static arguments to retrieve
 226      * @param buf array to receive the requested values
 227      * @param pos position in the array to offset storing the static arguments
 228      * @param ifNotPresent sentinel value to store if a static argument is not available
 229      * @return the limiting index, {@code end}
 230      * @throws LinkageError if {@code resolve} is true and a static argument cannot be resolved
 231      */
 232     default int copyConstants(int start, int end,
 233                               Object[] buf, int pos,
 234                               Object ifNotPresent) {
 235         int bufBase = pos - start;  // buf[bufBase + i] = get(i)
 236         for (int i = start; i < end; i++) {
 237             buf[bufBase + i] = get(i, ifNotPresent);
 238         }
 239         return end;
 240     }
 241 
 242 
 243     /** Returns the bootstrap method for this call.
 244      * @return the bootstrap method
 245      */
 246     MethodHandle bootstrapMethod();
 247 
 248     /** Returns the method name or constant name for this call.
 249      * @return the method name or constant name
 250      */
 251     String invocationName();
 252 
 253     /** Returns the method type or constant type for this call.
 254      * @return the method type or constant type
 255      */
 256     T invocationType();
 257 
 258     /**
 259      * Make a new bootstrap call descriptor with the given components.
 260      * @param bsm bootstrap method
 261      * @param name invocation name
 262      * @param type invocation type
 263      * @param args the additional static arguments for the bootstrap method
 264      * @param <T> the type of the invocation type, either {@link MethodHandle} or {@link Class}
 265      * @return a new bootstrap call descriptor with the given components
 266      */
 267     static <T> BootstrapCallInfo<T> makeBootstrapCallInfo(MethodHandle bsm,
 268                                                           String name,
 269                                                           T type,
 270                                                           Object... args) {
 271         AbstractConstantGroup.BSCIWithCache<T> bsci = new AbstractConstantGroup.BSCIWithCache<>(bsm, name, type, args.length);
 272         final Object NP = AbstractConstantGroup.BSCIWithCache.NOT_PRESENT;
 273         bsci.initializeCache(Arrays.asList(args), NP);
 274         return bsci;
 275     }
 276 
 277     /**
 278      * Invoke a bootstrap method handle with arguments obtained by resolving
 279      * the sequence of constants supplied by a given bootstrap call descriptor,
 280      * {@code bci}.
 281      * The first argument to the method will be {@code lookup}.
 282      * The second argument will be the invocation name of {@code bci}.
 283      * The third argument will be the invocation type of {@code bci}.
 284      * The fourth and subsequent arguments (if any) will be the resolved
 285      * constants, in order, supplied by {@code bci}.
 286      * <p>
 287      * @apiNote
 288      * This method behaves like the following but may be more optimal:
 289      * <blockquote><pre>{@code
 290      *   ArrayList<Object> args = new ArrayList<>();
 291      *   args.add(lookup);
 292      *   args.add(bsci.invocationName());
 293      *   args.add(bsci.invocationType());
 294      *   List<Object> constantArgs = bsci.asList();
 295      *   args.addAll(constantArgs);
 296      *   return handle.invokeWithArguments(args);
 297      * }</pre></blockquote>
 298      *
 299      * @param handle the bootstrap method handle to be invoked with resolved
 300      *        constants supplied by {@code bci}
 301      * @param lookup the lookup
 302      * @param bsci the bootstrap call descriptor
 303      * @return the result of invocation
 304      * @throws Throwable if an error occurs when resolving the constants from
 305      *         the bootstrap call descriptor or invoking the method handle
 306      */
 307     // @@@ More stuff to add as api note
 308     // This method is static so that it's possible to look it up and bind
 309     // to a method handle, thereby viewing that method handle as if accepts
 310     // a BootstrapCallInfo
 311     static Object invokeFromCallInfoToArguments(MethodHandle handle,
 312                                                 MethodHandles.Lookup lookup,
 313                                                 BootstrapCallInfo<?> bsci) throws Throwable {
 314         int argc = bsci.size();
 315         switch (argc) {
 316             case 0:
 317                 return handle.invoke(lookup, bsci.invocationName(), bsci.invocationType());
 318             case 1:
 319                 return handle.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 320                                      bsci.get(0));
 321             case 2:
 322                 return handle.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 323                                      bsci.get(0), bsci.get(1));
 324             case 3:
 325                 return handle.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 326                                      bsci.get(0), bsci.get(1), bsci.get(2));
 327             case 4:
 328                 return handle.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 329                                      bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3));
 330             case 5:
 331                 return handle.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 332                                      bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4));
 333             case 6:
 334                 return handle.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 335                                      bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5));
 336             default:
 337                 final int NON_SPREAD_ARG_COUNT = 3;  // (lookup, name, type)
 338                 final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
 339                 if (argc >= MAX_SAFE_SIZE) {
 340                     // to be on the safe side, use invokeWithArguments which handles jumbo lists
 341                     Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argc];
 342                     newargv[0] = lookup;
 343                     newargv[1] = bsci.invocationName();
 344                     newargv[2] = bsci.invocationType();
 345                     bsci.copyArguments(0, argc, newargv, NON_SPREAD_ARG_COUNT);
 346                     return handle.invokeWithArguments(newargv);
 347                 }
 348                 MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argc);
 349                 MethodHandle typedBSM = handle.asType(invocationType);
 350                 MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
 351                 Object[] argv = new Object[argc];
 352                 bsci.copyArguments(0, argc, argv, 0);
 353                 return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv);
 354         }
 355     }
 356 
 357     /**
 358      * Invoke a bootstrap method handle with a bootstrap call descriptor
 359      * argument composed from a given sequence of arguments.
 360      * <p>
 361      * @apiNote
 362      * This method behaves like the following but may be more optimal:
 363      * <blockquote><pre>{@code
 364      *   BootstrapCallInfo<Object> bsci = makeBootstrapCallInfo(handle, name, type, args);
 365      *   return handle.invoke(lookup, bsci);
 366      * }</pre></blockquote>
 367      *
 368      * @param handle the bootstrap method handle to be invoked with a bootstrap
 369      *        call descriptor composed from the sequence of arguments
 370      * @param lookup the lookup
 371      * @param name the method name or constant name
 372      * @param type the method type or constant type
 373      * @param args the sequence of arguments
 374      * @return the result of invocation
 375      * @throws Throwable if an error occurs when invoking the method handle
 376      */
 377     static Object invokeFromArgumentsToCallInfo(MethodHandle handle,
 378                                                 MethodHandles.Lookup lookup,
 379                                                 String name,
 380                                                 Object type,
 381                                                 Object... args) throws Throwable {
 382         BootstrapCallInfo<?> bsci = makeBootstrapCallInfo(handle, name, type, args);
 383         return handle.invoke(lookup, bsci);
 384     }
 385 }
--- EOF ---