8142334: Improve lazy initialization of java.lang.invoke

Reviewed-by: psandoz, vlivanov, mhaupt
This commit is contained in:
Claes Redestad 2015-11-17 11:51:45 +01:00
parent 63852abb50
commit 30545d614d
6 changed files with 202 additions and 189 deletions

View File

@ -224,12 +224,12 @@ class DirectMethodHandle extends MethodHandle {
assert(names.length == nameCursor);
if (doesAlloc) {
// names = { argx,y,z,... new C, init method }
names[NEW_OBJ] = new Name(Lazy.NF_allocateInstance, names[DMH_THIS]);
names[GET_MEMBER] = new Name(Lazy.NF_constructorMethod, names[DMH_THIS]);
names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]);
names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]);
} else if (needsInit) {
names[GET_MEMBER] = new Name(Lazy.NF_internalMemberNameEnsureInit, names[DMH_THIS]);
names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]);
} else {
names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]);
names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
}
assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
@ -250,9 +250,9 @@ class DirectMethodHandle extends MethodHandle {
}
static Object findDirectMethodHandle(Name name) {
if (name.function == Lazy.NF_internalMemberName ||
name.function == Lazy.NF_internalMemberNameEnsureInit ||
name.function == Lazy.NF_constructorMethod) {
if (name.function == NF_internalMemberName ||
name.function == NF_internalMemberNameEnsureInit ||
name.function == NF_constructorMethod) {
assert(name.arguments.length == 1);
return name.arguments[0];
}
@ -613,18 +613,18 @@ class DirectMethodHandle extends MethodHandle {
final int RESULT = nameCursor-1; // either the call or the cast
Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
if (needsInit)
names[INIT_BAR] = new Name(Lazy.NF_ensureInitialized, names[DMH_THIS]);
names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]);
if (needsCast && !isGetter)
names[PRE_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
Object[] outArgs = new Object[1 + linkerType.parameterCount()];
assert(outArgs.length == (isGetter ? 3 : 4));
outArgs[0] = UNSAFE;
if (isStatic) {
outArgs[1] = names[F_HOLDER] = new Name(Lazy.NF_staticBase, names[DMH_THIS]);
outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_staticOffset, names[DMH_THIS]);
outArgs[1] = names[F_HOLDER] = new Name(NF_staticBase, names[DMH_THIS]);
outArgs[2] = names[F_OFFSET] = new Name(NF_staticOffset, names[DMH_THIS]);
} else {
outArgs[1] = names[OBJ_CHECK] = new Name(Lazy.NF_checkBase, names[OBJ_BASE]);
outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_fieldOffset, names[DMH_THIS]);
outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]);
outArgs[2] = names[F_OFFSET] = new Name(NF_fieldOffset, names[DMH_THIS]);
}
if (!isGetter) {
outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
@ -632,7 +632,7 @@ class DirectMethodHandle extends MethodHandle {
for (Object a : outArgs) assert(a != null);
names[LINKER_CALL] = new Name(linker, outArgs);
if (needsCast && isGetter)
names[POST_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
for (Name n : names) assert(n != null);
String fieldOrStatic = (isStatic ? "Static" : "Field");
String lambdaName = (linkerName + fieldOrStatic); // significant only for debugging
@ -645,50 +645,45 @@ class DirectMethodHandle extends MethodHandle {
* Pre-initialized NamedFunctions for bootstrapping purposes.
* Factored in an inner class to delay initialization until first usage.
*/
private static class Lazy {
static final NamedFunction
NF_internalMemberName,
NF_internalMemberNameEnsureInit,
NF_ensureInitialized,
NF_fieldOffset,
NF_checkBase,
NF_staticBase,
NF_staticOffset,
NF_checkCast,
NF_allocateInstance,
NF_constructorMethod;
static {
try {
NamedFunction nfs[] = {
NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("internalMemberName", Object.class)),
NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("ensureInitialized", Object.class)),
NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("fieldOffset", Object.class)),
NF_checkBase = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("checkBase", Object.class)),
NF_staticBase = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("staticBase", Object.class)),
NF_staticOffset = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("staticOffset", Object.class)),
NF_checkCast = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("checkCast", Object.class, Object.class)),
NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("allocateInstance", Object.class)),
NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("constructorMethod", Object.class))
};
for (NamedFunction nf : nfs) {
// Each nf must be statically invocable or we get tied up in our bootstraps.
assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
nf.resolve();
}
} catch (ReflectiveOperationException ex) {
throw newInternalError(ex);
}
static final NamedFunction
NF_internalMemberName,
NF_internalMemberNameEnsureInit,
NF_ensureInitialized,
NF_fieldOffset,
NF_checkBase,
NF_staticBase,
NF_staticOffset,
NF_checkCast,
NF_allocateInstance,
NF_constructorMethod;
static {
try {
NamedFunction nfs[] = {
NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("internalMemberName", Object.class)),
NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("ensureInitialized", Object.class)),
NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("fieldOffset", Object.class)),
NF_checkBase = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("checkBase", Object.class)),
NF_staticBase = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("staticBase", Object.class)),
NF_staticOffset = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("staticOffset", Object.class)),
NF_checkCast = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("checkCast", Object.class, Object.class)),
NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("allocateInstance", Object.class)),
NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("constructorMethod", Object.class))
};
// Each nf must be statically invocable or we get tied up in our bootstraps.
assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));
} catch (ReflectiveOperationException ex) {
throw newInternalError(ex);
}
}
}

