453 lines
19 KiB
Java
453 lines
19 KiB
Java
/*
|
|
* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
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.
|
|
*/
|
|
/*non-public*/ abstract class BoundMethodHandle extends MethodHandle {
|
|
|
|
/*non-public*/ 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) {
|
|
// for some type signatures, there exist pre-defined concrete BMH classes
|
|
try {
|
|
switch (xtype) {
|
|
case L_TYPE:
|
|
return bindSingle(type, form, x); // Use known fast path.
|
|
case I_TYPE:
|
|
return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(I_TYPE_NUM).factory().invokeBasic(type, form, ValueConversions.widenSubword(x));
|
|
case J_TYPE:
|
|
return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(J_TYPE_NUM).factory().invokeBasic(type, form, (long) x);
|
|
case F_TYPE:
|
|
return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(F_TYPE_NUM).factory().invokeBasic(type, form, (float) x);
|
|
case D_TYPE:
|
|
return (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);
|
|
}
|
|
}
|
|
|
|
/*non-public*/
|
|
LambdaFormEditor editor() {
|
|
return form.editor();
|
|
}
|
|
|
|
static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
|
|
return Species_L.make(type, form, x);
|
|
}
|
|
|
|
@Override // there is a default binder in the super class, for 'L' types only
|
|
/*non-public*/
|
|
BoundMethodHandle bindArgumentL(int pos, Object value) {
|
|
return editor().bindArgumentL(this, pos, value);
|
|
}
|
|
|
|
/*non-public*/
|
|
BoundMethodHandle bindArgumentI(int pos, int value) {
|
|
return editor().bindArgumentI(this, pos, value);
|
|
}
|
|
/*non-public*/
|
|
BoundMethodHandle bindArgumentJ(int pos, long value) {
|
|
return editor().bindArgumentJ(this, pos, value);
|
|
}
|
|
/*non-public*/
|
|
BoundMethodHandle bindArgumentF(int pos, float value) {
|
|
return editor().bindArgumentF(this, pos, value);
|
|
}
|
|
/*non-public*/
|
|
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; // largest convenient BMH field count
|
|
private static final int FORM_EXPRESSION_THRESHOLD = 24; // largest convenient BMH expression count
|
|
|
|
/**
|
|
* A reinvoker MH has this form:
|
|
* {@code lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }}
|
|
*/
|
|
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.
|
|
*/
|
|
/*non-public*/ abstract BoundMethodHandle.SpeciesData speciesData();
|
|
|
|
/*non-public*/ static BoundMethodHandle.SpeciesData speciesDataFor(LambdaForm form) {
|
|
Object c = form.names[0].constraint;
|
|
if (c instanceof SpeciesData) {
|
|
return (SpeciesData) c;
|
|
}
|
|
// if there is no BMH constraint, then use the null constraint
|
|
return SPECIALIZER.topSpecies();
|
|
}
|
|
|
|
/**
|
|
* Return the number of fields in this BMH. Equivalent to speciesData().fieldCount().
|
|
*/
|
|
/*non-public*/ 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();
|
|
}
|
|
|
|
/*non-public*/ 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
|
|
//
|
|
|
|
/*non-public*/ abstract BoundMethodHandle copyWith(MethodType mt, LambdaForm lf);
|
|
/*non-public*/ abstract BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg);
|
|
/*non-public*/ abstract BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg);
|
|
/*non-public*/ abstract BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg);
|
|
/*non-public*/ abstract BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg);
|
|
/*non-public*/ abstract BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg);
|
|
|
|
//
|
|
// concrete BMH classes required to close bootstrap loops
|
|
//
|
|
|
|
private // make it private to force users to access the enclosing class first
|
|
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
|
|
/*non-public*/ SpeciesData speciesData() {
|
|
return BMH_SPECIES;
|
|
}
|
|
|
|
/*non-public*/ static @Stable SpeciesData BMH_SPECIES;
|
|
|
|
/*non-public*/ static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) {
|
|
return new Species_L(mt, lf, argL0);
|
|
}
|
|
@Override
|
|
/*non-public*/ final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
|
return new Species_L(mt, lf, argL0);
|
|
}
|
|
@Override
|
|
/*non-public*/ 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
|
|
/*non-public*/ 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
|
|
/*non-public*/ 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
|
|
/*non-public*/ 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
|
|
/*non-public*/ 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
|
|
//
|
|
|
|
/*non-public*/
|
|
static final class SpeciesData extends ClassSpecializer<BoundMethodHandle, String, SpeciesData>.SpeciesData {
|
|
// This array is filled in lazily, as new species come into being over time.
|
|
@Stable final private 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() {
|
|
// (If/when we have to add nominal types, just inherit the more complex default.)
|
|
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
|
|
// the same, then put in the fields, then put any other argument.
|
|
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;
|
|
}
|
|
|
|
/*non-public*/ 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;
|
|
}
|
|
}
|
|
|
|
/*non-public*/
|
|
static final Specializer SPECIALIZER = new Specializer();
|
|
static {
|
|
SimpleMethodHandle.BMH_SPECIES = BoundMethodHandle.SPECIALIZER.findSpecies("");
|
|
Species_L.BMH_SPECIES = BoundMethodHandle.SPECIALIZER.findSpecies("L");
|
|
}
|
|
|
|
/*non-public*/
|
|
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( // Reified type parameters:
|
|
BoundMethodHandle.class, String.class, BoundMethodHandle.SpeciesData.class,
|
|
// Principal constructor type:
|
|
MethodType.methodType(void.class, MethodType.class, LambdaForm.class),
|
|
// Required linkage between class and species:
|
|
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;
|
|
// copyWithExtendLIJFD + copyWith
|
|
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);
|
|
}
|
|
|
|
// as it happens, there is one transform per BasicType including V_TYPE
|
|
assert(BMH_TRANSFORMS.size() == TYPE_LIMIT);
|
|
}
|
|
|
|
/**
|
|
* Generation of concrete BMH classes.
|
|
*
|
|
* A concrete BMH species is fit for binding a number of values adhering to a
|
|
* given type pattern. Reference types are erased.
|
|
*
|
|
* BMH species are cached by type pattern.
|
|
*
|
|
* A BMH species has a number of fields with the concrete (possibly erased) types of
|
|
* bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
|
|
* which can be included as names in lambda forms.
|
|
*/
|
|
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"); }
|
|
}
|