mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-17 09:03:53 +00:00
8050052: Small cleanups in java.lang.invoke code
Reviewed-by: vlivanov, psandoz
This commit is contained in:
parent
0898161932
commit
da56d3f6d1
@ -50,9 +50,9 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
*
|
||||
* All bound arguments are encapsulated in dedicated species.
|
||||
*/
|
||||
/* non-public */ abstract class BoundMethodHandle extends MethodHandle {
|
||||
/*non-public*/ abstract class BoundMethodHandle extends MethodHandle {
|
||||
|
||||
/* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) {
|
||||
/*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) {
|
||||
super(type, form);
|
||||
}
|
||||
|
||||
@ -66,15 +66,15 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
switch (xtype) {
|
||||
case L_TYPE:
|
||||
if (true) return bindSingle(type, form, x); // Use known fast path.
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(L_TYPE).constructor[0].invokeBasic(type, form, x);
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(L_TYPE).constructor().invokeBasic(type, form, x);
|
||||
case I_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor().invokeBasic(type, form, ValueConversions.widenSubword(x));
|
||||
case J_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor[0].invokeBasic(type, form, (long) x);
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor().invokeBasic(type, form, (long) x);
|
||||
case F_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor[0].invokeBasic(type, form, (float) x);
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor().invokeBasic(type, form, (float) x);
|
||||
case D_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor[0].invokeBasic(type, form, (double) x);
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor().invokeBasic(type, form, (double) x);
|
||||
default : throw newInternalError("unexpected xtype: " + xtype);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
@ -139,8 +139,8 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
/*non-public*/ abstract int fieldCount();
|
||||
|
||||
@Override
|
||||
final Object internalProperties() {
|
||||
return "/BMH="+internalValues();
|
||||
Object internalProperties() {
|
||||
return "\n& BMH="+internalValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -219,7 +219,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -227,7 +227,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -235,7 +235,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -243,7 +243,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -251,7 +251,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -268,18 +268,20 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
* The fields are immutable; their values are fully specified at object construction.
|
||||
* Each BMH type supplies an array of getter functions which may be used in lambda forms.
|
||||
* A BMH is constructed by cloning a shorter BMH and adding one or more new field values.
|
||||
* As a degenerate and common case, the "shorter BMH" can be missing, and contributes zero prior fields.
|
||||
* The shortest possible BMH has zero fields; its class is SimpleMethodHandle.
|
||||
* BMH species are not interrelated by subtyping, even though it would appear that
|
||||
* a shorter BMH could serve as a supertype of a longer one which extends it.
|
||||
*/
|
||||
static class SpeciesData {
|
||||
final String typeChars;
|
||||
final BasicType[] typeCodes;
|
||||
final Class<? extends BoundMethodHandle> clazz;
|
||||
private final String typeChars;
|
||||
private final BasicType[] typeCodes;
|
||||
private final Class<? extends BoundMethodHandle> clazz;
|
||||
// Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
|
||||
// Therefore, we need a non-final link in the chain. Use array elements.
|
||||
final MethodHandle[] constructor;
|
||||
final MethodHandle[] getters;
|
||||
final NamedFunction[] nominalGetters;
|
||||
final SpeciesData[] extensions;
|
||||
@Stable private final MethodHandle[] constructor;
|
||||
@Stable private final MethodHandle[] getters;
|
||||
@Stable private final NamedFunction[] nominalGetters;
|
||||
@Stable private final SpeciesData[] extensions;
|
||||
|
||||
/*non-public*/ int fieldCount() {
|
||||
return typeCodes.length;
|
||||
@ -290,9 +292,14 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
/*non-public*/ char fieldTypeChar(int i) {
|
||||
return typeChars.charAt(i);
|
||||
}
|
||||
|
||||
Object fieldSignature() {
|
||||
return typeChars;
|
||||
}
|
||||
public Class<? extends BoundMethodHandle> fieldHolder() {
|
||||
return clazz;
|
||||
}
|
||||
public String toString() {
|
||||
return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+typeChars+"]";
|
||||
return "SpeciesData<"+fieldSignature()+">";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,7 +308,20 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
* getter.
|
||||
*/
|
||||
NamedFunction getterFunction(int i) {
|
||||
return nominalGetters[i];
|
||||
NamedFunction nf = nominalGetters[i];
|
||||
assert(nf.memberDeclaringClassOrNull() == fieldHolder());
|
||||
assert(nf.returnType() == fieldType(i));
|
||||
return nf;
|
||||
}
|
||||
|
||||
NamedFunction[] getterFunctions() {
|
||||
return nominalGetters;
|
||||
}
|
||||
|
||||
MethodHandle[] getterHandles() { return getters; }
|
||||
|
||||
MethodHandle constructor() {
|
||||
return constructor[0];
|
||||
}
|
||||
|
||||
static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
|
||||
@ -324,7 +344,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
private void initForBootstrap() {
|
||||
assert(!INIT_DONE);
|
||||
if (constructor[0] == null) {
|
||||
if (constructor() == null) {
|
||||
String types = typeChars;
|
||||
Factory.makeCtors(clazz, types, this.constructor);
|
||||
Factory.makeGetters(clazz, types, this.getters);
|
||||
@ -508,19 +528,19 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
* return new Species_LLI(mt, lf, argL0, argL1, argI2);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
|
||||
* return SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
|
||||
* return SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
|
||||
* return SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
|
||||
* return SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
|
||||
* return SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
@ -653,16 +673,14 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
char btChar = type.basicTypeChar();
|
||||
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "copyWithExtend" + btChar, makeSignature(String.valueOf(btChar), false), null, E_THROWABLE);
|
||||
mv.visitCode();
|
||||
// return SPECIES_DATA.extendWith(t).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
|
||||
// return SPECIES_DATA.extendWith(t).constructor().invokeBasic(mt, lf, argL0, ..., narg)
|
||||
// obtain constructor
|
||||
mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
|
||||
int iconstInsn = ICONST_0 + ord;
|
||||
assert(iconstInsn <= ICONST_5);
|
||||
mv.visitInsn(iconstInsn);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG, false);
|
||||
mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
|
||||
mv.visitInsn(ICONST_0);
|
||||
mv.visitInsn(AALOAD);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "constructor", "()" + MH_SIG, false);
|
||||
// load mt, lf
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
|
||||
@ -319,7 +319,7 @@ public class CallSite {
|
||||
throw new ClassCastException("bootstrap method failed to produce a CallSite");
|
||||
}
|
||||
if (!site.getTarget().type().equals(type))
|
||||
throw new WrongMethodTypeException("wrong type: "+site.getTarget());
|
||||
throw wrongTargetType(site.getTarget(), type);
|
||||
} catch (Throwable ex) {
|
||||
BootstrapMethodError bex;
|
||||
if (ex instanceof BootstrapMethodError)
|
||||
|
||||
@ -59,6 +59,7 @@ class DirectMethodHandle extends MethodHandle {
|
||||
MemberName m = new MemberName(Object.class, member.getName(), member.getMethodType(), member.getReferenceKind());
|
||||
m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null);
|
||||
if (m != null && m.isPublic()) {
|
||||
assert(member.getReferenceKind() == m.getReferenceKind()); // else this.form is wrong
|
||||
member = m;
|
||||
}
|
||||
}
|
||||
@ -127,7 +128,7 @@ class DirectMethodHandle extends MethodHandle {
|
||||
|
||||
@Override
|
||||
String internalProperties() {
|
||||
return "/DMH="+member.toString();
|
||||
return "\n& DMH.MN="+internalMemberName();
|
||||
}
|
||||
|
||||
//// Implementation methods.
|
||||
|
||||
@ -308,7 +308,7 @@ class Invokers {
|
||||
: Arrays.asList(mtype, customized, which, nameCursor, names.length);
|
||||
if (MTYPE_ARG >= INARG_LIMIT) {
|
||||
assert(names[MTYPE_ARG] == null);
|
||||
NamedFunction getter = BoundMethodHandle.getSpeciesData("L").getterFunction(0);
|
||||
NamedFunction getter = BoundMethodHandle.speciesData_L().getterFunction(0);
|
||||
names[MTYPE_ARG] = new Name(getter, names[THIS_MH]);
|
||||
// else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM)
|
||||
}
|
||||
@ -360,9 +360,6 @@ class Invokers {
|
||||
Object checkGenericType(Object mhObj, Object expectedObj) {
|
||||
MethodHandle mh = (MethodHandle) mhObj;
|
||||
MethodType expected = (MethodType) expectedObj;
|
||||
if (mh.type() == expected) return mh;
|
||||
MethodHandle atc = mh.asTypeCache;
|
||||
if (atc != null && atc.type() == expected) return atc;
|
||||
return mh.asType(expected);
|
||||
/* Maybe add more paths here. Possible optimizations:
|
||||
* for (R)MH.invoke(a*),
|
||||
@ -436,24 +433,25 @@ class Invokers {
|
||||
}
|
||||
|
||||
// Local constant functions:
|
||||
private static final NamedFunction NF_checkExactType;
|
||||
private static final NamedFunction NF_checkGenericType;
|
||||
private static final NamedFunction NF_asType;
|
||||
private static final NamedFunction NF_getCallSiteTarget;
|
||||
private static final NamedFunction
|
||||
NF_checkExactType,
|
||||
NF_checkGenericType,
|
||||
NF_getCallSiteTarget;
|
||||
static {
|
||||
try {
|
||||
NF_checkExactType = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("checkExactType", Object.class, Object.class));
|
||||
NF_checkGenericType = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("checkGenericType", Object.class, Object.class));
|
||||
NF_asType = new NamedFunction(MethodHandle.class
|
||||
.getDeclaredMethod("asType", MethodType.class));
|
||||
NF_getCallSiteTarget = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("getCallSiteTarget", Object.class));
|
||||
NF_checkExactType.resolve();
|
||||
NF_checkGenericType.resolve();
|
||||
NF_getCallSiteTarget.resolve();
|
||||
// bound
|
||||
NamedFunction nfs[] = {
|
||||
NF_checkExactType = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("checkExactType", Object.class, Object.class)),
|
||||
NF_checkGenericType = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("checkGenericType", Object.class, Object.class)),
|
||||
NF_getCallSiteTarget = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("getCallSiteTarget", 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);
|
||||
}
|
||||
|
||||
@ -243,7 +243,12 @@ class LambdaForm {
|
||||
this.result = fixResult(result, names);
|
||||
this.names = names.clone();
|
||||
this.debugName = fixDebugName(debugName);
|
||||
normalize();
|
||||
int maxOutArity = normalize();
|
||||
if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) {
|
||||
// Cannot use LF interpreter on very high arity expressions.
|
||||
assert(maxOutArity <= MethodType.MAX_JVM_ARITY);
|
||||
compileToBytecode();
|
||||
}
|
||||
}
|
||||
|
||||
LambdaForm(String debugName,
|
||||
@ -348,9 +353,12 @@ class LambdaForm {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Renumber and/or replace params so that they are interned and canonically numbered. */
|
||||
private void normalize() {
|
||||
/** Renumber and/or replace params so that they are interned and canonically numbered.
|
||||
* @return maximum argument list length among the names (since we have to pass over them anyway)
|
||||
*/
|
||||
private int normalize() {
|
||||
Name[] oldNames = null;
|
||||
int maxOutArity = 0;
|
||||
int changesStart = 0;
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
Name n = names[i];
|
||||
@ -361,6 +369,8 @@ class LambdaForm {
|
||||
}
|
||||
names[i] = n.cloneWithIndex(i);
|
||||
}
|
||||
if (n.arguments != null && maxOutArity < n.arguments.length)
|
||||
maxOutArity = n.arguments.length;
|
||||
}
|
||||
if (oldNames != null) {
|
||||
int startFixing = arity;
|
||||
@ -387,6 +397,7 @@ class LambdaForm {
|
||||
}
|
||||
assert(nameRefsAreLegal());
|
||||
}
|
||||
return maxOutArity;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -439,8 +450,15 @@ class LambdaForm {
|
||||
|
||||
/** Report the N-th argument type. */
|
||||
BasicType parameterType(int n) {
|
||||
return parameter(n).type;
|
||||
}
|
||||
|
||||
/** Report the N-th argument name. */
|
||||
Name parameter(int n) {
|
||||
assert(n < arity);
|
||||
return names[n].type;
|
||||
Name param = names[n];
|
||||
assert(param.isParam());
|
||||
return param;
|
||||
}
|
||||
|
||||
/** Report the arity. */
|
||||
@ -582,21 +600,12 @@ class LambdaForm {
|
||||
isCompiled = true;
|
||||
return vmentry;
|
||||
} catch (Error | Exception ex) {
|
||||
throw newInternalError("compileToBytecode: " + this, ex);
|
||||
throw newInternalError(this.toString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final ConcurrentHashMap<String,LambdaForm> PREPARED_FORMS;
|
||||
static {
|
||||
int capacity = 512; // expect many distinct signatures over time
|
||||
float loadFactor = 0.75f; // normal default
|
||||
int writers = 1;
|
||||
PREPARED_FORMS = new ConcurrentHashMap<>(capacity, loadFactor, writers);
|
||||
}
|
||||
|
||||
private static Map<String,LambdaForm> computeInitialPreparedForms() {
|
||||
private static void computeInitialPreparedForms() {
|
||||
// Find all predefined invokers and associate them with canonical empty lambda forms.
|
||||
HashMap<String,LambdaForm> forms = new HashMap<>();
|
||||
for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) {
|
||||
if (!m.isStatic() || !m.isPackage()) continue;
|
||||
MethodType mt = m.getMethodType();
|
||||
@ -607,13 +616,9 @@ class LambdaForm {
|
||||
assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_'))));
|
||||
LambdaForm form = new LambdaForm(sig);
|
||||
form.vmentry = m;
|
||||
form = mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form);
|
||||
// FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only
|
||||
forms.put(sig, form);
|
||||
form = mt.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, form);
|
||||
}
|
||||
}
|
||||
//System.out.println("computeInitialPreparedForms => "+forms);
|
||||
return forms;
|
||||
}
|
||||
|
||||
// Set this false to disable use of the interpret_L methods defined in this file.
|
||||
@ -647,13 +652,11 @@ class LambdaForm {
|
||||
}
|
||||
private static LambdaForm getPreparedForm(String sig) {
|
||||
MethodType mtype = signatureType(sig);
|
||||
//LambdaForm prep = PREPARED_FORMS.get(sig);
|
||||
LambdaForm prep = mtype.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET);
|
||||
if (prep != null) return prep;
|
||||
assert(isValidSignature(sig));
|
||||
prep = new LambdaForm(sig);
|
||||
prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig);
|
||||
//LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep);
|
||||
return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep);
|
||||
}
|
||||
|
||||
@ -709,10 +712,7 @@ class LambdaForm {
|
||||
/** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
|
||||
private static final int COMPILE_THRESHOLD;
|
||||
static {
|
||||
if (MethodHandleStatics.COMPILE_THRESHOLD != null)
|
||||
COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD;
|
||||
else
|
||||
COMPILE_THRESHOLD = 30; // default value
|
||||
COMPILE_THRESHOLD = Math.max(-1, MethodHandleStatics.COMPILE_THRESHOLD);
|
||||
}
|
||||
private int invocationCounter = 0;
|
||||
|
||||
@ -728,7 +728,9 @@ class LambdaForm {
|
||||
for (int i = argumentValues.length; i < values.length; i++) {
|
||||
values[i] = interpretName(names[i], values);
|
||||
}
|
||||
return (result < 0) ? null : values[result];
|
||||
Object rv = (result < 0) ? null : values[result];
|
||||
assert(resultCheck(argumentValues, rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@ -819,8 +821,16 @@ class LambdaForm {
|
||||
assert(argumentValues.length == arity) : arity+"!="+Arrays.asList(argumentValues)+".length";
|
||||
// also check that the leading (receiver) argument is somehow bound to this LF:
|
||||
assert(argumentValues[0] instanceof MethodHandle) : "not MH: " + argumentValues[0];
|
||||
assert(((MethodHandle)argumentValues[0]).internalForm() == this);
|
||||
MethodHandle mh = (MethodHandle) argumentValues[0];
|
||||
assert(mh.internalForm() == this);
|
||||
// note: argument #0 could also be an interface wrapper, in the future
|
||||
argumentTypesMatch(basicTypeSignature(), argumentValues);
|
||||
return true;
|
||||
}
|
||||
private boolean resultCheck(Object[] argumentValues, Object result) {
|
||||
MethodHandle mh = (MethodHandle) argumentValues[0];
|
||||
MethodType mt = mh.type();
|
||||
assert(valueMatches(returnType(), mt.returnType(), result));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -839,7 +849,7 @@ class LambdaForm {
|
||||
if (i == arity) buf.append(")=>{");
|
||||
Name n = names[i];
|
||||
if (i >= arity) buf.append("\n ");
|
||||
buf.append(n);
|
||||
buf.append(n.paramString());
|
||||
if (i < arity) {
|
||||
if (i+1 < arity) buf.append(",");
|
||||
continue;
|
||||
@ -847,6 +857,7 @@ class LambdaForm {
|
||||
buf.append("=").append(n.exprString());
|
||||
buf.append(";");
|
||||
}
|
||||
if (arity == names.length) buf.append(")=>{");
|
||||
buf.append(result < 0 ? "void" : names[result]).append("}");
|
||||
if (TRACE_INTERPRETER) {
|
||||
// Extra verbosity:
|
||||
@ -856,53 +867,16 @@ class LambdaForm {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply immediate binding for a Name in this form indicated by its position relative to the form.
|
||||
* The first parameter to a LambdaForm, a0:L, always represents the form's method handle, so 0 is not
|
||||
* accepted as valid.
|
||||
*/
|
||||
LambdaForm bindImmediate(int pos, BasicType basicType, Object value) {
|
||||
// must be an argument, and the types must match
|
||||
assert pos > 0 && pos < arity && names[pos].type == basicType && Name.typesMatch(basicType, value);
|
||||
|
||||
int arity2 = arity - 1;
|
||||
Name[] names2 = new Name[names.length - 1];
|
||||
for (int r = 0, w = 0; r < names.length; ++r, ++w) { // (r)ead from names, (w)rite to names2
|
||||
Name n = names[r];
|
||||
if (n.isParam()) {
|
||||
if (n.index == pos) {
|
||||
// do not copy over the argument that is to be replaced with a literal,
|
||||
// but adjust the write index
|
||||
--w;
|
||||
} else {
|
||||
names2[w] = new Name(w, n.type);
|
||||
}
|
||||
} else {
|
||||
Object[] arguments2 = new Object[n.arguments.length];
|
||||
for (int i = 0; i < n.arguments.length; ++i) {
|
||||
Object arg = n.arguments[i];
|
||||
if (arg instanceof Name) {
|
||||
int ni = ((Name) arg).index;
|
||||
if (ni == pos) {
|
||||
arguments2[i] = value;
|
||||
} else if (ni < pos) {
|
||||
// replacement position not yet passed
|
||||
arguments2[i] = names2[ni];
|
||||
} else {
|
||||
// replacement position passed
|
||||
arguments2[i] = names2[ni - 1];
|
||||
}
|
||||
} else {
|
||||
arguments2[i] = arg;
|
||||
}
|
||||
}
|
||||
names2[w] = new Name(n.function, arguments2);
|
||||
names2[w].initIndex(w);
|
||||
}
|
||||
}
|
||||
|
||||
int result2 = result == -1 ? -1 : result - 1;
|
||||
return new LambdaForm(debugName, arity2, names2, result2);
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof LambdaForm && equals((LambdaForm)obj);
|
||||
}
|
||||
public boolean equals(LambdaForm that) {
|
||||
if (this.result != that.result) return false;
|
||||
return Arrays.equals(this.names, that.names);
|
||||
}
|
||||
public int hashCode() {
|
||||
return result + 31 * Arrays.hashCode(names);
|
||||
}
|
||||
|
||||
LambdaForm bind(int namePos, BoundMethodHandle.SpeciesData oldData) {
|
||||
@ -918,8 +892,8 @@ class LambdaForm {
|
||||
assert(!binding.isParam());
|
||||
assert(name.type == binding.type);
|
||||
assert(0 <= pos && pos < arity && names[pos] == name);
|
||||
assert(binding.function.memberDeclaringClassOrNull() == newData.clazz);
|
||||
assert(oldData.getters.length == newData.getters.length-1);
|
||||
assert(binding.function.memberDeclaringClassOrNull() == newData.fieldHolder());
|
||||
assert(oldData.getterFunctions().length == newData.getterFunctions().length-1);
|
||||
if (bindCache != null) {
|
||||
LambdaForm form = bindCache[pos];
|
||||
if (form != null) {
|
||||
@ -940,12 +914,12 @@ class LambdaForm {
|
||||
for (int i = 0; i < names2.length; i++) {
|
||||
Name n = names[i];
|
||||
if (n.function != null &&
|
||||
n.function.memberDeclaringClassOrNull() == oldData.clazz) {
|
||||
n.function.memberDeclaringClassOrNull() == oldData.fieldHolder()) {
|
||||
MethodHandle oldGetter = n.function.resolvedHandle;
|
||||
MethodHandle newGetter = null;
|
||||
for (int j = 0; j < oldData.getters.length; j++) {
|
||||
if (oldGetter == oldData.getters[j])
|
||||
newGetter = newData.getters[j];
|
||||
for (int j = 0; j < oldData.getterHandles().length; j++) {
|
||||
if (oldGetter == oldData.getterHandles()[j])
|
||||
newGetter = newData.getterHandles()[j];
|
||||
}
|
||||
if (newGetter != null) {
|
||||
if (firstOldRef < 0) firstOldRef = i;
|
||||
@ -969,7 +943,7 @@ class LambdaForm {
|
||||
int insPos = pos;
|
||||
for (; insPos+1 < names2.length; insPos++) {
|
||||
Name n = names2[insPos+1];
|
||||
if (n.isSiblingBindingBefore(binding)) {
|
||||
if (n.isParam()) {
|
||||
names2[insPos] = n;
|
||||
} else {
|
||||
break;
|
||||
@ -1000,16 +974,16 @@ class LambdaForm {
|
||||
}
|
||||
|
||||
LambdaForm addArguments(int pos, BasicType... types) {
|
||||
assert(pos <= arity);
|
||||
// names array has MH in slot 0; skip it.
|
||||
int argpos = pos + 1;
|
||||
assert(argpos <= arity);
|
||||
int length = names.length;
|
||||
int inTypes = types.length;
|
||||
Name[] names2 = Arrays.copyOf(names, length + inTypes);
|
||||
int arity2 = arity + inTypes;
|
||||
int result2 = result;
|
||||
if (result2 >= arity)
|
||||
if (result2 >= argpos)
|
||||
result2 += inTypes;
|
||||
// names array has MH in slot 0; skip it.
|
||||
int argpos = pos + 1;
|
||||
// Note: The LF constructor will rename names2[argpos...].
|
||||
// Make space for new arguments (shift temporaries).
|
||||
System.arraycopy(names, argpos, names2, argpos + inTypes, length - argpos);
|
||||
@ -1102,8 +1076,9 @@ class LambdaForm {
|
||||
}
|
||||
NamedFunction(MemberName member, MethodHandle resolvedHandle) {
|
||||
this.member = member;
|
||||
//resolvedHandle = eraseSubwordTypes(resolvedHandle);
|
||||
this.resolvedHandle = resolvedHandle;
|
||||
// The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
|
||||
//assert(!isInvokeBasic());
|
||||
}
|
||||
NamedFunction(MethodType basicInvokerType) {
|
||||
assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
|
||||
@ -1114,6 +1089,13 @@ class LambdaForm {
|
||||
// necessary to pass BigArityTest
|
||||
this.member = Invokers.invokeBasicMethod(basicInvokerType);
|
||||
}
|
||||
assert(isInvokeBasic());
|
||||
}
|
||||
|
||||
private boolean isInvokeBasic() {
|
||||
return member != null &&
|
||||
member.isMethodHandleInvoke() &&
|
||||
"invokeBasic".equals(member.getName());
|
||||
}
|
||||
|
||||
// The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc.
|
||||
@ -1179,71 +1161,89 @@ class LambdaForm {
|
||||
/** void return type invokers. */
|
||||
@Hidden
|
||||
static Object invoke__V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 0);
|
||||
assert(arityCheck(0, void.class, mh, a));
|
||||
mh.invokeBasic();
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_L_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 1);
|
||||
assert(arityCheck(1, void.class, mh, a));
|
||||
mh.invokeBasic(a[0]);
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LL_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 2);
|
||||
assert(arityCheck(2, void.class, mh, a));
|
||||
mh.invokeBasic(a[0], a[1]);
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLL_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 3);
|
||||
assert(arityCheck(3, void.class, mh, a));
|
||||
mh.invokeBasic(a[0], a[1], a[2]);
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLLL_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 4);
|
||||
assert(arityCheck(4, void.class, mh, a));
|
||||
mh.invokeBasic(a[0], a[1], a[2], a[3]);
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLLLL_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 5);
|
||||
assert(arityCheck(5, void.class, mh, a));
|
||||
mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
|
||||
return null;
|
||||
}
|
||||
/** Object return type invokers. */
|
||||
@Hidden
|
||||
static Object invoke__L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 0);
|
||||
assert(arityCheck(0, mh, a));
|
||||
return mh.invokeBasic();
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_L_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 1);
|
||||
assert(arityCheck(1, mh, a));
|
||||
return mh.invokeBasic(a[0]);
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LL_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 2);
|
||||
assert(arityCheck(2, mh, a));
|
||||
return mh.invokeBasic(a[0], a[1]);
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLL_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 3);
|
||||
assert(arityCheck(3, mh, a));
|
||||
return mh.invokeBasic(a[0], a[1], a[2]);
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLLL_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 4);
|
||||
assert(arityCheck(4, mh, a));
|
||||
return mh.invokeBasic(a[0], a[1], a[2], a[3]);
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLLLL_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 5);
|
||||
assert(arityCheck(5, mh, a));
|
||||
return mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
|
||||
}
|
||||
private static boolean arityCheck(int arity, MethodHandle mh, Object[] a) {
|
||||
return arityCheck(arity, Object.class, mh, a);
|
||||
}
|
||||
private static boolean arityCheck(int arity, Class<?> rtype, MethodHandle mh, Object[] a) {
|
||||
assert(a.length == arity)
|
||||
: Arrays.asList(a.length, arity);
|
||||
assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype))
|
||||
: Arrays.asList(mh, rtype, arity);
|
||||
MemberName member = mh.internalMemberName();
|
||||
if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) {
|
||||
assert(arity > 0);
|
||||
assert(a[0] instanceof MethodHandle);
|
||||
MethodHandle mh2 = (MethodHandle) a[0];
|
||||
assert(mh2.type().basicType() == MethodType.genericMethodType(arity-1).changeReturnType(rtype))
|
||||
: Arrays.asList(member, mh2, rtype, arity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static final MethodType INVOKER_METHOD_TYPE =
|
||||
MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
|
||||
@ -1431,7 +1431,7 @@ class LambdaForm {
|
||||
this(new NamedFunction(function), arguments);
|
||||
}
|
||||
Name(NamedFunction function, Object... arguments) {
|
||||
this(-1, function.returnType(), function, arguments = arguments.clone());
|
||||
this(-1, function.returnType(), function, arguments = Arrays.copyOf(arguments, arguments.length, Object[].class));
|
||||
assert(arguments.length == function.arity()) : "arity mismatch: arguments.length=" + arguments.length + " == function.arity()=" + function.arity() + " in " + debugString();
|
||||
for (int i = 0; i < arguments.length; i++)
|
||||
assert(typesMatch(function.parameterType(i), arguments[i])) : "types don't match: function.parameterType(" + i + ")=" + function.parameterType(i) + ", arguments[" + i + "]=" + arguments[i] + " in " + debugString();
|
||||
@ -1487,7 +1487,11 @@ class LambdaForm {
|
||||
if (!replaced) return this;
|
||||
return new Name(function, arguments);
|
||||
}
|
||||
/** In the arguments of this Name, replace oldNames[i] pairwise by newNames[i].
|
||||
* Limit such replacements to {@code start<=i<end}. Return possibly changed self.
|
||||
*/
|
||||
Name replaceNames(Name[] oldNames, Name[] newNames, int start, int end) {
|
||||
if (start >= end) return this;
|
||||
@SuppressWarnings("LocalVariableHidesMemberVariable")
|
||||
Object[] arguments = this.arguments;
|
||||
boolean replaced = false;
|
||||
@ -1539,9 +1543,12 @@ class LambdaForm {
|
||||
return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+typeChar();
|
||||
}
|
||||
public String debugString() {
|
||||
String s = toString();
|
||||
String s = paramString();
|
||||
return (function == null) ? s : s + "=" + exprString();
|
||||
}
|
||||
public String paramString() {
|
||||
return toString();
|
||||
}
|
||||
public String exprString() {
|
||||
if (function == null) return toString();
|
||||
StringBuilder buf = new StringBuilder(function.toString());
|
||||
@ -1572,34 +1579,6 @@ class LambdaForm {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this Name precede the given binding node in some canonical order?
|
||||
* This predicate is used to order data bindings (via insertion sort)
|
||||
* with some stability.
|
||||
*/
|
||||
boolean isSiblingBindingBefore(Name binding) {
|
||||
assert(!binding.isParam());
|
||||
if (isParam()) return true;
|
||||
if (function.equals(binding.function) &&
|
||||
arguments.length == binding.arguments.length) {
|
||||
boolean sawInt = false;
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
Object a1 = arguments[i];
|
||||
Object a2 = binding.arguments[i];
|
||||
if (!a1.equals(a2)) {
|
||||
if (a1 instanceof Integer && a2 instanceof Integer) {
|
||||
if (sawInt) continue;
|
||||
sawInt = true;
|
||||
if ((int)a1 < (int)a2) continue; // still might be true
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return sawInt;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Return the index of the last occurrence of n in the argument array.
|
||||
* Return -1 if the name is not used.
|
||||
*/
|
||||
@ -1858,37 +1837,6 @@ class LambdaForm {
|
||||
@interface Hidden {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// Smoke-test for the invokers used in this file.
|
||||
static void testMethodHandleLinkers() throws Throwable {
|
||||
MemberName.Factory lookup = MemberName.getFactory();
|
||||
MemberName asList_MN = new MemberName(Arrays.class, "asList",
|
||||
MethodType.methodType(List.class, Object[].class),
|
||||
REF_invokeStatic);
|
||||
//MethodHandleNatives.resolve(asList_MN, null);
|
||||
asList_MN = lookup.resolveOrFail(asList_MN, REF_invokeStatic, null, NoSuchMethodException.class);
|
||||
System.out.println("about to call "+asList_MN);
|
||||
Object[] abc = { "a", "bc" };
|
||||
List<?> lst = (List<?>) MethodHandle.linkToStatic(abc, asList_MN);
|
||||
System.out.println("lst="+lst);
|
||||
MemberName toString_MN = new MemberName(Object.class.getMethod("toString"));
|
||||
String s1 = (String) MethodHandle.linkToVirtual(lst, toString_MN);
|
||||
toString_MN = new MemberName(Object.class.getMethod("toString"), true);
|
||||
String s2 = (String) MethodHandle.linkToSpecial(lst, toString_MN);
|
||||
System.out.println("[s1,s2,lst]="+Arrays.asList(s1, s2, lst.toString()));
|
||||
MemberName toArray_MN = new MemberName(List.class.getMethod("toArray"));
|
||||
Object[] arr = (Object[]) MethodHandle.linkToInterface(lst, toArray_MN);
|
||||
System.out.println("toArray="+Arrays.toString(arr));
|
||||
}
|
||||
static { try { testMethodHandleLinkers(); } catch (Throwable ex) { throw new RuntimeException(ex); } }
|
||||
// Requires these definitions in MethodHandle:
|
||||
static final native Object linkToStatic(Object x1, MemberName mn) throws Throwable;
|
||||
static final native Object linkToVirtual(Object x1, MemberName mn) throws Throwable;
|
||||
static final native Object linkToSpecial(Object x1, MemberName mn) throws Throwable;
|
||||
static final native Object linkToInterface(Object x1, MemberName mn) throws Throwable;
|
||||
*/
|
||||
|
||||
private static final HashMap<String,Integer> DEBUG_NAME_COUNTERS;
|
||||
static {
|
||||
if (debugEnabled())
|
||||
@ -1901,7 +1849,7 @@ class LambdaForm {
|
||||
static {
|
||||
createIdentityForms();
|
||||
if (USE_PREDEFINED_INTERPRET_METHODS)
|
||||
PREPARED_FORMS.putAll(computeInitialPreparedForms());
|
||||
computeInitialPreparedForms();
|
||||
NamedFunction.initializeInvokers();
|
||||
}
|
||||
|
||||
|
||||
@ -327,10 +327,6 @@ import java.util.Objects;
|
||||
assert(getReferenceKind() == oldKind);
|
||||
assert(MethodHandleNatives.refKindIsValid(refKind));
|
||||
flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
|
||||
// if (isConstructor() && refKind != REF_newInvokeSpecial)
|
||||
// flags += (IS_METHOD - IS_CONSTRUCTOR);
|
||||
// else if (refKind == REF_newInvokeSpecial && isMethod())
|
||||
// flags += (IS_CONSTRUCTOR - IS_METHOD);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -344,9 +340,11 @@ import java.util.Objects;
|
||||
return !testFlags(mask, 0);
|
||||
}
|
||||
|
||||
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
|
||||
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact).
|
||||
* Also returns true for the non-public MH.invokeBasic.
|
||||
*/
|
||||
public boolean isMethodHandleInvoke() {
|
||||
final int bits = MH_INVOKE_MODS;
|
||||
final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC;
|
||||
final int negs = Modifier.STATIC;
|
||||
if (testFlags(bits | negs, bits) &&
|
||||
clazz == MethodHandle.class) {
|
||||
@ -355,7 +353,14 @@ import java.util.Objects;
|
||||
return false;
|
||||
}
|
||||
public static boolean isMethodHandleInvokeName(String name) {
|
||||
return name.equals("invoke") || name.equals("invokeExact");
|
||||
switch (name) {
|
||||
case "invoke":
|
||||
case "invokeExact":
|
||||
case "invokeBasic": // internal sig-poly method
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC;
|
||||
|
||||
@ -720,16 +725,8 @@ import java.util.Objects;
|
||||
init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
|
||||
initResolved(false);
|
||||
}
|
||||
/** Create a field or type name from the given components: Declaring class, name, type.
|
||||
* The declaring class may be supplied as null if this is to be a bare name and type.
|
||||
* The modifier flags default to zero.
|
||||
* The resulting name will in an unresolved state.
|
||||
*/
|
||||
public MemberName(Class<?> defClass, String name, Class<?> type, Void unused) {
|
||||
this(defClass, name, type, REF_NONE);
|
||||
initResolved(false);
|
||||
}
|
||||
/** Create a method or constructor name from the given components: Declaring class, name, type, modifiers.
|
||||
/** Create a method or constructor name from the given components:
|
||||
* Declaring class, name, type, reference kind.
|
||||
* It will be a constructor if and only if the name is {@code "<init>"}.
|
||||
* The declaring class may be supplied as null if this is to be a bare name and type.
|
||||
* The last argument is optional, a boolean which requests REF_invokeSpecial.
|
||||
|
||||
@ -762,11 +762,19 @@ public abstract class MethodHandle {
|
||||
return this;
|
||||
}
|
||||
// Return 'this.asTypeCache' if the conversion is already memoized.
|
||||
MethodHandle atc = asTypeCached(newType);
|
||||
if (atc != null) {
|
||||
return atc;
|
||||
}
|
||||
return asTypeUncached(newType);
|
||||
}
|
||||
|
||||
private MethodHandle asTypeCached(MethodType newType) {
|
||||
MethodHandle atc = asTypeCache;
|
||||
if (atc != null && newType == atc.type) {
|
||||
return atc;
|
||||
}
|
||||
return asTypeUncached(newType);
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Override this to change asType behavior. */
|
||||
@ -991,8 +999,11 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
|
||||
return MethodHandles.collectArguments(target, collectArgPos, collector);
|
||||
}
|
||||
|
||||
// private API: return true if last param exactly matches arrayType
|
||||
private boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
|
||||
/**
|
||||
* See if {@code asCollector} can be validly called with the given arguments.
|
||||
* Return false if the last parameter is not an exact match to arrayType.
|
||||
*/
|
||||
/*non-public*/ boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
|
||||
spreadArrayChecks(arrayType, arrayLength);
|
||||
int nargs = type().parameterCount();
|
||||
if (nargs != 0) {
|
||||
@ -1154,7 +1165,7 @@ assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||
* @see #asFixedArity
|
||||
*/
|
||||
public MethodHandle asVarargsCollector(Class<?> arrayType) {
|
||||
Class<?> arrayElement = arrayType.getComponentType();
|
||||
arrayType.getClass(); // explicit NPE
|
||||
boolean lastMatch = asCollectorChecks(arrayType, 0);
|
||||
if (isVarargsCollector() && lastMatch)
|
||||
return this;
|
||||
@ -1283,14 +1294,17 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (DEBUG_METHOD_HANDLE_NAMES) return debugString();
|
||||
if (DEBUG_METHOD_HANDLE_NAMES) return "MethodHandle"+debugString();
|
||||
return standardString();
|
||||
}
|
||||
String standardString() {
|
||||
return "MethodHandle"+type;
|
||||
}
|
||||
/** Return a string with a several lines describing the method handle structure.
|
||||
* This string would be suitable for display in an IDE debugger.
|
||||
*/
|
||||
String debugString() {
|
||||
return standardString()+"/LF="+internalForm()+internalProperties();
|
||||
return type+" : "+internalForm()+internalProperties();
|
||||
}
|
||||
|
||||
//// Implementation methods.
|
||||
@ -1302,15 +1316,13 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
/*non-public*/
|
||||
MethodHandle setVarargs(MemberName member) throws IllegalAccessException {
|
||||
if (!member.isVarargs()) return this;
|
||||
int argc = type().parameterCount();
|
||||
if (argc != 0) {
|
||||
Class<?> arrayType = type().parameterType(argc-1);
|
||||
if (arrayType.isArray()) {
|
||||
return MethodHandleImpl.makeVarargsCollector(this, arrayType);
|
||||
}
|
||||
Class<?> arrayType = type().lastParameterType();
|
||||
if (arrayType.isArray()) {
|
||||
return MethodHandleImpl.makeVarargsCollector(this, arrayType);
|
||||
}
|
||||
throw member.makeAccessException("cannot make variable arity", null);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle viewAsType(MethodType newType) {
|
||||
// No actual conversions, just a new view of the same method.
|
||||
@ -1361,7 +1373,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
|
||||
/*non-public*/
|
||||
Object internalProperties() {
|
||||
// Override to something like "/FOO=bar"
|
||||
// Override to something to follow this.form, like "\n& FOO=bar"
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -1469,6 +1481,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
/*non-public*/
|
||||
void updateForm(LambdaForm newForm) {
|
||||
if (form == newForm) return;
|
||||
assert(this instanceof DirectMethodHandle && this.internalMemberName().isStatic());
|
||||
// ISSUE: Should we have a memory fence here?
|
||||
UNSAFE.putObject(this, FORM_OFFSET, newForm);
|
||||
this.form.prepare(); // as in MethodHandle.<init>
|
||||
|
||||
@ -357,7 +357,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
static class AsVarargsCollector extends MethodHandle {
|
||||
private final MethodHandle target;
|
||||
private final Class<?> arrayType;
|
||||
private /*@Stable*/ MethodHandle asCollectorCache;
|
||||
private @Stable MethodHandle asCollectorCache;
|
||||
|
||||
AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
|
||||
super(type, reinvokerForm(target));
|
||||
@ -534,7 +534,6 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
static final MethodHandle MH_castReference;
|
||||
static final MethodHandle MH_copyAsPrimitiveArray;
|
||||
static final MethodHandle MH_copyAsReferenceArray;
|
||||
static final MethodHandle MH_fillNewTypedArray;
|
||||
static final MethodHandle MH_fillNewArray;
|
||||
static final MethodHandle MH_arrayIdentity;
|
||||
@ -557,8 +556,6 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
MethodType.methodType(Object.class, Class.class, Object.class));
|
||||
MH_copyAsPrimitiveArray = IMPL_LOOKUP.findStatic(MHI, "copyAsPrimitiveArray",
|
||||
MethodType.methodType(Object.class, Wrapper.class, Object[].class));
|
||||
MH_copyAsReferenceArray = IMPL_LOOKUP.findStatic(MHI, "copyAsReferenceArray",
|
||||
MethodType.methodType(Object[].class, Class.class, Object[].class));
|
||||
MH_arrayIdentity = IMPL_LOOKUP.findStatic(MHI, "identity",
|
||||
MethodType.methodType(Object[].class, Object[].class));
|
||||
MH_fillNewArray = IMPL_LOOKUP.findStatic(MHI, "fillNewArray",
|
||||
@ -758,8 +755,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
BoundMethodHandle mh;
|
||||
try {
|
||||
mh = (BoundMethodHandle)
|
||||
data.constructor[0].invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher,
|
||||
(Object) collectArgs, (Object) unboxResult);
|
||||
data.constructor().invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher,
|
||||
(Object) collectArgs, (Object) unboxResult);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -1095,6 +1092,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) {
|
||||
Object[] a = Arrays.copyOf(example, len);
|
||||
assert(a.getClass() != Object[].class);
|
||||
fillWithArguments(a, 0, args);
|
||||
return a;
|
||||
}
|
||||
@ -1143,9 +1141,6 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
private static final MethodHandle[] FILL_ARRAYS = makeFillArrays();
|
||||
|
||||
private static Object[] copyAsReferenceArray(Class<? extends Object[]> arrayType, Object... a) {
|
||||
return Arrays.copyOf(a, a.length, arrayType);
|
||||
}
|
||||
private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
|
||||
Object a = w.makeArray(boxes.length);
|
||||
w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length);
|
||||
@ -1265,8 +1260,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
if (nargs >= MAX_JVM_ARITY/2 - 1) {
|
||||
int slots = nargs;
|
||||
final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH
|
||||
if (arrayType == double[].class || arrayType == long[].class)
|
||||
slots *= 2;
|
||||
if (slots <= MAX_ARRAY_SLOTS && elemType.isPrimitive())
|
||||
slots *= Wrapper.forPrimitiveType(elemType).stackSlots();
|
||||
if (slots > MAX_ARRAY_SLOTS)
|
||||
throw new IllegalArgumentException("too many arguments: "+arrayType.getSimpleName()+", length "+nargs);
|
||||
}
|
||||
@ -1276,16 +1271,18 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
MethodHandle cache[] = TYPED_COLLECTORS.get(elemType);
|
||||
MethodHandle mh = nargs < cache.length ? cache[nargs] : null;
|
||||
if (mh != null) return mh;
|
||||
if (elemType.isPrimitive()) {
|
||||
if (nargs == 0) {
|
||||
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 producer = buildArrayProducer(arrayType);
|
||||
mh = buildVarargsArray(builder, producer, nargs);
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Object[]> objArrayType = (Class<? extends Object[]>) arrayType;
|
||||
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;
|
||||
MethodHandle producer = Lazy.MH_arrayIdentity; // must be weakly typed
|
||||
mh = buildVarargsArray(builder, producer, nargs);
|
||||
}
|
||||
mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
|
||||
@ -1297,9 +1294,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
private static MethodHandle buildArrayProducer(Class<?> arrayType) {
|
||||
Class<?> elemType = arrayType.getComponentType();
|
||||
if (elemType.isPrimitive())
|
||||
return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType));
|
||||
else
|
||||
return Lazy.MH_copyAsReferenceArray.bindTo(arrayType);
|
||||
assert(elemType.isPrimitive());
|
||||
return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType));
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ import java.util.ArrayList;
|
||||
import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.Reflection;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* This class consists exclusively of static methods that help adapt
|
||||
@ -148,7 +149,7 @@ public class MethodHandleProxies {
|
||||
public static
|
||||
<T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) {
|
||||
if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers()))
|
||||
throw new IllegalArgumentException("not a public interface: "+intfc.getName());
|
||||
throw newIllegalArgumentException("not a public interface", intfc.getName());
|
||||
final MethodHandle mh;
|
||||
if (System.getSecurityManager() != null) {
|
||||
final Class<?> caller = Reflection.getCallerClass();
|
||||
@ -165,7 +166,7 @@ public class MethodHandleProxies {
|
||||
}
|
||||
final Method[] methods = getSingleNameMethods(intfc);
|
||||
if (methods == null)
|
||||
throw new IllegalArgumentException("not a single-method interface: "+intfc.getName());
|
||||
throw newIllegalArgumentException("not a single-method interface", intfc.getName());
|
||||
final MethodHandle[] vaTargets = new MethodHandle[methods.length];
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
Method sm = methods[i];
|
||||
@ -189,7 +190,7 @@ public class MethodHandleProxies {
|
||||
return getArg(method.getName());
|
||||
if (isObjectMethod(method))
|
||||
return callObjectMethod(proxy, method, args);
|
||||
throw new InternalError("bad proxy method: "+method);
|
||||
throw newInternalError("bad proxy method: "+method);
|
||||
}
|
||||
};
|
||||
|
||||
@ -240,7 +241,7 @@ public class MethodHandleProxies {
|
||||
return (WrapperInstance) x;
|
||||
} catch (ClassCastException ex) {
|
||||
}
|
||||
throw new IllegalArgumentException("not a wrapper instance");
|
||||
throw newIllegalArgumentException("not a wrapper instance");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -45,7 +45,7 @@ import sun.misc.Unsafe;
|
||||
static final boolean DUMP_CLASS_FILES;
|
||||
static final boolean TRACE_INTERPRETER;
|
||||
static final boolean TRACE_METHOD_LINKAGE;
|
||||
static final Integer COMPILE_THRESHOLD;
|
||||
static final int COMPILE_THRESHOLD;
|
||||
static final int PROFILE_LEVEL;
|
||||
|
||||
static {
|
||||
@ -56,7 +56,7 @@ import sun.misc.Unsafe;
|
||||
values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES");
|
||||
values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
|
||||
values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
|
||||
values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
|
||||
values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 30);
|
||||
values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
|
||||
return null;
|
||||
}
|
||||
@ -131,7 +131,10 @@ import sun.misc.Unsafe;
|
||||
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
|
||||
return new IllegalArgumentException(message(message, obj, obj2));
|
||||
}
|
||||
/** Propagate unchecked exceptions and errors, but wrap anything checked and throw that instead. */
|
||||
/*non-public*/ static Error uncaughtException(Throwable ex) {
|
||||
if (ex instanceof Error) throw (Error) ex;
|
||||
if (ex instanceof RuntimeException) throw (RuntimeException) ex;
|
||||
throw newInternalError("uncaught exception", ex);
|
||||
}
|
||||
static Error NYI() {
|
||||
|
||||
@ -862,6 +862,8 @@ assertEquals("", (String) MH_newString.invokeExact());
|
||||
return invoker(type);
|
||||
if ("invokeExact".equals(name))
|
||||
return exactInvoker(type);
|
||||
if ("invokeBasic".equals(name))
|
||||
return basicInvoker(type);
|
||||
assert(!MemberName.isMethodHandleInvokeName(name));
|
||||
return null;
|
||||
}
|
||||
@ -1879,7 +1881,7 @@ return invoker;
|
||||
static public
|
||||
MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
|
||||
if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
|
||||
throw new IllegalArgumentException("bad argument count "+leadingArgCount);
|
||||
throw newIllegalArgumentException("bad argument count", leadingArgCount);
|
||||
return type.invokers().spreadInvoker(leadingArgCount);
|
||||
}
|
||||
|
||||
@ -1964,7 +1966,7 @@ return invoker;
|
||||
|
||||
static /*non-public*/
|
||||
MethodHandle basicInvoker(MethodType type) {
|
||||
return type.form().basicInvoker();
|
||||
return type.invokers().basicInvoker();
|
||||
}
|
||||
|
||||
/// method handle modification (creation from other method handles)
|
||||
|
||||
@ -905,7 +905,7 @@ class MethodType implements java.io.Serializable {
|
||||
if (!descriptor.startsWith("(") || // also generates NPE if needed
|
||||
descriptor.indexOf(')') < 0 ||
|
||||
descriptor.indexOf('.') >= 0)
|
||||
throw new IllegalArgumentException("not a method descriptor: "+descriptor);
|
||||
throw newIllegalArgumentException("not a method descriptor: "+descriptor);
|
||||
List<Class<?>> types = BytecodeDescriptor.parseMethod(descriptor, loader);
|
||||
Class<?> rtype = types.remove(types.size() - 1);
|
||||
checkSlotCount(types.size());
|
||||
|
||||
@ -47,14 +47,10 @@ final class MethodTypeForm {
|
||||
final int[] argToSlotTable, slotToArgTable;
|
||||
final long argCounts; // packed slot & value counts
|
||||
final long primCounts; // packed prim & double counts
|
||||
final int vmslots; // total number of parameter slots
|
||||
final MethodType erasedType; // the canonical erasure
|
||||
final MethodType basicType; // the canonical erasure, with primitives simplified
|
||||
|
||||
// Cached adapter information:
|
||||
@Stable String typeString; // argument type signature characters
|
||||
@Stable MethodHandle genericInvoker; // JVM hook for inexact invoke
|
||||
@Stable MethodHandle basicInvoker; // cached instance of MH.invokeBasic
|
||||
@Stable MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction
|
||||
|
||||
// Cached lambda form information, for basic types only:
|
||||
@ -70,24 +66,40 @@ final class MethodTypeForm {
|
||||
LF_INTERPRET = 6, // LF interpreter
|
||||
LF_COUNTER = 7, // CMH wrapper
|
||||
LF_REINVOKE = 8, // other wrapper
|
||||
LF_EX_LINKER = 9, // invokeExact_MT
|
||||
LF_EX_INVOKER = 10, // invokeExact MH
|
||||
LF_GEN_LINKER = 11,
|
||||
LF_GEN_INVOKER = 12,
|
||||
LF_EX_LINKER = 9, // invokeExact_MT (for invokehandle)
|
||||
LF_EX_INVOKER = 10, // MHs.invokeExact
|
||||
LF_GEN_LINKER = 11, // generic invoke_MT (for invokehandle)
|
||||
LF_GEN_INVOKER = 12, // generic MHs.invoke
|
||||
LF_CS_LINKER = 13, // linkToCallSite_CS
|
||||
LF_MH_LINKER = 14, // linkToCallSite_MH
|
||||
LF_GWC = 15,
|
||||
LF_GWC = 15, // guardWithCatch (catchException)
|
||||
LF_LIMIT = 16;
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
public MethodType erasedType() {
|
||||
return erasedType;
|
||||
}
|
||||
|
||||
/** Return the basic type derived from the erased type of this MT-form.
|
||||
* A basic type is erased (all references Object) and also has all primitive
|
||||
* types (except int, long, float, double, void) normalized to int.
|
||||
* Such basic types correspond to low-level JVM calling sequences.
|
||||
*/
|
||||
public MethodType basicType() {
|
||||
return basicType;
|
||||
}
|
||||
|
||||
private boolean assertIsBasicType() {
|
||||
// primitives must be flattened also
|
||||
assert(erasedType == basicType)
|
||||
: "erasedType: " + erasedType + " != basicType: " + basicType;
|
||||
return true;
|
||||
}
|
||||
|
||||
public LambdaForm cachedLambdaForm(int which) {
|
||||
assert(assertIsBasicType());
|
||||
return lambdaForms[which];
|
||||
}
|
||||
|
||||
@ -98,28 +110,6 @@ final class MethodTypeForm {
|
||||
return lambdaForms[which] = form;
|
||||
}
|
||||
|
||||
public MethodHandle basicInvoker() {
|
||||
assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType; // primitives must be flattened also
|
||||
MethodHandle invoker = basicInvoker;
|
||||
if (invoker != null) return invoker;
|
||||
invoker = DirectMethodHandle.make(invokeBasicMethod(basicType));
|
||||
basicInvoker = invoker;
|
||||
return invoker;
|
||||
}
|
||||
|
||||
// This next one is called from LambdaForm.NamedFunction.<init>.
|
||||
/*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) {
|
||||
assert(basicType == basicType.basicType());
|
||||
try {
|
||||
// Do approximately the same as this public API call:
|
||||
// Lookup.findVirtual(MethodHandle.class, name, type);
|
||||
// But bypass access and corner case checks, since we know exactly what we need.
|
||||
return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, "invokeBasic", basicType);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError("JVM cannot find invoker for "+basicType, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an MTF for a given type, which must have all references erased to Object.
|
||||
* This MTF will stand for that type and all un-erased variations.
|
||||
@ -172,6 +162,15 @@ final class MethodTypeForm {
|
||||
this.basicType = erasedType;
|
||||
} else {
|
||||
this.basicType = MethodType.makeImpl(bt, bpts, true);
|
||||
// fill in rest of data from the basic type:
|
||||
MethodTypeForm that = this.basicType.form();
|
||||
assert(this != that);
|
||||
this.primCounts = that.primCounts;
|
||||
this.argCounts = that.argCounts;
|
||||
this.argToSlotTable = that.argToSlotTable;
|
||||
this.slotToArgTable = that.slotToArgTable;
|
||||
this.lambdaForms = null;
|
||||
return;
|
||||
}
|
||||
if (lac != 0) {
|
||||
int slot = ptypeCount + lac;
|
||||
@ -187,10 +186,14 @@ final class MethodTypeForm {
|
||||
argToSlotTab[1+i] = slot;
|
||||
}
|
||||
assert(slot == 0); // filled the table
|
||||
}
|
||||
this.primCounts = pack(lrc, prc, lac, pac);
|
||||
this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
|
||||
if (slotToArgTab == null) {
|
||||
} else if (pac != 0) {
|
||||
// have primitives but no long primitives; share slot counts with generic
|
||||
assert(ptypeCount == pslotCount);
|
||||
MethodTypeForm that = MethodType.genericMethodType(ptypeCount).form();
|
||||
assert(this != that);
|
||||
slotToArgTab = that.slotToArgTable;
|
||||
argToSlotTab = that.argToSlotTable;
|
||||
} else {
|
||||
int slot = ptypeCount; // first arg is deepest in stack
|
||||
slotToArgTab = new int[slot+1];
|
||||
argToSlotTab = new int[1+ptypeCount];
|
||||
@ -201,19 +204,16 @@ final class MethodTypeForm {
|
||||
argToSlotTab[1+i] = slot;
|
||||
}
|
||||
}
|
||||
this.primCounts = pack(lrc, prc, lac, pac);
|
||||
this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
|
||||
this.argToSlotTable = argToSlotTab;
|
||||
this.slotToArgTable = slotToArgTab;
|
||||
|
||||
if (pslotCount >= 256) throw newIllegalArgumentException("too many arguments");
|
||||
|
||||
// send a few bits down to the JVM:
|
||||
this.vmslots = parameterSlotCount();
|
||||
|
||||
if (basicType == erasedType) {
|
||||
lambdaForms = new LambdaForm[LF_LIMIT];
|
||||
} else {
|
||||
lambdaForms = null; // could be basicType.form().lambdaForms;
|
||||
}
|
||||
// Initialize caches, but only for basic types
|
||||
assert(basicType == erasedType);
|
||||
this.lambdaForms = new LambdaForm[LF_LIMIT];
|
||||
}
|
||||
|
||||
private static long pack(int a, int b, int c, int d) {
|
||||
@ -300,7 +300,7 @@ final class MethodTypeForm {
|
||||
*/
|
||||
public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
|
||||
Class<?>[] ptypes = mt.ptypes();
|
||||
Class<?>[] ptc = MethodTypeForm.canonicalizes(ptypes, howArgs);
|
||||
Class<?>[] ptc = MethodTypeForm.canonicalizeAll(ptypes, howArgs);
|
||||
Class<?> rtype = mt.returnType();
|
||||
Class<?> rtc = MethodTypeForm.canonicalize(rtype, howRet);
|
||||
if (ptc == null && rtc == null) {
|
||||
@ -368,7 +368,7 @@ final class MethodTypeForm {
|
||||
/** Canonicalize each param type in the given array.
|
||||
* Return null if all types are already canonicalized.
|
||||
*/
|
||||
static Class<?>[] canonicalizes(Class<?>[] ts, int how) {
|
||||
static Class<?>[] canonicalizeAll(Class<?>[] ts, int how) {
|
||||
Class<?>[] cs = null;
|
||||
for (int imax = ts.length, i = 0; i < imax; i++) {
|
||||
Class<?> c = canonicalize(ts[i], how);
|
||||
|
||||
@ -2160,15 +2160,23 @@ public class MethodHandlesTest {
|
||||
else
|
||||
type = type.changeParameterType(j, argType);
|
||||
if (done.add(type))
|
||||
testInvokers(type);
|
||||
testInvokersWithCatch(type);
|
||||
MethodType vtype = type.changeReturnType(void.class);
|
||||
if (done.add(vtype))
|
||||
testInvokers(vtype);
|
||||
testInvokersWithCatch(vtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testInvokersWithCatch(MethodType type) throws Throwable {
|
||||
try {
|
||||
testInvokers(type);
|
||||
} catch (Throwable ex) {
|
||||
System.out.println("*** testInvokers on "+type+" => ");
|
||||
ex.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
public void testInvokers(MethodType type) throws Throwable {
|
||||
if (verbosity >= 3)
|
||||
System.out.println("test invokers for "+type);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user