View File

@ -750,7 +750,7 @@ class InvokerBytecodeGenerator {
assert(!isLinkerMethodInvoke(name)); // should use the static path for these
if (true) {
// push receiver
MethodHandle target = name.function.resolvedHandle;
MethodHandle target = name.function.resolvedHandle();
assert(target != null) : name.exprString();
mv.visitLdcInsn(constantPlaceholder(target));
emitReferenceCast(MethodHandle.class, target);
@ -779,6 +779,15 @@ class InvokerBytecodeGenerator {
//MethodHandle.class already covered
};
static boolean isStaticallyInvocable(NamedFunction[] functions) {
for (NamedFunction nf : functions) {
if (!isStaticallyInvocable(nf.member)) {
return false;
}
}
return true;
}
static boolean isStaticallyInvocable(Name name) {
return isStaticallyInvocable(name.function.member());
}
@ -881,7 +890,7 @@ class InvokerBytecodeGenerator {
// The array will be a constant.
Object emptyArray;
try {
emptyArray = name.function.resolvedHandle.invoke();
emptyArray = name.function.resolvedHandle().invoke();
} catch (Throwable ex) {
throw newInternalError(ex);
}
@ -1085,8 +1094,8 @@ class InvokerBytecodeGenerator {
Label L_handler = new Label();
Label L_done = new Label();
Class<?> returnType = result.function.resolvedHandle.type().returnType();
MethodType type = args.function.resolvedHandle.type()
Class<?> returnType = result.function.resolvedHandle().type().returnType();
MethodType type = args.function.resolvedHandle().type()
.dropParameterTypes(0,1)
.changeReturnType(returnType);

View File

@ -429,11 +429,8 @@ class Invokers {
NF_checkCustomized = new NamedFunction(Invokers.class
.getDeclaredMethod("checkCustomized", Object.class))
};
for (NamedFunction nf : nfs) {
// Each nf must be statically invocable or we get tied up in our bootstraps.
assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
nf.resolve();
}
// Each nf must be statically invocable or we get tied up in our bootstraps.
assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));
} catch (ReflectiveOperationException ex) {
throw newInternalError(ex);
}

View File

@ -1024,7 +1024,7 @@ class LambdaForm {
static class NamedFunction {
final MemberName member;
@Stable MethodHandle resolvedHandle;
private @Stable MethodHandle resolvedHandle;
@Stable MethodHandle invoker;
NamedFunction(MethodHandle resolvedHandle) {
@ -1074,8 +1074,10 @@ class LambdaForm {
return resolvedHandle;
}
void resolve() {
resolvedHandle = DirectMethodHandle.make(member);
synchronized void resolve() {
if (resolvedHandle == null) {
resolvedHandle = DirectMethodHandle.make(member);
}
}
@Override
@ -1235,6 +1237,7 @@ class LambdaForm {
traceInterpreter("| getInvoker", this);
invoker();
}
// resolvedHandle might be uninitialized, ok for tracing
if (resolvedHandle == null) {
traceInterpreter("| resolve", this);
resolvedHandle();

View File

@ -541,7 +541,7 @@ class LambdaFormEditor {
assert(pos > 0); // cannot spread the MH arg itself
Name spreadParam = new Name(L_TYPE);
Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength);
Name checkSpread = new Name(MethodHandleImpl.NF_checkSpreadArgument, spreadParam, arrayLength);
// insert the new expressions
int exprPos = lambdaForm.arity();

View File

@ -219,7 +219,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
if (convSpec == null) continue;
MethodHandle fn;
if (convSpec instanceof Class) {
fn = Lazy.MH_cast.bindTo(convSpec);
fn = getConstantHandle(MH_cast).bindTo(convSpec);
} else {
fn = (MethodHandle) convSpec;
}
@ -239,7 +239,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
if (convSpec == void.class)
fn = null;
else
fn = Lazy.MH_cast.bindTo(convSpec);
fn = getConstantHandle(MH_cast).bindTo(convSpec);
} else {
fn = (MethodHandle) convSpec;
}
@ -302,7 +302,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
Name conv;
if (convSpec instanceof Class) {
Class<?> convClass = (Class<?>) convSpec;
conv = new Name(Lazy.MH_cast, convClass, names[INARG_BASE + i]);
conv = new Name(getConstantHandle(MH_cast), convClass, names[INARG_BASE + i]);
} else {
MethodHandle fn = (MethodHandle) convSpec;
conv = new Name(fn, names[INARG_BASE + i]);
@ -326,7 +326,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType())));
} else if (convSpec instanceof Class) {
Class<?> convClass = (Class<?>) convSpec;
conv = new Name(Lazy.MH_cast, convClass, names[OUT_CALL]);
conv = new Name(getConstantHandle(MH_cast), convClass, names[OUT_CALL]);
} else {
MethodHandle fn = (MethodHandle) convSpec;
if (fn.type().parameterCount() == 0)
@ -529,7 +529,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
// Spread the array.
MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
Name array = names[argIndex];
names[nameCursor++] = new Name(Lazy.NF_checkSpreadArgument, array, spreadArgCount);
names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
for (int j = 0; j < spreadArgCount; i++, j++) {
indexes[i] = nameCursor;
names[nameCursor++] = new Name(aload, array, j);
@ -566,66 +566,6 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
throw newIllegalArgumentException("array is not of length "+n);
}
/**
* Pre-initialized NamedFunctions for bootstrapping purposes.
* Factored in an inner class to delay initialization until first usage.
*/
static class Lazy {
private static final Class<?> MHI = MethodHandleImpl.class;
private static final Class<?> CLS = Class.class;
private static final MethodHandle[] ARRAYS;
private static final MethodHandle[] FILL_ARRAYS;
static final NamedFunction NF_checkSpreadArgument;
static final NamedFunction NF_guardWithCatch;
static final NamedFunction NF_throwException;
static final NamedFunction NF_profileBoolean;
static final MethodHandle MH_cast;
static final MethodHandle MH_selectAlternative;
static final MethodHandle MH_copyAsPrimitiveArray;
static final MethodHandle MH_fillNewTypedArray;
static final MethodHandle MH_fillNewArray;
static final MethodHandle MH_arrayIdentity;
static {
ARRAYS = makeArrays();
FILL_ARRAYS = makeFillArrays();
try {
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_throwException = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class));
NF_profileBoolean = new NamedFunction(MHI.getDeclaredMethod("profileBoolean", boolean.class, int[].class));
NF_checkSpreadArgument.resolve();
NF_guardWithCatch.resolve();
NF_throwException.resolve();
NF_profileBoolean.resolve();
MH_cast = IMPL_LOOKUP.findVirtual(CLS, "cast",
MethodType.methodType(Object.class, Object.class));
MH_copyAsPrimitiveArray = IMPL_LOOKUP.findStatic(MHI, "copyAsPrimitiveArray",
MethodType.methodType(Object.class, Wrapper.class, Object[].class));
MH_arrayIdentity = IMPL_LOOKUP.findStatic(MHI, "identity",
MethodType.methodType(Object[].class, Object[].class));
MH_fillNewArray = IMPL_LOOKUP.findStatic(MHI, "fillNewArray",
MethodType.methodType(Object[].class, Integer.class, Object[].class));
MH_fillNewTypedArray = IMPL_LOOKUP.findStatic(MHI, "fillNewTypedArray",
MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
MH_selectAlternative = makeIntrinsic(
IMPL_LOOKUP.findStatic(MHI, "selectAlternative",
MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)),
Intrinsic.SELECT_ALTERNATIVE);
} catch (ReflectiveOperationException ex) {
throw newInternalError(ex);
}
}
}
/** Factory method: Collect or filter selected argument(s). */
static MethodHandle makeCollectArguments(MethodHandle target,
MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
@ -911,10 +851,10 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
// profile branch
if (PROFILE != -1) {
names[PROFILE] = new Name(Lazy.NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]);
names[PROFILE] = new Name(NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]);
}
// call selectAlternative
names[SELECT_ALT] = new Name(Lazy.MH_selectAlternative, names[TEST], names[GET_TARGET], names[GET_FALLBACK]);
names[SELECT_ALT] = new Name(getConstantHandle(MH_selectAlternative), names[TEST], names[GET_TARGET], names[GET_FALLBACK]);
// call target or fallback
invokeArgs[0] = names[SELECT_ALT];
@ -989,7 +929,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
// 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);
names[TRY_CATCH] = new Name(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));
@ -1073,7 +1013,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity));
return mh;
}
return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, false, true);
return makePairwiseConvert(NF_throwException.resolvedHandle(), type, false, true);
}
static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
@ -1421,25 +1361,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
private static final int ARRAYS_COUNT = 11;
private static MethodHandle[] makeArrays() {
MethodHandle[] mhs = new MethodHandle[MAX_ARITY + 1];
for (int i = 0; i < ARRAYS_COUNT; i++) {
MethodHandle mh = findCollector("array", i, Object[].class);
mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
mhs[i] = mh;
}
assert(assertArrayMethodCount(mhs));
return mhs;
}
private static boolean assertArrayMethodCount(MethodHandle[] mhs) {
assert(findCollector("array", ARRAYS_COUNT, Object[].class) == null);
for (int i = 0; i < ARRAYS_COUNT; i++) {
assert(mhs[i] != null);
}
return true;
}
private static final @Stable MethodHandle[] ARRAYS = new MethodHandle[MAX_ARITY + 1];
// filling versions of the above:
// using Integer len instead of int len and no varargs to avoid bootstrapping problems
@ -1488,24 +1410,17 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
private static final int FILL_ARRAYS_COUNT = 11; // current number of fillArray methods
private static final @Stable MethodHandle[] FILL_ARRAYS = new MethodHandle[FILL_ARRAYS_COUNT];
private static MethodHandle[] makeFillArrays() {
MethodHandle[] mhs = new MethodHandle[FILL_ARRAYS_COUNT];
mhs[0] = null; // there is no empty fill; at least a0 is required
for (int i = 1; i < FILL_ARRAYS_COUNT; i++) {
MethodHandle mh = findCollector("fillArray", i, Object[].class, Integer.class, Object[].class);
mhs[i] = mh;
private static MethodHandle getFillArray(int count) {
assert (count > 0 && count < FILL_ARRAYS_COUNT);
MethodHandle mh = FILL_ARRAYS[count];
if (mh != null) {
return mh;
}
assert(assertFillArrayMethodCount(mhs));
return mhs;
}
private static boolean assertFillArrayMethodCount(MethodHandle[] mhs) {
assert(findCollector("fillArray", FILL_ARRAYS_COUNT, Object[].class, Integer.class, Object[].class) == null);
for (int i = 1; i < FILL_ARRAYS_COUNT; i++) {
assert(mhs[i] != null);
}
return true;
mh = findCollector("fillArray", count, Object[].class, Integer.class, Object[].class);
FILL_ARRAYS[count] = mh;
return mh;
}
private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
@ -1518,12 +1433,19 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
* arguments and returns an Object array of them, as if for varargs.
*/
static MethodHandle varargsArray(int nargs) {
MethodHandle mh = Lazy.ARRAYS[nargs];
if (mh != null) return mh;
mh = buildVarargsArray(Lazy.MH_fillNewArray, Lazy.MH_arrayIdentity, nargs);
MethodHandle mh = ARRAYS[nargs];
if (mh != null) {
return mh;
}
if (nargs < ARRAYS_COUNT) {
mh = findCollector("array", nargs, Object[].class);
} else {
mh = buildVarargsArray(getConstantHandle(MH_fillNewArray),
getConstantHandle(MH_arrayIdentity), nargs);
}
assert(assertCorrectArity(mh, nargs));
mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
return Lazy.ARRAYS[nargs] = mh;
return ARRAYS[nargs] = mh;
}
private static boolean assertCorrectArity(MethodHandle mh, int arity) {
@ -1531,7 +1453,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
return true;
}
// Array identity function (used as Lazy.MH_arrayIdentity).
// Array identity function (used as getConstantHandle(MH_arrayIdentity)).
static <T> T[] identity(T[] x) {
return x;
}
@ -1547,12 +1469,12 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
MethodHandle mh = finisher;
if (rightLen > 0) {
MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen);
if (mh == Lazy.MH_arrayIdentity)
if (mh.equals(getConstantHandle(MH_arrayIdentity)))
mh = rightFiller;
else
mh = MethodHandles.collectArguments(mh, 0, rightFiller);
}
if (mh == Lazy.MH_arrayIdentity)
if (mh.equals(getConstantHandle(MH_arrayIdentity)))
mh = leftCollector;
else
mh = MethodHandles.collectArguments(mh, 0, leftCollector);
@ -1560,7 +1482,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
}
private static final int LEFT_ARGS = FILL_ARRAYS_COUNT - 1;
private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1];
private static final @Stable MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1];
/** fill_array_to_right(N).invoke(a, argL..arg[N-1])
* fills a[L]..a[N-1] with corresponding arguments,
* and then returns a. The value L is a global constant (LEFT_ARGS).
@ -1574,7 +1496,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
}
private static MethodHandle buildFiller(int nargs) {
if (nargs <= LEFT_ARGS)
return Lazy.MH_arrayIdentity; // no args to fill; return the array unchanged
return getConstantHandle(MH_arrayIdentity); // no args to fill; return the array unchanged
// we need room for both mh and a in mh.invoke(a, arg*[nargs])
final int CHUNK = LEFT_ARGS;
int rightLen = nargs % CHUNK;
@ -1590,7 +1512,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS);
assert(rightLen > 0);
MethodHandle midFill = fillToRight(midLen); // recursive fill
MethodHandle rightFill = Lazy.FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1]
MethodHandle rightFill = getFillArray(rightLen).bindTo(midLen); // [midLen..nargs-1]
assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS);
assert(rightFill.type().parameterCount() == 1 + rightLen);
@ -1641,14 +1563,14 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
Object example = java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0);
mh = MethodHandles.constant(arrayType, example);
} else if (elemType.isPrimitive()) {
MethodHandle builder = Lazy.MH_fillNewArray;
MethodHandle builder = getConstantHandle(MH_fillNewArray);
MethodHandle producer = buildArrayProducer(arrayType);
mh = buildVarargsArray(builder, producer, nargs);
} else {
Class<? extends Object[]> objArrayType = arrayType.asSubclass(Object[].class);
Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType);
MethodHandle builder = Lazy.MH_fillNewTypedArray.bindTo(example);
MethodHandle producer = Lazy.MH_arrayIdentity; // must be weakly typed
MethodHandle builder = getConstantHandle(MH_fillNewTypedArray).bindTo(example);
MethodHandle producer = getConstantHandle(MH_arrayIdentity); // must be weakly typed
mh = buildVarargsArray(builder, producer, nargs);
}
mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
@ -1662,7 +1584,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
private static MethodHandle buildArrayProducer(Class<?> arrayType) {
Class<?> elemType = arrayType.getComponentType();
assert(elemType.isPrimitive());
return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType));
return getConstantHandle(MH_copyAsPrimitiveArray).bindTo(Wrapper.forPrimitiveType(elemType));
}
/*non-public*/ static void assertSame(Object mh1, Object mh2) {
@ -1673,4 +1595,91 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
throw newInternalError(msg);
}
}
// Local constant functions:
/*non-public*/ static final NamedFunction
NF_checkSpreadArgument,
NF_guardWithCatch,
NF_throwException,
NF_profileBoolean;
static {
try {
NamedFunction nfs[] = {
NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
.getDeclaredMethod("checkSpreadArgument", Object.class, int.class)),
NF_guardWithCatch = new NamedFunction(MethodHandleImpl.class
.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class,
MethodHandle.class, Object[].class)),
NF_throwException = new NamedFunction(MethodHandleImpl.class
.getDeclaredMethod("throwException", Throwable.class)),
NF_profileBoolean = new NamedFunction(MethodHandleImpl.class
.getDeclaredMethod("profileBoolean", boolean.class, int[].class))
};
// Each nf must be statically invocable or we get tied up in our bootstraps.
assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));
} catch (ReflectiveOperationException ex) {
throw newInternalError(ex);
}
}
// Indexes into constant method handles:
private static final int
MH_cast = 0,
MH_selectAlternative = 1,
MH_copyAsPrimitiveArray = 2,
MH_fillNewTypedArray = 3,
MH_fillNewArray = 4,
MH_arrayIdentity = 5,
MH_LIMIT = 6;
private static MethodHandle getConstantHandle(int idx) {
MethodHandle handle = HANDLES[idx];
if (handle != null) {
return handle;
}
return setCachedHandle(idx, makeConstantHandle(idx));
}
private static synchronized MethodHandle setCachedHandle(int idx, final MethodHandle method) {
// Simulate a CAS, to avoid racy duplication of results.
MethodHandle prev = HANDLES[idx];
if (prev != null) {
return prev;
}
HANDLES[idx] = method;
return method;
}
// Local constant method handles:
private static final @Stable MethodHandle[] HANDLES = new MethodHandle[MH_LIMIT];
private static MethodHandle makeConstantHandle(int idx) {
try {
switch (idx) {
case MH_cast:
return IMPL_LOOKUP.findVirtual(Class.class, "cast",
MethodType.methodType(Object.class, Object.class));
case MH_copyAsPrimitiveArray:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "copyAsPrimitiveArray",
MethodType.methodType(Object.class, Wrapper.class, Object[].class));
case MH_arrayIdentity:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "identity",
MethodType.methodType(Object[].class, Object[].class));
case MH_fillNewArray:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewArray",
MethodType.methodType(Object[].class, Integer.class, Object[].class));
case MH_fillNewTypedArray:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewTypedArray",
MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
case MH_selectAlternative:
return makeIntrinsic(IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)),
Intrinsic.SELECT_ALTERNATIVE);
}
} catch (ReflectiveOperationException ex) {
throw newInternalError(ex);
}
throw newInternalError("Unknown function index: " + idx);
}
}