|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package java.lang.invoke; |
|
|
|
import jdk.internal.vm.annotation.Stable; |
|
import sun.invoke.util.ValueConversions; |
|
|
|
import java.util.ArrayList; |
|
import java.util.List; |
|
|
|
import static java.lang.invoke.LambdaForm.BasicType; |
|
import static java.lang.invoke.LambdaForm.BasicType.*; |
|
import static java.lang.invoke.LambdaForm.BasicType.V_TYPE_NUM; |
|
import static java.lang.invoke.LambdaForm.BasicType.V_TYPE_NUM; |
|
import static java.lang.invoke.LambdaForm.BasicType.V_TYPE_NUM; |
|
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; |
|
import static java.lang.invoke.MethodHandleNatives.Constants.*; |
|
import static java.lang.invoke.MethodHandleStatics.newInternalError; |
|
import static java.lang.invoke.MethodHandleStatics.uncaughtException; |
|
|
|
/** |
|
* The flavor of method handle which emulates an invoke instruction |
|
* on a predetermined argument. The JVM dispatches to the correct method |
|
* when the handle is created, not when it is invoked. |
|
* |
|
* All bound arguments are encapsulated in dedicated species. |
|
*/ |
|
|
|
abstract class BoundMethodHandle extends MethodHandle { |
|
|
|
|
|
BoundMethodHandle(MethodType type, LambdaForm form) { |
|
super(type, form); |
|
assert(speciesData() == speciesDataFor(form)); |
|
} |
|
|
|
// |
|
// BMH API and internals |
|
// |
|
|
|
static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) { |
|
|
|
try { |
|
return switch (xtype) { |
|
case L_TYPE -> bindSingle(type, form, x); |
|
case I_TYPE -> (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(I_TYPE_NUM).factory().invokeBasic(type, form, ValueConversions.widenSubword(x)); |
|
case J_TYPE -> (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(J_TYPE_NUM).factory().invokeBasic(type, form, (long) x); |
|
case F_TYPE -> (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(F_TYPE_NUM).factory().invokeBasic(type, form, (float) x); |
|
case D_TYPE -> (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(D_TYPE_NUM).factory().invokeBasic(type, form, (double) x); |
|
default -> throw newInternalError("unexpected xtype: " + xtype); |
|
}; |
|
} catch (Throwable t) { |
|
throw uncaughtException(t); |
|
} |
|
} |
|
|
|
|
|
LambdaFormEditor editor() { |
|
return form.editor(); |
|
} |
|
|
|
static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) { |
|
return Species_L.make(type, form, x); |
|
} |
|
|
|
@Override |
|
|
|
BoundMethodHandle bindArgumentL(int pos, Object value) { |
|
return editor().bindArgumentL(this, pos, value); |
|
} |
|
|
|
|
|
BoundMethodHandle bindArgumentI(int pos, int value) { |
|
return editor().bindArgumentI(this, pos, value); |
|
} |
|
|
|
BoundMethodHandle bindArgumentJ(int pos, long value) { |
|
return editor().bindArgumentJ(this, pos, value); |
|
} |
|
|
|
BoundMethodHandle bindArgumentF(int pos, float value) { |
|
return editor().bindArgumentF(this, pos, value); |
|
} |
|
|
|
BoundMethodHandle bindArgumentD(int pos, double value) { |
|
return editor().bindArgumentD(this, pos, value); |
|
} |
|
@Override |
|
BoundMethodHandle rebind() { |
|
if (!tooComplex()) { |
|
return this; |
|
} |
|
return makeReinvoker(this); |
|
} |
|
|
|
private boolean tooComplex() { |
|
return (fieldCount() > FIELD_COUNT_THRESHOLD || |
|
form.expressionCount() > FORM_EXPRESSION_THRESHOLD); |
|
} |
|
private static final int FIELD_COUNT_THRESHOLD = 12; |
|
private static final int FORM_EXPRESSION_THRESHOLD = 24; |
|
|
|
|
|
|
|
|
|
*/ |
|
static BoundMethodHandle makeReinvoker(MethodHandle target) { |
|
LambdaForm form = DelegatingMethodHandle.makeReinvokerForm( |
|
target, MethodTypeForm.LF_REBIND, |
|
Species_L.BMH_SPECIES, Species_L.BMH_SPECIES.getterFunction(0)); |
|
return Species_L.make(target.type(), form, target); |
|
} |
|
|
|
/** |
|
* Return the {@link BoundMethodHandle.SpeciesData} instance representing this BMH species. All subclasses must provide a |
|
* static field containing this value, and they must accordingly implement this method. |
|
*/ |
|
|
|
abstract BoundMethodHandle.SpeciesData speciesData(); |
|
|
|
|
|
static BoundMethodHandle.SpeciesData speciesDataFor(LambdaForm form) { |
|
Object c = form.names[0].constraint; |
|
if (c instanceof SpeciesData) { |
|
return (SpeciesData) c; |
|
} |
|
|
|
return SPECIALIZER.topSpecies(); |
|
} |
|
|
|
/** |
|
* Return the number of fields in this BMH. Equivalent to speciesData().fieldCount(). |
|
*/ |
|
|
|
final int fieldCount() { return speciesData().fieldCount(); } |
|
|
|
@Override |
|
Object internalProperties() { |
|
return "\n& BMH="+internalValues(); |
|
} |
|
|
|
@Override |
|
final String internalValues() { |
|
int count = fieldCount(); |
|
if (count == 1) { |
|
return "[" + arg(0) + "]"; |
|
} |
|
StringBuilder sb = new StringBuilder("["); |
|
for (int i = 0; i < count; ++i) { |
|
sb.append("\n ").append(i).append(": ( ").append(arg(i)).append(" )"); |
|
} |
|
return sb.append("\n]").toString(); |
|
} |
|
|
|
|
|
final Object arg(int i) { |
|
try { |
|
Class<?> fieldType = speciesData().fieldTypes().get(i); |
|
switch (BasicType.basicType(fieldType)) { |
|
case L_TYPE: return speciesData().getter(i).invokeBasic(this); |
|
case I_TYPE: return (int) speciesData().getter(i).invokeBasic(this); |
|
case J_TYPE: return (long) speciesData().getter(i).invokeBasic(this); |
|
case F_TYPE: return (float) speciesData().getter(i).invokeBasic(this); |
|
case D_TYPE: return (double) speciesData().getter(i).invokeBasic(this); |
|
} |
|
} catch (Throwable ex) { |
|
throw uncaughtException(ex); |
|
} |
|
throw new InternalError("unexpected type: " + speciesData().key()+"."+i); |
|
} |
|
|
|
// |
|
// cloning API |
|
// |
|
|
|
|
|
abstract BoundMethodHandle copyWith(MethodType mt, LambdaForm lf); |
|
|
|
abstract BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg); |
|
|
|
abstract BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg); |
|
|
|
abstract BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg); |
|
|
|
abstract BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg); |
|
|
|
abstract BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg); |
|
|
|
// |
|
// concrete BMH classes required to close bootstrap loops |
|
// |
|
|
|
private |
|
static final class Species_L extends BoundMethodHandle { |
|
|
|
final Object argL0; |
|
|
|
private Species_L(MethodType mt, LambdaForm lf, Object argL0) { |
|
super(mt, lf); |
|
this.argL0 = argL0; |
|
} |
|
|
|
@Override |
|
|
|
SpeciesData speciesData() { |
|
return BMH_SPECIES; |
|
} |
|
|
|
|
|
static @Stable SpeciesData BMH_SPECIES; |
|
|
|
|
|
static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) { |
|
return new Species_L(mt, lf, argL0); |
|
} |
|
@Override |
|
|
|
final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) { |
|
return new Species_L(mt, lf, argL0); |
|
} |
|
@Override |
|
|
|
final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) { |
|
try { |
|
return (BoundMethodHandle) BMH_SPECIES.extendWith(L_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg); |
|
} catch (Throwable ex) { |
|
throw uncaughtException(ex); |
|
} |
|
} |
|
@Override |
|
|
|
final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) { |
|
try { |
|
return (BoundMethodHandle) BMH_SPECIES.extendWith(I_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg); |
|
} catch (Throwable ex) { |
|
throw uncaughtException(ex); |
|
} |
|
} |
|
@Override |
|
|
|
final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) { |
|
try { |
|
return (BoundMethodHandle) BMH_SPECIES.extendWith(J_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg); |
|
} catch (Throwable ex) { |
|
throw uncaughtException(ex); |
|
} |
|
} |
|
@Override |
|
|
|
final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) { |
|
try { |
|
return (BoundMethodHandle) BMH_SPECIES.extendWith(F_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg); |
|
} catch (Throwable ex) { |
|
throw uncaughtException(ex); |
|
} |
|
} |
|
@Override |
|
|
|
final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) { |
|
try { |
|
return (BoundMethodHandle) BMH_SPECIES.extendWith(D_TYPE_NUM).factory().invokeBasic(mt, lf, argL0, narg); |
|
} catch (Throwable ex) { |
|
throw uncaughtException(ex); |
|
} |
|
} |
|
} |
|
|
|
// |
|
// BMH species meta-data |
|
// |
|
|
|
|
|
static final class SpeciesData |
|
extends ClassSpecializer<BoundMethodHandle, String, SpeciesData>.SpeciesData { |
|
|
|
@Stable private final SpeciesData[] extensions = new SpeciesData[ARG_TYPE_LIMIT]; |
|
|
|
public SpeciesData(Specializer outer, String key) { |
|
outer.super(key); |
|
} |
|
|
|
@Override |
|
protected String deriveClassName() { |
|
String typeString = deriveTypeString(); |
|
if (typeString.isEmpty()) { |
|
return SimpleMethodHandle.class.getName(); |
|
} |
|
return BoundMethodHandle.class.getName() + "$Species_" + typeString; |
|
} |
|
|
|
@Override |
|
protected List<Class<?>> deriveFieldTypes(String key) { |
|
ArrayList<Class<?>> types = new ArrayList<>(key.length()); |
|
for (int i = 0; i < key.length(); i++) { |
|
types.add(basicType(key.charAt(i)).basicTypeClass()); |
|
} |
|
return types; |
|
} |
|
|
|
@Override |
|
protected String deriveTypeString() { |
|
|
|
return key(); |
|
} |
|
|
|
@Override |
|
protected MethodHandle deriveTransformHelper(MemberName transform, int whichtm) { |
|
if (whichtm == Specializer.TN_COPY_NO_EXTEND) { |
|
return factory(); |
|
} else if (whichtm < ARG_TYPE_LIMIT) { |
|
return extendWith((byte) whichtm).factory(); |
|
} else { |
|
throw newInternalError("bad transform"); |
|
} |
|
} |
|
|
|
@Override |
|
protected <X> List<X> deriveTransformHelperArguments(MemberName transform, int whichtm, List<X> args, List<X> fields) { |
|
assert(verifyTHAargs(transform, whichtm, args, fields)); |
|
// The rule is really simple: Keep the first two arguments |
|
|
|
args.addAll(2, fields); |
|
return args; |
|
} |
|
|
|
private boolean verifyTHAargs(MemberName transform, int whichtm, List<?> args, List<?> fields) { |
|
assert(transform == Specializer.BMH_TRANSFORMS.get(whichtm)); |
|
assert(args.size() == transform.getMethodType().parameterCount()); |
|
assert(fields.size() == this.fieldCount()); |
|
final int MH_AND_LF = 2; |
|
if (whichtm == Specializer.TN_COPY_NO_EXTEND) { |
|
assert(transform.getMethodType().parameterCount() == MH_AND_LF); |
|
} else if (whichtm < ARG_TYPE_LIMIT) { |
|
assert(transform.getMethodType().parameterCount() == MH_AND_LF+1); |
|
final BasicType type = basicType((byte) whichtm); |
|
assert(transform.getParameterTypes()[MH_AND_LF] == type.basicTypeClass()); |
|
} else { |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
SpeciesData extendWith(byte typeNum) { |
|
SpeciesData sd = extensions[typeNum]; |
|
if (sd != null) return sd; |
|
sd = SPECIALIZER.findSpecies(key() + BasicType.basicType(typeNum).basicTypeChar()); |
|
extensions[typeNum] = sd; |
|
return sd; |
|
} |
|
} |
|
|
|
|
|
static final Specializer SPECIALIZER = new Specializer(); |
|
static { |
|
SimpleMethodHandle.BMH_SPECIES = BoundMethodHandle.SPECIALIZER.findSpecies(""); |
|
Species_L.BMH_SPECIES = BoundMethodHandle.SPECIALIZER.findSpecies("L"); |
|
} |
|
|
|
|
|
static final class Specializer |
|
extends ClassSpecializer<BoundMethodHandle, String, SpeciesData> { |
|
|
|
private static final MemberName SPECIES_DATA_ACCESSOR; |
|
|
|
static { |
|
try { |
|
SPECIES_DATA_ACCESSOR = IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BoundMethodHandle.class, |
|
"speciesData", MethodType.methodType(BoundMethodHandle.SpeciesData.class)); |
|
} catch (ReflectiveOperationException ex) { |
|
throw newInternalError("Bootstrap link error", ex); |
|
} |
|
} |
|
|
|
private Specializer() { |
|
super( |
|
BoundMethodHandle.class, String.class, BoundMethodHandle.SpeciesData.class, |
|
|
|
MethodType.methodType(void.class, MethodType.class, LambdaForm.class), |
|
|
|
SPECIES_DATA_ACCESSOR, |
|
"BMH_SPECIES", |
|
BMH_TRANSFORMS); |
|
} |
|
|
|
@Override |
|
protected String topSpeciesKey() { |
|
return ""; |
|
} |
|
|
|
@Override |
|
protected BoundMethodHandle.SpeciesData newSpeciesData(String key) { |
|
return new BoundMethodHandle.SpeciesData(this, key); |
|
} |
|
|
|
static final List<MemberName> BMH_TRANSFORMS; |
|
static final int TN_COPY_NO_EXTEND = V_TYPE_NUM; |
|
static { |
|
final Class<BoundMethodHandle> BMH = BoundMethodHandle.class; |
|
|
|
try { |
|
BMH_TRANSFORMS = List.of( |
|
IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendL", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, Object.class)), |
|
IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendI", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, int.class)), |
|
IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendJ", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, long.class)), |
|
IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendF", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, float.class)), |
|
IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendD", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, double.class)), |
|
IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWith", MethodType.methodType(BMH, MethodType.class, LambdaForm.class)) |
|
); |
|
} catch (ReflectiveOperationException ex) { |
|
throw newInternalError("Failed resolving copyWith methods", ex); |
|
} |
|
|
|
|
|
assert(BMH_TRANSFORMS.size() == TYPE_LIMIT); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class Factory extends ClassSpecializer<BoundMethodHandle, String, BoundMethodHandle.SpeciesData>.Factory { |
|
@Override |
|
protected String chooseFieldName(Class<?> type, int index) { |
|
return "arg" + super.chooseFieldName(type, index); |
|
} |
|
} |
|
|
|
@Override |
|
protected Factory makeFactory() { |
|
return new Factory(); |
|
} |
|
} |
|
|
|
static SpeciesData speciesData_L() { return Species_L.BMH_SPECIES; } |
|
static SpeciesData speciesData_LL() { return SPECIALIZER.findSpecies("LL"); } |
|
static SpeciesData speciesData_LLL() { return SPECIALIZER.findSpecies("LLL"); } |
|
static SpeciesData speciesData_LLLL() { return SPECIALIZER.findSpecies("LLLL"); } |
|
static SpeciesData speciesData_LLLLL() { return SPECIALIZER.findSpecies("LLLLL"); } |
|
} |