8143211: provide bytecode intrinsics for loop and try/finally executors
Reviewed-by: psandoz, redestad, vlivanov
--- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,17 +26,16 @@
package java.lang.invoke;
import jdk.internal.loader.BootLoader;
-import jdk.internal.vm.annotation.Stable;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.Wrapper;
import java.lang.invoke.LambdaForm.NamedFunction;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
-import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -168,12 +167,16 @@
}
@Override
- final Object internalValues() {
- Object[] boundValues = new Object[speciesData().fieldCount()];
- for (int i = 0; i < boundValues.length; ++i) {
- boundValues[i] = arg(i);
+ final String internalValues() {
+ int count = speciesData().fieldCount();
+ if (count == 1) {
+ return "[" + arg(0) + "]";
}
- return Arrays.asList(boundValues);
+ StringBuilder sb = new StringBuilder("[");
+ for (int i = 0; i < count; ++i) {
+ sb.append("\n ").append(i).append(": ( ").append(arg(i)).append(" )");
+ }
+ return sb.append("\n]").toString();
}
/*non-public*/ final Object arg(int i) {
@@ -869,7 +872,7 @@
*/
static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
- private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[5];
+ private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[6];
private static SpeciesData checkCache(int size, String types) {
int idx = size - 1;
SpeciesData data = SPECIES_DATA_CACHE[idx];
@@ -877,9 +880,10 @@
SPECIES_DATA_CACHE[idx] = data = getSpeciesData(types);
return data;
}
- static SpeciesData speciesData_L() { return checkCache(1, "L"); }
- static SpeciesData speciesData_LL() { return checkCache(2, "LL"); }
- static SpeciesData speciesData_LLL() { return checkCache(3, "LLL"); }
- static SpeciesData speciesData_LLLL() { return checkCache(4, "LLLL"); }
- static SpeciesData speciesData_LLLLL() { return checkCache(5, "LLLLL"); }
+ static SpeciesData speciesData_L() { return checkCache(1, "L"); }
+ static SpeciesData speciesData_LL() { return checkCache(2, "LL"); }
+ static SpeciesData speciesData_LLL() { return checkCache(3, "LLL"); }
+ static SpeciesData speciesData_LLLL() { return checkCache(4, "LLLL"); }
+ static SpeciesData speciesData_LLLLL() { return checkCache(5, "LLLLL"); }
+ static SpeciesData speciesData_LLLLLL() { return checkCache(6, "LLLLLL"); }
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Mon Jul 04 10:08:18 2016 +0200
@@ -25,22 +25,30 @@
package java.lang.invoke;
-import java.io.*;
-import java.util.*;
-import java.lang.reflect.Modifier;
-
-import jdk.internal.org.objectweb.asm.*;
-
-import static java.lang.invoke.LambdaForm.*;
-import static java.lang.invoke.LambdaForm.BasicType.*;
-import static java.lang.invoke.MethodHandleStatics.*;
-import static java.lang.invoke.MethodHandleNatives.Constants.*;
-
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
import sun.invoke.util.VerifyAccess;
import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
import sun.reflect.misc.ReflectUtil;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.LambdaForm.BasicType.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+
/**
* Code generation backend for LambdaForm.
* <p>
@@ -75,8 +83,8 @@
private final MethodType invokerType;
/** Info about local variables in compiled lambda form */
- private final int[] localsMap; // index
- private final Class<?>[] localClasses; // type
+ private int[] localsMap; // index
+ private Class<?>[] localClasses; // type
/** ASM bytecode generation. */
private ClassWriter cw;
@@ -101,8 +109,7 @@
this.lambdaForm = lambdaForm;
this.invokerName = invokerName;
this.invokerType = invokerType;
- this.localsMap = new int[localsMapSize+1];
- // last entry of localsMap is count of allocated local slots
+ this.localsMap = new int[localsMapSize+1]; // last entry of localsMap is count of allocated local slots
this.localClasses = new Class<?>[localsMapSize+1];
}
@@ -131,7 +138,6 @@
}
}
-
/** instance counters for dumped classes */
private static final HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
/** debugging flag for saving generated class files */
@@ -177,7 +183,6 @@
}
});
}
-
}
private static String makeDumpableClassName(String className) {
@@ -280,14 +285,11 @@
private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
- //System.out.println("resolveInvokerMember => "+member);
- //for (Method m : invokerClass.getDeclaredMethods()) System.out.println(" "+m);
try {
member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
} catch (ReflectiveOperationException e) {
throw newInternalError(e);
}
- //System.out.println("resolveInvokerMember => "+member);
return member;
}
@@ -541,11 +543,12 @@
Name writeBack = null; // local to write back result
if (arg instanceof Name) {
Name n = (Name) arg;
- if (assertStaticType(cls, n))
- return; // this cast was already performed
if (lambdaForm.useCount(n) > 1) {
// This guy gets used more than once.
writeBack = n;
+ if (assertStaticType(cls, n)) {
+ return; // this cast was already performed
+ }
}
}
if (isStaticallyNameable(cls)) {
@@ -679,19 +682,29 @@
MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
switch (intr) {
case SELECT_ALTERNATIVE:
- assert isSelectAlternative(i);
+ assert lambdaForm.isSelectAlternative(i);
if (PROFILE_GWT) {
assert(name.arguments[0] instanceof Name &&
- nameRefersTo((Name)name.arguments[0], MethodHandleImpl.class, "profileBoolean"));
+ ((Name)name.arguments[0]).refersTo(MethodHandleImpl.class, "profileBoolean"));
mv.visitAnnotation(INJECTEDPROFILE_SIG, true);
}
onStack = emitSelectAlternative(name, lambdaForm.names[i+1]);
i++; // skip MH.invokeBasic of the selectAlternative result
continue;
case GUARD_WITH_CATCH:
- assert isGuardWithCatch(i);
+ assert lambdaForm.isGuardWithCatch(i);
onStack = emitGuardWithCatch(i);
- i = i+2; // Jump to the end of GWC idiom
+ i += 2; // jump to the end of GWC idiom
+ continue;
+ case TRY_FINALLY:
+ assert lambdaForm.isTryFinally(i);
+ onStack = emitTryFinally(i);
+ i += 2; // jump to the end of the TF idiom
+ continue;
+ case LOOP:
+ assert lambdaForm.isLoop(i);
+ onStack = emitLoop(i);
+ i += 2; // jump to the end of the LOOP idiom
continue;
case NEW_ARRAY:
Class<?> rtype = name.function.methodType().returnType();
@@ -763,7 +776,7 @@
* Emit an invoke for the given name.
*/
void emitInvoke(Name name) {
- assert(!isLinkerMethodInvoke(name)); // should use the static path for these
+ assert(!name.isLinkerMethodInvoke()); // should use the static path for these
if (true) {
// push receiver
MethodHandle target = name.function.resolvedHandle();
@@ -952,84 +965,6 @@
}
/**
- * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
- */
- private boolean memberRefersTo(MemberName member, Class<?> declaringClass, String name) {
- return member != null &&
- member.getDeclaringClass() == declaringClass &&
- member.getName().equals(name);
- }
- private boolean nameRefersTo(Name name, Class<?> declaringClass, String methodName) {
- return name.function != null &&
- memberRefersTo(name.function.member(), declaringClass, methodName);
- }
-
- /**
- * Check if MemberName is a call to MethodHandle.invokeBasic.
- */
- private boolean isInvokeBasic(Name name) {
- if (name.function == null)
- return false;
- if (name.arguments.length < 1)
- return false; // must have MH argument
- MemberName member = name.function.member();
- return memberRefersTo(member, MethodHandle.class, "invokeBasic") &&
- !member.isPublic() && !member.isStatic();
- }
-
- /**
- * Check if MemberName is a call to MethodHandle.linkToStatic, etc.
- */
- private boolean isLinkerMethodInvoke(Name name) {
- if (name.function == null)
- return false;
- if (name.arguments.length < 1)
- return false; // must have MH argument
- MemberName member = name.function.member();
- return member != null &&
- member.getDeclaringClass() == MethodHandle.class &&
- !member.isPublic() && member.isStatic() &&
- member.getName().startsWith("linkTo");
- }
-
- /**
- * Check if i-th name is a call to MethodHandleImpl.selectAlternative.
- */
- private boolean isSelectAlternative(int pos) {
- // selectAlternative idiom:
- // t_{n}:L=MethodHandleImpl.selectAlternative(...)
- // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
- if (pos+1 >= lambdaForm.names.length) return false;
- Name name0 = lambdaForm.names[pos];
- Name name1 = lambdaForm.names[pos+1];
- return nameRefersTo(name0, MethodHandleImpl.class, "selectAlternative") &&
- isInvokeBasic(name1) &&
- name1.lastUseIndex(name0) == 0 && // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
- lambdaForm.lastUseIndex(name0) == pos+1; // t_{n} is local: used only in t_{n+1}
- }
-
- /**
- * Check if i-th name is a start of GuardWithCatch idiom.
- */
- private boolean isGuardWithCatch(int pos) {
- // GuardWithCatch idiom:
- // t_{n}:L=MethodHandle.invokeBasic(...)
- // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
- // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1})
- if (pos+2 >= lambdaForm.names.length) return false;
- Name name0 = lambdaForm.names[pos];
- Name name1 = lambdaForm.names[pos+1];
- Name name2 = lambdaForm.names[pos+2];
- return nameRefersTo(name1, MethodHandleImpl.class, "guardWithCatch") &&
- isInvokeBasic(name0) &&
- isInvokeBasic(name2) &&
- name1.lastUseIndex(name0) == 3 && // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
- lambdaForm.lastUseIndex(name0) == pos+1 && // t_{n} is local: used only in t_{n+1}
- name2.lastUseIndex(name1) == 1 && // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1})
- lambdaForm.lastUseIndex(name1) == pos+2; // t_{n+1} is local: used only in t_{n+2}
- }
-
- /**
* Emit bytecode for the selectAlternative idiom.
*
* The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
@@ -1155,6 +1090,329 @@
return result;
}
+ /**
+ * Emit bytecode for the tryFinally idiom.
+ * <p>
+ * The pattern looks like (Cf. MethodHandleImpl.makeTryFinally):
+ * <blockquote><pre>{@code
+ * // a0: BMH
+ * // a1: target, a2: cleanup
+ * // a3: box, a4: unbox
+ * // a5 (and following): arguments
+ * tryFinally=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L)=>{
+ * t6:L=MethodHandle.invokeBasic(a3:L,a5:L); // box the arguments into an Object[]
+ * t7:L=MethodHandleImpl.tryFinally(a1:L,a2:L,t6:L); // call the tryFinally executor
+ * t8:L=MethodHandle.invokeBasic(a4:L,t7:L);t8:L} // unbox the result; return the result
+ * }</pre></blockquote>
+ * <p>
+ * It is compiled into bytecode equivalent to the following code:
+ * <blockquote><pre>{@code
+ * Throwable t;
+ * Object r;
+ * try {
+ * r = a1.invokeBasic(a5);
+ * } catch (Throwable thrown) {
+ * t = thrown;
+ * throw t;
+ * } finally {
+ * r = a2.invokeBasic(t, r, a5);
+ * }
+ * return r;
+ * }</pre></blockquote>
+ * <p>
+ * Specifically, the bytecode will have the following form (the stack effects are given for the beginnings of
+ * blocks, and for the situations after executing the given instruction - the code will have a slightly different
+ * shape if the return type is {@code void}):
+ * <blockquote><pre>{@code
+ * TRY: (--)
+ * load target (-- target)
+ * load args (-- args... target)
+ * INVOKEVIRTUAL MethodHandle.invokeBasic (depends)
+ * FINALLY_NORMAL: (-- r)
+ * load cleanup (-- cleanup r)
+ * SWAP (-- r cleanup)
+ * ACONST_NULL (-- t r cleanup)
+ * SWAP (-- r t cleanup)
+ * load args (-- args... r t cleanup)
+ * INVOKEVIRTUAL MethodHandle.invokeBasic (-- r)
+ * GOTO DONE
+ * CATCH: (-- t)
+ * DUP (-- t t)
+ * FINALLY_EXCEPTIONAL: (-- t t)
+ * load cleanup (-- cleanup t t)
+ * SWAP (-- t cleanup t)
+ * load default for r (-- r t cleanup t)
+ * load args (-- args... r t cleanup t)
+ * INVOKEVIRTUAL MethodHandle.invokeBasic (-- r t)
+ * POP (-- t)
+ * ATHROW
+ * DONE: (-- r)
+ * }</pre></blockquote>
+ */
+ private Name emitTryFinally(int pos) {
+ Name args = lambdaForm.names[pos];
+ Name invoker = lambdaForm.names[pos+1];
+ Name result = lambdaForm.names[pos+2];
+
+ Label lFrom = new Label();
+ Label lTo = new Label();
+ Label lCatch = new Label();
+ Label lDone = new Label();
+
+ Class<?> returnType = result.function.resolvedHandle().type().returnType();
+ boolean isNonVoid = returnType != void.class;
+ MethodType type = args.function.resolvedHandle().type()
+ .dropParameterTypes(0,1)
+ .changeReturnType(returnType);
+ MethodType cleanupType = type.insertParameterTypes(0, Throwable.class);
+ if (isNonVoid) {
+ cleanupType = cleanupType.insertParameterTypes(1, returnType);
+ }
+ String cleanupDesc = cleanupType.basicType().toMethodDescriptorString();
+
+ // exception handler table
+ mv.visitTryCatchBlock(lFrom, lTo, lCatch, "java/lang/Throwable");
+
+ // TRY:
+ mv.visitLabel(lFrom);
+ emitPushArgument(invoker, 0); // load target
+ emitPushArguments(args, 1); // load args (skip 0: method handle)
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
+ mv.visitLabel(lTo);
+
+ // FINALLY_NORMAL:
+ emitPushArgument(invoker, 1); // load cleanup
+ if (isNonVoid) {
+ mv.visitInsn(Opcodes.SWAP);
+ }
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ if (isNonVoid) {
+ mv.visitInsn(Opcodes.SWAP);
+ }
+ emitPushArguments(args, 1); // load args (skip 0: method handle)
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", cleanupDesc, false);
+ mv.visitJumpInsn(Opcodes.GOTO, lDone);
+
+ // CATCH:
+ mv.visitLabel(lCatch);
+ mv.visitInsn(Opcodes.DUP);
+
+ // FINALLY_EXCEPTIONAL:
+ emitPushArgument(invoker, 1); // load cleanup
+ mv.visitInsn(Opcodes.SWAP);
+ if (isNonVoid) {
+ emitZero(BasicType.basicType(returnType)); // load default for result
+ }
+ emitPushArguments(args, 1); // load args (skip 0: method handle)
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", cleanupDesc, false);
+ if (isNonVoid) {
+ mv.visitInsn(Opcodes.POP);
+ }
+ mv.visitInsn(Opcodes.ATHROW);
+
+ // DONE:
+ mv.visitLabel(lDone);
+
+ return result;
+ }
+
+ /**
+ * Emit bytecode for the loop idiom.
+ * <p>
+ * The pattern looks like (Cf. MethodHandleImpl.loop):
+ * <blockquote><pre>{@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
+ * }</pre></blockquote>
+ * <p>
+ * 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
+ * will be used for local state storage. Instead, the local state will be mapped to actual stack slots.
+ * <p>
+ * 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.
+ * <blockquote><pre>{@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: ...
+ * }</pre></blockquote>
+ * <p>
+ * 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}.
+ * <blockquote><pre>{@code
+ * INIT_SEQ_x: ALOAD inits
+ * CHECKCAST MethodHandle[]
+ * ICONST x
+ * AALOAD // load the init handle for clause x
+ * load args
+ * INVOKEVIRTUAL MethodHandle.invokeBasic
+ * store vx
+ * }</pre></blockquote>
+ * <p>
+ * 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}.
+ * <blockquote><pre>{@code
+ * LOOP_SEQ_x: ALOAD steps
+ * CHECKCAST MethodHandle[]
+ * ICONST x
+ * AALOAD // load the step handle for clause x
+ * load locals
+ * load args
+ * INVOKEVIRTUAL MethodHandle.invokeBasic
+ * store vx
+ * ALOAD preds
+ * CHECKCAST MethodHandle[]
+ * 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[]
+ * ICONST x
+ * AALOAD // load the fini handle for clause x
+ * load locals
+ * load args
+ * INVOKEVIRTUAL MethodHandle.invokeBasic
+ * GOTO DONE // jump beyond end of clauses to return from loop
+ * }</pre></blockquote>
+ */
+ private Name emitLoop(int pos) {
+ Name args = lambdaForm.names[pos];
+ Name invoker = lambdaForm.names[pos+1];
+ Name result = lambdaForm.names[pos+2];
+
+ // extract clause and loop-local state types
+ // find the type info in the loop invocation
+ BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0];
+ Class<?>[] loopLocalStateTypes = Stream.of(loopClauseTypes).
+ filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class<?>[]::new);
+
+ final int firstLoopStateIndex = extendLocalsMap(loopLocalStateTypes);
+
+ Class<?> returnType = result.function.resolvedHandle().type().returnType();
+ MethodType loopType = args.function.resolvedHandle().type()
+ .dropParameterTypes(0,1)
+ .changeReturnType(returnType);
+ MethodType loopHandleType = loopType.insertParameterTypes(0, loopLocalStateTypes);
+ MethodType predType = loopHandleType.changeReturnType(boolean.class);
+ MethodType finiType = loopHandleType;
+
+ final int nClauses = loopClauseTypes.length;
+
+ // indices to invoker arguments to load method handle arrays
+ final int inits = 1;
+ final int steps = 2;
+ final int preds = 3;
+ final int finis = 4;
+
+ Label lLoop = new Label();
+ Label lDone = new Label();
+ Label lNext;
+
+ // 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);
+ if (cInitType.returnType() != void.class) {
+ emitStoreInsn(BasicType.basicType(cInitType.returnType()), firstLoopStateIndex + state);
+ ++state;
+ }
+ }
+
+ // LOOP:
+ mv.visitLabel(lLoop);
+
+ for (int c = 0, state = 0; c < nClauses; ++c) {
+ lNext = new Label();
+
+ MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass());
+ boolean isVoid = stepType.returnType() == void.class;
+
+ // invoke loop step
+ emitLoopHandleInvoke(invoker, steps, c, args, true, stepType, loopLocalStateTypes, firstLoopStateIndex);
+ if (!isVoid) {
+ emitStoreInsn(BasicType.basicType(stepType.returnType()), firstLoopStateIndex + state);
+ ++state;
+ }
+
+ // invoke loop predicate
+ emitLoopHandleInvoke(invoker, preds, c, args, true, predType, loopLocalStateTypes, firstLoopStateIndex);
+ mv.visitJumpInsn(Opcodes.IFNE, lNext);
+
+ // invoke fini
+ emitLoopHandleInvoke(invoker, finis, c, args, true, finiType, loopLocalStateTypes, firstLoopStateIndex);
+ mv.visitJumpInsn(Opcodes.GOTO, lDone);
+
+ // this is the beginning of the next loop clause
+ mv.visitLabel(lNext);
+ }
+
+ mv.visitJumpInsn(Opcodes.GOTO, lLoop);
+
+ // DONE:
+ mv.visitLabel(lDone);
+
+ return result;
+ }
+
+ private int extendLocalsMap(Class<?>[] types) {
+ int firstSlot = localsMap.length - 1;
+ localsMap = Arrays.copyOf(localsMap, localsMap.length + types.length);
+ localClasses = Arrays.copyOf(localClasses, localClasses.length + types.length);
+ System.arraycopy(types, 0, localClasses, firstSlot, types.length);
+ int index = localsMap[firstSlot - 1] + 1;
+ int lastSlots = 0;
+ for (int i = 0; i < types.length; ++i) {
+ localsMap[firstSlot + i] = index;
+ lastSlots = BasicType.basicType(localClasses[firstSlot + i]).basicTypeSlots();
+ index += lastSlots;
+ }
+ localsMap[localsMap.length - 1] = index - lastSlots;
+ return firstSlot;
+ }
+
+ private void emitLoopHandleInvoke(Name holder, int handles, int clause, Name args, boolean pushLocalState,
+ MethodType type, Class<?>[] loopLocalStateTypes, int firstLoopStateSlot) {
+ // load handle for clause
+ emitPushArgument(holder, handles);
+ emitIconstInsn(clause);
+ mv.visitInsn(Opcodes.AALOAD);
+ // load loop state (preceding the other arguments)
+ if (pushLocalState) {
+ for (int s = 0; s < loopLocalStateTypes.length; ++s) {
+ emitLoadInsn(BasicType.basicType(loopLocalStateTypes[s]), firstLoopStateSlot + s);
+ }
+ }
+ // load loop args (skip 0: method handle)
+ emitPushArguments(args, 1);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.toMethodDescriptorString(), false);
+ }
+
+ private void emitZero(BasicType type) {
+ switch (type) {
+ case I_TYPE: mv.visitInsn(Opcodes.ICONST_0); break;
+ case J_TYPE: mv.visitInsn(Opcodes.LCONST_0); break;
+ case F_TYPE: mv.visitInsn(Opcodes.FCONST_0); break;
+ case D_TYPE: mv.visitInsn(Opcodes.DCONST_0); break;
+ case L_TYPE: mv.visitInsn(Opcodes.ACONST_NULL); break;
+ default: throw new InternalError("unknown type: " + type);
+ }
+ }
+
private void emitPushArguments(Name args) {
emitPushArguments(args, 0);
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,11 +33,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.List;
import static java.lang.invoke.LambdaForm.BasicType.*;
import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
@@ -210,6 +208,29 @@
}
return btypes;
}
+ static String basicTypeDesc(BasicType[] types) {
+ if (types == null) {
+ return null;
+ }
+ if (types.length == 0) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ for (BasicType bt : types) {
+ sb.append(bt.basicTypeChar());
+ }
+ return sb.toString();
+ }
+ static int[] basicTypeOrds(BasicType[] types) {
+ if (types == null) {
+ return null;
+ }
+ int[] a = new int[types.length];
+ for(int i = 0; i < types.length; ++i) {
+ a[i] = types[i].ordinal();
+ }
+ return a;
+ }
static char basicTypeChar(Class<?> type) {
return basicType(type).btChar;
@@ -565,6 +586,69 @@
return MethodType.methodType(rtype, ptypes);
}
+ /**
+ * Check if i-th name is a call to MethodHandleImpl.selectAlternative.
+ */
+ boolean isSelectAlternative(int pos) {
+ // selectAlternative idiom:
+ // t_{n}:L=MethodHandleImpl.selectAlternative(...)
+ // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
+ if (pos+1 >= names.length) return false;
+ Name name0 = names[pos];
+ Name name1 = names[pos+1];
+ return name0.refersTo(MethodHandleImpl.class, "selectAlternative") &&
+ name1.isInvokeBasic() &&
+ name1.lastUseIndex(name0) == 0 && // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
+ lastUseIndex(name0) == pos+1; // t_{n} is local: used only in t_{n+1}
+ }
+
+ private boolean isMatchingIdiom(int pos, String idiomName, int nArgs) {
+ if (pos+2 >= names.length) return false;
+ Name name0 = names[pos];
+ Name name1 = names[pos+1];
+ Name name2 = names[pos+2];
+ return name1.refersTo(MethodHandleImpl.class, idiomName) &&
+ name0.isInvokeBasic() &&
+ name2.isInvokeBasic() &&
+ name1.lastUseIndex(name0) == nArgs && // t_{n+1}:L=MethodHandleImpl.<invoker>(<args>, t_{n});
+ lastUseIndex(name0) == pos+1 && // t_{n} is local: used only in t_{n+1}
+ name2.lastUseIndex(name1) == 1 && // t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
+ lastUseIndex(name1) == pos+2; // t_{n+1} is local: used only in t_{n+2}
+ }
+
+ /**
+ * Check if i-th name is a start of GuardWithCatch idiom.
+ */
+ boolean isGuardWithCatch(int pos) {
+ // GuardWithCatch idiom:
+ // t_{n}:L=MethodHandle.invokeBasic(...)
+ // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
+ // t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
+ return isMatchingIdiom(pos, "guardWithCatch", 3);
+ }
+
+ /**
+ * Check if i-th name is a start of the tryFinally idiom.
+ */
+ boolean isTryFinally(int pos) {
+ // tryFinally idiom:
+ // t_{n}:L=MethodHandle.invokeBasic(...)
+ // t_{n+1}:L=MethodHandleImpl.tryFinally(*, *, t_{n})
+ // t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
+ return isMatchingIdiom(pos, "tryFinally", 2);
+ }
+
+ /**
+ * Check if i-th name is a start of the loop idiom.
+ */
+ boolean isLoop(int pos) {
+ // loop idiom:
+ // t_{n}:L=MethodHandle.invokeBasic(...)
+ // t_{n+1}:L=MethodHandleImpl.loop(types, *, *, *, *, t_{n})
+ // t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
+ return isMatchingIdiom(pos, "loop", 5);
+ }
+
/*
* Code generation issues:
*
@@ -1421,6 +1505,39 @@
return !isParam() && arguments.length == 0 && function.isConstantZero();
}
+ boolean refersTo(Class<?> declaringClass, String methodName) {
+ return function != null &&
+ function.member() != null && function.member().refersTo(declaringClass, methodName);
+ }
+
+ /**
+ * Check if MemberName is a call to MethodHandle.invokeBasic.
+ */
+ boolean isInvokeBasic() {
+ if (function == null)
+ return false;
+ if (arguments.length < 1)
+ return false; // must have MH argument
+ MemberName member = function.member();
+ return member != null && member.refersTo(MethodHandle.class, "invokeBasic") &&
+ !member.isPublic() && !member.isStatic();
+ }
+
+ /**
+ * Check if MemberName is a call to MethodHandle.linkToStatic, etc.
+ */
+ boolean isLinkerMethodInvoke() {
+ if (function == null)
+ return false;
+ if (arguments.length < 1)
+ return false; // must have MH argument
+ MemberName member = function.member();
+ return member != null &&
+ member.getDeclaringClass() == MethodHandle.class &&
+ !member.isPublic() && member.isStatic() &&
+ member.getName().startsWith("linkTo");
+ }
+
public String toString() {
return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+typeChar();
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -249,7 +249,7 @@
assert(inTrans());
}
- private void changeName(int i, Name name) {
+ void changeName(int i, Name name) {
assert(inTrans());
assert(i < length);
Name oldName = names[i];
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,15 +25,17 @@
package java.lang.invoke;
+import sun.invoke.util.Wrapper;
+
import java.lang.ref.SoftReference;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+
import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.LambdaForm.BasicType.*;
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
-import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
-
-import sun.invoke.util.Wrapper;
+import static java.lang.invoke.MethodHandleImpl.NF_loop;
/** Transforms on LFs.
* A lambda-form editor can derive new LFs from its base LF.
@@ -73,7 +75,8 @@
FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
FOLD_ARGS, FOLD_ARGS_TO_VOID,
- PERMUTE_ARGS
+ PERMUTE_ARGS,
+ LOCAL_TYPES
//maybe add more for guard with test, catch exception, pointwise type conversions
}
@@ -847,6 +850,32 @@
return putInCache(key, form);
}
+ LambdaForm noteLoopLocalTypesForm(int pos, BasicType[] localTypes) {
+ assert(lambdaForm.isLoop(pos));
+ int[] desc = BasicType.basicTypeOrds(localTypes);
+ desc = Arrays.copyOf(desc, desc.length + 1);
+ desc[desc.length - 1] = pos;
+ Transform key = Transform.of(Transform.Kind.LOCAL_TYPES, desc);
+ LambdaForm form = getInCache(key);
+ if (form != null) {
+ return form;
+ }
+
+ // replace the null entry in the MHImpl.loop invocation with localTypes
+ Name invokeLoop = lambdaForm.names[pos + 1];
+ assert(invokeLoop.function == NF_loop);
+ Object[] args = Arrays.copyOf(invokeLoop.arguments, invokeLoop.arguments.length);
+ assert(args[0] == null);
+ args[0] = localTypes;
+
+ LambdaFormBuffer buf = buffer();
+ buf.startEdit();
+ buf.changeName(pos + 1, new Name(NF_loop, args));
+ form = buf.endEdit();
+
+ return putInCache(key, form);
+ }
+
static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) {
for (int i = 0; i < reorder.length; i++) {
assert (names[skip + i].isParam());
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,8 +32,8 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
-import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.lang.reflect.Module;
import java.util.ArrayList;
@@ -41,9 +41,11 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
+
import static java.lang.invoke.MethodHandleNatives.Constants.*;
-import static java.lang.invoke.MethodHandleStatics.*;
-import java.util.Objects;
+import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
+import static java.lang.invoke.MethodHandleStatics.newInternalError;
/**
* A {@code MemberName} is a compact symbolic datum which fully characterizes
@@ -500,6 +502,13 @@
lookupClass, mode);
}
+ /**
+ * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
+ */
+ public boolean refersTo(Class<?> declc, String n) {
+ return clazz == declc && getName().equals(n);
+ }
+
/** Initialize a query. It is not resolved. */
private void init(Class<?> defClass, String name, Object type, int flags) {
// defining class is allowed to be null (for a naked name/type pair)
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Mon Jul 04 10:08:18 2016 +0200
@@ -25,6 +25,17 @@
package java.lang.invoke;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.Reflection;
+import jdk.internal.vm.annotation.Stable;
+import sun.invoke.empty.Empty;
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
+import sun.invoke.util.Wrapper;
+
import java.lang.reflect.Array;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -33,18 +44,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
-
-import jdk.internal.reflect.CallerSensitive;
-import jdk.internal.reflect.Reflection;
-import jdk.internal.vm.annotation.Stable;
-import sun.invoke.empty.Empty;
-import sun.invoke.util.ValueConversions;
-import sun.invoke.util.VerifyType;
-import sun.invoke.util.Wrapper;
-
-import jdk.internal.org.objectweb.asm.AnnotationVisitor;
-import jdk.internal.org.objectweb.asm.ClassWriter;
-import jdk.internal.org.objectweb.asm.MethodVisitor;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.MethodHandleStatics.*;
@@ -1046,26 +1047,13 @@
// Box arguments and wrap them into Object[]: ValueConversions.array().
MethodType varargsType = type.changeReturnType(Object[].class);
MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
- // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore().
- MethodHandle unboxResult;
- Class<?> rtype = type.returnType();
- if (rtype.isPrimitive()) {
- if (rtype == void.class) {
- unboxResult = ValueConversions.ignore();
- } else {
- Wrapper w = Wrapper.forPrimitiveType(type.returnType());
- unboxResult = ValueConversions.unboxExact(w);
- }
- } else {
- unboxResult = MethodHandles.identity(Object.class);
- }
+ MethodHandle unboxResult = unboxResultHandle(type.returnType());
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL();
BoundMethodHandle mh;
try {
- mh = (BoundMethodHandle)
- data.constructor().invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher,
- (Object) collectArgs, (Object) unboxResult);
+ mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) target, (Object) exType,
+ (Object) catcher, (Object) collectArgs, (Object) unboxResult);
} catch (Throwable ex) {
throw uncaughtException(ex);
}
@@ -1085,16 +1073,18 @@
return target.asFixedArity().invokeWithArguments(av);
} catch (Throwable t) {
if (!exType.isInstance(t)) throw t;
- return catcher.asFixedArity().invokeWithArguments(prepend(t, av));
+ return catcher.asFixedArity().invokeWithArguments(prepend(av, t));
}
}
- /** Prepend an element {@code elem} to an {@code array}. */
+ /** Prepend elements to an array. */
@LambdaForm.Hidden
- private static Object[] prepend(Object elem, Object[] array) {
- Object[] newArray = new Object[array.length+1];
- newArray[0] = elem;
- System.arraycopy(array, 0, newArray, 1, array.length);
+ private static Object[] prepend(Object[] array, Object... elems) {
+ int nArray = array.length;
+ int nElems = elems.length;
+ Object[] newArray = new Object[nArray + nElems];
+ System.arraycopy(elems, 0, newArray, 0, nElems);
+ System.arraycopy(array, 0, newArray, nElems, nArray);
return newArray;
}
@@ -1352,6 +1342,8 @@
enum Intrinsic {
SELECT_ALTERNATIVE,
GUARD_WITH_CATCH,
+ TRY_FINALLY,
+ LOOP,
NEW_ARRAY,
ARRAY_LOAD,
ARRAY_STORE,
@@ -1363,7 +1355,7 @@
/** Mark arbitrary method handle as intrinsic.
* InvokerBytecodeGenerator uses this info to produce more efficient bytecode shape. */
- private static final class IntrinsicMethodHandle extends DelegatingMethodHandle {
+ static final class IntrinsicMethodHandle extends DelegatingMethodHandle {
private final MethodHandle target;
private final Intrinsic intrinsicName;
@@ -1694,6 +1686,8 @@
NF_checkSpreadArgument,
NF_guardWithCatch,
NF_throwException,
+ NF_tryFinally,
+ NF_loop,
NF_profileBoolean;
static {
@@ -1703,6 +1697,11 @@
NF_guardWithCatch = new NamedFunction(MethodHandleImpl.class
.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class,
MethodHandle.class, Object[].class));
+ 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));
NF_throwException = new NamedFunction(MethodHandleImpl.class
.getDeclaredMethod("throwException", Throwable.class));
NF_profileBoolean = new NamedFunction(MethodHandleImpl.class
@@ -1712,14 +1711,25 @@
}
}
+ /** Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). */
+ private static MethodHandle unboxResultHandle(Class<?> returnType) {
+ if (returnType.isPrimitive()) {
+ if (returnType == void.class) {
+ return ValueConversions.ignore();
+ } else {
+ Wrapper w = Wrapper.forPrimitiveType(returnType);
+ return ValueConversions.unboxExact(w);
+ }
+ } else {
+ return MethodHandles.identity(Object.class);
+ }
+ }
+
/**
- * Assembles a loop method handle from the given handles and type information. This works by binding and configuring
- * the {@linkplain #looper(MethodHandle[], MethodHandle[], MethodHandle[], MethodHandle[], int, int, Object[]) "most
- * generic loop"}.
+ * Assembles a loop method handle from the given handles and type information.
*
* @param tloop the return type of the loop.
* @param targs types of the arguments to be passed to the loop.
- * @param tvars types of loop-local variables.
* @param init sanitized array of initializers for loop-local variables.
* @param step sanitited array of loop bodies.
* @param pred sanitized array of predicates.
@@ -1727,64 +1737,144 @@
*
* @return a handle that, when invoked, will execute the loop.
*/
- static MethodHandle makeLoop(Class<?> tloop, List<Class<?>> targs, List<Class<?>> tvars, List<MethodHandle> init,
- List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini) {
- MethodHandle[] ainit = toArrayArgs(init);
- MethodHandle[] astep = toArrayArgs(step);
- MethodHandle[] apred = toArrayArgs(pred);
- MethodHandle[] afini = toArrayArgs(fini);
+ static MethodHandle makeLoop(Class<?> tloop, List<Class<?>> targs, List<MethodHandle> init, List<MethodHandle> step,
+ List<MethodHandle> pred, List<MethodHandle> fini) {
+ MethodType type = MethodType.methodType(tloop, targs);
+ BasicType[] initClauseTypes =
+ init.stream().map(h -> h.type().returnType()).map(BasicType::basicType).toArray(BasicType[]::new);
+ LambdaForm form = makeLoopForm(type.basicType(), initClauseTypes);
- MethodHandle l = getConstantHandle(MH_looper);
+ // Prepare auxiliary method handles used during LambdaForm interpretation.
+ // Box arguments and wrap them into Object[]: ValueConversions.array().
+ MethodType varargsType = type.changeReturnType(Object[].class);
+ MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
+ MethodHandle unboxResult = unboxResultHandle(tloop);
- // Bind the statically known arguments.
- l = MethodHandles.insertArguments(l, 0, ainit, astep, apred, afini, tvars.size(), targs.size());
-
- // Turn the args array into an argument list.
- l = l.asCollector(Object[].class, targs.size());
+ BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLLL();
+ 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);
+ } catch (Throwable ex) {
+ throw uncaughtException(ex);
+ }
+ assert(mh.type() == type);
+ return mh;
+ }
- // Finally, make loop type.
- MethodType loopType = MethodType.methodType(tloop, targs);
- l = l.asType(loopType);
-
- return l;
+ private static MethodHandle[] toArray(List<MethodHandle> l) {
+ return l.toArray(new MethodHandle[0]);
}
/**
- * Converts all handles in the {@code hs} array to handles that accept an array of arguments.
- *
- * @param hs method handles to be converted.
- *
- * @return the {@code hs} array, with all method handles therein converted.
+ * Loops introduce some complexity as they can have additional local state. Hence, LambdaForms for loops are
+ * generated from a template. The LambdaForm template shape for the loop combinator is as follows (assuming one
+ * reference parameter passed in {@code a1}, and a reference return type, with the return value represented by
+ * {@code t12}):
+ * <blockquote><pre>{@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
+ * }</pre></blockquote>
+ * <p>
+ * {@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 ValueConversions.unbox()}).
+ * <p>
+ * Having {@code t6} and {@code t7} 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.
+ * <p>
+ * The above template is instantiated by using the {@link LambdaFormEditor} to replace the {@code null} argument to
+ * the {@code loop} invocation with the {@code BasicType} array describing the loop clause types. This argument is
+ * ignored in the loop invoker, but will be extracted and used in {@linkplain InvokerBytecodeGenerator#emitLoop(int)
+ * bytecode generation}.
*/
- static MethodHandle[] toArrayArgs(List<MethodHandle> hs) {
- return hs.stream().map(h -> h.asSpreader(Object[].class, h.type().parameterCount())).toArray(MethodHandle[]::new);
+ private static LambdaForm makeLoopForm(MethodType basicType, BasicType[] localVarTypes) {
+ MethodType lambdaType = basicType.invokerType();
+
+ final int THIS_MH = 0; // the BMH_LLLLLL
+ 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_COLLECT_ARGS = nameCursor++;
+ final int GET_UNBOX_RESULT = nameCursor++;
+ final int BOXED_ARGS = nameCursor++;
+ final int LOOP = nameCursor++;
+ final int UNBOX_RESULT = nameCursor++;
+
+ LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_LOOP);
+ if (lform == null) {
+ Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
+
+ BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLLL();
+ 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]);
+
+ // t_{i}:L=MethodHandle.invokeBasic(collectArgs:L,a1:L,...);
+ MethodType collectArgsType = basicType.changeReturnType(Object.class);
+ MethodHandle invokeBasic = MethodHandles.basicInvoker(collectArgsType);
+ Object[] args = new Object[invokeBasic.type().parameterCount()];
+ args[0] = names[GET_COLLECT_ARGS];
+ 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);
+ 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[LOOP] = new Name(NF_loop, lArgs);
+
+ // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
+ MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class));
+ Object[] unboxArgs = new Object[]{names[GET_UNBOX_RESULT], names[LOOP]};
+ names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs);
+
+ lform = basicType.form().setCachedLambdaForm(MethodTypeForm.LF_LOOP,
+ new LambdaForm("loop", lambdaType.parameterCount(), names));
+ }
+
+ // BOXED_ARGS is the index into the names array where the loop idiom starts
+ return lform.editor().noteLoopLocalTypesForm(BOXED_ARGS, localVarTypes);
}
+
/**
- * This method embodies the most generic loop for use by {@link MethodHandles#loop(MethodHandle[][])}. A handle on
- * it will be transformed into a handle on a concrete loop instantiation by {@link #makeLoop}.
- *
- * @param init loop-local variable initializers.
- * @param step bodies.
- * @param pred predicates.
- * @param fini finalizers.
- * @param varSize number of loop-local variables.
- * @param nArgs number of arguments passed to the loop.
- * @param args arguments to the loop invocation.
- *
- * @return the result of executing the loop.
+ * Intrinsified during LambdaForm compilation
+ * (see {@link InvokerBytecodeGenerator#emitLoop(int)}).
*/
- static Object looper(MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini,
- int varSize, int nArgs, Object[] args) throws Throwable {
+ @LambdaForm.Hidden
+ static Object loop(BasicType[] localTypes, MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred,
+ MethodHandle[] fini, Object... av) throws Throwable {
+ 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].invoke(args);
+ init[i].asFixedArity().invokeWithArguments(av);
} else {
- varsAndArgs[v++] = init[i].invoke(args);
+ varsAndArgs[v++] = init[i].asFixedArity().invokeWithArguments(av);
}
}
- System.arraycopy(args, 0, varsAndArgs, varSize, nArgs);
+ System.arraycopy(av, 0, varsAndArgs, varSize, nArgs);
final int nSteps = step.length;
for (; ; ) {
for (int i = 0, v = 0; i < nSteps; ++i) {
@@ -1792,12 +1882,12 @@
MethodHandle s = step[i];
MethodHandle f = fini[i];
if (s.type().returnType() == void.class) {
- s.invoke(varsAndArgs);
+ s.asFixedArity().invokeWithArguments(varsAndArgs);
} else {
- varsAndArgs[v++] = s.invoke(varsAndArgs);
+ varsAndArgs[v++] = s.asFixedArity().invokeWithArguments(varsAndArgs);
}
- if (!(boolean) p.invoke(varsAndArgs)) {
- return f.invoke(varsAndArgs);
+ if (!(boolean) p.asFixedArity().invokeWithArguments(varsAndArgs)) {
+ return f.asFixedArity().invokeWithArguments(varsAndArgs);
}
}
}
@@ -1879,77 +1969,127 @@
*
* @param target the target to execute in a {@code try-finally} block.
* @param cleanup the cleanup to execute in the {@code finally} block.
- * @param type the result type of the entire construct.
+ * @param rtype the result type of the entire construct.
* @param argTypes the types of the arguments.
*
* @return a handle on the constructed {@code try-finally} block.
*/
- static MethodHandle makeTryFinally(MethodHandle target, MethodHandle cleanup, Class<?> type, List<Class<?>> argTypes) {
- MethodHandle tf = getConstantHandle(type == void.class ? MH_tryFinallyVoidExec : MH_tryFinallyExec);
+ static MethodHandle makeTryFinally(MethodHandle target, MethodHandle cleanup, Class<?> rtype, List<Class<?>> argTypes) {
+ MethodType type = MethodType.methodType(rtype, argTypes);
+ LambdaForm form = makeTryFinallyForm(type.basicType());
- // Bind the statically known arguments.
- tf = MethodHandles.insertArguments(tf, 0, target, cleanup);
+ // Prepare auxiliary method handles used during LambdaForm interpretation.
+ // Box arguments and wrap them into Object[]: ValueConversions.array().
+ MethodType varargsType = type.changeReturnType(Object[].class);
+ MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
+ MethodHandle unboxResult = unboxResultHandle(rtype);
- // Turn the args array into an argument list.
- tf = tf.asCollector(Object[].class, argTypes.size());
-
- // Finally, make try-finally type.
- MethodType tfType = MethodType.methodType(type, argTypes);
- tf = tf.asType(tfType);
-
- return tf;
+ BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL();
+ BoundMethodHandle mh;
+ try {
+ mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) target, (Object) cleanup,
+ (Object) collectArgs, (Object) unboxResult);
+ } catch (Throwable ex) {
+ throw uncaughtException(ex);
+ }
+ assert(mh.type() == type);
+ return mh;
}
/**
- * A method that will be bound during construction of a {@code try-finally} handle with non-{@code void} return type
- * by {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}.
- *
- * @param target the handle to wrap in a {@code try-finally} block. This will be bound.
- * @param cleanup the handle to run in any case before returning. This will be bound.
- * @param args the arguments to the call. These will remain as the argument list.
- *
- * @return whatever the execution of the {@code target} returned (it may have been modified by the execution of
- * {@code cleanup}).
- * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be
- * passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit.
+ * The LambdaForm shape for the tryFinally combinator is as follows (assuming one reference parameter passed in
+ * {@code a1}, and a reference return type, with the return value represented by {@code t8}):
+ * <blockquote><pre>{@code
+ * tryFinally=Lambda(a0:L,a1:L)=>{
+ * t2:L=BoundMethodHandle$Species_LLLL.argL0(a0:L); // target method handle
+ * t3:L=BoundMethodHandle$Species_LLLL.argL1(a0:L); // cleanup method handle
+ * t4:L=BoundMethodHandle$Species_LLLL.argL2(a0:L); // helper handle to box the arguments into an Object[]
+ * t5:L=BoundMethodHandle$Species_LLLL.argL3(a0:L); // helper handle to unbox the result
+ * t6:L=MethodHandle.invokeBasic(t4:L,a1:L); // box the arguments into an Object[]
+ * t7:L=MethodHandleImpl.tryFinally(t2:L,t3:L,t6:L); // call the tryFinally executor
+ * t8:L=MethodHandle.invokeBasic(t5:L,t7:L);t8:L} // unbox the result; return the result
+ * }</pre></blockquote>
+ * <p>
+ * {@code argL0} and {@code argL1} are the target and cleanup method handles.
+ * {@code argL2} and {@code argL3} 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 ValueConversions.unbox()}).
+ * <p>
+ * Having {@code t4} and {@code t5} passed in via a BMH and not hardcoded in the lambda form allows to share lambda
+ * forms among tryFinally combinators with the same basic type.
*/
- static Object tryFinallyExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable {
+ private static LambdaForm makeTryFinallyForm(MethodType basicType) {
+ MethodType lambdaType = basicType.invokerType();
+
+ LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_TF);
+ if (lform != null) {
+ return lform;
+ }
+ final int THIS_MH = 0; // the BMH_LLLL
+ final int ARG_BASE = 1; // start of incoming arguments
+ final int ARG_LIMIT = ARG_BASE + basicType.parameterCount();
+
+ int nameCursor = ARG_LIMIT;
+ final int GET_TARGET = nameCursor++;
+ final int GET_CLEANUP = nameCursor++;
+ final int GET_COLLECT_ARGS = nameCursor++;
+ final int GET_UNBOX_RESULT = nameCursor++;
+ final int BOXED_ARGS = nameCursor++;
+ final int TRY_FINALLY = nameCursor++;
+ final int UNBOX_RESULT = nameCursor++;
+
+ Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
+
+ BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL();
+ names[THIS_MH] = names[THIS_MH].withConstraint(data);
+ names[GET_TARGET] = new Name(data.getterFunction(0), names[THIS_MH]);
+ names[GET_CLEANUP] = new Name(data.getterFunction(1), names[THIS_MH]);
+ names[GET_COLLECT_ARGS] = new Name(data.getterFunction(2), names[THIS_MH]);
+ names[GET_UNBOX_RESULT] = new Name(data.getterFunction(3), names[THIS_MH]);
+
+ // t_{i}:L=MethodHandle.invokeBasic(collectArgs:L,a1:L,...);
+ MethodType collectArgsType = basicType.changeReturnType(Object.class);
+ MethodHandle invokeBasic = MethodHandles.basicInvoker(collectArgsType);
+ Object[] args = new Object[invokeBasic.type().parameterCount()];
+ args[0] = names[GET_COLLECT_ARGS];
+ System.arraycopy(names, ARG_BASE, args, 1, ARG_LIMIT-ARG_BASE);
+ names[BOXED_ARGS] = new Name(makeIntrinsic(invokeBasic, Intrinsic.TRY_FINALLY), args);
+
+ // t_{i+1}:L=MethodHandleImpl.tryFinally(target:L,exType:L,catcher:L,t_{i}:L);
+ Object[] tfArgs = new Object[] {names[GET_TARGET], names[GET_CLEANUP], names[BOXED_ARGS]};
+ names[TRY_FINALLY] = new Name(NF_tryFinally, tfArgs);
+
+ // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
+ MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class));
+ Object[] unboxArgs = new Object[] {names[GET_UNBOX_RESULT], names[TRY_FINALLY]};
+ names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs);
+
+ lform = new LambdaForm("tryFinally", lambdaType.parameterCount(), names);
+
+ return basicType.form().setCachedLambdaForm(MethodTypeForm.LF_TF, lform);
+ }
+
+ /**
+ * Intrinsified during LambdaForm compilation
+ * (see {@link InvokerBytecodeGenerator#emitTryFinally emitTryFinally}).
+ */
+ @LambdaForm.Hidden
+ static Object tryFinally(MethodHandle target, MethodHandle cleanup, Object... av) throws Throwable {
Throwable t = null;
Object r = null;
try {
- r = target.invoke(args);
+ // Use asFixedArity() to avoid unnecessary boxing of last argument for VarargsCollector case.
+ r = target.asFixedArity().invokeWithArguments(av);
} catch (Throwable thrown) {
t = thrown;
throw t;
} finally {
- r = cleanup.invoke(t, r, args);
+ Object[] args = target.type().returnType() == void.class ? prepend(av, t) : prepend(av, t, r);
+ r = cleanup.asFixedArity().invokeWithArguments(args);
}
return r;
}
- /**
- * A method that will be bound during construction of a {@code try-finally} handle with {@code void} return type by
- * {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}.
- *
- * @param target the handle to wrap in a {@code try-finally} block. This will be bound.
- * @param cleanup the handle to run in any case before returning. This will be bound.
- * @param args the arguments to the call. These will remain as the argument list.
- *
- * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be
- * passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit.
- */
- static void tryFinallyVoidExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable {
- Throwable t = null;
- try {
- target.invoke(args);
- } catch (Throwable thrown) {
- t = thrown;
- throw t;
- } finally {
- cleanup.invoke(t, args);
- }
- }
-
// Indexes into constant method handles:
static final int
MH_cast = 0,
@@ -1958,17 +2098,14 @@
MH_fillNewTypedArray = 3,
MH_fillNewArray = 4,
MH_arrayIdentity = 5,
- MH_looper = 6,
- MH_countedLoopPred = 7,
- MH_countedLoopStep = 8,
- MH_iteratePred = 9,
- MH_initIterator = 10,
- MH_iterateNext = 11,
- MH_tryFinallyExec = 12,
- MH_tryFinallyVoidExec = 13,
- MH_decrementCounter = 14,
- MH_Array_newInstance = 15,
- MH_LIMIT = 16;
+ MH_countedLoopPred = 6,
+ MH_countedLoopStep = 7,
+ MH_iteratePred = 8,
+ MH_initIterator = 9,
+ MH_iterateNext = 10,
+ MH_decrementCounter = 11,
+ MH_Array_newInstance = 12,
+ MH_LIMIT = 13;
static MethodHandle getConstantHandle(int idx) {
MethodHandle handle = HANDLES[idx];
@@ -2013,10 +2150,6 @@
return makeIntrinsic(IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)),
Intrinsic.SELECT_ALTERNATIVE);
- case MH_looper:
- return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "looper", MethodType.methodType(Object.class,
- MethodHandle[].class, MethodHandle[].class, MethodHandle[].class, MethodHandle[].class,
- int.class, int.class, Object[].class));
case MH_countedLoopPred:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopPredicate",
MethodType.methodType(boolean.class, int.class, int.class));
@@ -2032,12 +2165,6 @@
case MH_iterateNext:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext",
MethodType.methodType(Object.class, Iterator.class));
- case MH_tryFinallyExec:
- return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyExecutor",
- MethodType.methodType(Object.class, MethodHandle.class, MethodHandle.class, Object[].class));
- case MH_tryFinallyVoidExec:
- return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyVoidExecutor",
- MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, Object[].class));
case MH_decrementCounter:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "decrementCounter",
MethodType.methodType(int.class, int.class));
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Mon Jul 04 10:08:18 2016 +0200
@@ -4233,7 +4233,7 @@
assert Stream.of(fstep, fpred, ffini).flatMap(List::stream).map(MethodHandle::type).map(MethodType::parameterList).
allMatch(pl -> pl.equals(commonParameterSequence));
- return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, commonPrefix, finit, fstep, fpred, ffini);
+ return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, finit, fstep, fpred, ffini);
}
private static List<MethodHandle> fillParameterTypes(List<MethodHandle> hs, final List<Class<?>> targetParams) {
@@ -4740,10 +4740,8 @@
// The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the
// target parameter list.
cleanup = dropArgumentsToMatch(cleanup, (rtype == void.class ? 1 : 2), targetParamTypes, 0);
- MethodHandle aTarget = target.asSpreader(Object[].class, target.type().parameterCount());
- MethodHandle aCleanup = cleanup.asSpreader(Object[].class, targetParamTypes.size());
- return MethodHandleImpl.makeTryFinally(aTarget, aCleanup, rtype, targetParamTypes);
+ return MethodHandleImpl.makeTryFinally(target, cleanup, rtype, targetParamTypes);
}
/**
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,8 +27,10 @@
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.Wrapper;
+
import java.lang.ref.SoftReference;
-import static java.lang.invoke.MethodHandleStatics.*;
+
+import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
/**
* Shared information for a group of method types, which differ
@@ -81,7 +83,9 @@
LF_MH_LINKER = 15, // linkToCallSite_MH
LF_GWC = 16, // guardWithCatch (catchException)
LF_GWT = 17, // guardWithTest
- LF_LIMIT = 18;
+ LF_TF = 18, // tryFinally
+ LF_LOOP = 19, // loop
+ LF_LIMIT = 20;
/** Return the type corresponding uniquely (1-1) to this MT-form.
* It might have any primitive returns or arguments, but will have no references except Object.
--- a/jdk/test/java/lang/invoke/MethodHandlesTest.java Mon Jul 04 13:00:15 2016 +0900
+++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java Mon Jul 04 10:08:18 2016 +0200
@@ -2792,7 +2792,7 @@
System.arraycopy(steps, 0, preSteps, 1, nargs);
System.arraycopy(finis, 0, preFinis, 0, nargs); // finis are also offset by 1 for pre-checked loops
// Convert to clause-major form.
- MethodHandle[][] preClauses = new MethodHandle[nargs+1][4];
+ MethodHandle[][] preClauses = new MethodHandle[nargs + 1][4];
MethodHandle[][] postClauses = new MethodHandle[nargs][4];
toClauseMajor(preClauses, preInits, preSteps, prePreds, preFinis);
toClauseMajor(postClauses, inits, steps, usePreds, finis);