Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8267267

Add a MethodHandle combinator that switches over a set of MethodHandles

    XMLWordPrintable

    Details

    • Type: CSR
    • Status: Closed
    • Priority: P3
    • Resolution: Approved
    • Fix Version/s: 17
    • Component/s: core-libs
    • Labels:
    • Subcomponent:
    • Compatibility Risk:
      minimal
    • Compatibility Risk Description:
      Minimal to no risk. It's a new API
    • Interface Kind:
      Java API
    • Scope:
      SE

      Description

      Summary

      Add a tableSwitch method handle combinator in java.lang.invoke.MethodHandles that emulates the tableswitch bytecode.

      Problem

      A method handle API primitive that can emulate the tableswitch byte code is currently missing.

      It is only possible to switch between two method handles using the guardWithTest combinator, with a boolean used as selector value. Multiple of these guardWithTest handles could be chained together to achieve a functionally similar result as the proposed combinator, but constructing such chains is much more cumbersome, as well as having additional overhead due to needing many method handles instead of just one, and the different way the JIT optimizes this case compared to the tableswitch byte code.

      Looking a method handle up in an array using an index and then invoking it is also a possibility, but because in that case the method handle is non-constant, the call can not be inlined.

      Solution

      Add a tableSwitch combinator that can be used to create a method handle to switch over a list of method handles using an index.

      Using the tableswitch bytecode in the backing lambda form, with an individual call along each switch case, will make sure that each method handle can be inlined.

      Specification

      /**
       * Creates a table switch method handle, which can be used to switch over a set of target
       * method handles, based on a given target index, called selector.
       * <p>
       * For a selector value of {@code n}, where {@code n} falls in the range {@code [0, N)},
       * and where {@code N} is the number of target method handles, the table switch method
       * handle will invoke the n-th target method handle from the list of target method handles.
       * <p>
       * For a selector value that does not fall in the range {@code [0, N)}, the table switch
       * method handle will invoke the given fallback method handle.
       * <p>
       * All method handles passed to this method must have the same type, with the additional
       * requirement that the leading parameter be of type {@code int}. The leading parameter
       * represents the selector.
       * <p>
       * Any trailing parameters present in the type will appear on the returned table switch
       * method handle as well. Any arguments assigned to these parameters will be forwarded,
       * together with the selector value, to the selected method handle when invoking it.
       *
       * @apiNote Example:
       * The cases each drop the {@code selector} value they are given, and take an additional
       * {@code String} argument, which is concatenated (using {@link String#concat(String)})
       * to a specific constant label string for each case:
       * <blockquote><pre>{@code
       * MethodHandles.Lookup lookup = MethodHandles.lookup();
       * MethodHandle caseMh = lookup.findVirtual(String.class, "concat",
       *         MethodType.methodType(String.class, String.class));
       * caseMh = MethodHandles.dropArguments(caseMh, 0, int.class);
       *
       * MethodHandle caseDefault = MethodHandles.insertArguments(caseMh, 1, "default: ");
       * MethodHandle case0 = MethodHandles.insertArguments(caseMh, 1, "case 0: ");
       * MethodHandle case1 = MethodHandles.insertArguments(caseMh, 1, "case 1: ");
       *
       * MethodHandle mhSwitch = MethodHandles.tableSwitch(
       *     caseDefault,
       *     case0,
       *     case1
       * );
       *
       * assertEquals("default: data", (String) mhSwitch.invokeExact(-1, "data"));
       * assertEquals("case 0: data", (String) mhSwitch.invokeExact(0, "data"));
       * assertEquals("case 1: data", (String) mhSwitch.invokeExact(1, "data"));
       * assertEquals("default: data", (String) mhSwitch.invokeExact(2, "data"));
       * }</pre></blockquote>
       *
       * @param fallback the fallback method handle that is called when the selector is not
       *                 within the range {@code [0, N)}.
       * @param targets array of target method handles.
       * @return the table switch method handle.
       * @throws NullPointerException if {@code fallback}, the {@code targets} array, or any
       *                              any of the elements of the {@code targets} array are
       *                              {@code null}.
       * @throws IllegalArgumentException if the {@code targets} array is empty, if the leading
       *                                  parameter of the fallback handle or any of the target
       *                                  handles is not {@code int}, or if the types of
       *                                  the fallback handle and all of target handles are
       *                                  not the same.
       */
      public static MethodHandle tableSwitch(MethodHandle fallback, MethodHandle... targets) {

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              pgundarlahal Prashanthram Gundarlahally
              Reporter:
              jvernee Jorn Vernee
              Reviewed By:
              Claes Redestad
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: