diff --git a/.hgtags b/.hgtags index 14aa9215059..a457e5268d8 100644 --- a/.hgtags +++ b/.hgtags @@ -379,3 +379,4 @@ e17429a7e843c4a4ed3651458d0f950970edcbcc jdk-9+133 a71210c0d9800eb6925b61ecd6198abd554f90ee jdk-9+134 e384420383a5b79fa0012ebcb25d8f83cff7f777 jdk-9+135 1b4b5d01aa11edf24b6fadbe3d2f3e411e3b02cd jdk-9+136 +9cb87c88ed851c0575b8ead753ea238ed5b544e9 jdk-9+137 diff --git a/.hgtags-top-repo b/.hgtags-top-repo index e540680549f..a0cde0979fb 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -379,3 +379,4 @@ be1218f792a450dfb5d4b1f82616b9d95a6a732e jdk-9+133 065724348690eda41fc69112278d8da6dcde548c jdk-9+134 82b94cb5f342319d2cda77f9fa59703ad7fde576 jdk-9+135 3ec350f5f32af249b59620d7e37b54bdcd77b233 jdk-9+136 +d7f519b004254b19e384131d9f0d0e40e31a0fd3 jdk-9+137 diff --git a/Makefile b/Makefile index 2460cd414d8..ebe52d5d7f2 100644 --- a/Makefile +++ b/Makefile @@ -28,8 +28,8 @@ ### It also performs some sanity checks on make. ### -# The shell code below will be executed on /usr/ccs/bin/make on Solaris, but not in GNU Make. -# /usr/ccs/bin/make lacks basically every other flow control mechanism. +# The shell code below will be executed on /usr/bin/make on Solaris, but not in GNU Make. +# /usr/bin/make lacks basically every other flow control mechanism. .TEST_FOR_NON_GNUMAKE:sh=echo You are not using GNU Make/gmake, this is a requirement. Check your path. 1>&2 && exit 1 # The .FEATURES variable is likely to be unique for GNU Make. diff --git a/README-builds.html b/README-builds.html index 42cc0f11b7c..6d7d5b52461 100644 --- a/README-builds.html +++ b/README-builds.html @@ -626,8 +626,7 @@ number of different configurations, e.g. debug, release, 32, 64, etc.
The Common UNIX Printing System (CUPS) Headers are required for building the
OpenJDK on Solaris and Linux. The Solaris header files can be obtained by
- installing the package SFWcups from the Solaris Software Companion
- CD/DVD, these often will be installed into the directory /opt/sfw/cups.
The CUPS header files can always be downloaded from www.cups.org.
@@ -1111,8 +1110,7 @@ version, see "Building GNU make".PATH./usr/bin/make on Solaris. If your Solaris system
has the software from the Solaris Developer Companion CD installed, you
-should try and use gmake which will be located in either the /usr/bin,
-/opt/sfw/bin or /usr/sfw/bin directory./usr/bin/gmake or /usr/gnu/bin/make.
{@code
* // a0: BMH
- * // a1: inits, a2: steps, a3: preds, a4: finis
- * // a5: box, a6: unbox
- * // a7 (and following): arguments
- * loop=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{
- * t8:L=MethodHandle.invokeBasic(a5:L,a7:L); // box the arguments into an Object[]
- * t9:L=MethodHandleImpl.loop(bt:L,a1:L,a2:L,a3:L,a4:L,t8:L); // call the loop executor (with supplied types in bt)
- * t10:L=MethodHandle.invokeBasic(a6:L,t9:L);t10:L} // unbox the result; return the result
+ * // a1: LoopClauses (containing an array of arrays: inits, steps, preds, finis)
+ * // a2: box, a3: unbox
+ * // a4 (and following): arguments
+ * loop=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L)=>{
+ * t5:L=MethodHandle.invokeBasic(a2:L,a4:L); // box the arguments into an Object[]
+ * t6:L=MethodHandleImpl.loop(bt:L,a1:L,t5:L); // call the loop executor (with supplied types in bt)
+ * t7:L=MethodHandle.invokeBasic(a3:L,t6:L);t7:L} // unbox the result; return the result
* }
* * It is compiled into bytecode equivalent to the code seen in {@link MethodHandleImpl#loop(BasicType[], - * MethodHandle[], MethodHandle[], MethodHandle[], MethodHandle[], Object...)}, with the difference that no arrays + * MethodHandleImpl.LoopClauses, Object...)}, with the difference that no arrays * will be used for local state storage. Instead, the local state will be mapped to actual stack slots. *
* Bytecode generation applies an unrolling scheme to enable better bytecode generation regarding local state type * handling. The generated bytecode will have the following form ({@code void} types are ignored for convenience). * Assume there are {@code C} clauses in the loop. *
{@code
- * INIT: (INIT_SEQ for clause 1)
- * ...
- * (INIT_SEQ for clause C)
- * LOOP: (LOOP_SEQ for clause 1)
- * ...
- * (LOOP_SEQ for clause C)
- * GOTO LOOP
- * DONE: ...
+ * PREINIT: ALOAD_1
+ * CHECKCAST LoopClauses
+ * GETFIELD LoopClauses.clauses
+ * ASTORE clauseDataIndex // place the clauses 2-dimensional array on the stack
+ * INIT: (INIT_SEQ for clause 1)
+ * ...
+ * (INIT_SEQ for clause C)
+ * LOOP: (LOOP_SEQ for clause 1)
+ * ...
+ * (LOOP_SEQ for clause C)
+ * GOTO LOOP
+ * DONE: ...
* }
* * The {@code INIT_SEQ_x} sequence for clause {@code x} (with {@code x} ranging from {@code 0} to {@code C-1}) has * the following shape. Assume slot {@code vx} is used to hold the state for clause {@code x}. *
{@code
- * INIT_SEQ_x: ALOAD inits
- * CHECKCAST MethodHandle[]
+ * INIT_SEQ_x: ALOAD clauseDataIndex
+ * ICONST_0
+ * AALOAD // load the inits array
* ICONST x
* AALOAD // load the init handle for clause x
* load args
@@ -1361,24 +1369,27 @@ class InvokerBytecodeGenerator {
* The {@code LOOP_SEQ_x} sequence for clause {@code x} (with {@code x} ranging from {@code 0} to {@code C-1}) has
* the following shape. Again, assume slot {@code vx} is used to hold the state for clause {@code x}.
* {@code
- * LOOP_SEQ_x: ALOAD steps
- * CHECKCAST MethodHandle[]
+ * LOOP_SEQ_x: ALOAD clauseDataIndex
+ * ICONST_1
+ * AALOAD // load the steps array
* ICONST x
* AALOAD // load the step handle for clause x
* load locals
* load args
* INVOKEVIRTUAL MethodHandle.invokeBasic
* store vx
- * ALOAD preds
- * CHECKCAST MethodHandle[]
+ * ALOAD clauseDataIndex
+ * ICONST_2
+ * AALOAD // load the preds array
* ICONST x
* AALOAD // load the pred handle for clause x
* load locals
* load args
* INVOKEVIRTUAL MethodHandle.invokeBasic
* IFNE LOOP_SEQ_x+1 // predicate returned false -> jump to next clause
- * ALOAD finis
- * CHECKCAST MethodHandle[]
+ * ALOAD clauseDataIndex
+ * ICONST_3
+ * AALOAD // load the finis array
* ICONST x
* AALOAD // load the fini handle for clause x
* load locals
@@ -1397,8 +1408,12 @@ class InvokerBytecodeGenerator {
BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0];
Class>[] loopLocalStateTypes = Stream.of(loopClauseTypes).
filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class>[]::new);
+ Class>[] localTypes = new Class>[loopLocalStateTypes.length + 1];
+ localTypes[0] = MethodHandleImpl.LoopClauses.class;
+ System.arraycopy(loopLocalStateTypes, 0, localTypes, 1, loopLocalStateTypes.length);
- final int firstLoopStateIndex = extendLocalsMap(loopLocalStateTypes);
+ final int clauseDataIndex = extendLocalsMap(localTypes);
+ final int firstLoopStateIndex = clauseDataIndex + 1;
Class> returnType = result.function.resolvedHandle().type().returnType();
MethodType loopType = args.function.resolvedHandle().type()
@@ -1420,10 +1435,16 @@ class InvokerBytecodeGenerator {
Label lDone = new Label();
Label lNext;
+ // PREINIT:
+ emitPushArgument(MethodHandleImpl.LoopClauses.class, invoker.arguments[1]);
+ mv.visitFieldInsn(Opcodes.GETFIELD, LOOP_CLAUSES, "clauses", MHARY2);
+ emitAstoreInsn(clauseDataIndex);
+
// INIT:
for (int c = 0, state = 0; c < nClauses; ++c) {
MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass());
- emitLoopHandleInvoke(invoker, inits, c, args, false, cInitType, loopLocalStateTypes, firstLoopStateIndex);
+ emitLoopHandleInvoke(invoker, inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex,
+ firstLoopStateIndex);
if (cInitType.returnType() != void.class) {
emitStoreInsn(BasicType.basicType(cInitType.returnType()), firstLoopStateIndex + state);
++state;
@@ -1440,18 +1461,21 @@ class InvokerBytecodeGenerator {
boolean isVoid = stepType.returnType() == void.class;
// invoke loop step
- emitLoopHandleInvoke(invoker, steps, c, args, true, stepType, loopLocalStateTypes, firstLoopStateIndex);
+ emitLoopHandleInvoke(invoker, steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex,
+ firstLoopStateIndex);
if (!isVoid) {
emitStoreInsn(BasicType.basicType(stepType.returnType()), firstLoopStateIndex + state);
++state;
}
// invoke loop predicate
- emitLoopHandleInvoke(invoker, preds, c, args, true, predType, loopLocalStateTypes, firstLoopStateIndex);
+ emitLoopHandleInvoke(invoker, preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex,
+ firstLoopStateIndex);
mv.visitJumpInsn(Opcodes.IFNE, lNext);
// invoke fini
- emitLoopHandleInvoke(invoker, finis, c, args, true, finiType, loopLocalStateTypes, firstLoopStateIndex);
+ emitLoopHandleInvoke(invoker, finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex,
+ firstLoopStateIndex);
mv.visitJumpInsn(Opcodes.GOTO, lDone);
// this is the beginning of the next loop clause
@@ -1483,9 +1507,10 @@ class InvokerBytecodeGenerator {
}
private void emitLoopHandleInvoke(Name holder, int handles, int clause, Name args, boolean pushLocalState,
- MethodType type, Class>[] loopLocalStateTypes, int firstLoopStateSlot) {
+ MethodType type, Class>[] loopLocalStateTypes, int clauseDataSlot,
+ int firstLoopStateSlot) {
// load handle for clause
- emitPushArgument(holder, handles);
+ emitPushClauseArray(clauseDataSlot, handles);
emitIconstInsn(clause);
mv.visitInsn(Opcodes.AALOAD);
// load loop state (preceding the other arguments)
@@ -1499,6 +1524,12 @@ class InvokerBytecodeGenerator {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.toMethodDescriptorString(), false);
}
+ private void emitPushClauseArray(int clauseDataSlot, int which) {
+ emitAloadInsn(clauseDataSlot);
+ emitIconstInsn(which - 1);
+ mv.visitInsn(Opcodes.AALOAD);
+ }
+
private void emitZero(BasicType type) {
switch (type) {
case I_TYPE: mv.visitInsn(Opcodes.ICONST_0); break;
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
index 6fdf21e2c91..bcc825bcf15 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
@@ -41,7 +41,6 @@ import java.util.HashMap;
import static java.lang.invoke.LambdaForm.BasicType.*;
import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
import static java.lang.invoke.MethodHandleStatics.*;
-import java.util.Objects;
/**
* The symbolic, non-executable form of a method handle's invocation semantics.
@@ -732,9 +731,9 @@ class LambdaForm {
boolean isLoop(int pos) {
// loop idiom:
// t_{n}:L=MethodHandle.invokeBasic(...)
- // t_{n+1}:L=MethodHandleImpl.loop(types, *, *, *, *, t_{n})
+ // t_{n+1}:L=MethodHandleImpl.loop(types, *, t_{n})
// t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
- return isMatchingIdiom(pos, "loop", 5);
+ return isMatchingIdiom(pos, "loop", 2);
}
/*
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
index 4fe4bb524a7..05ef3c983f0 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
@@ -1689,8 +1689,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
NF_tryFinally = new NamedFunction(MethodHandleImpl.class
.getDeclaredMethod("tryFinally", MethodHandle.class, MethodHandle.class, Object[].class));
NF_loop = new NamedFunction(MethodHandleImpl.class
- .getDeclaredMethod("loop", BasicType[].class, MethodHandle[].class, MethodHandle[].class,
- MethodHandle[].class, MethodHandle[].class, Object[].class));
+ .getDeclaredMethod("loop", BasicType[].class, LoopClauses.class, Object[].class));
NF_throwException = new NamedFunction(MethodHandleImpl.class
.getDeclaredMethod("throwException", Throwable.class));
NF_profileBoolean = new NamedFunction(MethodHandleImpl.class
@@ -1794,12 +1793,13 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
MethodHandle unboxResult = unboxResultHandle(tloop);
- BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLLL();
+ LoopClauses clauseData =
+ new LoopClauses(new MethodHandle[][]{toArray(init), toArray(step), toArray(pred), toArray(fini)});
+ BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
BoundMethodHandle mh;
try {
- mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) toArray(init),
- (Object) toArray(step), (Object) toArray(pred), (Object) toArray(fini), (Object) collectArgs,
- (Object) unboxResult);
+ mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) clauseData,
+ (Object) collectArgs, (Object) unboxResult);
} catch (Throwable ex) {
throw uncaughtException(ex);
}
@@ -1818,23 +1818,20 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
* {@code t12}):
* {@code
* loop=Lambda(a0:L,a1:L)=>{
- * t2:L=BoundMethodHandle$Species_L6.argL0(a0:L); // array of init method handles
- * t3:L=BoundMethodHandle$Species_L6.argL1(a0:L); // array of step method handles
- * t4:L=BoundMethodHandle$Species_L6.argL2(a0:L); // array of pred method handles
- * t5:L=BoundMethodHandle$Species_L6.argL3(a0:L); // array of fini method handles
- * t6:L=BoundMethodHandle$Species_L6.argL4(a0:L); // helper handle to box the arguments into an Object[]
- * t7:L=BoundMethodHandle$Species_L6.argL5(a0:L); // helper handle to unbox the result
- * t8:L=MethodHandle.invokeBasic(t6:L,a1:L); // box the arguments into an Object[]
- * t9:L=MethodHandleImpl.loop(null,t2:L,t3:L,t4:L,t5:L,t6:L); // call the loop executor
- * t10:L=MethodHandle.invokeBasic(t7:L,t9:L);t10:L} // unbox the result; return the result
+ * t2:L=BoundMethodHandle$Species_L3.argL0(a0:L); // LoopClauses holding init, step, pred, fini handles
+ * t3:L=BoundMethodHandle$Species_L3.argL1(a0:L); // helper handle to box the arguments into an Object[]
+ * t4:L=BoundMethodHandle$Species_L3.argL2(a0:L); // helper handle to unbox the result
+ * t5:L=MethodHandle.invokeBasic(t3:L,a1:L); // box the arguments into an Object[]
+ * t6:L=MethodHandleImpl.loop(null,t2:L,t3:L); // call the loop executor
+ * t7:L=MethodHandle.invokeBasic(t4:L,t6:L);t7:L} // unbox the result; return the result
* }
*
- * {@code argL0} through {@code argL3} are the arrays of init, step, pred, and fini method handles.
- * {@code argL4} and {@code argL5} are auxiliary method handles: {@code argL2} boxes arguments and wraps them into
- * {@code Object[]} ({@code ValueConversions.array()}), and {@code argL3} unboxes the result if necessary
+ * {@code argL0} is a LoopClauses instance holding, in a 2-dimensional array, the init, step, pred, and fini method
+ * handles. {@code argL1} and {@code argL2} are auxiliary method handles: {@code argL1} boxes arguments and wraps
+ * them into {@code Object[]} ({@code ValueConversions.array()}), and {@code argL2} unboxes the result if necessary
* ({@code ValueConversions.unbox()}).
*
- * Having {@code t6} and {@code t7} passed in via a BMH and not hardcoded in the lambda form allows to share lambda
+ * Having {@code t3} and {@code t4} passed in via a BMH and not hardcoded in the lambda form allows to share lambda
* forms among loop combinators with the same basic type.
*
* The above template is instantiated by using the {@link LambdaFormEditor} to replace the {@code null} argument to
@@ -1845,15 +1842,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
private static LambdaForm makeLoopForm(MethodType basicType, BasicType[] localVarTypes) {
MethodType lambdaType = basicType.invokerType();
- final int THIS_MH = 0; // the BMH_LLLLLL
+ final int THIS_MH = 0; // the BMH_LLL
final int ARG_BASE = 1; // start of incoming arguments
final int ARG_LIMIT = ARG_BASE + basicType.parameterCount();
int nameCursor = ARG_LIMIT;
- final int GET_INITS = nameCursor++;
- final int GET_STEPS = nameCursor++;
- final int GET_PREDS = nameCursor++;
- final int GET_FINIS = nameCursor++;
+ final int GET_CLAUSE_DATA = nameCursor++;
final int GET_COLLECT_ARGS = nameCursor++;
final int GET_UNBOX_RESULT = nameCursor++;
final int BOXED_ARGS = nameCursor++;
@@ -1864,14 +1858,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
if (lform == null) {
Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
- BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLLL();
+ BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
names[THIS_MH] = names[THIS_MH].withConstraint(data);
- names[GET_INITS] = new Name(data.getterFunction(0), names[THIS_MH]);
- names[GET_STEPS] = new Name(data.getterFunction(1), names[THIS_MH]);
- names[GET_PREDS] = new Name(data.getterFunction(2), names[THIS_MH]);
- names[GET_FINIS] = new Name(data.getterFunction(3), names[THIS_MH]);
- names[GET_COLLECT_ARGS] = new Name(data.getterFunction(4), names[THIS_MH]);
- names[GET_UNBOX_RESULT] = new Name(data.getterFunction(5), names[THIS_MH]);
+ names[GET_CLAUSE_DATA] = new Name(data.getterFunction(0), names[THIS_MH]);
+ names[GET_COLLECT_ARGS] = new Name(data.getterFunction(1), names[THIS_MH]);
+ names[GET_UNBOX_RESULT] = new Name(data.getterFunction(2), names[THIS_MH]);
// t_{i}:L=MethodHandle.invokeBasic(collectArgs:L,a1:L,...);
MethodType collectArgsType = basicType.changeReturnType(Object.class);
@@ -1881,10 +1872,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
System.arraycopy(names, ARG_BASE, args, 1, ARG_LIMIT - ARG_BASE);
names[BOXED_ARGS] = new Name(makeIntrinsic(invokeBasic, Intrinsic.LOOP), args);
- // t_{i+1}:L=MethodHandleImpl.loop(localTypes:L,inits:L,steps:L,preds:L,finis:L,t_{i}:L);
+ // t_{i+1}:L=MethodHandleImpl.loop(localTypes:L,clauses:L,t_{i}:L);
Object[] lArgs =
new Object[]{null, // placeholder for BasicType[] localTypes - will be added by LambdaFormEditor
- names[GET_INITS], names[GET_STEPS], names[GET_PREDS], names[GET_FINIS], names[BOXED_ARGS]};
+ names[GET_CLAUSE_DATA], names[BOXED_ARGS]};
names[LOOP] = new Name(NF_loop, lArgs);
// t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
@@ -1900,22 +1891,52 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
return lform.editor().noteLoopLocalTypesForm(BOXED_ARGS, localVarTypes);
}
+ static class LoopClauses {
+ @Stable final MethodHandle[][] clauses;
+ LoopClauses(MethodHandle[][] clauses) {
+ assert clauses.length == 4;
+ this.clauses = clauses;
+ }
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer("LoopClauses -- ");
+ for (int i = 0; i < 4; ++i) {
+ if (i > 0) {
+ sb.append(" ");
+ }
+ sb.append('<').append(i).append(">: ");
+ MethodHandle[] hs = clauses[i];
+ for (int j = 0; j < hs.length; ++j) {
+ if (j > 0) {
+ sb.append(" ");
+ }
+ sb.append('*').append(j).append(": ").append(hs[j]).append('\n');
+ }
+ }
+ sb.append(" --\n");
+ return sb.toString();
+ }
+ }
/**
* Intrinsified during LambdaForm compilation
* (see {@link InvokerBytecodeGenerator#emitLoop(int)}).
*/
@LambdaForm.Hidden
- static Object loop(BasicType[] localTypes, MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred,
- MethodHandle[] fini, Object... av) throws Throwable {
+ static Object loop(BasicType[] localTypes, LoopClauses clauseData, Object... av) throws Throwable {
+ final MethodHandle[] init = clauseData.clauses[0];
+ final MethodHandle[] step = clauseData.clauses[1];
+ final MethodHandle[] pred = clauseData.clauses[2];
+ final MethodHandle[] fini = clauseData.clauses[3];
int varSize = (int) Stream.of(init).filter(h -> h.type().returnType() != void.class).count();
int nArgs = init[0].type().parameterCount();
Object[] varsAndArgs = new Object[varSize + nArgs];
for (int i = 0, v = 0; i < init.length; ++i) {
- if (init[i].type().returnType() == void.class) {
- init[i].asFixedArity().invokeWithArguments(av);
+ MethodHandle ih = init[i];
+ if (ih.type().returnType() == void.class) {
+ ih.invokeWithArguments(av);
} else {
- varsAndArgs[v++] = init[i].asFixedArity().invokeWithArguments(av);
+ varsAndArgs[v++] = ih.invokeWithArguments(av);
}
}
System.arraycopy(av, 0, varsAndArgs, varSize, nArgs);
@@ -1926,12 +1947,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
MethodHandle s = step[i];
MethodHandle f = fini[i];
if (s.type().returnType() == void.class) {
- s.asFixedArity().invokeWithArguments(varsAndArgs);
+ s.invokeWithArguments(varsAndArgs);
} else {
- varsAndArgs[v++] = s.asFixedArity().invokeWithArguments(varsAndArgs);
+ varsAndArgs[v++] = s.invokeWithArguments(varsAndArgs);
}
- if (!(boolean) p.asFixedArity().invokeWithArguments(varsAndArgs)) {
- return f.asFixedArity().invokeWithArguments(varsAndArgs);
+ if (!(boolean) p.invokeWithArguments(varsAndArgs)) {
+ return f.invokeWithArguments(varsAndArgs);
}
}
}
@@ -1941,12 +1962,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
* This method is bound as the predicate in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle,
* MethodHandle) counting loops}.
*
- * @param counter the counter parameter, passed in during loop execution.
* @param limit the upper bound of the parameter, statically bound at loop creation time.
+ * @param counter the counter parameter, passed in during loop execution.
*
* @return whether the counter has reached the limit.
*/
- static boolean countedLoopPredicate(int counter, int limit) {
+ static boolean countedLoopPredicate(int limit, int counter) {
return counter < limit;
}
@@ -1954,26 +1975,15 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
* This method is bound as the step function in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle,
* MethodHandle) counting loops} to increment the counter.
*
+ * @param limit the upper bound of the loop counter (ignored).
* @param counter the loop counter.
*
* @return the loop counter incremented by 1.
*/
- static int countedLoopStep(int counter, int limit) {
+ static int countedLoopStep(int limit, int counter) {
return counter + 1;
}
- /**
- * This method is bound as a filter in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle,
- * MethodHandle) counting loops} to pass the correct counter value to the body.
- *
- * @param counter the loop counter.
- *
- * @return the loop counter decremented by 1.
- */
- static int decrementCounter(int counter) {
- return counter - 1;
- }
-
/**
* This is bound to initialize the loop-local iterator in {@linkplain MethodHandles#iteratedLoop iterating loops}.
*
@@ -2122,14 +2132,13 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
Throwable t = null;
Object r = null;
try {
- // Use asFixedArity() to avoid unnecessary boxing of last argument for VarargsCollector case.
- r = target.asFixedArity().invokeWithArguments(av);
+ r = target.invokeWithArguments(av);
} catch (Throwable thrown) {
t = thrown;
throw t;
} finally {
Object[] args = target.type().returnType() == void.class ? prepend(av, t) : prepend(av, t, r);
- r = cleanup.asFixedArity().invokeWithArguments(args);
+ r = cleanup.invokeWithArguments(args);
}
return r;
}
@@ -2144,12 +2153,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
MH_arrayIdentity = 5,
MH_countedLoopPred = 6,
MH_countedLoopStep = 7,
- MH_iteratePred = 8,
- MH_initIterator = 9,
+ MH_initIterator = 8,
+ MH_iteratePred = 9,
MH_iterateNext = 10,
- MH_decrementCounter = 11,
- MH_Array_newInstance = 12,
- MH_LIMIT = 13;
+ MH_Array_newInstance = 11,
+ MH_LIMIT = 12;
static MethodHandle getConstantHandle(int idx) {
MethodHandle handle = HANDLES[idx];
@@ -2200,18 +2208,15 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
case MH_countedLoopStep:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopStep",
MethodType.methodType(int.class, int.class, int.class));
- case MH_iteratePred:
- return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate",
- MethodType.methodType(boolean.class, Iterator.class));
case MH_initIterator:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "initIterator",
MethodType.methodType(Iterator.class, Iterable.class));
+ case MH_iteratePred:
+ return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate",
+ MethodType.methodType(boolean.class, Iterator.class));
case MH_iterateNext:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext",
MethodType.methodType(Object.class, Iterator.class));
- case MH_decrementCounter:
- return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "decrementCounter",
- MethodType.methodType(int.class, int.class));
case MH_Array_newInstance:
return IMPL_LOOKUP.findStatic(Array.class, "newInstance",
MethodType.methodType(Object.class, Class.class, int.class));
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
index 86685b605de..104aa220932 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
@@ -44,8 +44,6 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ReflectPermission;
import java.nio.ByteOrder;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@@ -3114,7 +3112,7 @@ assert((int)twice.invokeExact(21) == 42);
* @see MethodHandles#explicitCastArguments
* @since 9
*/
- public static MethodHandle zero(Class> type) {
+ public static MethodHandle zero(Class> type) {
Objects.requireNonNull(type);
return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type);
}
@@ -3403,7 +3401,8 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
throw newIllegalArgumentException("illegal pos", pos, newTypes);
}
addTypes = addTypes.subList(pos, add);
- add -= pos; assert(addTypes.size() == add);
+ add -= pos;
+ assert(addTypes.size() == add);
}
// Do not add types which already match the existing arguments.
if (match > add || !oldTypes.equals(addTypes.subList(0, match))) {
@@ -3413,7 +3412,8 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
throw newIllegalArgumentException("argument lists do not match", oldTypes, newTypes);
}
addTypes = addTypes.subList(match, add);
- add -= match; assert(addTypes.size() == add);
+ add -= match;
+ assert(addTypes.size() == add);
// newTypes: ( P*[pos], M*[match], A*[add] )
// target: ( S*[skip], M*[match] )
MethodHandle adapter = target;
@@ -3423,26 +3423,37 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
// adapter: (S*[skip], M*[match], A*[add] )
if (pos > 0) {
adapter = dropArguments0(adapter, skip, newTypes.subList(0, pos));
- }
+ }
// adapter: (S*[skip], P*[pos], M*[match], A*[add] )
return adapter;
}
/**
- * Adapts a target method handle to match the given parameter type list, if necessary, by adding dummy arguments.
- * Some leading parameters are first skipped; they will be left unchanged and are otherwise ignored.
- * The remaining types in the target's parameter type list must be contained as a sub-list of the given type list,
- * at the given position.
- * Any non-matching parameter types (before or after the matching sub-list) are inserted in corresponding
- * positions of the target method handle's parameters, as if by {@link #dropArguments}.
- * (More precisely, elements in the new list before {@code pos} are inserted into the target list at {@code skip},
- * while elements in the new list after the match beginning at {@code pos} are inserted at the end of the
- * target list.)
- * The target's return type will be unchanged.
+ * Adapts a target method handle to match the given parameter type list. If necessary, adds dummy arguments. Some
+ * leading parameters can be skipped before matching begins. The remaining types in the {@code target}'s parameter
+ * type list must be a sub-list of the {@code newTypes} type list at the starting position {@code pos}. The
+ * resulting handle will have the target handle's parameter type list, with any non-matching parameter types (before
+ * or after the matching sub-list) inserted in corresponding positions of the target's original parameters, as if by
+ * {@link #dropArguments(MethodHandle, int, Class[])}.
+ *
+ * The resulting handle will have the same return type as the target handle.
+ *
+ * In more formal terms, assume these two type lists:
+ * - The target handle has the parameter type list {@code S..., M...}, with as many types in {@code S} as
+ * indicated by {@code skip}. The {@code M} types are those that are supposed to match part of the given type list,
+ * {@code newTypes}.
+ *
- The {@code newTypes} list contains types {@code P..., M..., A...}, with as many types in {@code P} as
+ * indicated by {@code pos}. The {@code M} types are precisely those that the {@code M} types in the target handle's
+ * parameter type list are supposed to match. The types in {@code A} are additional types found after the matching
+ * sub-list.
+ *
+ * Given these assumptions, the result of an invocation of {@code dropArgumentsToMatch} will have the parameter type
+ * list {@code S..., P..., M..., A...}, with the {@code P} and {@code A} types inserted as if by
+ * {@link #dropArguments(MethodHandle, int, Class[])}.
+ *
* @apiNote
- * Two method handles whose argument lists are "effectively identical" (i.e., identical
- * in a common prefix) may be mutually converted to a common type
- * by two calls to {@code dropArgumentsToMatch}, as follows:
+ * Two method handles whose argument lists are "effectively identical" (i.e., identical in a common prefix) may be
+ * mutually converted to a common type by two calls to {@code dropArgumentsToMatch}, as follows:
*
{@code
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
@@ -3461,14 +3472,15 @@ assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
* }
* @param target the method handle to adapt
* @param skip number of targets parameters to disregard (they will be unchanged)
- * @param newTypes the desired argument list of the method handle
+ * @param newTypes the list of types to match {@code target}'s parameter type list to
* @param pos place in {@code newTypes} where the non-skipped target parameters must occur
* @return a possibly adapted method handle
* @throws NullPointerException if either argument is null
* @throws IllegalArgumentException if any element of {@code newTypes} is {@code void.class},
* or if {@code skip} is negative or greater than the arity of the target,
* or if {@code pos} is negative or greater than the newTypes list size,
- * or if the non-skipped target parameter types match the new types at {@code pos}
+ * or if {@code newTypes} does not contain the {@code target}'s non-skipped parameter types at position
+ * {@code pos}.
* @since 9
*/
public static
@@ -3922,6 +3934,113 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
return foldArguments(target, 0, combiner);
}
+ /**
+ * 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.
+ *
+ * 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.
+ *
+ * @apiNote Example:
+ *
{@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"));
+ * }
+ * Here is pseudocode for the resulting adapter. In the code, {@code T}
+ * represents the result type of the {@code target} and resulting adapter.
+ * {@code V}/{@code v} represent the type and value of the parameter and argument
+ * of {@code target} that precedes the folding position; {@code V} also is
+ * the result type of the {@code combiner}. {@code A}/{@code a} denote the
+ * types and values of the {@code N} parameters and arguments at the folding
+ * position. {@code Z}/{@code z} and {@code B}/{@code b} represent the types
+ * and values of the {@code target} parameters and arguments that precede and
+ * follow the folded parameters and arguments starting at {@code pos},
+ * respectively.
+ *
{@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...);
+ * }
+ * }
+ *
+ * Note: The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
+ *
+ * @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 either of the following two conditions holds:
+ * (1) {@code combiner}'s return type is non-{@code void} and not the same as the argument type at position
+ * {@code pos} of the target signature;
+ * (2) 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;
+ }
+
+ /**
+ * As {@see foldArguments(MethodHandle, int, MethodHandle)}, but with the
+ * added capability of selecting the arguments from the targets parameters
+ * to call the combiner with. This allows us to avoid some simple cases of
+ * permutations and padding the combiner with dropArguments to select the
+ * right argument, which may ultimately produce fewer intermediaries.
+ */
+ static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner, int ... argPositions) {
+ MethodType targetType = target.type();
+ MethodType combinerType = combiner.type();
+ Class> rtype = foldArgumentChecks(pos, targetType, combinerType, argPositions);
+ BoundMethodHandle result = target.rebind();
+ boolean dropResult = rtype == void.class;
+ LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType(), argPositions);
+ MethodType newType = targetType;
+ if (!dropResult) {
+ newType = newType.dropParameterTypes(pos, pos + 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();
@@ -4125,32 +4244,69 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* 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.
*
- * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration value and/or a loop
+ * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration variable 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:
- * - Before the loop executes, the initialization of an iteration variable or loop invariant local.
- *
- When a clause executes, an update step for the iteration variable.
- *
- When a clause executes, a predicate execution to test for loop exit.
- *
- If a clause causes a loop exit, a finalizer execution to compute the loop's return value.
+ * terms of method handles, each clause will specify up to four independent actions:
+ * - init: Before the loop executes, the initialization of an iteration variable {@code v} of type {@code V}.
+ *
- step: When a clause executes, an update step for the iteration variable {@code v}.
+ *
- pred: When a clause executes, a predicate execution to test for loop exit.
+ *
- fini: If a clause causes a loop exit, a finalizer execution to compute the loop's return value.
*
+ * The full sequence of all iteration variable types, in clause order, will be notated as {@code (V...)}.
+ * The values themselves will be {@code (v...)}. When we speak of "parameter lists", we will usually
+ * be referring to types, but in some contexts (describing execution) the lists will be of actual values.
*
* 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.
*
- * Each clause function, with the exception of clause initializers, is able to observe the entire loop state,
- * because it will be passed all 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}.
+ * Parameters optional everywhere:
+ * Each clause function is allowed but not required to accept a parameter for each iteration variable {@code v}.
+ * As an exception, the init functions cannot take any {@code v} parameters,
+ * because those values are not yet computed when the init functions are executed.
+ * Any clause function may neglect to take any trailing subsequence of parameters it is entitled to take.
+ * In fact, any clause function may take no arguments at all.
*
+ * Loop parameters:
+ * A clause function may take all the iteration variable values it is entitled to, in which case
+ * it may also take more trailing parameters. Such extra values are called loop parameters,
+ * with their types and values notated as {@code (A...)} and {@code (a...)}.
+ * These become the parameters of the resulting loop handle, to be supplied whenever the loop is executed.
+ * (Since init functions do not accept iteration variables {@code v}, any parameter to an
+ * init function is automatically a loop parameter {@code a}.)
+ * As with iteration variables, clause functions are allowed but not required to accept loop parameters.
+ * These loop parameters act as loop-invariant values visible across the whole loop.
+ *
+ * Parameters visible everywhere:
+ * Each non-init clause function is permitted to observe the entire loop state, because it can be passed the full
+ * list {@code (v... a...)} of current iteration variable values and incoming loop parameters.
+ * The init functions can observe initial pre-loop state, in the form {@code (a...)}.
+ * Most clause functions will not need all of this information, but they will be formally connected to it
+ * as if by {@link #dropArguments}.
+ *
+ * More specifically, we shall use the notation {@code (V*)} to express an arbitrary prefix of a full
+ * sequence {@code (V...)} (and likewise for {@code (v*)}, {@code (A*)}, {@code (a*)}).
+ * In that notation, the general form of an init function parameter list
+ * is {@code (A*)}, and the general form of a non-init function parameter list is {@code (V*)} or {@code (V... A*)}.
+ *
+ * Checking clause structure:
* 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.
+ * corresponds to a place where {@link IllegalArgumentException} will be thrown if the required constraint is not
+ * met by the inputs to the loop combinator.
+ *
+ * Effectively identical sequences:
+ *
+ * A parameter list {@code A} is defined to be effectively identical to another parameter list {@code B}
+ * if {@code A} and {@code B} are identical, or if {@code A} is shorter and is identical with a proper prefix of {@code B}.
+ * When speaking of an unordered set of parameter lists, we say they the set is "effectively identical"
+ * as a whole if the set contains a longest list, and all members of the set are effectively identical to
+ * that longest list.
+ * For example, any set of type sequences of the form {@code (V*)} is effectively identical,
+ * and the same is true if more sequences of the form {@code (V... A*)} are added.
*
* Step 0: Determine clause structure.
- * - The clause array (of type {@code MethodHandle[][]} must be non-{@code null} and contain at least one element.
+ *
- The clause array (of type {@code MethodHandle[][]}) must be non-{@code null} and contain at least one element.
*
- The clause array may not contain {@code null}s or sub-arrays longer than four elements.
*
- 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.
@@ -4158,30 +4314,35 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
*
- Each clause is treated as a four-tuple of functions, called "init", "step", "pred", and "fini".
*
*
- * Step 1A: Determine iteration variables.
- * - Examine init and step function return types, pairwise, to determine each clause's iteration variable type.
- *
- 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).
+ * Step 1A: Determine iteration variable types {@code (V...)}.
+ * - The iteration variable type for each clause is determined using the clause's init and step return types.
+ *
- If both functions are omitted, there is no iteration variable for the corresponding clause ({@code void} is
+ * used as the type to indicate that). If one of them is omitted, the other's return type defines the clause's
+ * iteration variable type. If both are given, the common return type (they must be identical) defines the clause's
+ * iteration variable type.
*
- Form the list of return types (in clause order), omitting all occurrences of {@code void}.
- *
- This list of types is called the "common prefix".
+ *
- This list of types is called the "iteration variable types" ({@code (V...)}).
*
*
- * Step 1B: Determine loop parameters.
- * - If at least one init function is given,
- * - Examine init function parameter lists.
- *
- Omitted init functions are deemed to have {@code null} parameter lists.
- *
- All init function parameter lists must be effectively identical.
- *
- The longest parameter list (which is necessarily unique) is called the "common suffix".
- *
- * - If no init function is given,
- * - Examine the suffixes of the step, pred, and fini parameter lists, after removing the "common prefix".
- *
- The longest of these suffixes is taken as the "common suffix".
- *
+ * Step 1B: Determine loop parameters {@code (A...)}.
+ * - Examine and collect init function parameter lists (which are of the form {@code (A*)}).
+ *
- Examine and collect the suffixes of the step, pred, and fini parameter lists, after removing the iteration variable types.
+ * (They must have the form {@code (V... A*)}; collect the {@code (A*)} parts only.)
+ *
- Do not collect suffixes from step, pred, and fini parameter lists that do not begin with all the iteration variable types.
+ * (These types will checked in step 2, along with all the clause function types.)
+ *
- Omitted clause functions are ignored. (Equivalently, they are deemed to have empty parameter lists.)
+ *
- All of the collected parameter lists must be effectively identical.
+ *
- The longest parameter list (which is necessarily unique) is called the "external parameter list" ({@code (A...)}).
+ *
- If there is no such parameter list, the external parameter list is taken to be the empty sequence.
+ *
- The combined list consisting of iteration variable types followed by the external parameter types is called
+ * the "internal parameter list".
+ *
*
* Step 1C: Determine loop return type.
* - Examine fini function return types, disregarding omitted fini functions.
- *
- If there are no fini functions, use {@code void} as the loop return type.
- *
- Otherwise, use the common return type of the fini functions; they must all be identical.
+ *
- If there are no fini functions, the loop return type is {@code void}.
+ *
- Otherwise, the common return type {@code R} of the fini functions (their return types must be identical) defines the loop return
+ * type.
*
*
* Step 1D: Check other types.
@@ -4190,69 +4351,107 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
*
*
* Step 2: Determine parameter lists.
- * - The parameter list for the resulting loop handle will be the "common suffix".
- *
- 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.)
- *
- 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".
- *
- Every non-init, non-omitted function parameter list must be effectively identical to the common parameter
- * sequence.
+ *
- The parameter list for the resulting loop handle will be the external parameter list {@code (A...)}.
+ *
- The parameter list for init functions will be adjusted to the external parameter list.
+ * (Note that their parameter lists are already effectively identical to this list.)
+ *
- The parameter list for every non-omitted, non-init (step, pred, and fini) function must be
+ * effectively identical to the internal parameter list {@code (V... A...)}.
*
*
* Step 3: Fill in omitted functions.
- * - 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}.)
+ *
- If an init function is omitted, use a {@linkplain #empty default value} for the clause's iteration variable
+ * type.
*
- 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.)
- *
- If a pred function is omitted, the corresponding fini function must also be omitted.
*
- 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.)
- *
- If a fini function is omitted, use a constant {@code null}/zero/{@code false}/{@code void} function of the
+ * as this clause is concerned. Note that in such cases the corresponding fini function is unreachable.)
+ *
- If a fini function is omitted, use a {@linkplain #empty default value} for the
* loop return type.
*
*
* Step 4: Fill in missing parameter types.
- * - 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}.
- *
- 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}.
+ *
- At this point, every init function parameter list is effectively identical to the external parameter list {@code (A...)},
+ * but some lists may be shorter. For every init function with a short parameter list, pad out the end of the list.
+ *
- At this point, every non-init function parameter list is effectively identical to the internal parameter
+ * list {@code (V... A...)}, but some lists may be shorter. For every non-init function with a short parameter list,
+ * pad out the end of the list.
+ *
- Argument lists are padded out by {@linkplain #dropArgumentsToMatch dropping unused trailing arguments}.
*
*
* Final observations.
* - After these steps, all clauses have been adjusted by supplying omitted functions and arguments.
- *
- All init functions have a common parameter type list, which the final loop handle will also have.
- *
- All fini functions have a common return type, which the final loop handle will also have.
- *
- 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.
- *
- Each pair of init and step functions agrees in their return types.
- *
- Each non-init function will be able to observe the current values of all iteration variables, by means of the
- * common prefix.
+ *
- All init functions have a common parameter type list {@code (A...)}, which the final loop handle will also have.
+ *
- All fini functions have a common return type {@code R}, which the final loop handle will also have.
+ *
- All non-init functions have a common parameter type list {@code (V... A...)}, of
+ * (non-{@code void}) iteration variables {@code V} followed by loop parameters.
+ *
- Each pair of init and step functions agrees in their return type {@code V}.
+ *
- Each non-init function will be able to observe the current values {@code (v...)} of all iteration variables.
+ *
- Every function will be able to observe the incoming values {@code (a...)} of all loop parameters.
*
*
+ * Example. As a consequence of step 1A above, the {@code loop} combinator has the following property:
+ *
+ * - Given {@code N} clauses {@code Cn = {null, Sn, Pn}} with {@code n = 1..N}.
+ *
- Suppose predicate handles {@code Pn} are either {@code null} or have no parameters.
+ * (Only one {@code Pn} has to be non-{@code null}.)
+ *
- Suppose step handles {@code Sn} have signatures {@code (B1..BX)Rn}, for some constant {@code X>=N}.
+ *
- Suppose {@code Q} is the count of non-void types {@code Rn}, and {@code (V1...VQ)} is the sequence of those types.
+ *
- It must be that {@code Vn == Bn} for {@code n = 1..min(X,Q)}.
+ *
- The parameter types {@code Vn} will be interpreted as loop-local state elements {@code (V...)}.
+ *
- Any remaining types {@code BQ+1..BX} (if {@code Q
+ * In this example, the loop handle parameters {@code (A...)} were derived from the step functions,
+ * which is natural if most of the loop computation happens in the steps. For some loops,
+ * the burden of computation might be heaviest in the pred functions, and so the pred functions
+ * might need to accept the loop parameter values. For loops with complex exit logic, the fini
+ * functions might need to accept loop parameters, and likewise for loops with complex entry logic,
+ * where the init functions will need the extra parameters. For such reasons, the rules for
+ * determining these parameters are as symmetric as possible, across all clause parts.
+ * In general, the loop parameters function as common invariant values across the whole
+ * loop, while the iteration variables function as common variant values, or (if there is
+ * no step function) as internal loop invariant temporaries.
+ *
* Loop execution.
- * - When the loop is called, the loop input values are saved in locals, to be passed (as the common suffix) to
+ *
- When the loop is called, the loop input values are saved in locals, to be passed to
* every clause function. These locals are loop invariant.
- *
- 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).
- *
- 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).
+ *
- Each init function is executed in clause order (passing the external arguments {@code (a...)})
+ * and the non-{@code void} values are saved (as the iteration variables {@code (v...)}) into locals.
+ * These locals will be loop varying (unless their steps behave as identity functions, as noted above).
+ *
- All function executions (except init functions) will be passed the internal parameter list, consisting of
+ * the non-{@code void} iteration values {@code (v...)} (in clause order) and then the loop inputs {@code (a...)}
+ * (in argument order).
*
- The step and pred functions are then executed, in clause order (step before pred), until a pred function
* returns {@code false}.
- *
- 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.
+ *
- The non-{@code void} result from a step function call is used to update the corresponding value in the
+ * sequence {@code (v...)} of loop variables.
+ * The updated value is immediately visible to all subsequent function calls.
*
- 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.
+ * (of type {@code R}) is returned from the loop as a whole.
+ *
- If all the pred functions always return true, no fini function is ever invoked, and the loop cannot exit
+ * except by throwing an exception.
*
*
- * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the types / values
- * of loop variables; {@code A}/{@code a}, those of arguments passed to the resulting loop; and {@code R}, the
- * result types of finalizers as well as of the resulting loop.
+ * Usage tips.
+ *
+ * - Although each step function will receive the current values of all the loop variables,
+ * sometimes a step function only needs to observe the current value of its own variable.
+ * In that case, the step function may need to explicitly {@linkplain #dropArguments drop all preceding loop variables}.
+ * This will require mentioning their types, in an expression like {@code dropArguments(step, 0, V0.class, ...)}.
+ *
- Loop variables are not required to vary; they can be loop invariant. A clause can create
+ * a loop invariant by a suitable init function with no step, pred, or fini function. This may be
+ * useful to "wire" an incoming loop argument into the step or pred function of an adjacent loop variable.
+ *
- If some of the clause functions are virtual methods on an instance, the instance
+ * itself can be conveniently placed in an initial invariant loop "variable", using an initial clause
+ * like {@code new MethodHandle[]{identity(ObjType.class)}}. In that case, the instance reference
+ * will be the first iteration variable value, and it will be easy to use virtual
+ * methods as clause parts, since all of them will take a leading instance reference matching that value.
+ *
+ *
+ * Here is pseudocode for the resulting loop handle. As above, {@code V} and {@code v} represent the types
+ * and values of loop variables; {@code A} and {@code a} represent arguments passed to the whole loop;
+ * and {@code R} is the common result type of all finalizers as well as of the resulting loop.
*
{@code
* V... init...(A...);
* boolean pred...(V..., A...);
@@ -4270,6 +4469,9 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* }
* }
* }
+ * Note that the parameter type lists {@code (V...)} and {@code (A...)} have been expanded
+ * to their full length, even though individual clause functions may neglect to take them all.
+ * As noted above, missing parameters are filled in as if by {@link #dropArgumentsToMatch}.
*
* @apiNote Example:
*
{@code
@@ -4286,6 +4488,43 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
* assertEquals(120, loop.invoke(5));
* }
+ * The same example, dropping arguments and using combinators:
+ * {@code
+ * // simplified implementation of the factorial function as a loop handle
+ * static int inc(int i) { return i + 1; } // drop acc, k
+ * static int mult(int i, int acc) { return i * acc; } //drop k
+ * static boolean cmp(int i, int k) { return i < k; }
+ * // assume MH_inc, MH_mult, and MH_cmp are handles to the above methods
+ * // null initializer for counter, should initialize to 0
+ * MethodHandle MH_one = MethodHandles.constant(int.class, 1);
+ * MethodHandle MH_pred = MethodHandles.dropArguments(MH_cmp, 1, int.class); // drop acc
+ * MethodHandle MH_fin = MethodHandles.dropArguments(MethodHandles.identity(int.class), 0, int.class); // drop i
+ * 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(720, loop.invoke(6));
+ * }
+ * A similar example, using a helper object to hold a loop parameter:
+ * {@code
+ * // instance-based implementation of the factorial function as a loop handle
+ * static class FacLoop {
+ * final int k;
+ * FacLoop(int k) { this.k = k; }
+ * int inc(int i) { return i + 1; }
+ * int mult(int i, int acc) { return i * acc; }
+ * boolean pred(int i) { return i < k; }
+ * int fin(int i, int acc) { return acc; }
+ * }
+ * // assume MH_FacLoop is a handle to the constructor
+ * // assume MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
+ * // null initializer for counter, should initialize to 0
+ * MethodHandle MH_one = MethodHandles.constant(int.class, 1);
+ * MethodHandle[] instanceClause = new MethodHandle[]{MH_FacLoop};
+ * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
+ * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
+ * MethodHandle loop = MethodHandles.loop(instanceClause, counterClause, accumulatorClause);
+ * assertEquals(5040, loop.invoke(7));
+ * }
*
* @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above.
*
@@ -4301,7 +4540,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
*/
public static MethodHandle loop(MethodHandle[]... clauses) {
// Step 0: determine clause structure.
- checkLoop0(clauses);
+ loopChecks0(clauses);
List init = new ArrayList<>();
List step = new ArrayList<>();
@@ -4318,7 +4557,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
assert Stream.of(init, step, pred, fini).map(List::size).distinct().count() == 1;
final int nclauses = init.size();
- // Step 1A: determine iteration variables.
+ // Step 1A: determine iteration variables (V...).
final List> iterationVariableTypes = new ArrayList<>();
for (int i = 0; i < nclauses; ++i) {
MethodHandle in = init.get(i);
@@ -4326,7 +4565,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
if (in == null && st == null) {
iterationVariableTypes.add(void.class);
} else if (in != null && st != null) {
- checkLoop1a(i, in, st);
+ loopChecks1a(i, in, st);
iterationVariableTypes.add(in.type().returnType());
} else {
iterationVariableTypes.add(in == null ? st.type().returnType() : in.type().returnType());
@@ -4335,20 +4574,20 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
final List> commonPrefix = iterationVariableTypes.stream().filter(t -> t != void.class).
collect(Collectors.toList());
- // Step 1B: determine loop parameters.
+ // Step 1B: determine loop parameters (A...).
final List> commonSuffix = buildCommonSuffix(init, step, pred, fini, commonPrefix.size());
- checkLoop1b(init, commonSuffix);
+ loopChecks1b(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);
+ loopChecks1cd(pred, fini, loopReturnType);
// Step 2: determine parameter lists.
final List> commonParameterSequence = new ArrayList<>(commonPrefix);
commonParameterSequence.addAll(commonSuffix);
- checkLoop2(step, pred, fini, commonParameterSequence);
+ loopChecks2(step, pred, fini, commonParameterSequence);
// Step 3: fill in omitted functions.
for (int i = 0; i < nclauses; ++i) {
@@ -4368,10 +4607,11 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
}
// Step 4: fill in missing parameter types.
- List finit = fillParameterTypes(init, commonSuffix);
- List fstep = fillParameterTypes(step, commonParameterSequence);
- List fpred = fillParameterTypes(pred, commonParameterSequence);
- List ffini = fillParameterTypes(fini, commonParameterSequence);
+ // Also convert all handles to fixed-arity handles.
+ List finit = fixArities(fillParameterTypes(init, commonSuffix));
+ List fstep = fixArities(fillParameterTypes(step, commonParameterSequence));
+ List fpred = fixArities(fillParameterTypes(pred, commonParameterSequence));
+ List ffini = fixArities(fillParameterTypes(fini, commonParameterSequence));
assert finit.stream().map(MethodHandle::type).map(MethodType::parameterList).
allMatch(pl -> pl.equals(commonSuffix));
@@ -4381,6 +4621,79 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, finit, fstep, fpred, ffini);
}
+ private static void loopChecks0(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 loopChecks1a(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 List> longestParameterList(Stream mhs, int skipSize) {
+ final List> empty = List.of();
+ final List> longest = mhs.filter(Objects::nonNull).
+ // take only those that can contribute to a common suffix because they are longer than the prefix
+ map(MethodHandle::type).
+ filter(t -> t.parameterCount() > skipSize).
+ map(MethodType::parameterList).
+ reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty);
+ return longest.size() == 0 ? empty : longest.subList(skipSize, longest.size());
+ }
+
+ private static List> longestParameterList(List>> lists) {
+ final List> empty = List.of();
+ return lists.stream().reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty);
+ }
+
+ private static List> buildCommonSuffix(List init, List step, List pred, List fini, int cpSize) {
+ final List> longest1 = longestParameterList(Stream.of(step, pred, fini).flatMap(List::stream), cpSize);
+ final List> longest2 = longestParameterList(init.stream(), 0);
+ return longestParameterList(Arrays.asList(longest1, longest2));
+ }
+
+ private static void loopChecks1b(List init, List> commonSuffix) {
+ if (init.stream().filter(Objects::nonNull).map(MethodHandle::type).
+ anyMatch(t -> !t.effectivelyIdenticalParameters(0, commonSuffix))) {
+ throw newIllegalArgumentException("found non-effectively identical init parameter type lists: " + init +
+ " (common suffix: " + commonSuffix + ")");
+ }
+ }
+
+ private static void loopChecks1cd(List pred, List 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 loopChecks2(List step, List pred, List fini, List> commonParameterSequence) {
+ if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type).
+ anyMatch(t -> !t.effectivelyIdenticalParameters(0, commonParameterSequence))) {
+ throw newIllegalArgumentException("found non-effectively identical parameter type lists:\nstep: " + step +
+ "\npred: " + pred + "\nfini: " + fini + " (common parameter sequence: " + commonParameterSequence + ")");
+ }
+ }
+
private static List fillParameterTypes(List hs, final List> targetParams) {
return hs.stream().map(h -> {
int pc = h.type().parameterCount();
@@ -4389,27 +4702,65 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
}).collect(Collectors.toList());
}
+ private static List fixArities(List hs) {
+ return hs.stream().map(MethodHandle::asFixedArity).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}.
+ * 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}.
*
- * 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}.
+ * The {@code pred} handle describes the loop condition; and {@code body}, its body. The loop resulting from this
+ * method will, in each iteration, first evaluate the predicate and then execute its body (if the predicate
+ * evaluates to {@code true}).
+ * The loop will terminate once the predicate evaluates to {@code false} (the body will not be executed in this case).
+ *
+ * The {@code init} handle describes the initial value of an additional optional loop-local variable.
+ * In each iteration, this loop-local variable, if present, will be passed to the {@code body}
+ * and updated with the value returned from its invocation. The result of loop execution will be
+ * the final value of the additional loop-local variable (if present).
+ *
+ * The following rules hold for these argument handles:
+ * - The {@code body} handle must not be {@code null}; its type must be of the form
+ * {@code (V A...)V}, where {@code V} is non-{@code void}, or else {@code (A...)void}.
+ * (In the {@code void} case, we assign the type {@code void} to the name {@code V},
+ * and we will write {@code (V A...)V} with the understanding that a {@code void} type {@code V}
+ * is quietly dropped from the parameter list, leaving {@code (A...)V}.)
+ *
- The parameter list {@code (V A...)} of the body is called the internal parameter list.
+ * It will constrain the parameter lists of the other loop parts.
+ *
- If the iteration variable type {@code V} is dropped from the internal parameter list, the resulting shorter
+ * list {@code (A...)} is called the external parameter list.
+ *
- The body return type {@code V}, if non-{@code void}, determines the type of an
+ * additional state variable of the loop.
+ * The body must both accept and return a value of this type {@code V}.
+ *
- If {@code init} is non-{@code null}, it must have return type {@code V}.
+ * Its parameter list (of some form {@code (A*)}) must be
+ * effectively identical
+ * to the external parameter list {@code (A...)}.
+ *
- If {@code init} is {@code null}, the loop variable will be initialized to its
+ * {@linkplain #empty default value}.
+ *
- The {@code pred} handle must not be {@code null}. It must have {@code boolean} as its return type.
+ * Its parameter list (either empty or of the form {@code (V A*)}) must be
+ * effectively identical to the internal parameter list.
+ *
+ *
+ * The resulting loop handle's result type and parameter signature are determined as follows:
+ * - The loop handle's result type is the result type {@code V} of the body.
+ *
- The loop handle's parameter types are the types {@code (A...)},
+ * from the external parameter list.
+ *
*
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
* the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument
* passed to the loop.
*
{@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);
+ * 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;
* }
@@ -4434,58 +4785,96 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* }
*
*
- * @implSpec The implementation of this method is equivalent to:
+ * @apiNote The implementation of this method can be expressed as follows:
*
{@code
* MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
+ * MethodHandle fini = (body.type().returnType() == void.class
+ * ? null : identity(body.type().returnType()));
* MethodHandle[]
- * checkExit = {null, null, pred, identity(init.type().returnType())},
- * varBody = {init, body};
+ * checkExit = { null, null, pred, fini },
+ * varBody = { init, body };
* return loop(checkExit, varBody);
* }
* }
*
- * @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}.
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param pred condition for the loop, which may not be {@code null}. Its result type must be {@code boolean}. See
+ * above for other constraints.
+ * @param body body of the loop, which may not be {@code null}. It controls the loop parameters and result type.
+ * See above for other constraints.
*
- * @return the value of the loop variable as the loop terminates.
- * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ * @return a method handle implementing the {@code while} loop as described by the arguments.
+ * @throws IllegalArgumentException if the rules for the arguments are violated.
+ * @throws NullPointerException if {@code pred} or {@code body} are {@code null}.
*
- * @see MethodHandles#loop(MethodHandle[][])
+ * @see #loop(MethodHandle[][])
+ * @see #doWhileLoop(MethodHandle, MethodHandle, MethodHandle)
* @since 9
*/
public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
- MethodHandle fin = init == null || init.type().returnType() == void.class ? zero(void.class) :
- identity(init.type().returnType());
- MethodHandle[] checkExit = {null, null, pred, fin};
- MethodHandle[] varBody = {init, body};
+ whileLoopChecks(init, pred, body);
+ MethodHandle fini = identityOrVoid(body.type().returnType());
+ MethodHandle[] checkExit = { null, null, pred, fini };
+ 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}.
+ * Constructs a {@code do-while} loop from an initializer, a body, and a predicate.
+ * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
*
- * 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}.
+ * The {@code pred} handle describes the loop condition; and {@code body}, its body. The loop resulting from this
+ * method will, in each iteration, first execute its body and then evaluate the predicate.
+ * The loop will terminate once the predicate evaluates to {@code false} after an execution of the body.
+ *
+ * The {@code init} handle describes the initial value of an additional optional loop-local variable.
+ * In each iteration, this loop-local variable, if present, will be passed to the {@code body}
+ * and updated with the value returned from its invocation. The result of loop execution will be
+ * the final value of the additional loop-local variable (if present).
+ *
+ * The following rules hold for these argument handles:
+ * - The {@code body} handle must not be {@code null}; its type must be of the form
+ * {@code (V A...)V}, where {@code V} is non-{@code void}, or else {@code (A...)void}.
+ * (In the {@code void} case, we assign the type {@code void} to the name {@code V},
+ * and we will write {@code (V A...)V} with the understanding that a {@code void} type {@code V}
+ * is quietly dropped from the parameter list, leaving {@code (A...)V}.)
+ *
- The parameter list {@code (V A...)} of the body is called the internal parameter list.
+ * It will constrain the parameter lists of the other loop parts.
+ *
- If the iteration variable type {@code V} is dropped from the internal parameter list, the resulting shorter
+ * list {@code (A...)} is called the external parameter list.
+ *
- The body return type {@code V}, if non-{@code void}, determines the type of an
+ * additional state variable of the loop.
+ * The body must both accept and return a value of this type {@code V}.
+ *
- If {@code init} is non-{@code null}, it must have return type {@code V}.
+ * Its parameter list (of some form {@code (A*)}) must be
+ * effectively identical
+ * to the external parameter list {@code (A...)}.
+ *
- If {@code init} is {@code null}, the loop variable will be initialized to its
+ * {@linkplain #empty default value}.
+ *
- The {@code pred} handle must not be {@code null}. It must have {@code boolean} as its return type.
+ * Its parameter list (either empty or of the form {@code (V A*)}) must be
+ * effectively identical to the internal parameter list.
+ *
+ *
+ * The resulting loop handle's result type and parameter signature are determined as follows:
+ * - The loop handle's result type is the result type {@code V} of the body.
+ *
- The loop handle's parameter types are the types {@code (A...)},
+ * from the external parameter list.
+ *
*
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
* the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument
* passed to the loop.
*
{@code
- * V init(A);
- * boolean pred(V, A);
- * V body(V, A);
- * V doWhileLoop(A a) {
- * V v = init(a);
+ * 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));
+ * v = body(v, a...);
+ * } while (pred(v, a...));
* return v;
* }
* }
@@ -4502,303 +4891,656 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* }
*
* - * @implSpec The implementation of this method is equivalent to: + * @apiNote The implementation of this method can be expressed as follows: *
{@code
* MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
- * MethodHandle[] clause = { init, body, pred, identity(init.type().returnType()) };
+ * MethodHandle fini = (body.type().returnType() == void.class
+ * ? null : identity(body.type().returnType()));
+ * MethodHandle[] clause = { init, body, pred, fini };
* return loop(clause);
* }
* }
*
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param body body of the loop, which may not be {@code null}. It controls the loop parameters and result type.
+ * See above for other constraints.
+ * @param pred condition for the loop, which may not be {@code null}. Its result type must be {@code boolean}. See
+ * above for other constraints.
*
- * @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 a method handle implementing the {@code while} loop as described by the arguments.
+ * @throws IllegalArgumentException if the rules for the arguments are violated.
+ * @throws NullPointerException if {@code pred} or {@code body} are {@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[][])
+ * @see #loop(MethodHandle[][])
+ * @see #whileLoop(MethodHandle, MethodHandle, MethodHandle)
* @since 9
*/
public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
- MethodHandle fin = init == null || init.type().returnType() == void.class ? zero(void.class) :
- identity(init.type().returnType());
- MethodHandle[] clause = {init, body, pred, fin};
+ whileLoopChecks(init, pred, body);
+ MethodHandle fini = identityOrVoid(body.type().returnType());
+ MethodHandle[] clause = {init, body, pred, fini };
return loop(clause);
}
+ private static void whileLoopChecks(MethodHandle init, MethodHandle pred, MethodHandle body) {
+ Objects.requireNonNull(pred);
+ Objects.requireNonNull(body);
+ MethodType bodyType = body.type();
+ Class> returnType = bodyType.returnType();
+ List- * 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}. + * The number of iterations is determined by the {@code iterations} handle evaluation result. + * The loop counter {@code i} is an extra loop iteration variable of type {@code int}. + * It will be initialized to 0 and incremented by 1 in each iteration. + *
+ * If the {@code body} handle returns a non-{@code void} type {@code V}, a leading loop iteration variable + * of that type is also present. This variable is initialized using the optional {@code init} handle, + * or to the {@linkplain #empty default value} of type {@code V} if that handle is {@code null}. + *
+ * In each iteration, the iteration variables are passed to an invocation of the {@code body} handle. + * A non-{@code void} value returned from the body (of type {@code V}) updates the leading + * iteration variable. + * The result of the loop handle execution will be the final {@code V} value of that variable + * (or {@code void} if there is no {@code V} variable). + *
+ * The following rules hold for the argument handles:
+ * The resulting loop handle's result type and parameter signature are determined as follows:
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of - * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument - * passed to the loop. + * the second loop variable as well as the result type of the loop; and {@code A...}/{@code a...} represent + * arguments passed to the loop. *
{@code
- * int iterations(A);
- * V init(A);
- * V body(int, V, A);
- * V countedLoop(A a) {
- * int end = iterations(a);
- * V v = init(a);
+ * int iterations(A...);
+ * V init(A...);
+ * V body(V, int, 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);
+ * v = body(v, i, a...);
* }
* return v;
* }
* }
* - * @apiNote Example: + * @apiNote Example with a fully conformant body method: *
{@code
* // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
* // => a variation on a well known theme
- * static String start(String arg) { return arg; }
- * static String step(int counter, String v, String arg) { return "na " + v; }
- * // assume MH_start and MH_step are handles to the two methods above
+ * static String step(String v, int counter, String init) { return "na " + v; }
+ * // assume MH_step is a handle to the method above
* MethodHandle fit13 = MethodHandles.constant(int.class, 13);
- * MethodHandle loop = MethodHandles.countedLoop(fit13, MH_start, MH_step);
+ * MethodHandle start = MethodHandles.identity(String.class);
+ * MethodHandle loop = MethodHandles.countedLoop(fit13, start, MH_step);
* assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
* }
- *
* - * @implSpec The implementation of this method is equivalent to: + * @apiNote Example with the simplest possible body method type, + * and passing the number of iterations to the loop invocation: + *
{@code
+ * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
+ * // => a variation on a well known theme
+ * static String step(String v, int counter ) { return "na " + v; }
+ * // assume MH_step is a handle to the method above
+ * MethodHandle count = MethodHandles.dropArguments(MethodHandles.identity(int.class), 1, String.class);
+ * MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class);
+ * MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i) -> "na " + v
+ * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "Lambdaman!"));
+ * }
+ * + * @apiNote Example that treats the number of iterations, string to append to, and string to append + * as loop parameters: + *
{@code
+ * // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
+ * // => a variation on a well known theme
+ * static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; }
+ * // assume MH_step is a handle to the method above
+ * MethodHandle count = MethodHandles.identity(int.class);
+ * MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class, String.class);
+ * MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i, _, pre, _) -> pre + " " + v
+ * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "na", "Lambdaman!"));
+ * }
+ * + * @apiNote Example that illustrates the usage of {@link #dropArgumentsToMatch(MethodHandle, int, List, int)} + * to enforce a loop type: + *
{@code
+ * // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
+ * // => a variation on a well known theme
+ * static String step(String v, int counter, String pre) { return pre + " " + v; }
+ * // assume MH_step is a handle to the method above
+ * MethodType loopType = methodType(String.class, String.class, int.class, String.class);
+ * MethodHandle count = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(int.class), 0, loopType.parameterList(), 1);
+ * MethodHandle start = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(String.class), 0, loopType.parameterList(), 2);
+ * MethodHandle body = MethodHandles.dropArgumentsToMatch(MH_step, 2, loopType.parameterList(), 0);
+ * MethodHandle loop = MethodHandles.countedLoop(count, start, body); // (v, i, pre, _, _) -> pre + " " + v
+ * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("na", 13, "Lambdaman!"));
+ * }
+ * + * @apiNote The implementation of this method can be expressed as follows: *
{@code
* MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
- * return countedLoop(null, iterations, init, body); // null => constant zero
+ * return countedLoop(empty(iterations.type()), iterations, init, body);
* }
* }
*
- * @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.
+ * @param iterations a non-{@code null} handle to return the number of iterations this loop should run. The handle's
+ * result type must be {@code int}. See above for other constraints.
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param body body of the loop, which may not be {@code null}.
+ * It controls the loop parameters and result type in the standard case (see above for details).
+ * It must accept its own return type (if non-void) plus an {@code int} parameter (for the counter),
+ * and may accept any number of additional types.
+ * See above for other constraints.
*
* @return a method handle representing the loop.
- * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ * @throws NullPointerException if either of the {@code iterations} or {@code body} handles is {@code null}.
+ * @throws IllegalArgumentException if any argument violates the rules formulated above.
*
+ * @see #countedLoop(MethodHandle, MethodHandle, MethodHandle, MethodHandle)
* @since 9
*/
public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
- return countedLoop(null, iterations, init, body);
+ return countedLoop(empty(iterations.type()), 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}.
+ * Constructs a loop that counts over a range of numbers.
+ * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
* - * 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}. + * The loop counter {@code i} is a loop iteration variable of type {@code int}. + * The {@code start} and {@code end} handles determine the start (inclusive) and end (exclusive) + * values of the loop counter. + * The loop counter 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. + *
+ * If the {@code body} handle returns a non-{@code void} type {@code V}, a leading loop iteration variable + * of that type is also present. This variable is initialized using the optional {@code init} handle, + * or to the {@linkplain #empty default value} of type {@code V} if that handle is {@code null}. + *
+ * In each iteration, the iteration variables are passed to an invocation of the {@code body} handle. + * A non-{@code void} value returned from the body (of type {@code V}) updates the leading + * iteration variable. + * The result of the loop handle execution will be the final {@code V} value of that variable + * (or {@code void} if there is no {@code V} variable). + *
+ * The following rules hold for the argument handles:
+ * The resulting loop handle's result type and parameter signature are determined as follows:
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of - * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument - * passed to the loop. + * the second loop variable as well as the result type of the loop; and {@code A...}/{@code a...} represent + * arguments passed to the loop. *
{@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);
+ * int start(A...);
+ * int end(A...);
+ * V init(A...);
+ * V body(V, int, A...);
+ * V countedLoop(A... a...) {
+ * int e = end(a...);
+ * int s = start(a...);
+ * V v = init(a...);
* for (int i = s; i < e; ++i) {
- * v = body(i, v, a);
+ * v = body(v, i, a...);
* }
* return v;
* }
* }
*
* - * @implSpec The implementation of this method is equivalent to: + * @apiNote The implementation of this method can be expressed as follows: *
{@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 counter + 1
+ * // MH_predicate: (int limit, int counter) -> counter < limit
+ * Class> counterType = start.type().returnType(); // int
+ * Class> returnType = body.type().returnType();
+ * MethodHandle incr = MH_increment, pred = MH_predicate, retv = null;
+ * if (returnType != void.class) { // ignore the V variable
+ * incr = dropArguments(incr, 1, returnType); // (limit, v, i) => (limit, i)
+ * pred = dropArguments(pred, 1, returnType); // ditto
+ * retv = dropArguments(identity(returnType), 0, counterType); // ignore limit
+ * }
+ * body = dropArguments(body, 0, counterType); // ignore the limit variable
* MethodHandle[]
- * indexVar = {start, MH_increment}, // i = start; i = i+1
- * loopLimit = {end, null,
- * filterArgument(MH_lessThan, 0, MH_decrement), returnVar}, // i-1
*
- * @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.
+ * @param start a non-{@code null} handle to return the start value of the loop counter, which must be {@code int}.
+ * See above for other constraints.
+ * @param end a non-{@code null} handle to return the end value of the loop counter (the loop will run to
+ * {@code end-1}). The result type must be {@code int}. See above for other constraints.
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param body body of the loop, which may not be {@code null}.
+ * It controls the loop parameters and result type in the standard case (see above for details).
+ * It must accept its own return type (if non-void) plus an {@code int} parameter (for the counter),
+ * and may accept any number of additional types.
+ * See above for other constraints.
*
* @return a method handle representing the loop.
- * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ * @throws NullPointerException if any of the {@code start}, {@code end}, or {@code body} handles is {@code null}.
+ * @throws IllegalArgumentException if any argument violates the rules formulated above.
*
+ * @see #countedLoop(MethodHandle, MethodHandle, MethodHandle)
* @since 9
*/
public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
- Class> resultType;
- MethodHandle actualInit;
- if (init == null) {
- resultType = body == null ? void.class : body.type().returnType();
- actualInit = empty(methodType(resultType));
- } else {
- resultType = init.type().returnType();
- actualInit = init;
+ countedLoopChecks(start, end, init, body);
+ Class> counterType = start.type().returnType(); // int, but who's counting?
+ Class> limitType = end.type().returnType(); // yes, int again
+ Class> returnType = body.type().returnType();
+ MethodHandle incr = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep);
+ MethodHandle pred = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred);
+ MethodHandle retv = null;
+ if (returnType != void.class) {
+ incr = dropArguments(incr, 1, returnType); // (limit, v, i) => (limit, i)
+ pred = dropArguments(pred, 1, returnType); // ditto
+ retv = dropArguments(identity(returnType), 0, counterType);
+ }
+ body = dropArguments(body, 0, counterType); // ignore the limit variable
+ MethodHandle[]
+ loopLimit = { end, null, pred, retv }, // limit = end(); i < limit || return v
+ bodyClause = { init, body }, // v = init(); v = body(v, i)
+ indexVar = { start, incr }; // i = start(); i = i + 1
+ return loop(loopLimit, bodyClause, indexVar);
+ }
+
+ private static void countedLoopChecks(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
+ Objects.requireNonNull(start);
+ Objects.requireNonNull(end);
+ Objects.requireNonNull(body);
+ Class> counterType = start.type().returnType();
+ if (counterType != int.class) {
+ MethodType expected = start.type().changeReturnType(int.class);
+ throw misMatchedTypes("start function", start.type(), expected);
+ } else if (end.type().returnType() != counterType) {
+ MethodType expected = end.type().changeReturnType(counterType);
+ throw misMatchedTypes("end function", end.type(), expected);
+ }
+ MethodType bodyType = body.type();
+ Class> returnType = bodyType.returnType();
+ List- * 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. + * The iterator itself will be determined by the evaluation of the {@code iterator} handle. + * Each value it produces will be stored in a loop iteration variable of type {@code T}. + *
+ * If the {@code body} handle returns a non-{@code void} type {@code V}, a leading loop iteration variable + * of that type is also present. This variable is initialized using the optional {@code init} handle, + * or to the {@linkplain #empty default value} of type {@code V} if that handle is {@code null}. + *
+ * In each iteration, the iteration variables are passed to an invocation of the {@code body} handle. + * A non-{@code void} value returned from the body (of type {@code V}) updates the leading + * iteration variable. + * The result of the loop handle execution will be the final {@code V} value of that variable + * (or {@code void} if there is no {@code V} variable). + *
+ * The following rules hold for the argument handles:
+ * The type {@code T} may be either a primitive or reference.
+ * Since type {@code Iterator
+ * The resulting loop handle's result type and parameter signature are determined as follows:
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
* the loop variable as well as the result type of the loop; {@code T}/{@code t}, that of the elements of the
- * structure the loop iterates over, and {@code A}/{@code a}, that of the argument passed to the loop.
+ * structure the loop iterates over, and {@code A...}/{@code a...} represent arguments passed to the loop.
*
- * The type {@code T} may be either a primitive or reference.
- * Since type {@code Iterator
* @apiNote Example:
*
- * @implSpec The implementation of this method is equivalent to (excluding error handling):
+ * @apiNote The implementation of this method can be expressed approximately as follows:
*
- * 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.
- *
- * @apiNote Example:
- * Here is pseudocode for the resulting adapter. In the code, {@code T}
- * represents the result type of the {@code target} and resulting adapter.
- * {@code V}/{@code v} represent the type and value of the parameter and argument
- * of {@code target} that precedes the folding position; {@code V} also is
- * the result type of the {@code combiner}. {@code A}/{@code a} denote the
- * types and values of the {@code N} parameters and arguments at the folding
- * position. {@code Z}/{@code z} and {@code B}/{@code b} represent the types
- * and values of the {@code target} parameters and arguments that precede and
- * follow the folded parameters and arguments starting at {@code pos},
- * respectively.
- *
- * Note: The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
- * variable-arity method handle}, even if the original target method handle was.
- *
- * @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;
- }
-
- /**
- * As {@see foldArguments(MethodHandle, int, MethodHandle)}, but with the
- * added capability of selecting the arguments from the targets parameters
- * to call the combiner with. This allows us to avoid some simple cases of
- * permutations and padding the combiner with dropArguments to select the
- * right argument, which may ultimately produce fewer intermediaries.
- */
- static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner, int ... argPositions) {
- MethodType targetType = target.type();
- MethodType combinerType = combiner.type();
- Class> rtype = foldArgumentChecks(pos, targetType, combinerType, argPositions);
- BoundMethodHandle result = target.rebind();
- boolean dropResult = rtype == void.class;
- LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType(), argPositions);
- MethodType newType = targetType;
- if (!dropResult) {
- newType = newType.dropParameterTypes(pos, pos + 1);
- }
- result = result.copyWithExtendL(newType, lform, combiner);
- return result;
- }
-
- 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 List Unless overridden by a subclass, a new non-minimal
+ * CompletableFuture with all methods available can be obtained from
+ * a minimal CompletionStage via {@link #toCompletableFuture()}.
+ * For example, completion of a minimal stage can be awaited by
+ *
+ * The JNI documents specify that, at least for returning
+ * values from native methods, a Java boolean value is converted
+ * to the value-set 0..1 by first truncating to a byte (0..255 or
+ * maybe -128..127) and then testing against zero. Thus, Java
+ * booleans in non-Java data structures are by convention
+ * represented as 8-bit containers containing either zero (for
+ * false) or any non-zero value (for true).
+ *
+ * Java booleans in the heap are also stored in bytes, but are
+ * strongly normalized to the value-set 0..1 (i.e., they are
+ * truncated to the least-significant bit).
+ *
+ * The main reason for having different conventions for
+ * conversion is performance: Truncation to the least-significant
+ * bit can be usually implemented with fewer (machine)
+ * instructions than byte testing against zero.
+ *
+ * A number of Unsafe methods load boolean values from the heap
+ * as bytes. Unsafe converts those values according to the JNI
+ * rules (i.e, using the "testing against zero" convention). The
+ * method {@code byte2bool} implements that conversion.
+ *
+ * @param b the byte to be converted to boolean
+ * @return the result of the conversion
+ */
@ForceInline
private boolean byte2bool(byte b) {
- return b > 0;
+ return b != 0;
}
+ /**
+ * Convert a boolean value to a byte. The return value is strongly
+ * normalized to the value-set 0..1 (i.e., the value is truncated
+ * to the least-significant bit). See {@link #byte2bool(byte)} for
+ * more details on conversion conventions.
+ *
+ * @param b the boolean to be converted to byte (and then normalized)
+ * @return the result of the conversion
+ */
@ForceInline
private byte bool2byte(boolean b) {
return b ? (byte)1 : (byte)0;
diff --git a/jdk/src/java.base/share/classes/jdk/internal/misc/VM.java b/jdk/src/java.base/share/classes/jdk/internal/misc/VM.java
index 9fe63d56e5e..695795e4338 100644
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/VM.java
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/VM.java
@@ -50,7 +50,7 @@ public class VM {
public static void initLevel(int value) {
synchronized (lock) {
if (value <= initLevel || value > SYSTEM_BOOTED)
- throw new InternalError();
+ throw new InternalError("Bad level: " + value);
initLevel = value;
lock.notifyAll();
}
diff --git a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
index b9b93b275ef..ca40bf2706e 100644
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
@@ -153,27 +153,24 @@ public final class ModuleBootstrap {
boolean addAllDefaultModules = false;
boolean addAllSystemModules = false;
boolean addAllApplicationModules = false;
- String propValue = getAndRemoveProperty("jdk.module.addmods");
- if (propValue != null) {
- for (String mod: propValue.split(",")) {
- switch (mod) {
- case ALL_DEFAULT:
- addAllDefaultModules = true;
- break;
- case ALL_SYSTEM:
- addAllSystemModules = true;
- break;
- case ALL_MODULE_PATH:
- addAllApplicationModules = true;
- break;
- default :
- roots.add(mod);
- }
+ for (String mod: getExtraAddModules()) {
+ switch (mod) {
+ case ALL_DEFAULT:
+ addAllDefaultModules = true;
+ break;
+ case ALL_SYSTEM:
+ addAllSystemModules = true;
+ break;
+ case ALL_MODULE_PATH:
+ addAllApplicationModules = true;
+ break;
+ default :
+ roots.add(mod);
}
}
// --limit-modules
- propValue = getAndRemoveProperty("jdk.module.limitmods");
+ String propValue = getAndRemoveProperty("jdk.module.limitmods");
if (propValue != null) {
Set
+ *
*
* {@code
- * Iterator
* {@code
- * // reverse a list
- * static List
*
- * @param iterator a handle to return the iterator to start the loop.
- * The handle must have {@link java.util.Iterator} as its return type.
- * 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.
+ * @param iterator an optional handle to return the iterator to start the loop.
+ * If non-{@code null}, the handle must return {@link java.util.Iterator} or a subtype.
+ * See above for other constraints.
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param body body of the loop, which may not be {@code null}.
+ * It controls the loop parameters and result type in the standard case (see above for details).
+ * It must accept its own return type (if non-void) plus a {@code T} parameter (for the iterated values),
+ * and may accept any number of additional types.
+ * See above for other constraints.
*
* @return a method handle embodying the iteration loop functionality.
- * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ * @throws NullPointerException if the {@code body} handle is {@code null}.
+ * @throws IllegalArgumentException if any argument violates the above requirements.
*
* @since 9
*/
public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
- checkIteratedLoop(iterator, body);
- Class> resultType = init == null ?
- body == null ? void.class : body.type().returnType() :
- init.type().returnType();
- boolean voidResult = resultType == void.class;
+ Class> iterableType = iteratedLoopChecks(iterator, init, body);
+ Class> returnType = body.type().returnType();
+ MethodHandle hasNext = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iteratePred);
+ MethodHandle nextRaw = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext);
+ MethodHandle startIter;
+ MethodHandle nextVal;
+ {
+ MethodType iteratorType;
+ if (iterator == null) {
+ // derive argument type from body, if available, else use Iterable
+ startIter = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator);
+ iteratorType = startIter.type().changeParameterType(0, iterableType);
+ } else {
+ // force return type to the internal iterator class
+ iteratorType = iterator.type().changeReturnType(Iterator.class);
+ startIter = iterator;
+ }
+ Class> ttype = body.type().parameterType(returnType == void.class ? 0 : 1);
+ MethodType nextValType = nextRaw.type().changeReturnType(ttype);
- MethodHandle initIterator;
- if (iterator == null) {
- MethodHandle initit = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator);
- initIterator = initit.asType(initit.type().changeParameterType(0,
- body.type().parameterType(voidResult ? 1 : 2)));
- } else {
- initIterator = iterator.asType(iterator.type().changeReturnType(Iterator.class));
+ // perform the asType transforms under an exception transformer, as per spec.:
+ try {
+ startIter = startIter.asType(iteratorType);
+ nextVal = nextRaw.asType(nextValType);
+ } catch (WrongMethodTypeException ex) {
+ throw new IllegalArgumentException(ex);
+ }
}
- Class> ttype = body.type().parameterType(0);
-
- MethodHandle returnVar =
- dropArguments(voidResult ? zero(void.class) : identity(resultType), 0, Iterator.class);
- 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)};
+ MethodHandle retv = null, step = body;
+ if (returnType != void.class) {
+ // the simple thing first: in (I V A...), drop the I to get V
+ retv = dropArguments(identity(returnType), 0, Iterator.class);
+ // body type signature (V T A...), internal loop types (I V A...)
+ step = swapArguments(body, 0, 1); // swap V <-> T
+ }
+ MethodHandle[]
+ iterVar = { startIter, null, hasNext, retv },
+ bodyClause = { init, filterArgument(step, 0, nextVal) };
return loop(iterVar, bodyClause);
}
+ private static Class> iteratedLoopChecks(MethodHandle iterator, MethodHandle init, MethodHandle body) {
+ Objects.requireNonNull(body);
+ MethodType bodyType = body.type();
+ Class> returnType = bodyType.returnType();
+ List{@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);
+ * // assume MH_next, MH_hasNext, MH_startIter are handles to methods of Iterator/Iterable
+ * Class> returnType = body.type().returnType();
+ * Class> ttype = body.type().parameterType(returnType == void.class ? 0 : 1);
* MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
+ * MethodHandle retv = null, step = body, startIter = iterator;
+ * if (returnType != void.class) {
+ * // the simple thing first: in (I V A...), drop the I to get V
+ * retv = dropArguments(identity(returnType), 0, Iterator.class);
+ * // body type signature (V T A...), internal loop types (I V A...)
+ * step = swapArguments(body, 0, 1); // swap V <-> T
+ * }
+ * if (startIter == null) startIter = MH_getIter;
* MethodHandle[]
- * iterVar = {iterator, null, MH_hasNext, returnVar}, // it = iterator(); while (it.hasNext)
- * bodyClause = {init, filterArgument(body, 0, nextVal)}; // v = body(t, v, a);
+ * iterVar = { startIter, null, MH_hasNext, retv }, // it = iterator; while (it.hasNext())
+ * bodyClause = { init, filterArguments(step, 0, nextVal) }; // v = body(v, t, a)
* return loop(iterVar, bodyClause);
* }
* }
- * {@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"));
- * }
- * {@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...);
- * }
- * } {@code minimalStage.toCompletableFuture().join(); }
+ *
* @return the new CompletionStage
* @since 9
*/
@@ -2853,6 +2860,16 @@ public class CompletableFuture
* Secmod secmod = Secmod.getInstance();
* if (secmod.isInitialized() == false) {
- * secmod.initialize("/home/myself/.mozilla", "/usr/sfw/lib/mozilla");
+ * secmod.initialize("/home/myself/.mozilla");
* }
*
* Provider p = secmod.getModule(ModuleType.KEYSTORE).getProvider();
@@ -743,6 +743,7 @@ public final class Secmod {
Map