8198523: Refactor BootstrapMethodInvoker to further avoid runtime type checks
Reviewed-by: psandoz
This commit is contained in:
parent
cff8ccbcf7
commit
ac45567657
@ -37,6 +37,7 @@ import static java.lang.invoke.MethodHandles.Lookup;
|
|||||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||||
|
|
||||||
final class BootstrapMethodInvoker {
|
final class BootstrapMethodInvoker {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factored code for invoking a bootstrap method for invokedynamic
|
* Factored code for invoking a bootstrap method for invokedynamic
|
||||||
* or a dynamic constant.
|
* or a dynamic constant.
|
||||||
@ -76,14 +77,30 @@ final class BootstrapMethodInvoker {
|
|||||||
bootstrapMethod = null;
|
bootstrapMethod = null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
// As an optimization we special case various known BSMs,
|
||||||
|
// such as LambdaMetafactory::metafactory and
|
||||||
|
// StringConcatFactory::makeConcatWithConstants.
|
||||||
|
//
|
||||||
|
// By providing static type information or even invoking
|
||||||
|
// exactly, we avoid emitting code to perform runtime
|
||||||
|
// checking.
|
||||||
info = maybeReBox(info);
|
info = maybeReBox(info);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
// VM is allowed to pass up a null meaning no BSM args
|
// VM is allowed to pass up a null meaning no BSM args
|
||||||
result = bootstrapMethod.invoke(caller, name, type);
|
result = invoke(bootstrapMethod, caller, name, type);
|
||||||
}
|
}
|
||||||
else if (!info.getClass().isArray()) {
|
else if (!info.getClass().isArray()) {
|
||||||
// VM is allowed to pass up a single BSM arg directly
|
// VM is allowed to pass up a single BSM arg directly
|
||||||
result = bootstrapMethod.invoke(caller, name, type, info);
|
|
||||||
|
// Call to StringConcatFactory::makeConcatWithConstants
|
||||||
|
// with empty constant arguments?
|
||||||
|
if (isStringConcatFactoryBSM(bootstrapMethod.type())) {
|
||||||
|
result = (CallSite)bootstrapMethod
|
||||||
|
.invokeExact(caller, name, (MethodType)type,
|
||||||
|
(String)info, new Object[0]);
|
||||||
|
} else {
|
||||||
|
result = invoke(bootstrapMethod, caller, name, type, info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (info.getClass() == int[].class) {
|
else if (info.getClass() == int[].class) {
|
||||||
// VM is allowed to pass up a pair {argc, index}
|
// VM is allowed to pass up a pair {argc, index}
|
||||||
@ -103,67 +120,52 @@ final class BootstrapMethodInvoker {
|
|||||||
// VM is allowed to pass up a full array of resolved BSM args
|
// VM is allowed to pass up a full array of resolved BSM args
|
||||||
Object[] argv = (Object[]) info;
|
Object[] argv = (Object[]) info;
|
||||||
maybeReBoxElements(argv);
|
maybeReBoxElements(argv);
|
||||||
switch (argv.length) {
|
|
||||||
case 0:
|
MethodType bsmType = bootstrapMethod.type();
|
||||||
result = bootstrapMethod.invoke(caller, name, type);
|
if (isLambdaMetafactoryIndyBSM(bsmType) && argv.length == 3) {
|
||||||
break;
|
result = (CallSite)bootstrapMethod
|
||||||
case 1:
|
.invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
|
||||||
result = bootstrapMethod.invoke(caller, name, type,
|
(MethodHandle)argv[1], (MethodType)argv[2]);
|
||||||
argv[0]);
|
} else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
|
||||||
break;
|
result = bootstrapMethod
|
||||||
case 2:
|
.invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
|
||||||
result = bootstrapMethod.invoke(caller, name, type,
|
(MethodHandle)argv[1], (MethodType)argv[2]);
|
||||||
argv[0], argv[1]);
|
} else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
|
||||||
break;
|
String recipe = (String)argv[0];
|
||||||
case 3:
|
Object[] shiftedArgs = Arrays.copyOfRange(argv, 1, argv.length);
|
||||||
// Special case the LambdaMetafactory::metafactory BSM
|
result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs);
|
||||||
//
|
} else {
|
||||||
// By invoking exactly, we can avoid generating a number of
|
switch (argv.length) {
|
||||||
// classes on first (and subsequent) lambda initialization,
|
case 0:
|
||||||
// most of which won't be shared with other invoke uses.
|
result = invoke(bootstrapMethod, caller, name, type);
|
||||||
MethodType bsmType = bootstrapMethod.type();
|
|
||||||
if (isLambdaMetafactoryIndyBSM(bsmType)) {
|
|
||||||
result = (CallSite)bootstrapMethod
|
|
||||||
.invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
|
|
||||||
(MethodHandle)argv[1], (MethodType)argv[2]);
|
|
||||||
} else if (isLambdaMetafactoryCondyBSM(bsmType)) {
|
|
||||||
result = bootstrapMethod
|
|
||||||
.invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
|
|
||||||
(MethodHandle)argv[1], (MethodType)argv[2]);
|
|
||||||
} else {
|
|
||||||
result = bootstrapMethod.invoke(caller, name, type,
|
|
||||||
argv[0], argv[1], argv[2]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
result = bootstrapMethod.invoke(caller, name, type,
|
|
||||||
argv[0], argv[1], argv[2], argv[3]);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
result = bootstrapMethod.invoke(caller, name, type,
|
|
||||||
argv[0], argv[1], argv[2], argv[3], argv[4]);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
result = bootstrapMethod.invoke(caller, name, type,
|
|
||||||
argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type)
|
|
||||||
final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
|
|
||||||
if (argv.length >= MAX_SAFE_SIZE) {
|
|
||||||
// to be on the safe side, use invokeWithArguments which handles jumbo lists
|
|
||||||
Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
|
|
||||||
newargv[0] = caller;
|
|
||||||
newargv[1] = name;
|
|
||||||
newargv[2] = type;
|
|
||||||
System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
|
|
||||||
result = bootstrapMethod.invokeWithArguments(newargv);
|
|
||||||
break;
|
break;
|
||||||
}
|
case 1:
|
||||||
MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
|
result = invoke(bootstrapMethod, caller, name, type,
|
||||||
MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
|
argv[0]);
|
||||||
MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
|
break;
|
||||||
result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
|
case 2:
|
||||||
|
result = invoke(bootstrapMethod, caller, name, type,
|
||||||
|
argv[0], argv[1]);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
result = invoke(bootstrapMethod, caller, name, type,
|
||||||
|
argv[0], argv[1], argv[2]);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
result = invoke(bootstrapMethod, caller, name, type,
|
||||||
|
argv[0], argv[1], argv[2], argv[3]);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
result = invoke(bootstrapMethod, caller, name, type,
|
||||||
|
argv[0], argv[1], argv[2], argv[3], argv[4]);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
result = invoke(bootstrapMethod, caller, name, type,
|
||||||
|
argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (resultType.isPrimitive()) {
|
if (resultType.isPrimitive()) {
|
||||||
@ -191,12 +193,114 @@ final class BootstrapMethodInvoker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we don't provide static type information for type, we'll generate runtime
|
||||||
|
// checks. Let's try not to...
|
||||||
|
|
||||||
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
|
||||||
|
String name, Object type) throws Throwable {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type);
|
||||||
|
} else {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (MethodType)type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
|
||||||
|
String name, Object type, Object arg0) throws Throwable {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0);
|
||||||
|
} else {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
|
||||||
|
Object type, Object arg0, Object arg1) throws Throwable {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1);
|
||||||
|
} else {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
|
||||||
|
Object type, Object arg0, Object arg1,
|
||||||
|
Object arg2) throws Throwable {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2);
|
||||||
|
} else {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
|
||||||
|
Object type, Object arg0, Object arg1,
|
||||||
|
Object arg2, Object arg3) throws Throwable {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3);
|
||||||
|
} else {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
|
||||||
|
String name, Object type, Object arg0, Object arg1,
|
||||||
|
Object arg2, Object arg3, Object arg4) throws Throwable {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4);
|
||||||
|
} else {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
|
||||||
|
String name, Object type, Object arg0, Object arg1,
|
||||||
|
Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4, arg5);
|
||||||
|
} else {
|
||||||
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4, arg5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller,
|
||||||
|
String name, Object type, Object[] argv) throws Throwable {
|
||||||
|
final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type)
|
||||||
|
final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
|
||||||
|
if (argv.length >= MAX_SAFE_SIZE) {
|
||||||
|
// to be on the safe side, use invokeWithArguments which handles jumbo lists
|
||||||
|
Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
|
||||||
|
newargv[0] = caller;
|
||||||
|
newargv[1] = name;
|
||||||
|
newargv[2] = type;
|
||||||
|
System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
|
||||||
|
return bootstrapMethod.invokeWithArguments(newargv);
|
||||||
|
} else {
|
||||||
|
MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
|
||||||
|
MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
|
||||||
|
MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
|
||||||
|
return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
|
private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
|
||||||
Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
|
Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
|
||||||
|
|
||||||
private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
|
private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
|
||||||
Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
|
Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
|
||||||
|
|
||||||
|
private static final MethodType SCF_MT = MethodType.methodType(CallSite.class,
|
||||||
|
Lookup.class, String.class, MethodType.class, String.class, Object[].class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true iff the BSM method type exactly matches
|
||||||
|
* {@see java.lang.invoke.StringConcatFactory#makeConcatWithConstants(MethodHandles.Lookup,
|
||||||
|
* String,MethodType,String,Object...))}
|
||||||
|
*/
|
||||||
|
private static boolean isStringConcatFactoryBSM(MethodType bsmType) {
|
||||||
|
return bsmType == SCF_MT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true iff the BSM method type exactly matches
|
* @return true iff the BSM method type exactly matches
|
||||||
* {@see java.lang.invoke.LambdaMetafactory#metafactory(
|
* {@see java.lang.invoke.LambdaMetafactory#metafactory(
|
||||||
|
@ -373,6 +373,12 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringConcatFactory bootstrap methods are startup sensitive, and may be
|
||||||
|
// special cased in java.lang.invokeBootstrapMethodInvoker to ensure
|
||||||
|
// methods are invoked with exact type information to avoid generating
|
||||||
|
// code for runtime checks. Take care any changes or additions here are
|
||||||
|
// reflected there as appropriate.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Facilitates the creation of optimized String concatenation methods, that
|
* Facilitates the creation of optimized String concatenation methods, that
|
||||||
* can be used to efficiently concatenate a known number of arguments of
|
* can be used to efficiently concatenate a known number of arguments of
|
||||||
|
Loading…
Reference in New Issue
Block a user