8027827: Improve performance of catchException combinator
8034120: MethodHandles.catchException doesn't handle VarargsCollector right Reviewed-by: lagergren, psandoz, jrose
This commit is contained in:
parent
81b6eb6a69
commit
856f5c5823
@ -861,4 +861,18 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
* All subclasses must provide such a value describing their type signature.
|
||||
*/
|
||||
static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
|
||||
|
||||
private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[5];
|
||||
private static SpeciesData checkCache(int size, String types) {
|
||||
int idx = size - 1;
|
||||
SpeciesData data = SPECIES_DATA_CACHE[idx];
|
||||
if (data != null) return data;
|
||||
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"); }
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ package java.lang.invoke;
|
||||
|
||||
import sun.invoke.util.VerifyAccess;
|
||||
import java.lang.invoke.LambdaForm.Name;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
@ -39,8 +38,6 @@ import jdk.internal.org.objectweb.asm.*;
|
||||
import java.lang.reflect.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
import sun.invoke.util.ValueConversions;
|
||||
import sun.invoke.util.VerifyType;
|
||||
|
||||
/**
|
||||
@ -51,7 +48,6 @@ import sun.invoke.util.VerifyType;
|
||||
class InvokerBytecodeGenerator {
|
||||
/** Define class names for convenience. */
|
||||
private static final String MH = "java/lang/invoke/MethodHandle";
|
||||
private static final String BMH = "java/lang/invoke/BoundMethodHandle";
|
||||
private static final String LF = "java/lang/invoke/LambdaForm";
|
||||
private static final String LFN = "java/lang/invoke/LambdaForm$Name";
|
||||
private static final String CLS = "java/lang/Class";
|
||||
@ -511,17 +507,22 @@ class InvokerBytecodeGenerator {
|
||||
Name name = lambdaForm.names[i];
|
||||
MemberName member = name.function.member();
|
||||
|
||||
if (isSelectAlternative(member)) {
|
||||
// selectAlternative idiom
|
||||
// FIXME: make sure this idiom is really present!
|
||||
if (isSelectAlternative(i)) {
|
||||
emitSelectAlternative(name, lambdaForm.names[i + 1]);
|
||||
i++; // skip MH.invokeBasic of the selectAlternative result
|
||||
} else if (isGuardWithCatch(i)) {
|
||||
emitGuardWithCatch(i);
|
||||
i = i+2; // Jump to the end of GWC idiom
|
||||
} else if (isStaticallyInvocable(member)) {
|
||||
emitStaticInvoke(member, name);
|
||||
} else {
|
||||
emitInvoke(name);
|
||||
}
|
||||
|
||||
// Update cached form name's info in case an intrinsic spanning multiple names was encountered.
|
||||
name = lambdaForm.names[i];
|
||||
member = name.function.member();
|
||||
|
||||
// store the result from evaluating to the target name in a local if required
|
||||
// (if this is the last value, i.e., the one that is going to be returned,
|
||||
// avoid store/load/return and just return)
|
||||
@ -674,12 +675,66 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if MemberName is a call to MethodHandleImpl.selectAlternative.
|
||||
* Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
|
||||
*/
|
||||
private boolean isSelectAlternative(MemberName member) {
|
||||
private boolean memberRefersTo(MemberName member, Class<?> declaringClass, String name) {
|
||||
return member != null &&
|
||||
member.getDeclaringClass() == MethodHandleImpl.class &&
|
||||
member.getName().equals("selectAlternative");
|
||||
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 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}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -694,8 +749,6 @@ class InvokerBytecodeGenerator {
|
||||
* }</pre></blockquote>
|
||||
*/
|
||||
private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
|
||||
MethodType type = selectAlternativeName.function.methodType();
|
||||
|
||||
Name receiver = (Name) invokeBasicName.arguments[0];
|
||||
|
||||
Label L_fallback = new Label();
|
||||
@ -709,7 +762,6 @@ class InvokerBytecodeGenerator {
|
||||
mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
|
||||
|
||||
// invoke selectAlternativeName.arguments[1]
|
||||
MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1];
|
||||
emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
|
||||
emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
|
||||
emitInvoke(invokeBasicName);
|
||||
@ -721,7 +773,6 @@ class InvokerBytecodeGenerator {
|
||||
mv.visitLabel(L_fallback);
|
||||
|
||||
// invoke selectAlternativeName.arguments[2]
|
||||
MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
|
||||
emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
|
||||
emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
|
||||
emitInvoke(invokeBasicName);
|
||||
@ -730,6 +781,85 @@ class InvokerBytecodeGenerator {
|
||||
mv.visitLabel(L_done);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode for the guardWithCatch idiom.
|
||||
*
|
||||
* The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch):
|
||||
* <blockquote><pre>{@code
|
||||
* guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{
|
||||
* t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L);
|
||||
* t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L);
|
||||
* t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I}
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* It is compiled into bytecode equivalent of the following code:
|
||||
* <blockquote><pre>{@code
|
||||
* try {
|
||||
* return a1.invokeBasic(a6, a7);
|
||||
* } catch (Throwable e) {
|
||||
* if (!a2.isInstance(e)) throw e;
|
||||
* return a3.invokeBasic(ex, a6, a7);
|
||||
* }}
|
||||
*/
|
||||
private void emitGuardWithCatch(int pos) {
|
||||
Name args = lambdaForm.names[pos];
|
||||
Name invoker = lambdaForm.names[pos+1];
|
||||
Name result = lambdaForm.names[pos+2];
|
||||
|
||||
Label L_startBlock = new Label();
|
||||
Label L_endBlock = new Label();
|
||||
Label L_handler = new Label();
|
||||
Label L_done = new Label();
|
||||
|
||||
Class<?> returnType = result.function.resolvedHandle.type().returnType();
|
||||
MethodType type = args.function.resolvedHandle.type()
|
||||
.dropParameterTypes(0,1)
|
||||
.changeReturnType(returnType);
|
||||
|
||||
mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable");
|
||||
|
||||
// Normal case
|
||||
mv.visitLabel(L_startBlock);
|
||||
// load target
|
||||
emitPushArgument(invoker, 0);
|
||||
emitPushArguments(args, 1); // skip 1st argument: method handle
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
|
||||
mv.visitLabel(L_endBlock);
|
||||
mv.visitJumpInsn(Opcodes.GOTO, L_done);
|
||||
|
||||
// Exceptional case
|
||||
mv.visitLabel(L_handler);
|
||||
|
||||
// Check exception's type
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
// load exception class
|
||||
emitPushArgument(invoker, 1);
|
||||
mv.visitInsn(Opcodes.SWAP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z", false);
|
||||
Label L_rethrow = new Label();
|
||||
mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow);
|
||||
|
||||
// Invoke catcher
|
||||
// load catcher
|
||||
emitPushArgument(invoker, 2);
|
||||
mv.visitInsn(Opcodes.SWAP);
|
||||
emitPushArguments(args, 1); // skip 1st argument: method handle
|
||||
MethodType catcherType = type.insertParameterTypes(0, Throwable.class);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString(), false);
|
||||
mv.visitJumpInsn(Opcodes.GOTO, L_done);
|
||||
|
||||
mv.visitLabel(L_rethrow);
|
||||
mv.visitInsn(Opcodes.ATHROW);
|
||||
|
||||
mv.visitLabel(L_done);
|
||||
}
|
||||
|
||||
private void emitPushArguments(Name args, int start) {
|
||||
for (int i = start; i < args.arguments.length; i++) {
|
||||
emitPushArgument(args, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void emitPushArgument(Name name, int paramIndex) {
|
||||
Object arg = name.arguments[paramIndex];
|
||||
char ptype = name.function.parameterType(paramIndex);
|
||||
|
@ -1465,6 +1465,33 @@ class LambdaForm {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Return the index of the last occurrence of n in the argument array.
|
||||
* Return -1 if the name is not used.
|
||||
*/
|
||||
int lastUseIndex(Name n) {
|
||||
if (arguments == null) return -1;
|
||||
for (int i = arguments.length; --i >= 0; ) {
|
||||
if (arguments[i] == n) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Return the number of occurrences of n in the argument array.
|
||||
* Return 0 if the name is not used.
|
||||
*/
|
||||
int useCount(Name n) {
|
||||
if (arguments == null) return 0;
|
||||
int count = 0;
|
||||
for (int i = arguments.length; --i >= 0; ) {
|
||||
if (arguments[i] == n) ++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
boolean contains(Name n) {
|
||||
return this == n || lastUseIndex(n) >= 0;
|
||||
}
|
||||
|
||||
public boolean equals(Name that) {
|
||||
if (this == that) return true;
|
||||
if (isParam())
|
||||
@ -1488,6 +1515,35 @@ class LambdaForm {
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the index of the last name which contains n as an argument.
|
||||
* Return -1 if the name is not used. Return names.length if it is the return value.
|
||||
*/
|
||||
int lastUseIndex(Name n) {
|
||||
int ni = n.index, nmax = names.length;
|
||||
assert(names[ni] == n);
|
||||
if (result == ni) return nmax; // live all the way beyond the end
|
||||
for (int i = nmax; --i > ni; ) {
|
||||
if (names[i].lastUseIndex(n) >= 0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Return the number of times n is used as an argument or return value. */
|
||||
int useCount(Name n) {
|
||||
int ni = n.index, nmax = names.length;
|
||||
int end = lastUseIndex(n);
|
||||
if (end < 0) return 0;
|
||||
int count = 0;
|
||||
if (end == nmax) { count++; end--; }
|
||||
int beg = n.index() + 1;
|
||||
if (beg < arity) beg = arity;
|
||||
for (int i = beg; i <= end; i++) {
|
||||
count += names[i].useCount(n);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static Name argument(int which, char type) {
|
||||
int tn = ALL_TYPES.indexOf(type);
|
||||
if (tn < 0 || which >= INTERNED_ARGUMENT_LIMIT)
|
||||
|
@ -27,7 +27,6 @@ package java.lang.invoke;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import sun.invoke.empty.Empty;
|
||||
@ -482,12 +481,26 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
* Factored in an inner class to delay initialization until first usage.
|
||||
*/
|
||||
private static class Lazy {
|
||||
private static final Class<?> MHI = MethodHandleImpl.class;
|
||||
|
||||
static final NamedFunction NF_checkSpreadArgument;
|
||||
static final NamedFunction NF_guardWithCatch;
|
||||
static final NamedFunction NF_selectAlternative;
|
||||
static final NamedFunction NF_throwException;
|
||||
|
||||
static {
|
||||
try {
|
||||
NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
|
||||
.getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
|
||||
NF_checkSpreadArgument = new NamedFunction(MHI.getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
|
||||
NF_guardWithCatch = new NamedFunction(MHI.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class,
|
||||
MethodHandle.class, Object[].class));
|
||||
NF_selectAlternative = new NamedFunction(MHI.getDeclaredMethod("selectAlternative", boolean.class, MethodHandle.class,
|
||||
MethodHandle.class));
|
||||
NF_throwException = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class));
|
||||
|
||||
NF_checkSpreadArgument.resolve();
|
||||
NF_guardWithCatch.resolve();
|
||||
NF_selectAlternative.resolve();
|
||||
NF_throwException.resolve();
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
@ -548,24 +561,12 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
return SimpleMethodHandle.make(srcType, form);
|
||||
}
|
||||
|
||||
@LambdaForm.Hidden
|
||||
static
|
||||
MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) {
|
||||
return testResult ? target : fallback;
|
||||
}
|
||||
|
||||
static MethodHandle SELECT_ALTERNATIVE;
|
||||
static MethodHandle selectAlternative() {
|
||||
if (SELECT_ALTERNATIVE != null) return SELECT_ALTERNATIVE;
|
||||
try {
|
||||
SELECT_ALTERNATIVE
|
||||
= IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
|
||||
MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
return SELECT_ALTERNATIVE;
|
||||
}
|
||||
|
||||
static
|
||||
MethodHandle makeGuardWithTest(MethodHandle test,
|
||||
MethodHandle target,
|
||||
@ -585,7 +586,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
// call selectAlternative
|
||||
Object[] selectArgs = { names[arity + 1], target, fallback };
|
||||
names[arity + 2] = new Name(MethodHandleImpl.selectAlternative(), selectArgs);
|
||||
names[arity + 2] = new Name(Lazy.NF_selectAlternative, selectArgs);
|
||||
targetArgs[0] = names[arity + 2];
|
||||
|
||||
// call target or fallback
|
||||
@ -595,167 +596,137 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
return SimpleMethodHandle.make(target.type(), form);
|
||||
}
|
||||
|
||||
private static class GuardWithCatch {
|
||||
private final MethodHandle target;
|
||||
private final Class<? extends Throwable> exType;
|
||||
private final MethodHandle catcher;
|
||||
// FIXME: Build the control flow out of foldArguments.
|
||||
GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
|
||||
this.target = target;
|
||||
this.exType = exType;
|
||||
this.catcher = catcher;
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_V(Object... av) throws Throwable {
|
||||
try {
|
||||
return target.invokeExact(av);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t, av);
|
||||
}
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_L0() throws Throwable {
|
||||
try {
|
||||
return target.invokeExact();
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t);
|
||||
}
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_L1(Object a0) throws Throwable {
|
||||
try {
|
||||
return target.invokeExact(a0);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t, a0);
|
||||
}
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_L2(Object a0, Object a1) throws Throwable {
|
||||
try {
|
||||
return target.invokeExact(a0, a1);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t, a0, a1);
|
||||
}
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
|
||||
try {
|
||||
return target.invokeExact(a0, a1, a2);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t, a0, a1, a2);
|
||||
}
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
|
||||
try {
|
||||
return target.invokeExact(a0, a1, a2, a3);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t, a0, a1, a2, a3);
|
||||
}
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
|
||||
try {
|
||||
return target.invokeExact(a0, a1, a2, a3, a4);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t, a0, a1, a2, a3, a4);
|
||||
}
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
|
||||
try {
|
||||
return target.invokeExact(a0, a1, a2, a3, a4, a5);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5);
|
||||
}
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
|
||||
try {
|
||||
return target.invokeExact(a0, a1, a2, a3, a4, a5, a6);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6);
|
||||
}
|
||||
}
|
||||
@LambdaForm.Hidden
|
||||
private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
|
||||
try {
|
||||
return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7);
|
||||
}
|
||||
}
|
||||
static MethodHandle[] makeInvokes() {
|
||||
ArrayList<MethodHandle> invokes = new ArrayList<>();
|
||||
MethodHandles.Lookup lookup = IMPL_LOOKUP;
|
||||
for (;;) {
|
||||
int nargs = invokes.size();
|
||||
String name = "invoke_L"+nargs;
|
||||
MethodHandle invoke = null;
|
||||
try {
|
||||
invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
}
|
||||
if (invoke == null) break;
|
||||
invokes.add(invoke);
|
||||
}
|
||||
assert(invokes.size() == 9); // current number of methods
|
||||
return invokes.toArray(new MethodHandle[0]);
|
||||
};
|
||||
static final MethodHandle[] INVOKES = makeInvokes();
|
||||
// For testing use this:
|
||||
//static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2);
|
||||
static final MethodHandle VARARGS_INVOKE;
|
||||
static {
|
||||
try {
|
||||
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The LambaForm shape for catchException combinator is the following:
|
||||
* <blockquote><pre>{@code
|
||||
* guardWithCatch=Lambda(a0:L,a1:L,a2:L)=>{
|
||||
* t3:L=BoundMethodHandle$Species_LLLLL.argL0(a0:L);
|
||||
* t4:L=BoundMethodHandle$Species_LLLLL.argL1(a0:L);
|
||||
* t5:L=BoundMethodHandle$Species_LLLLL.argL2(a0:L);
|
||||
* t6:L=BoundMethodHandle$Species_LLLLL.argL3(a0:L);
|
||||
* t7:L=BoundMethodHandle$Species_LLLLL.argL4(a0:L);
|
||||
* t8:L=MethodHandle.invokeBasic(t6:L,a1:L,a2:L);
|
||||
* t9:L=MethodHandleImpl.guardWithCatch(t3:L,t4:L,t5:L,t8:L);
|
||||
* t10:I=MethodHandle.invokeBasic(t7:L,t9:L);t10:I}
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* argL0 and argL2 are target and catcher method handles. argL1 is exception class.
|
||||
* argL3 and argL4 are auxiliary method handles: argL3 boxes arguments and wraps them into Object[]
|
||||
* (ValueConversions.array()) and argL4 unboxes result if necessary (ValueConversions.unbox()).
|
||||
*
|
||||
* Having t8 and t10 passed outside and not hardcoded into a lambda form allows to share lambda forms
|
||||
* among catchException combinators with the same basic type.
|
||||
*/
|
||||
private static LambdaForm makeGuardWithCatchForm(MethodType basicType) {
|
||||
MethodType lambdaType = basicType.invokerType();
|
||||
|
||||
LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_GWC);
|
||||
if (lform != null) {
|
||||
return lform;
|
||||
}
|
||||
final int THIS_MH = 0; // the BMH_LLLLL
|
||||
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_CLASS = nameCursor++;
|
||||
final int GET_CATCHER = nameCursor++;
|
||||
final int GET_COLLECT_ARGS = nameCursor++;
|
||||
final int GET_UNBOX_RESULT = nameCursor++;
|
||||
final int BOXED_ARGS = nameCursor++;
|
||||
final int TRY_CATCH = nameCursor++;
|
||||
final int UNBOX_RESULT = nameCursor++;
|
||||
|
||||
Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
|
||||
|
||||
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL();
|
||||
names[GET_TARGET] = new Name(data.getterFunction(0), names[THIS_MH]);
|
||||
names[GET_CLASS] = new Name(data.getterFunction(1), names[THIS_MH]);
|
||||
names[GET_CATCHER] = new Name(data.getterFunction(2), names[THIS_MH]);
|
||||
names[GET_COLLECT_ARGS] = new Name(data.getterFunction(3), names[THIS_MH]);
|
||||
names[GET_UNBOX_RESULT] = new Name(data.getterFunction(4), names[THIS_MH]);
|
||||
|
||||
// FIXME: rework argument boxing/result unboxing logic for LF interpretation
|
||||
|
||||
// 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(new NamedFunction(invokeBasic), args);
|
||||
|
||||
// t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L);
|
||||
Object[] gwcArgs = new Object[] {names[GET_TARGET], names[GET_CLASS], names[GET_CATCHER], names[BOXED_ARGS]};
|
||||
names[TRY_CATCH] = new Name(Lazy.NF_guardWithCatch, gwcArgs);
|
||||
|
||||
// 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_CATCH]};
|
||||
names[UNBOX_RESULT] = new Name(new NamedFunction(invokeBasicUnbox), unboxArgs);
|
||||
|
||||
lform = new LambdaForm("guardWithCatch", lambdaType.parameterCount(), names);
|
||||
|
||||
basicType.form().setCachedLambdaForm(MethodTypeForm.LF_GWC, lform);
|
||||
return lform;
|
||||
}
|
||||
|
||||
static
|
||||
MethodHandle makeGuardWithCatch(MethodHandle target,
|
||||
Class<? extends Throwable> exType,
|
||||
MethodHandle catcher) {
|
||||
MethodType type = target.type();
|
||||
MethodType ctype = catcher.type();
|
||||
int nargs = type.parameterCount();
|
||||
if (nargs < GuardWithCatch.INVOKES.length) {
|
||||
MethodType gtype = type.generic();
|
||||
MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
|
||||
// Note: convertArguments(...2) avoids interface casts present in convertArguments(...0)
|
||||
MethodHandle gtarget = makePairwiseConvert(target, gtype, 2);
|
||||
MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2);
|
||||
GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
|
||||
if (gtarget == null || gcatcher == null) throw new InternalError();
|
||||
MethodHandle ginvoker = GuardWithCatch.INVOKES[nargs].bindReceiver(gguard);
|
||||
return makePairwiseConvert(ginvoker, type, 2);
|
||||
LambdaForm form = makeGuardWithCatchForm(type.basicType());
|
||||
|
||||
// Prepare auxiliary method handles used during LambdaForm interpreation.
|
||||
// Box arguments and wrap them into Object[]: ValueConversions.array().
|
||||
MethodType varargsType = type.changeReturnType(Object[].class);
|
||||
MethodHandle collectArgs = ValueConversions.varargsArray(type.parameterCount())
|
||||
.asType(varargsType);
|
||||
// Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore().
|
||||
MethodHandle unboxResult;
|
||||
if (type.returnType().isPrimitive()) {
|
||||
unboxResult = ValueConversions.unbox(type.returnType());
|
||||
} else {
|
||||
target = target.asType(type.changeReturnType(Object.class));
|
||||
MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs);
|
||||
MethodType catcherType = ctype.changeParameterType(0, Throwable.class)
|
||||
.changeReturnType(Object.class);
|
||||
catcher = catcher.asType(catcherType);
|
||||
MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs);
|
||||
GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
|
||||
if (gtarget == null || gcatcher == null) throw new InternalError();
|
||||
MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard);
|
||||
MethodHandle gcollect = makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false);
|
||||
return makePairwiseConvert(gcollect, type, 2);
|
||||
unboxResult = ValueConversions.identity();
|
||||
}
|
||||
|
||||
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL();
|
||||
BoundMethodHandle mh;
|
||||
try {
|
||||
mh = (BoundMethodHandle)
|
||||
data.constructor[0].invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher,
|
||||
(Object) collectArgs, (Object) unboxResult);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
assert(mh.type() == type);
|
||||
return mh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intrinsified during LambdaForm compilation
|
||||
* (see {@link InvokerBytecodeGenerator#emitGuardWithCatch emitGuardWithCatch}).
|
||||
*/
|
||||
@LambdaForm.Hidden
|
||||
static Object guardWithCatch(MethodHandle target, Class exType, MethodHandle catcher,
|
||||
Object... av) throws Throwable {
|
||||
try {
|
||||
return target.invokeWithArguments(av);
|
||||
} catch (Throwable t) {
|
||||
if (!exType.isInstance(t)) throw t;
|
||||
Object[] args = prepend(t, av);
|
||||
return catcher.invokeWithArguments(args);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prepend an element {@code elem} to an {@code array}. */
|
||||
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);
|
||||
return newArray;
|
||||
}
|
||||
|
||||
static
|
||||
@ -765,23 +736,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
if (arity > 1) {
|
||||
return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1);
|
||||
}
|
||||
return makePairwiseConvert(throwException(), type, 2);
|
||||
return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, 2);
|
||||
}
|
||||
|
||||
static MethodHandle THROW_EXCEPTION;
|
||||
static MethodHandle throwException() {
|
||||
MethodHandle mh = THROW_EXCEPTION;
|
||||
if (mh != null) return mh;
|
||||
try {
|
||||
mh
|
||||
= IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
|
||||
MethodType.methodType(Empty.class, Throwable.class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
THROW_EXCEPTION = mh;
|
||||
return mh;
|
||||
}
|
||||
static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
|
||||
|
||||
static MethodHandle[] FAKE_METHOD_HANDLE_INVOKE = new MethodHandle[2];
|
||||
|
@ -76,7 +76,8 @@ final class MethodTypeForm {
|
||||
LF_GEN_INVOKER = 12,
|
||||
LF_CS_LINKER = 13, // linkToCallSite_CS
|
||||
LF_MH_LINKER = 14, // linkToCallSite_MH
|
||||
LF_LIMIT = 15;
|
||||
LF_GWC = 15,
|
||||
LF_LIMIT = 16;
|
||||
|
||||
public MethodType erasedType() {
|
||||
return erasedType;
|
||||
|
@ -72,10 +72,45 @@ public class TestCatchException {
|
||||
assertEquals(x, 17);
|
||||
}
|
||||
|
||||
|
||||
public static Object m1(Object o1, Object o2, Object o3, Object o4, Object o5,
|
||||
Object o6, Object o7, Object o8, Object... tail) {
|
||||
return tail;
|
||||
}
|
||||
|
||||
public static Object m2(Exception e, Object o1, Object o2, Object o3, Object o4,
|
||||
Object o5, Object o6, Object o7, Object o8, Object... tail) {
|
||||
return tail;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVarargsCollector() throws Throwable {
|
||||
MethodType t1 = MethodType.methodType(Object.class, Object.class, Object.class, Object.class, Object.class,
|
||||
Object.class, Object.class, Object.class, Object.class, Object[].class);
|
||||
|
||||
MethodType t2 = t1.insertParameterTypes(0, Exception.class);
|
||||
|
||||
MethodHandle target = LOOKUP.findStatic(TestCatchException.class, "m1", t1)
|
||||
.asVarargsCollector(Object[].class);
|
||||
|
||||
MethodHandle catcher = LOOKUP.findStatic(TestCatchException.class, "m2", t2);
|
||||
|
||||
MethodHandle gwc = MethodHandles.catchException(target, Exception.class, catcher);
|
||||
|
||||
Object o = new Object();
|
||||
Object[] obj1 = new Object[] { "str" };
|
||||
|
||||
Object r1 = target.invokeExact(o, o, o, o, o, o, o, o, obj1);
|
||||
Object r2 = gwc.invokeExact(o, o, o, o, o, o, o, o, obj1);
|
||||
assertEquals(r1, obj1);
|
||||
assertEquals(r2, obj1);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
TestCatchException test = new TestCatchException();
|
||||
test.testNoThrowPath();
|
||||
test.testThrowPath();
|
||||
test.testVarargsCollector();
|
||||
System.out.println("TEST PASSED");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user