< prev index next >
src/java.base/share/classes/java/lang/invoke/MethodHandles.java
Print this page
rev 13106 : 8139885: implement JEP 274: enhanced method handles
*** 24,51 ****
*/
package java.lang.invoke;
import java.lang.reflect.*;
! import java.util.BitSet;
! import java.util.List;
! import java.util.Arrays;
! import java.util.Objects;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.VerifyAccess;
import sun.invoke.util.Wrapper;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.reflect.misc.ReflectUtil;
import sun.security.util.SecurityConstants;
import java.lang.invoke.LambdaForm.BasicType;
! import static java.lang.invoke.LambdaForm.BasicType.*;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* This class consists exclusively of static methods that operate on or return
* method handles. They fall into several categories:
* <ul>
--- 24,50 ----
*/
package java.lang.invoke;
import java.lang.reflect.*;
! import java.util.*;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.VerifyAccess;
import sun.invoke.util.Wrapper;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.reflect.misc.ReflectUtil;
import sun.security.util.SecurityConstants;
import java.lang.invoke.LambdaForm.BasicType;
!
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import java.util.concurrent.ConcurrentHashMap;
+ import java.util.stream.Collectors;
+ import java.util.stream.Stream;
/**
* This class consists exclusively of static methods that operate on or return
* method handles. They fall into several categories:
* <ul>
*** 174,184 ****
* use cases for methods, constructors, and fields.
* Each method handle created by a factory method is the functional
* equivalent of a particular <em>bytecode behavior</em>.
* (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.)
* Here is a summary of the correspondence between these factory methods and
! * the behavior the resulting method handles:
* <table border=1 cellpadding=5 summary="lookup method behaviors">
* <tr>
* <th><a name="equiv"></a>lookup expression</th>
* <th>member</th>
* <th>bytecode behavior</th>
--- 173,183 ----
* use cases for methods, constructors, and fields.
* Each method handle created by a factory method is the functional
* equivalent of a particular <em>bytecode behavior</em>.
* (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.)
* Here is a summary of the correspondence between these factory methods and
! * the behavior of the resulting method handles:
* <table border=1 cellpadding=5 summary="lookup method behaviors">
* <tr>
* <th><a name="equiv"></a>lookup expression</th>
* <th>member</th>
* <th>bytecode behavior</th>
*** 233,242 ****
--- 232,245 ----
* </tr>
* <tr>
* <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}</td>
* <td>({@code static})?<br>{@code T m(A*);}</td><td>{@code (T) aMethod.invoke(thisOrNull, arg*);}</td>
* </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#findClass lookup.findClass("C")}</td>
+ * <td>{@code class C { ... }}</td><td>{@code C.class;}</td>
+ * </tr>
* </table>
*
* Here, the type {@code C} is the class or interface being searched for a member,
* documented as a parameter named {@code refc} in the lookup methods.
* The method type {@code MT} is composed from the return type {@code T}
*** 253,262 ****
--- 256,269 ----
* stands for a null reference if the accessed method or field is static,
* and {@code this} otherwise.
* The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand
* for reflective objects corresponding to the given members.
* <p>
+ * The bytecode behavior for a {@code findClass} operation is a load of a constant class,
+ * as if by {@code ldc CONSTANT_Class}.
+ * The behavior is represented, not as a method handle, but directly as a {@code Class} constant.
+ * <p>
* In cases where the given member is of variable arity (i.e., a method or constructor)
* the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}.
* In all other cases, the returned method handle will be of fixed arity.
* <p style="font-size:smaller;">
* <em>Discussion:</em>
*** 421,440 ****
* to allow applications to check such cross-loader references.
* These checks apply to both the {@code MethodHandles.Lookup} API
* and the Core Reflection API
* (as found on {@link java.lang.Class Class}).
* <p>
! * If a security manager is present, member lookups are subject to
* additional checks.
* From one to three calls are made to the security manager.
* Any of these calls can refuse access by throwing a
* {@link java.lang.SecurityException SecurityException}.
* Define {@code smgr} as the security manager,
* {@code lookc} as the lookup class of the current lookup object,
* {@code refc} as the containing class in which the member
* is being sought, and {@code defc} as the class in which the
* member is actually defined.
* The value {@code lookc} is defined as <em>not present</em>
* if the current lookup object does not have
* <a href="MethodHandles.Lookup.html#privacc">private access</a>.
* The calls are made according to the following rules:
* <ul>
--- 428,449 ----
* to allow applications to check such cross-loader references.
* These checks apply to both the {@code MethodHandles.Lookup} API
* and the Core Reflection API
* (as found on {@link java.lang.Class Class}).
* <p>
! * If a security manager is present, member and class lookups are subject to
* additional checks.
* From one to three calls are made to the security manager.
* Any of these calls can refuse access by throwing a
* {@link java.lang.SecurityException SecurityException}.
* Define {@code smgr} as the security manager,
* {@code lookc} as the lookup class of the current lookup object,
* {@code refc} as the containing class in which the member
* is being sought, and {@code defc} as the class in which the
* member is actually defined.
+ * (If a class or other type is being accessed,
+ * the {@code refc} and {@code defc} values are the class itself.)
* The value {@code lookc} is defined as <em>not present</em>
* if the current lookup object does not have
* <a href="MethodHandles.Lookup.html#privacc">private access</a>.
* The calls are made according to the following rules:
* <ul>
*** 442,468 ****
* If {@code lookc} is not present, or if its class loader is not
* the same as or an ancestor of the class loader of {@code refc},
* then {@link SecurityManager#checkPackageAccess
* smgr.checkPackageAccess(refcPkg)} is called,
* where {@code refcPkg} is the package of {@code refc}.
! * <li><b>Step 2:</b>
* If the retrieved member is not public and
* {@code lookc} is not present, then
* {@link SecurityManager#checkPermission smgr.checkPermission}
* with {@code RuntimePermission("accessDeclaredMembers")} is called.
* <li><b>Step 3:</b>
* If the retrieved member is not public,
* and if {@code lookc} is not present,
* and if {@code defc} and {@code refc} are different,
* then {@link SecurityManager#checkPackageAccess
* smgr.checkPackageAccess(defcPkg)} is called,
* where {@code defcPkg} is the package of {@code defc}.
* </ul>
* Security checks are performed after other access checks have passed.
! * Therefore, the above rules presuppose a member that is public,
* or else that is being accessed from a lookup class that has
! * rights to access the member.
*
* <h1><a name="callsens"></a>Caller sensitive methods</h1>
* A small number of Java methods have a special property called caller sensitivity.
* A <em>caller-sensitive</em> method can behave differently depending on the
* identity of its immediate caller.
--- 451,482 ----
* If {@code lookc} is not present, or if its class loader is not
* the same as or an ancestor of the class loader of {@code refc},
* then {@link SecurityManager#checkPackageAccess
* smgr.checkPackageAccess(refcPkg)} is called,
* where {@code refcPkg} is the package of {@code refc}.
! * <li><b>Step 2a:</b>
* If the retrieved member is not public and
* {@code lookc} is not present, then
* {@link SecurityManager#checkPermission smgr.checkPermission}
* with {@code RuntimePermission("accessDeclaredMembers")} is called.
+ * <li><b>Step 2b:</b>
+ * If the retrieved class has a {@code null} class loader,
+ * and {@code lookc} is not present, then
+ * {@link SecurityManager#checkPermission smgr.checkPermission}
+ * with {@code RuntimePermission("getClassLoader")} is called.
* <li><b>Step 3:</b>
* If the retrieved member is not public,
* and if {@code lookc} is not present,
* and if {@code defc} and {@code refc} are different,
* then {@link SecurityManager#checkPackageAccess
* smgr.checkPackageAccess(defcPkg)} is called,
* where {@code defcPkg} is the package of {@code defc}.
* </ul>
* Security checks are performed after other access checks have passed.
! * Therefore, the above rules presuppose a member or class that is public,
* or else that is being accessed from a lookup class that has
! * rights to access the member or class.
*
* <h1><a name="callsens"></a>Caller sensitive methods</h1>
* A small number of Java methods have a special property called caller sensitivity.
* A <em>caller-sensitive</em> method can behave differently depending on the
* identity of its immediate caller.
*** 920,929 ****
--- 934,986 ----
MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type);
return getDirectConstructor(refc, ctor);
}
/**
+ * Looks up a class by name from the lookup context defined by this {@code Lookup} object. The static
+ * initializer of the class is not run.
+ *
+ * @param targetName the fully qualified name of the class to be looked up.
+ * @return the requested class.
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws LinkageError if the linkage fails
+ * @throws ClassNotFoundException if the class does not exist.
+ * @throws IllegalAccessException if the class is not accessible, using the allowed access
+ * modes.
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @since 9
+ */
+ public Class<?> findClass(String targetName) throws ClassNotFoundException, IllegalAccessException {
+ Class<?> targetClass = Class.forName(targetName, false, lookupClass.getClassLoader());
+ return accessClass(targetClass);
+ }
+
+ /**
+ * Determines if a class can be accessed from the lookup context defined by this {@code Lookup} object. The
+ * static initializer of the class is not run.
+ *
+ * @param targetClass the class to be access-checked
+ *
+ * @return the class that has been access-checked
+ *
+ * @throws IllegalAccessException if the class is not accessible from the lookup class, using the allowed access
+ * modes.
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @since 9
+ */
+ public Class<?> accessClass(Class<?> targetClass) throws IllegalAccessException {
+ if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, allowedModes)) {
+ throw new MemberName(targetClass).makeAccessException("access violation", this);
+ }
+ checkSecurityManager(targetClass, null);
+ return targetClass;
+ }
+
+ /**
* Produces an early-bound method handle for a virtual method.
* It will bypass checks for overriding methods on the receiver,
* <a href="MethodHandles.Lookup.html#equiv">as if called</a> from an {@code invokespecial}
* instruction from within the explicitly specified {@code specialCaller}.
* The type of the method handle will be that of the method,
*** 993,1003 ****
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
* @throws NullPointerException if any argument is null
*/
public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
! checkSpecialCaller(specialCaller);
Lookup specialLookup = this.in(specialCaller);
MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, findBoundCallerClass(method));
}
--- 1050,1060 ----
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
* @throws NullPointerException if any argument is null
*/
public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
! checkSpecialCaller(specialCaller, refc);
Lookup specialLookup = this.in(specialCaller);
MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, findBoundCallerClass(method));
}
*** 1222,1232 ****
* or if the method's variable arity modifier bit
* is set and {@code asVarargsCollector} fails
* @throws NullPointerException if any argument is null
*/
public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
! checkSpecialCaller(specialCaller);
Lookup specialLookup = this.in(specialCaller);
MemberName method = new MemberName(m, true);
assert(method.isMethod());
// ignore m.isAccessible: this is a new kind of access
return specialLookup.getDirectMethodNoSecurityManager(REF_invokeSpecial, method.getDeclaringClass(), method, findBoundCallerClass(method));
--- 1279,1289 ----
* or if the method's variable arity modifier bit
* is set and {@code asVarargsCollector} fails
* @throws NullPointerException if any argument is null
*/
public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
! checkSpecialCaller(specialCaller, null);
Lookup specialLookup = this.in(specialCaller);
MemberName method = new MemberName(m, true);
assert(method.isMethod());
// ignore m.isAccessible: this is a new kind of access
return specialLookup.getDirectMethodNoSecurityManager(REF_invokeSpecial, method.getDeclaringClass(), method, findBoundCallerClass(method));
*** 1442,1452 ****
if (!fullPowerLookup ||
!VerifyAccess.classLoaderIsAncestor(lookupClass, refc)) {
ReflectUtil.checkPackageAccess(refc);
}
! // Step 2:
if (m.isPublic()) return;
if (!fullPowerLookup) {
smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
--- 1499,1517 ----
if (!fullPowerLookup ||
!VerifyAccess.classLoaderIsAncestor(lookupClass, refc)) {
ReflectUtil.checkPackageAccess(refc);
}
! if (m == null) { // findClass or accessClass
! // Step 2b:
! if (!fullPowerLookup) {
! smgr.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
! }
! return;
! }
!
! // Step 2a:
if (m.isPublic()) return;
if (!fullPowerLookup) {
smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
*** 1555,1569 ****
return "member is private to package";
}
private static final boolean ALLOW_NESTMATE_ACCESS = false;
! private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
int allowedModes = this.allowedModes;
if (allowedModes == TRUSTED) return;
if (!hasPrivateAccess()
|| (specialCaller != lookupClass()
&& !(ALLOW_NESTMATE_ACCESS &&
VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))))
throw new MemberName(specialCaller).
makeAccessException("no private access for invokespecial", this);
}
--- 1620,1636 ----
return "member is private to package";
}
private static final boolean ALLOW_NESTMATE_ACCESS = false;
! private void checkSpecialCaller(Class<?> specialCaller, Class<?> refc) throws IllegalAccessException {
int allowedModes = this.allowedModes;
if (allowedModes == TRUSTED) return;
if (!hasPrivateAccess()
|| (specialCaller != lookupClass()
+ // ensure non-abstract methods in superinterfaces can be special-invoked
+ && !(refc != null && refc.isInterface() && refc.isAssignableFrom(specialCaller))
&& !(ALLOW_NESTMATE_ACCESS &&
VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))))
throw new MemberName(specialCaller).
makeAccessException("no private access for invokespecial", this);
}
*** 1886,1896 ****
*/
public static
MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
throw newIllegalArgumentException("bad argument count", leadingArgCount);
! type = type.asSpreaderType(Object[].class, type.parameterCount() - leadingArgCount);
return type.invokers().spreadInvoker(leadingArgCount);
}
/**
* Produces a special <em>invoker method handle</em> which can be used to
--- 1953,1963 ----
*/
public static
MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
throw newIllegalArgumentException("bad argument count", leadingArgCount);
! type = type.asSpreaderType(Object[].class, leadingArgCount, type.parameterCount() - leadingArgCount);
return type.invokers().spreadInvoker(leadingArgCount);
}
/**
* Produces a special <em>invoker method handle</em> which can be used to
*** 2922,2944 ****
* (skipping one matching the {@code combiner}'s return type)
* are not identical with the argument types of {@code combiner}
*/
public static
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
! int foldPos = 0;
! MethodType targetType = target.type();
! MethodType combinerType = combiner.type();
! Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
! BoundMethodHandle result = target.rebind();
! boolean dropResult = (rtype == void.class);
! // Note: This may cache too many distinct LFs. Consider backing off to varargs code.
! LambdaForm lform = result.editor().foldArgumentsForm(1 + foldPos, dropResult, combinerType.basicType());
! MethodType newType = targetType;
! if (!dropResult)
! newType = newType.dropParameterTypes(foldPos, foldPos + 1);
! result = result.copyWithExtendL(newType, lform, combiner);
! return result;
}
private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
int foldArgs = combinerType.parameterCount();
Class<?> rtype = combinerType.returnType();
--- 2989,2999 ----
* (skipping one matching the {@code combiner}'s return type)
* are not identical with the argument types of {@code combiner}
*/
public static
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
! return foldArguments(target, 0, combiner);
}
private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
int foldArgs = combinerType.parameterCount();
Class<?> rtype = combinerType.returnType();
*** 2947,2957 ****
boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
if (ok && !(combinerType.parameterList()
.equals(targetType.parameterList().subList(afterInsertPos,
afterInsertPos + foldArgs))))
ok = false;
! if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0))
ok = false;
if (!ok)
throw misMatchedTypes("target and combiner types", targetType, combinerType);
return rtype;
}
--- 3002,3012 ----
boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
if (ok && !(combinerType.parameterList()
.equals(targetType.parameterList().subList(afterInsertPos,
afterInsertPos + foldArgs))))
ok = false;
! if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos))
ok = false;
if (!ok)
throw misMatchedTypes("target and combiner types", targetType, combinerType);
return rtype;
}
*** 3009,3019 ****
gtype = test.type();
}
return MethodHandleImpl.makeGuardWithTest(test, target, fallback);
}
! static RuntimeException misMatchedTypes(String what, MethodType t1, MethodType t2) {
return newIllegalArgumentException(what + " must match: " + t1 + " != " + t2);
}
/**
* Makes a method handle which adapts a target method handle,
--- 3064,3074 ----
gtype = test.type();
}
return MethodHandleImpl.makeGuardWithTest(test, target, fallback);
}
! static <T> RuntimeException misMatchedTypes(String what, T t1, T t2) {
return newIllegalArgumentException(what + " must match: " + t1 + " != " + t2);
}
/**
* Makes a method handle which adapts a target method handle,
*** 3055,3064 ****
--- 3110,3120 ----
* @throws NullPointerException if any argument is null
* @throws IllegalArgumentException if {@code handler} does not accept
* the given exception type, or if the method handle types do
* not match in their return types and their
* corresponding parameters
+ * @see MethodHandles#tryFinally(MethodHandle, MethodHandle)
*/
public static
MethodHandle catchException(MethodHandle target,
Class<? extends Throwable> exType,
MethodHandle handler) {
*** 3098,3103 ****
--- 3154,4052 ----
MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
if (!Throwable.class.isAssignableFrom(exType))
throw new ClassCastException(exType.getName());
return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType));
}
+
+ /**
+ * Constructs a method handle representing a loop with several loop variables that are updated and checked upon each
+ * iteration. Upon termination of the loop due to one of the predicates, a corresponding finalizer is run and
+ * delivers the loop's result, which is the return value of the resulting handle.
+ * <p>
+ * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration value and/or a loop
+ * exit. Each iteration of the loop executes each clause in order. A clause can optionally update its iteration
+ * variable; it can also optionally perform a test and conditional loop exit. In order to express this logic in
+ * terms of method handles, each clause will determine four actions:<ul>
+ * <li>Before the loop executes, the initialization of an iteration variable or loop invariant local.
+ * <li>When a clause executes, an update step for the iteration variable.
+ * <li>When a clause executes, a predicate execution to test for loop exit.
+ * <li>If a clause causes a loop exit, a finalizer execution to compute the loop's return value.
+ * </ul>
+ * <p>
+ * Some of these clause parts may be omitted according to certain rules, and useful default behavior is provided in
+ * this case. See below for a detailed description.
+ * <p>
+ * Each clause function, with the exception of clause initializers, is able to observe the entire loop state,
+ * because it will be passed <em>all</em> current iteration variable values, as well as all incoming loop
+ * parameters. Most clause functions will not need all of this information, but they will be formally connected as
+ * if by {@link #dropArguments}.
+ * <p>
+ * Given a set of clauses, there is a number of checks and adjustments performed to connect all the parts of the
+ * loop. They are spelled out in detail in the steps below. In these steps, every occurrence of the word "must"
+ * corresponds to a place where {@link IllegalArgumentException} may be thrown if the required constraint is not met
+ * by the inputs to the loop combinator. The term "effectively identical", applied to parameter type lists, means
+ * that they must be identical, or else one list must be a proper prefix of the other.
+ * <p>
+ * <em>Step 0: Determine clause structure.</em><ol type="a">
+ * <li>The clause array (of type {@code MethodHandle[][]} must be non-{@code null} and contain at least one element.
+ * <li>The clause array may not contain {@code null}s or sub-arrays longer than four elements.
+ * <li>Clauses shorter than four elements are treated as if they were padded by {@code null} elements to length
+ * four. Padding takes place by appending elements to the array.
+ * <li>Clauses with all {@code null}s are disregarded.
+ * <li>Each clause is treated as a four-tuple of functions, called "init", "step", "pred", and "fini".
+ * </ol>
+ * <p>
+ * <em>Step 1A: Determine iteration variables.</em><ol type="a">
+ * <li>Examine init and step function return types, pairwise, to determine each clause's iteration variable type.
+ * <li>If both functions are omitted, use {@code void}; else if one is omitted, use the other's return type; else
+ * use the common return type (they must be identical).
+ * <li>Form the list of return types (in clause order), omitting all occurrences of {@code void}.
+ * <li>This list of types is called the "common prefix".
+ * </ol>
+ * <p>
+ * <em>Step 1B: Determine loop parameters.</em><ol type="a">
+ * <li>Examine init function parameter lists.
+ * <li>Omitted init functions are deemed to have {@code null} parameter lists.
+ * <li>All init function parameter lists must be effectively identical.
+ * <li>The longest parameter list (which is necessarily unique) is called the "common suffix".
+ * </ol>
+ * <p>
+ * <em>Step 1C: Determine loop return type.</em><ol type="a">
+ * <li>Examine fini function return types, disregarding omitted fini functions.
+ * <li>If there are no fini functions, use {@code void} as the loop return type.
+ * <li>Otherwise, use the common return type of the fini functions; they must all be identical.
+ * </ol>
+ * <p>
+ * <em>Step 1D: Check other types.</em><ol type="a">
+ * <li>There must be at least one non-omitted pred function.
+ * <li>Every non-omitted pred function must have a {@code boolean} return type.
+ * </ol>
+ * <p>
+ * (Implementation Note: Steps 1A, 1B, 1C, 1D are logically independent of each other, and may be performed in any
+ * order.)
+ * <p>
+ * <em>Step 2: Determine parameter lists.</em><ol type="a">
+ * <li>The parameter list for the resulting loop handle will be the "common suffix".
+ * <li>The parameter list for init functions will be adjusted to the "common suffix". (Note that their parameter
+ * lists are already effectively identical to the common suffix.)
+ * <li>The parameter list for non-init (step, pred, and fini) functions will be adjusted to the common prefix
+ * followed by the common suffix, called the "common parameter sequence".
+ * <li>Every non-init, non-omitted function parameter list must be effectively identical to the common parameter
+ * sequence.
+ * </ol>
+ * <p>
+ * <em>Step 3: Fill in omitted functions.</em><ol type="a">
+ * <li>If an init function is omitted, use a {@linkplain #constant constant function} of the appropriate
+ * {@code null}/zero/{@code false}/{@code void} type. (For this purpose, a constant {@code void} is simply a
+ * function which does nothing and returns {@code void}; it can be obtained from another constant function by
+ * {@linkplain MethodHandle#asType type conversion}.)
+ * <li>If a step function is omitted, use an {@linkplain #identity identity function} of the clause's iteration
+ * variable type; insert dropped argument parameters before the identity function parameter for the non-{@code void}
+ * iteration variables of preceding clauses. (This will turn the loop variable into a local loop invariant.)
+ * <li>If a pred function is omitted, the corresponding fini function must also be omitted.
+ * <li>If a pred function is omitted, use a constant {@code true} function. (This will keep the loop going, as far
+ * as this clause is concerned.)
+ * <li>If a fini function is omitted, use a constant {@code null}/zero/{@code false}/{@code void} function of the
+ * loop return type.
+ * </ol>
+ * <p>
+ * <em>Step 4: Fill in missing parameter types.</em><ol type="a">
+ * <li>At this point, every init function parameter list is effectively identical to the common suffix, but some
+ * lists may be shorter. For every init function with a short parameter list, pad out the end of the list by
+ * {@linkplain #dropArguments dropping arguments}.
+ * <li>At this point, every non-init function parameter list is effectively identical to the common parameter
+ * sequence, but some lists may be shorter. For every non-init function with a short parameter list, pad out the end
+ * of the list by {@linkplain #dropArguments dropping arguments}.
+ * </ol>
+ * <p>
+ * <em>Final observations.</em><ol type="a">
+ * <li>After these steps, all clauses have been adjusted by supplying omitted functions and arguments.
+ * <li>All init functions have a common parameter type list, which the final loop handle will also have.
+ * <li>All fini functions have a common return type, which the final loop handle will also have.
+ * <li>All non-init functions have a common parameter type list, which is the common parameter sequence, of
+ * (non-{@code void}) iteration variables followed by loop parameters.
+ * <li>Each pair of init and step functions agrees in their return types.
+ * <li>Each non-init function will be able to observe the current values of all iteration variables, by means of the
+ * common prefix.
+ * </ol>
+ * <p>
+ * <em>Loop execution.</em><ol type="a">
+ * <li>When the loop is called, the loop input values are saved in locals, to be passed (as the common suffix) to
+ * every clause function. These locals are loop invariant.
+ * <li>Each init function is executed in clause order (passing the common suffix) and the non-{@code void} values
+ * are saved (as the common prefix) into locals. These locals are loop varying (unless their steps are identity
+ * functions, as noted above).
+ * <li>All function executions (except init functions) will be passed the common parameter sequence, consisting of
+ * the non-{@code void} iteration values (in clause order) and then the loop inputs (in argument order).
+ * <li>The step and pred functions are then executed, in clause order (step before pred), until a pred function
+ * returns {@code false}.
+ * <li>The non-{@code void} result from a step function call is used to update the corresponding loop variable. The
+ * updated value is immediately visible to all subsequent function calls.
+ * <li>If a pred function returns {@code false}, the corresponding fini function is called, and the resulting value
+ * is returned from the loop as a whole.
+ * </ol>
+ * <p>
+ * Here is pseudocode for the resulting loop handle:
+ * <blockquote><pre>{@code
+ * V... init...(A...);
+ * boolean pred...(V..., A...);
+ * V... step...(V..., A...);
+ * R fini...(V..., A...);
+ * R loop(A... a) {
+ * V... v... = init...(a...);
+ * for (;;) {
+ * for ((v, p, s, f) in (v..., pred..., step..., fini...)) {
+ * v = s(v..., a...);
+ * if (!p(v..., a...)) {
+ * return f(v..., a...);
+ * }
+ * }
+ * }
+ * }
+ * }</pre></blockquote>
+ * <p>
+ * @apiNote Example:
+ * <blockquote><pre>{@code
+ * // iterative implementation of the factorial function as a loop handle
+ * static int one(int k) { return 1; }
+ * int inc(int i, int acc, int k) { return i + 1; }
+ * int mult(int i, int acc, int k) { return i * acc; }
+ * boolean pred(int i, int acc, int k) { return i < k; }
+ * int fin(int i, int acc, int k) { return acc; }
+ * // assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
+ * // null initializer for counter, should initialize to 0
+ * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
+ * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
+ * MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
+ * assertEquals(120, loop.invoke(5));
+ * }</pre></blockquote>
+ *
+ * @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above.
+ *
+ * @return a method handle embodying the looping behavior as defined by the arguments.
+ *
+ * @throws IllegalArgumentException in case any of the constraints described above is violated.
+ *
+ * @see MethodHandles#whileLoop(MethodHandle, MethodHandle, MethodHandle)
+ * @see MethodHandles#doWhileLoop(MethodHandle, MethodHandle, MethodHandle)
+ * @see MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle)
+ * @see MethodHandles#iteratedLoop(MethodHandle, MethodHandle, MethodHandle)
+ * @since 9
+ */
+ public static MethodHandle loop(MethodHandle[]... clauses) {
+ // Step 0: determine clause structure.
+ checkLoop0(clauses);
+
+ List<MethodHandle> init = new ArrayList<>();
+ List<MethodHandle> step = new ArrayList<>();
+ List<MethodHandle> pred = new ArrayList<>();
+ List<MethodHandle> fini = new ArrayList<>();
+
+ Stream.of(clauses).filter(c -> Stream.of(c).anyMatch(Objects::nonNull)).forEach(clause -> {
+ init.add(clause[0]); // all clauses have at least length 1
+ step.add(clause.length <= 1 ? null : clause[1]);
+ pred.add(clause.length <= 2 ? null : clause[2]);
+ fini.add(clause.length <= 3 ? null : clause[3]);
+ });
+
+ assert Stream.of(init, step, pred, fini).map(List::size).distinct().count() == 1;
+ final int nclauses = init.size();
+
+ // Step 1A: determine iteration variables.
+ final List<Class<?>> iterationVariableTypes = new ArrayList<>();
+ for (int i = 0; i < nclauses; ++i) {
+ MethodHandle in = init.get(i);
+ MethodHandle st = step.get(i);
+ if (in == null && st == null) {
+ iterationVariableTypes.add(void.class);
+ } else if (in != null && st != null) {
+ checkLoop1a(i, in, st);
+ iterationVariableTypes.add(in.type().returnType());
+ } else {
+ iterationVariableTypes.add(in == null ? st.type().returnType() : in.type().returnType());
+ }
+ }
+ final List<Class<?>> commonPrefix = iterationVariableTypes.stream().filter(t -> t != void.class).
+ collect(Collectors.toList());
+
+ // Step 1B: determine loop parameters.
+ final List<Class<?>> empty = new ArrayList<>();
+ final List<Class<?>> commonSuffix = init.stream().filter(Objects::nonNull).map(MethodHandle::type).
+ map(MethodType::parameterList).reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty);
+ checkLoop1b(init, commonSuffix);
+
+ // Step 1C: determine loop return type.
+ // Step 1D: check other types.
+ final Class<?> loopReturnType = fini.stream().filter(Objects::nonNull).map(MethodHandle::type).
+ map(MethodType::returnType).findFirst().orElse(void.class);
+ checkLoop1cd(pred, fini, loopReturnType);
+
+ // Step 2: determine parameter lists.
+ final List<Class<?>> commonParameterSequence = new ArrayList<>(commonPrefix);
+ commonParameterSequence.addAll(commonSuffix);
+ checkLoop2(step, pred, fini, commonParameterSequence);
+
+ // Step 3: fill in omitted functions.
+ for (int i = 0; i < nclauses; ++i) {
+ Class<?> t = iterationVariableTypes.get(i);
+ if (init.get(i) == null) {
+ init.set(i, zeroHandle(t));
+ }
+ if (step.get(i) == null) {
+ step.set(i, dropArguments(t == void.class ? zeroHandle(t) : identity(t), 0, commonPrefix.subList(0, i)));
+ }
+ if (pred.get(i) == null) {
+ pred.set(i, constant(boolean.class, true));
+ }
+ if (fini.get(i) == null) {
+ fini.set(i, zeroHandle(t));
+ }
+ }
+
+ // Step 4: fill in missing parameter types.
+ List<MethodHandle> finit = fillParameterTypes(init, commonSuffix);
+ List<MethodHandle> fstep = fillParameterTypes(step, commonParameterSequence);
+ List<MethodHandle> fpred = fillParameterTypes(pred, commonParameterSequence);
+ List<MethodHandle> ffini = fillParameterTypes(fini, commonParameterSequence);
+
+ assert finit.stream().map(MethodHandle::type).map(MethodType::parameterList).
+ allMatch(pl -> pl.equals(commonSuffix));
+ assert Stream.of(fstep, fpred, ffini).flatMap(List::stream).map(MethodHandle::type).map(MethodType::parameterList).
+ allMatch(pl -> pl.equals(commonParameterSequence));
+
+ return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, commonPrefix, finit, fstep, fpred, ffini);
+ }
+
+ private static List<MethodHandle> fillParameterTypes(List<MethodHandle> hs, final List<Class<?>> targetParams) {
+ return hs.stream().map(h -> {
+ int pc = h.type().parameterCount();
+ int tpsize = targetParams.size();
+ return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h;
+ }).collect(Collectors.toList());
+ }
+
+ /**
+ * Constructs a {@code while} loop from an initializer, a body, and a predicate. This is a convenience wrapper for
+ * the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
+ * <p>
+ * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}.
+ * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle
+ * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code
+ * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][])
+ * generic loop combinator}.
+ * <p>
+ * Here is pseudocode for the resulting loop handle:
+ * <blockquote><pre>{@code
+ * V init(A);
+ * boolean pred(V, A);
+ * V body(V, A);
+ * V whileLoop(A a) {
+ * V v = init(a);
+ * while (pred(v, a)) {
+ * v = body(v, a);
+ * }
+ * return v;
+ * }
+ * }</pre></blockquote>
+ * <p>
+ * @apiNote Example:
+ * <blockquote><pre>{@code
+ * // implement the zip function for lists as a loop handle
+ * List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); }
+ * boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); }
+ * List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
+ * zip.add(a.next());
+ * zip.add(b.next());
+ * return zip;
+ * }
+ * // assume MH_initZip, MH_zipPred, and MH_zipStep are handles to the above methods
+ * MethodHandle loop = MethodHandles.doWhileLoop(MH_initZip, MH_zipPred, MH_zipStep);
+ * List<String> a = Arrays.asList("a", "b", "c", "d");
+ * List<String> b = Arrays.asList("e", "f", "g", "h");
+ * List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
+ * assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
+ * }</pre></blockquote>
+ *
+ * <p>
+ * The implementation of this method is equivalent to:
+ * <blockquote><pre>{@code
+ * MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
+ * MethodHandle[]
+ * checkExit = {null, null, pred, identity(init.type().returnType())},
+ * varBody = {init, body};
+ * return loop(checkExit, varBody);
+ * }
+ * }</pre></blockquote>
+ *
+ * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's
+ * result type. Passing {@code null} or a {@code void} init function will make the loop's result type
+ * {@code void}.
+ * @param pred condition for the loop, which may not be {@code null}.
+ * @param body body of the loop, which may not be {@code null}.
+ *
+ * @return the value of the loop variable as the loop terminates.
+ * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ *
+ * @see MethodHandles#loop(MethodHandle[][])
+ * @since 9
+ */
+ public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
+ MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+ MethodHandle[] checkExit = {null, null, pred, fin};
+ MethodHandle[] varBody = {init, body};
+ return loop(checkExit, varBody);
+ }
+
+ /**
+ * Constructs a {@code do-while} loop from an initializer, a body, and a predicate. This is a convenience wrapper
+ * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}.
+ * <p>
+ * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}.
+ * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle
+ * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code
+ * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][])
+ * generic loop combinator}.
+ * <p>
+ * Here is pseudocode for the resulting loop handle:
+ * <blockquote><pre>{@code
+ * V init(A);
+ * boolean pred(V, A);
+ * V body(V, A);
+ * V doWhileLoop(A a) {
+ * V v = init(a);
+ * do {
+ * v = body(v, a);
+ * } while (pred(v, a));
+ * return v;
+ * }
+ * }</pre></blockquote>
+ * <p>
+ * @apiNote Example:
+ * <blockquote><pre>{@code
+ * // int i = 0; while (i < limit) { ++i; } return i; => limit
+ * int zero(int limit) { return 0; }
+ * int step(int i, int limit) { return i + 1; }
+ * boolean pred(int i, int limit) { return i < limit; }
+ * // assume MH_zero, MH_step, and MH_pred are handles to the above methods
+ * MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
+ * assertEquals(23, loop.invoke(23));
+ * }</pre></blockquote>
+ *
+ * * <p>
+ * The implementation of this method is equivalent to:
+ * <blockquote><pre>{@code
+ * MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
+ * MethodHandle[] clause = { init, body, pred, identity(init.type().returnType()) };
+ * return loop(clause);
+ * }
+ * }</pre></blockquote>
+ *
+ *
+ * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's
+ * result type. Passing {@code null} or a {@code void} init function will make the loop's result type
+ * {@code void}.
+ * @param pred condition for the loop, which may not be {@code null}.
+ * @param body body of the loop, which may not be {@code null}.
+ *
+ * @return the value of the loop variable as the loop terminates.
+ * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ *
+ * @see MethodHandles#loop(MethodHandle[][])
+ * @since 9
+ */
+ public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
+ MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+ MethodHandle[] clause = {init, body, pred, fin};
+ return loop(clause);
+ }
+
+ /**
+ * Constructs a loop that runs a given number of iterations. The loop counter is an {@code int} initialized from the
+ * {@code iterations} handle evaluation result. The counter is passed to the {@code body} function, so that must
+ * accept an initial {@code int} argument. The result of the loop execution is the final value of the additional
+ * local state. This is a convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}.
+ * <p>
+ * The result type and parameter type list of {@code init} determine those of the resulting handle. The {@code
+ * iterations} handle must accept the same parameter types as {@code init} but return an {@code int}. The {@code
+ * body} handle must accept the same parameter types as well, preceded by an {@code int} parameter for the counter,
+ * and a parameter of the same type as {@code init}'s result. These constraints follow directly from those described
+ * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}.
+ * <p>
+ * Here is pseudocode for the resulting loop handle:
+ * <blockquote><pre>{@code
+ * int iterations(A);
+ * V init(A);
+ * V body(int, V, A);
+ * V countedLoop(A a) {
+ * int end = iterations(a);
+ * V v = init(a);
+ * for (int i = 0; i < end; ++i) {
+ * v = body(i, v, a);
+ * }
+ * return v;
+ * }
+ * }</pre></blockquote>
+ * <p>
+ * @apiNote Example:
+ * <blockquote><pre>{@code
+ * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
+ * // => a variation on a well known theme
+ * String start(String arg) { return arg; }
+ * String step(int counter, String v, String arg) { return "na " + v; }
+ * // assume MH_start and MH_step are handles to the two methods above
+ * MethodHandle loop = MethodHandles.countedLoop(13, MH_start, MH_step);
+ * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
+ * }</pre></blockquote>
+ *
+ * <p>
+ * The implementation of this method is equivalent to:
+ * <blockquote><pre>{@code
+ * MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
+ * return countedLoop(null, iterations, init, body); // null => constant zero
+ * }
+ * }</pre></blockquote>
+ *
+ * @param iterations a handle to return the number of iterations this loop should run.
+ * @param init initializer for additional loop state. This determines the loop's result type.
+ * Passing {@code null} or a {@code void} init function will make the loop's result type
+ * {@code void}.
+ * @param body the body of the loop, which must not be {@code null}.
+ * It must accept an initial {@code int} parameter (for the counter), and then any
+ * additional loop-local variable plus loop parameters.
+ *
+ * @return a method handle representing the loop.
+ * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ *
+ * @since 9
+ */
+ public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
+ return countedLoop(null, iterations, init, body);
+ }
+
+ /**
+ * Constructs a loop that counts over a range of numbers. The loop counter is an {@code int} that will be
+ * initialized to the {@code int} value returned from the evaluation of the {@code start} handle and run to the
+ * value returned from {@code end} (exclusively) with a step width of 1. The counter value is passed to the {@code
+ * body} function in each iteration; it has to accept an initial {@code int} parameter
+ * for that. The result of the loop execution is the final value of the additional local state
+ * obtained by running {@code init}.
+ * This is a
+ * convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}.
+ * <p>
+ * The constraints for the {@code init} and {@code body} handles are the same as for {@link
+ * #countedLoop(MethodHandle, MethodHandle, MethodHandle)}. Additionally, the {@code start} and {@code end} handles
+ * must return an {@code int} and accept the same parameters as {@code init}.
+ * <p>
+ * Here is pseudocode for the resulting loop handle:
+ * <blockquote><pre>{@code
+ * int start(A);
+ * int end(A);
+ * V init(A);
+ * V body(int, V, A);
+ * V countedLoop(A a) {
+ * int s = start(a);
+ * int e = end(a);
+ * V v = init(a);
+ * for (int i = s; i < e; ++i) {
+ * v = body(i, v, a);
+ * }
+ * return v;
+ * }
+ * }</pre></blockquote>
+ *
+ * <p>
+ * The implementation of this method is equivalent to:
+ * <blockquote><pre>{@code
+ * MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
+ * MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class);
+ * // assume MH_increment and MH_lessThan are handles to x+1 and x<y of type int
+ * MethodHandle[]
+ * indexVar = {start, MH_increment}, // i = start; i = i+1
+ * loopLimit = {end, null, MH_lessThan, returnVar }, // i<end
+ * bodyClause = {init, dropArguments(body, 1, int.class)}; // v = body(i, v);
+ * return loop(indexVar, loopLimit, bodyClause);
+ * }
+ * }</pre></blockquote>
+ *
+ * @param start a handle to return the start value of the loop counter.
+ * If it is {@code null}, a constant zero is assumed.
+ * @param end a non-{@code null} handle to return the end value of the loop counter (the loop will run to {@code end-1}).
+ * @param init initializer for additional loop state. This determines the loop's result type.
+ * Passing {@code null} or a {@code void} init function will make the loop's result type
+ * {@code void}.
+ * @param body the body of the loop, which must not be {@code null}.
+ * It must accept an initial {@code int} parameter (for the counter), and then any
+ * additional loop-local variable plus loop parameters.
+ *
+ * @return a method handle representing the loop.
+ * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ *
+ * @since 9
+ */
+ public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
+ MethodHandle returnVar = dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()),
+ 0, int.class, int.class);
+ MethodHandle[] indexVar = {start, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep)};
+ MethodHandle[] loopLimit = {end, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred), returnVar};
+ MethodHandle[] bodyClause = {init, dropArguments(body, 1, int.class)};
+ return loop(indexVar, loopLimit, bodyClause);
+ }
+
+ /**
+ * Constructs a loop that ranges over the elements produced by an {@code Iterator<T>}.
+ * The iterator will be produced by the evaluation of the {@code iterator} handle.
+ * If this handle is passed as {@code null} the method {@link Iterable#iterator} will be used instead,
+ * and will be applied to a leading argument of the loop handle.
+ * Each value produced by the iterator is passed to the {@code body}, which must accept an initial {@code T} parameter.
+ * The result of the loop execution is the final value of the additional local state
+ * obtained by running {@code init}.
+ * <p>
+ * This is a convenience wrapper for the
+ * {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}, and the constraints imposed on the {@code body}
+ * handle follow directly from those described for the latter.
+ * <p>
+ * Here is pseudocode for the resulting loop handle:
+ * <blockquote><pre>{@code
+ * Iterator<T> iterator(A); // defaults to Iterable::iterator
+ * V init(A);
+ * V body(T,V,A);
+ * V iteratedLoop(A a) {
+ * Iterator<T> it = iterator(a);
+ * V v = init(a);
+ * for (T t : it) {
+ * v = body(t, v, a);
+ * }
+ * return v;
+ * }
+ * }</pre></blockquote>
+ * <p>
+ * The type {@code T} may be either a primitive or reference.
+ * Since type {@code Iterator<T>} is erased in the method handle representation to the raw type
+ * {@code Iterator}, the {@code iteratedLoop} combinator adjusts the leading argument type for {@code body}
+ * to {@code Object} as if by the {@link MethodHandle#asType asType} conversion method.
+ * Therefore, if an iterator of the wrong type appears as the loop is executed,
+ * runtime exceptions may occur as the result of dynamic conversions performed by {@code asType}.
+ * <p>
+ * @apiNote Example:
+ * <blockquote><pre>{@code
+ * // reverse a list
+ * List<String> reverseStep(String e, List<String> r) {
+ * r.add(0, e);
+ * return r;
+ * }
+ * List<String> newArrayList() { return new ArrayList<>(); }
+ * // assume MH_reverseStep, MH_newArrayList are handles to the above methods
+ * MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep);
+ * List<String> list = Arrays.asList("a", "b", "c", "d", "e");
+ * List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
+ * assertEquals(reversedList, (List<String>) loop.invoke(list));
+ * }</pre></blockquote>
+ * <p>
+ * The implementation of this method is equivalent to:
+ * <blockquote><pre>{@code
+ * MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
+ * // assume MH_next and MH_hasNext are handles to methods of Iterator
+ * Class<?> itype = iterator.type().returnType();
+ * Class<?> ttype = body.type().parameterType(0);
+ * MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, itype);
+ * MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
+ * MethodHandle[]
+ * iterVar = {iterator, null, MH_hasNext, returnVar}, // it = iterator(); while (it.hasNext)
+ * bodyClause = {init, filterArgument(body, 0, nextVal)}; // v = body(t, v, a);
+ * return loop(iterVar, bodyClause);
+ * }
+ * }</pre></blockquote>
+ *
+ * @param iterator a handle to return the iterator to start the loop.
+ * Passing {@code null} will make the loop call {@link Iterable#iterator()} on the first
+ * incoming value.
+ * @param init initializer for additional loop state. This determines the loop's result type.
+ * Passing {@code null} or a {@code void} init function will make the loop's result type
+ * {@code void}.
+ * @param body the body of the loop, which must not be {@code null}.
+ * It must accept an initial {@code T} parameter (for the iterated values), and then any
+ * additional loop-local variable plus loop parameters.
+ *
+ * @return a method handle embodying the iteration loop functionality.
+ * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ *
+ * @since 9
+ */
+ public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
+ checkIteratedLoop(body);
+
+ MethodHandle initit = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator);
+ MethodHandle initIterator = iterator == null ?
+ initit.asType(initit.type().changeParameterType(0, body.type().parameterType(init == null ? 1 : 2))) :
+ iterator;
+ Class<?> itype = initIterator.type().returnType();
+ Class<?> ttype = body.type().parameterType(0);
+
+ MethodHandle returnVar =
+ dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), 0, itype);
+ MethodHandle initnx = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext);
+ MethodHandle nextVal = initnx.asType(initnx.type().changeReturnType(ttype));
+
+ MethodHandle[] iterVar = {initIterator, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iteratePred), returnVar};
+ MethodHandle[] bodyClause = {init, filterArgument(body, 0, nextVal)};
+
+ return loop(iterVar, bodyClause);
+ }
+
+ /**
+ * Makes a method handle that adapts a {@code target} method handle by wrapping it in a {@code try-finally} block.
+ * Another method handle, {@code cleanup}, represents the functionality of the {@code finally} block. Any exception
+ * thrown during the execution of the {@code target} handle will be passed to the {@code cleanup} handle. The
+ * exception will be rethrown, unless {@code cleanup} handle throws an exception first. The
+ * value returned from the {@code cleanup} handle's execution will be the result of the execution of the
+ * {@code try-finally} handle.
+ * <p>
+ * The {@code cleanup} handle will be passed one or two additional leading arguments.
+ * The first is the exception thrown during the
+ * execution of the {@code target} handle, or {@code null} if no exception was thrown.
+ * The second is the result of the execution of the {@code target} handle, or, if it throws an exception,
+ * a {@code null}, zero, or {@code false} value of the required type is supplied as a placeholder.
+ * The second argument is not present if the {@code target} handle has a {@code void} return type.
+ * (Note that, except for argument type conversions, combinators represent {@code void} values in parameter lists
+ * by omitting the corresponding paradoxical arguments, not by inserting {@code null} or zero values.)
+ * <p>
+ * The {@code target} and {@code cleanup} handles' return types must be the same. Their parameter type lists also
+ * must be the same, but the {@code cleanup} handle must accept one or two more leading parameters:<ul>
+ * <li>a {@code Throwable}, which will carry the exception thrown by the {@code target} handle (if any); and
+ * <li>a parameter of the same type as the return type of both {@code target} and {@code cleanup}, which will carry
+ * the result from the execution of the {@code target} handle.
+ * This parameter is not present if the {@code target} returns {@code void}.
+ * </ul>
+ * <p>
+ * The pseudocode for the resulting adapter looks like this:
+ * <blockquote><pre>{@code
+ * T target(A..., B...);
+ * T cleanup(Throwable, T, A...);
+ * T adapter(A... a, B... b) {
+ * T result = (zero value for T);
+ * Throwable throwable = null;
+ * try {
+ * result = target(a..., b...);
+ * } catch (Throwable t) {
+ * throwable = t;
+ * throw t;
+ * } finally {
+ * result = cleanup(throwable, result, a...);
+ * }
+ * return result;
+ * }
+ * }</pre></blockquote>
+ * <p>
+ * Note that the saved arguments ({@code a...} in the pseudocode) cannot
+ * be modified by execution of the target, and so are passed unchanged
+ * from the caller to the cleanup, if it is invoked.
+ * <p>
+ * The target and cleanup must return the same type, even if the cleanup
+ * always throws.
+ * To create such a throwing cleanup, compose the cleanup logic
+ * with {@link #throwException throwException},
+ * in order to create a method handle of the correct return type.
+ * <p>
+ * Note that {@code tryFinally} never converts exceptions into normal returns.
+ * In rare cases where exceptions must be converted in that way, first wrap
+ * the target with {@link #catchException(MethodHandle, Class, MethodHandle)}
+ * to capture an outgoing exception, and then wrap with {@code tryFinally}.
+ *
+ * @param target the handle whose execution is to be wrapped in a {@code try} block.
+ * @param cleanup the handle that is invoked in the finally block.
+ *
+ * @return a method handle embodying the {@code try-finally} block composed of the two arguments.
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if {@code cleanup} does not accept
+ * the required leading arguments, or if the method handle types do
+ * not match in their return types and their
+ * corresponding trailing parameters
+ *
+ * @see MethodHandles#catchException(MethodHandle, Class, MethodHandle)
+ * @since 9
+ */
+ public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup) {
+ List<Class<?>> targetParamTypes = target.type().parameterList();
+ List<Class<?>> cleanupParamTypes = cleanup.type().parameterList();
+ Class<?> rtype = target.type().returnType();
+
+ checkTryFinally(target, cleanup);
+
+ // Match parameter lists: if the cleanup has a shorter parameter list than the target, add ignored arguments.
+ int tpSize = targetParamTypes.size();
+ int cpPrefixLength = rtype == void.class ? 1 : 2;
+ int cpSize = cleanupParamTypes.size();
+ MethodHandle aCleanup = cpSize - cpPrefixLength < tpSize ?
+ dropArguments(cleanup, cpSize, targetParamTypes.subList(tpSize - (cpSize - cpPrefixLength), tpSize)) :
+ cleanup;
+
+ MethodHandle aTarget = target.asSpreader(Object[].class, target.type().parameterCount());
+ aCleanup = aCleanup.asSpreader(Object[].class, tpSize);
+
+ return MethodHandleImpl.makeTryFinally(aTarget, aCleanup, rtype, targetParamTypes);
+ }
+
+ /**
+ * Adapts a target method handle by pre-processing some of its arguments, starting at a given position, and then
+ * calling the target with the result of the pre-processing, inserted into the original sequence of arguments just
+ * before the folded arguments.
+ * <p>
+ * This method is closely related to {@link #foldArguments(MethodHandle, MethodHandle)}, but allows to control the
+ * position in the parameter list at which folding takes place. The argument controlling this, {@code pos}, is a
+ * zero-based index. The aforementioned method {@link #foldArguments(MethodHandle, MethodHandle)} assumes position
+ * 0.
+ * <p>
+ * @apiNote Example:
+ * <blockquote><pre>{@code
+ import static java.lang.invoke.MethodHandles.*;
+ import static java.lang.invoke.MethodType.*;
+ ...
+ MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
+ "println", methodType(void.class, String.class))
+ .bindTo(System.out);
+ MethodHandle cat = lookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+ assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
+ MethodHandle catTrace = foldArguments(cat, 1, trace);
+ // also prints "jum":
+ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+ * }</pre></blockquote>
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>{@code
+ * // there are N arguments in A...
+ * T target(Z..., V, A[N]..., B...);
+ * V combiner(A...);
+ * T adapter(Z... z, A... a, B... b) {
+ * V v = combiner(a...);
+ * return target(z..., v, a..., b...);
+ * }
+ * // and if the combiner has a void return:
+ * T target2(Z..., A[N]..., B...);
+ * void combiner2(A...);
+ * T adapter2(Z... z, A... a, B... b) {
+ * combiner2(a...);
+ * return target2(z..., a..., b...);
+ * }
+ * }</pre></blockquote>
+ *
+ * @param target the method handle to invoke after arguments are combined
+ * @param pos the position at which to start folding and at which to insert the folding result; if this is {@code
+ * 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}.
+ * @param combiner method handle to call initially on the incoming arguments
+ * @return method handle which incorporates the specified argument folding logic
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException if {@code combiner}'s return type
+ * is non-void and not the same as the argument type at position {@code pos} of
+ * the target signature, or if the {@code N} argument types at position {@code pos}
+ * of the target signature
+ * (skipping one matching the {@code combiner}'s return type)
+ * are not identical with the argument types of {@code combiner}
+ *
+ * @see #foldArguments(MethodHandle, MethodHandle)
+ * @since 9
+ */
+ public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner) {
+ MethodType targetType = target.type();
+ MethodType combinerType = combiner.type();
+ Class<?> rtype = foldArgumentChecks(pos, targetType, combinerType);
+ BoundMethodHandle result = target.rebind();
+ boolean dropResult = rtype == void.class;
+ LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType());
+ MethodType newType = targetType;
+ if (!dropResult) {
+ newType = newType.dropParameterTypes(pos, pos + 1);
+ }
+ result = result.copyWithExtendL(newType, lform, combiner);
+ return result;
+ }
+
+ /**
+ * Wrap creation of a proper zero handle for a given type.
+ *
+ * @param type the type.
+ *
+ * @return a zero value for the given type.
+ */
+ static MethodHandle zeroHandle(Class<?> type) {
+ return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type);
+ }
+
+ private static void checkLoop0(MethodHandle[][] clauses) {
+ if (clauses == null || clauses.length == 0) {
+ throw newIllegalArgumentException("null or no clauses passed");
+ }
+ if (Stream.of(clauses).anyMatch(Objects::isNull)) {
+ throw newIllegalArgumentException("null clauses are not allowed");
+ }
+ if (Stream.of(clauses).anyMatch(c -> c.length > 4)) {
+ throw newIllegalArgumentException("All loop clauses must be represented as MethodHandle arrays with at most 4 elements.");
+ }
+ }
+
+ private static void checkLoop1a(int i, MethodHandle in, MethodHandle st) {
+ if (in.type().returnType() != st.type().returnType()) {
+ throw misMatchedTypes("clause " + i + ": init and step return types", in.type().returnType(),
+ st.type().returnType());
+ }
+ }
+
+ private static void checkLoop1b(List<MethodHandle> init, List<Class<?>> commonSuffix) {
+ if (init.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::parameterList).
+ anyMatch(pl -> !pl.equals(commonSuffix.subList(0, pl.size())))) {
+ throw newIllegalArgumentException("found non-effectively identical init parameter type lists: " + init +
+ " (common suffix: " + commonSuffix + ")");
+ }
+ }
+
+ private static void checkLoop1cd(List<MethodHandle> pred, List<MethodHandle> fini, Class<?> loopReturnType) {
+ if (fini.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType).
+ anyMatch(t -> t != loopReturnType)) {
+ throw newIllegalArgumentException("found non-identical finalizer return types: " + fini + " (return type: " +
+ loopReturnType + ")");
+ }
+
+ if (!pred.stream().filter(Objects::nonNull).findFirst().isPresent()) {
+ throw newIllegalArgumentException("no predicate found", pred);
+ }
+ if (pred.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType).
+ anyMatch(t -> t != boolean.class)) {
+ throw newIllegalArgumentException("predicates must have boolean return type", pred);
+ }
+ }
+
+ private static void checkLoop2(List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, List<Class<?>> commonParameterSequence) {
+ if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type).
+ map(MethodType::parameterList).anyMatch(pl -> !pl.equals(commonParameterSequence.subList(0, pl.size())))) {
+ throw newIllegalArgumentException("found non-effectively identical parameter type lists:\nstep: " + step +
+ "\npred: " + pred + "\nfini: " + fini + " (common parameter sequence: " + commonParameterSequence + ")");
+ }
+ }
+
+ private static void checkIteratedLoop(MethodHandle body) {
+ if (null == body) {
+ throw newIllegalArgumentException("iterated loop body must not be null");
+ }
+ }
+
+ private static void checkTryFinally(MethodHandle target, MethodHandle cleanup) {
+ Class<?> rtype = target.type().returnType();
+ if (rtype != cleanup.type().returnType()) {
+ throw misMatchedTypes("target and return types", cleanup.type().returnType(), rtype);
+ }
+ List<Class<?>> cleanupParamTypes = cleanup.type().parameterList();
+ if (!Throwable.class.isAssignableFrom(cleanupParamTypes.get(0))) {
+ throw misMatchedTypes("cleanup first argument and Throwable", cleanup.type(), Throwable.class);
+ }
+ if (rtype != void.class && cleanupParamTypes.get(1) != rtype) {
+ throw misMatchedTypes("cleanup second argument and target return type", cleanup.type(), rtype);
+ }
+ // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the
+ // target parameter list.
+ int cleanupArgIndex = rtype == void.class ? 1 : 2;
+ if (!cleanupParamTypes.subList(cleanupArgIndex, cleanupParamTypes.size()).
+ equals(target.type().parameterList().subList(0, cleanupParamTypes.size() - cleanupArgIndex))) {
+ throw misMatchedTypes("cleanup parameters after (Throwable,result) and target parameter list prefix",
+ cleanup.type(), target.type());
+ }
+ }
+
}
< prev index next >