8163370: Reduce number of classes loaded by common usage of java.lang.invoke
Reviewed-by: igerasim, psandoz
This commit is contained in:
parent
23853ed3e4
commit
bb95ea6101
@ -515,7 +515,7 @@ class DirectMethodHandle extends MethodHandle {
|
||||
// Enumerate the different field kinds using Wrapper,
|
||||
// with an extra case added for checked references.
|
||||
private static final int
|
||||
FT_LAST_WRAPPER = Wrapper.values().length-1,
|
||||
FT_LAST_WRAPPER = Wrapper.COUNT-1,
|
||||
FT_UNCHECKED_REF = Wrapper.OBJECT.ordinal(),
|
||||
FT_CHECKED_REF = FT_LAST_WRAPPER+1,
|
||||
FT_LIMIT = FT_LAST_WRAPPER+2;
|
||||
|
@ -60,7 +60,7 @@ class LambdaFormEditor {
|
||||
}
|
||||
|
||||
/** A description of a cached transform, possibly associated with the result of the transform.
|
||||
* The logical content is a sequence of byte values, starting with a Kind.ordinal value.
|
||||
* The logical content is a sequence of byte values, starting with a kind value.
|
||||
* The sequence is unterminated, ending with an indefinite number of zero bytes.
|
||||
* Sequences that are simple (short enough and with small enough values) pack into a 64-bit long.
|
||||
*/
|
||||
@ -68,17 +68,22 @@ class LambdaFormEditor {
|
||||
final long packedBytes;
|
||||
final byte[] fullBytes;
|
||||
|
||||
private enum Kind {
|
||||
NO_KIND, // necessary because ordinal must be greater than zero
|
||||
BIND_ARG, ADD_ARG, DUP_ARG,
|
||||
SPREAD_ARGS,
|
||||
FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
|
||||
COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
|
||||
FOLD_ARGS, FOLD_ARGS_TO_VOID,
|
||||
PERMUTE_ARGS,
|
||||
LOCAL_TYPES
|
||||
//maybe add more for guard with test, catch exception, pointwise type conversions
|
||||
}
|
||||
// maybe add more for guard with test, catch exception, pointwise type conversions
|
||||
private static final byte
|
||||
BIND_ARG = 1,
|
||||
ADD_ARG = 2,
|
||||
DUP_ARG = 3,
|
||||
SPREAD_ARGS = 4,
|
||||
FILTER_ARG = 5,
|
||||
FILTER_RETURN = 6,
|
||||
FILTER_RETURN_TO_ZERO = 7,
|
||||
COLLECT_ARGS = 8,
|
||||
COLLECT_ARGS_TO_VOID = 9,
|
||||
COLLECT_ARGS_TO_ARRAY = 10,
|
||||
FOLD_ARGS = 11,
|
||||
FOLD_ARGS_TO_VOID = 12,
|
||||
PERMUTE_ARGS = 13,
|
||||
LOCAL_TYPES = 14;
|
||||
|
||||
private static final boolean STRESS_TEST = false; // turn on to disable most packing
|
||||
private static final int
|
||||
@ -131,20 +136,6 @@ class LambdaFormEditor {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private byte byteAt(int i) {
|
||||
long pb = packedBytes;
|
||||
if (pb == 0) {
|
||||
if (i >= fullBytes.length) return 0;
|
||||
return fullBytes[i];
|
||||
}
|
||||
assert(fullBytes == null);
|
||||
if (i > PACKED_BYTE_MAX_LENGTH) return 0;
|
||||
int pos = (i * PACKED_BYTE_SIZE);
|
||||
return (byte)((pb >>> pos) & PACKED_BYTE_MASK);
|
||||
}
|
||||
|
||||
Kind kind() { return Kind.values()[byteAt(0)]; }
|
||||
|
||||
private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
|
||||
super(result);
|
||||
this.packedBytes = packedBytes;
|
||||
@ -162,44 +153,39 @@ class LambdaFormEditor {
|
||||
assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte
|
||||
return (byte)b;
|
||||
}
|
||||
private static byte bval(Kind k) {
|
||||
return bval(k.ordinal());
|
||||
}
|
||||
static Transform of(Kind k, int b1) {
|
||||
static Transform of(byte k, int b1) {
|
||||
byte b0 = bval(k);
|
||||
if (inRange(b0 | b1))
|
||||
return new Transform(packedBytes(b0, b1));
|
||||
else
|
||||
return new Transform(fullBytes(b0, b1));
|
||||
}
|
||||
static Transform of(Kind k, int b1, int b2) {
|
||||
byte b0 = (byte) k.ordinal();
|
||||
static Transform of(byte b0, int b1, int b2) {
|
||||
if (inRange(b0 | b1 | b2))
|
||||
return new Transform(packedBytes(b0, b1, b2));
|
||||
else
|
||||
return new Transform(fullBytes(b0, b1, b2));
|
||||
}
|
||||
static Transform of(Kind k, int b1, int b2, int b3) {
|
||||
byte b0 = (byte) k.ordinal();
|
||||
static Transform of(byte b0, int b1, int b2, int b3) {
|
||||
if (inRange(b0 | b1 | b2 | b3))
|
||||
return new Transform(packedBytes(b0, b1, b2, b3));
|
||||
else
|
||||
return new Transform(fullBytes(b0, b1, b2, b3));
|
||||
}
|
||||
private static final byte[] NO_BYTES = {};
|
||||
static Transform of(Kind k, int... b123) {
|
||||
return ofBothArrays(k, b123, NO_BYTES);
|
||||
static Transform of(byte kind, int... b123) {
|
||||
return ofBothArrays(kind, b123, NO_BYTES);
|
||||
}
|
||||
static Transform of(Kind k, int b1, byte[] b234) {
|
||||
return ofBothArrays(k, new int[]{ b1 }, b234);
|
||||
static Transform of(byte kind, int b1, byte[] b234) {
|
||||
return ofBothArrays(kind, new int[]{ b1 }, b234);
|
||||
}
|
||||
static Transform of(Kind k, int b1, int b2, byte[] b345) {
|
||||
return ofBothArrays(k, new int[]{ b1, b2 }, b345);
|
||||
static Transform of(byte kind, int b1, int b2, byte[] b345) {
|
||||
return ofBothArrays(kind, new int[]{ b1, b2 }, b345);
|
||||
}
|
||||
private static Transform ofBothArrays(Kind k, int[] b123, byte[] b456) {
|
||||
private static Transform ofBothArrays(byte kind, int[] b123, byte[] b456) {
|
||||
byte[] fullBytes = new byte[1 + b123.length + b456.length];
|
||||
int i = 0;
|
||||
fullBytes[i++] = bval(k);
|
||||
fullBytes[i++] = bval(kind);
|
||||
for (int bv : b123) {
|
||||
fullBytes[i++] = bval(bv);
|
||||
}
|
||||
@ -449,7 +435,7 @@ class LambdaFormEditor {
|
||||
// Each editing method can (potentially) cache the edited LF so that it can be reused later.
|
||||
|
||||
LambdaForm bindArgumentForm(int pos) {
|
||||
Transform key = Transform.of(Transform.Kind.BIND_ARG, pos);
|
||||
Transform key = Transform.of(Transform.BIND_ARG, pos);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos)));
|
||||
@ -484,7 +470,7 @@ class LambdaFormEditor {
|
||||
}
|
||||
|
||||
LambdaForm addArgumentForm(int pos, BasicType type) {
|
||||
Transform key = Transform.of(Transform.Kind.ADD_ARG, pos, type.ordinal());
|
||||
Transform key = Transform.of(Transform.ADD_ARG, pos, type.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity+1);
|
||||
@ -501,7 +487,7 @@ class LambdaFormEditor {
|
||||
}
|
||||
|
||||
LambdaForm dupArgumentForm(int srcPos, int dstPos) {
|
||||
Transform key = Transform.of(Transform.Kind.DUP_ARG, srcPos, dstPos);
|
||||
Transform key = Transform.of(Transform.DUP_ARG, srcPos, dstPos);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity-1);
|
||||
@ -530,7 +516,7 @@ class LambdaFormEditor {
|
||||
elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
|
||||
}
|
||||
}
|
||||
Transform key = Transform.of(Transform.Kind.SPREAD_ARGS, pos, elementTypeKey, arrayLength);
|
||||
Transform key = Transform.of(Transform.SPREAD_ARGS, pos, elementTypeKey, arrayLength);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - arrayLength + 1);
|
||||
@ -569,9 +555,9 @@ class LambdaFormEditor {
|
||||
return filterArgumentForm(pos, basicType(collectorType.parameterType(0)));
|
||||
}
|
||||
byte[] newTypes = BasicType.basicTypesOrd(collectorType.parameterArray());
|
||||
Transform.Kind kind = (dropResult
|
||||
? Transform.Kind.COLLECT_ARGS_TO_VOID
|
||||
: Transform.Kind.COLLECT_ARGS);
|
||||
byte kind = (dropResult
|
||||
? Transform.COLLECT_ARGS_TO_VOID
|
||||
: Transform.COLLECT_ARGS);
|
||||
if (dropResult && collectorArity == 0) pos = 1; // pure side effect
|
||||
Transform key = Transform.of(kind, pos, collectorArity, newTypes);
|
||||
LambdaForm form = getInCache(key);
|
||||
@ -598,7 +584,7 @@ class LambdaFormEditor {
|
||||
argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
|
||||
}
|
||||
assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType)));
|
||||
Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY;
|
||||
byte kind = Transform.COLLECT_ARGS_TO_ARRAY;
|
||||
Transform key = Transform.of(kind, pos, collectorArity, argTypeKey);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
@ -634,7 +620,7 @@ class LambdaFormEditor {
|
||||
}
|
||||
|
||||
LambdaForm filterArgumentForm(int pos, BasicType newType) {
|
||||
Transform key = Transform.of(Transform.Kind.FILTER_ARG, pos, newType.ordinal());
|
||||
Transform key = Transform.of(Transform.FILTER_ARG, pos, newType.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity);
|
||||
@ -710,7 +696,7 @@ class LambdaFormEditor {
|
||||
}
|
||||
|
||||
LambdaForm filterReturnForm(BasicType newType, boolean constantZero) {
|
||||
Transform.Kind kind = (constantZero ? Transform.Kind.FILTER_RETURN_TO_ZERO : Transform.Kind.FILTER_RETURN);
|
||||
byte kind = (constantZero ? Transform.FILTER_RETURN_TO_ZERO : Transform.FILTER_RETURN);
|
||||
Transform key = Transform.of(kind, newType.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
@ -762,11 +748,11 @@ class LambdaFormEditor {
|
||||
|
||||
LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) {
|
||||
int combinerArity = combinerType.parameterCount();
|
||||
Transform.Kind kind = (dropResult ? Transform.Kind.FOLD_ARGS_TO_VOID : Transform.Kind.FOLD_ARGS);
|
||||
byte kind = (dropResult ? Transform.FOLD_ARGS_TO_VOID : Transform.FOLD_ARGS);
|
||||
Transform key = Transform.of(kind, foldPos, combinerArity);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - (kind == Transform.Kind.FOLD_ARGS ? 1 : 0));
|
||||
assert(form.arity == lambdaForm.arity - (kind == Transform.FOLD_ARGS ? 1 : 0));
|
||||
return form;
|
||||
}
|
||||
form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult);
|
||||
@ -786,7 +772,7 @@ class LambdaFormEditor {
|
||||
}
|
||||
assert(skip + reorder.length == lambdaForm.arity);
|
||||
if (nullPerm) return lambdaForm; // do not bother to cache
|
||||
Transform key = Transform.of(Transform.Kind.PERMUTE_ARGS, reorder);
|
||||
Transform key = Transform.of(Transform.PERMUTE_ARGS, reorder);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == skip+inTypes) : form;
|
||||
@ -855,7 +841,7 @@ class LambdaFormEditor {
|
||||
int[] desc = BasicType.basicTypeOrds(localTypes);
|
||||
desc = Arrays.copyOf(desc, desc.length + 1);
|
||||
desc[desc.length - 1] = pos;
|
||||
Transform key = Transform.of(Transform.Kind.LOCAL_TYPES, desc);
|
||||
Transform key = Transform.of(Transform.LOCAL_TYPES, desc);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
return form;
|
||||
|
@ -1002,7 +1002,9 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
||||
Collections.addAll(result, buf0);
|
||||
}
|
||||
}
|
||||
result.addAll(Arrays.asList(buf).subList(0, bufCount));
|
||||
for (int i = 0; i < bufCount; i++) {
|
||||
result.add(buf[i]);
|
||||
}
|
||||
// Signature matching is not the same as type matching, since
|
||||
// one signature might correspond to several types.
|
||||
// So if matchType is a Class or MethodType, refilter the results.
|
||||
|
@ -3115,7 +3115,7 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
return dropArguments(zero(type.returnType()), 0, type.parameterList());
|
||||
}
|
||||
|
||||
private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.values().length];
|
||||
private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.COUNT];
|
||||
private static MethodHandle makeIdentity(Class<?> ptype) {
|
||||
MethodType mtype = methodType(ptype, ptype);
|
||||
LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype));
|
||||
@ -3133,7 +3133,7 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
assert(btw == Wrapper.OBJECT);
|
||||
return makeZero(rtype);
|
||||
}
|
||||
private static final MethodHandle[] ZERO_MHS = new MethodHandle[Wrapper.values().length];
|
||||
private static final MethodHandle[] ZERO_MHS = new MethodHandle[Wrapper.COUNT];
|
||||
private static MethodHandle makeZero(Class<?> rtype) {
|
||||
MethodType mtype = methodType(rtype);
|
||||
LambdaForm lform = LambdaForm.zeroForm(BasicType.basicType(rtype));
|
||||
|
@ -281,8 +281,7 @@ public final class StringConcatFactory {
|
||||
if (c == TAG_CONST) {
|
||||
Object cnst = constants[constC++];
|
||||
el.add(new RecipeElement(cnst));
|
||||
}
|
||||
if (c == TAG_ARG) {
|
||||
} else if (c == TAG_ARG) {
|
||||
el.add(new RecipeElement(argC++));
|
||||
}
|
||||
} else {
|
||||
@ -322,32 +321,30 @@ public final class StringConcatFactory {
|
||||
private static final class RecipeElement {
|
||||
private final Object value;
|
||||
private final int argPos;
|
||||
private final Tag tag;
|
||||
|
||||
public RecipeElement(Object cnst) {
|
||||
this.value = Objects.requireNonNull(cnst);
|
||||
this.argPos = -1;
|
||||
this.tag = Tag.CONST;
|
||||
}
|
||||
|
||||
public RecipeElement(int arg) {
|
||||
this.value = null;
|
||||
assert (arg >= 0);
|
||||
this.argPos = arg;
|
||||
this.tag = Tag.ARG;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
assert (tag == Tag.CONST);
|
||||
assert (isConst());
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getArgPos() {
|
||||
assert (tag == Tag.ARG);
|
||||
assert (!isConst());
|
||||
return argPos;
|
||||
}
|
||||
|
||||
public Tag getTag() {
|
||||
return tag;
|
||||
public boolean isConst() {
|
||||
return argPos == -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -357,22 +354,19 @@ public final class StringConcatFactory {
|
||||
|
||||
RecipeElement that = (RecipeElement) o;
|
||||
|
||||
if (tag != that.tag) return false;
|
||||
if (tag == Tag.CONST && (!value.equals(that.value))) return false;
|
||||
if (tag == Tag.ARG && (argPos != that.argPos)) return false;
|
||||
boolean isConst = isConst();
|
||||
if (isConst != that.isConst()) return false;
|
||||
if (isConst && (!value.equals(that.value))) return false;
|
||||
if (!isConst && (argPos != that.argPos)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return tag.hashCode();
|
||||
return argPos;
|
||||
}
|
||||
}
|
||||
|
||||
private enum Tag {
|
||||
CONST, ARG
|
||||
}
|
||||
|
||||
/**
|
||||
* Facilitates the creation of optimized String concatenation methods, that
|
||||
* can be used to efficiently concatenate a known number of arguments of
|
||||
@ -880,31 +874,24 @@ public final class StringConcatFactory {
|
||||
|
||||
int off = 0;
|
||||
for (RecipeElement el : recipe.getElements()) {
|
||||
switch (el.getTag()) {
|
||||
case CONST: {
|
||||
// Guaranteed non-null, no null check required.
|
||||
break;
|
||||
if (el.isConst()) {
|
||||
// Guaranteed non-null, no null check required.
|
||||
} else {
|
||||
// Null-checks are needed only for String arguments, and when a previous stage
|
||||
// did not do implicit null-checks. If a String is null, we eagerly replace it
|
||||
// with "null" constant. Note, we omit Objects here, because we don't call
|
||||
// .length() on them down below.
|
||||
int ac = el.getArgPos();
|
||||
Class<?> cl = arr[ac];
|
||||
if (cl == String.class && !guaranteedNonNull[ac]) {
|
||||
Label l0 = new Label();
|
||||
mv.visitIntInsn(ALOAD, off);
|
||||
mv.visitJumpInsn(IFNONNULL, l0);
|
||||
mv.visitLdcInsn("null");
|
||||
mv.visitIntInsn(ASTORE, off);
|
||||
mv.visitLabel(l0);
|
||||
}
|
||||
case ARG: {
|
||||
// Null-checks are needed only for String arguments, and when a previous stage
|
||||
// did not do implicit null-checks. If a String is null, we eagerly replace it
|
||||
// with "null" constant. Note, we omit Objects here, because we don't call
|
||||
// .length() on them down below.
|
||||
int ac = el.getArgPos();
|
||||
Class<?> cl = arr[ac];
|
||||
if (cl == String.class && !guaranteedNonNull[ac]) {
|
||||
Label l0 = new Label();
|
||||
mv.visitIntInsn(ALOAD, off);
|
||||
mv.visitJumpInsn(IFNONNULL, l0);
|
||||
mv.visitLdcInsn("null");
|
||||
mv.visitIntInsn(ASTORE, off);
|
||||
mv.visitLabel(l0);
|
||||
}
|
||||
off += getParameterSize(cl);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||
off += getParameterSize(cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -925,37 +912,30 @@ public final class StringConcatFactory {
|
||||
mv.visitInsn(ICONST_0);
|
||||
|
||||
for (RecipeElement el : recipe.getElements()) {
|
||||
switch (el.getTag()) {
|
||||
case CONST: {
|
||||
Object cnst = el.getValue();
|
||||
len += cnst.toString().length();
|
||||
break;
|
||||
if (el.isConst()) {
|
||||
Object cnst = el.getValue();
|
||||
len += cnst.toString().length();
|
||||
} else {
|
||||
/*
|
||||
If an argument is String, then we can call .length() on it. Sized/Exact modes have
|
||||
converted arguments for us. If an argument is primitive, we can provide a guess
|
||||
for its String representation size.
|
||||
*/
|
||||
Class<?> cl = arr[el.getArgPos()];
|
||||
if (cl == String.class) {
|
||||
mv.visitIntInsn(ALOAD, off);
|
||||
mv.visitMethodInsn(
|
||||
INVOKEVIRTUAL,
|
||||
"java/lang/String",
|
||||
"length",
|
||||
"()I",
|
||||
false
|
||||
);
|
||||
mv.visitInsn(IADD);
|
||||
} else if (cl.isPrimitive()) {
|
||||
len += estimateSize(cl);
|
||||
}
|
||||
case ARG: {
|
||||
/*
|
||||
If an argument is String, then we can call .length() on it. Sized/Exact modes have
|
||||
converted arguments for us. If an argument is primitive, we can provide a guess
|
||||
for its String representation size.
|
||||
*/
|
||||
Class<?> cl = arr[el.getArgPos()];
|
||||
if (cl == String.class) {
|
||||
mv.visitIntInsn(ALOAD, off);
|
||||
mv.visitMethodInsn(
|
||||
INVOKEVIRTUAL,
|
||||
"java/lang/String",
|
||||
"length",
|
||||
"()I",
|
||||
false
|
||||
);
|
||||
mv.visitInsn(IADD);
|
||||
} else if (cl.isPrimitive()) {
|
||||
len += estimateSize(cl);
|
||||
}
|
||||
off += getParameterSize(cl);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||
off += getParameterSize(cl);
|
||||
}
|
||||
}
|
||||
|
||||
@ -987,23 +967,17 @@ public final class StringConcatFactory {
|
||||
int off = 0;
|
||||
for (RecipeElement el : recipe.getElements()) {
|
||||
String desc;
|
||||
switch (el.getTag()) {
|
||||
case CONST: {
|
||||
Object cnst = el.getValue();
|
||||
mv.visitLdcInsn(cnst);
|
||||
desc = getSBAppendDesc(cnst.getClass());
|
||||
break;
|
||||
}
|
||||
case ARG: {
|
||||
Class<?> cl = arr[el.getArgPos()];
|
||||
mv.visitVarInsn(getLoadOpcode(cl), off);
|
||||
off += getParameterSize(cl);
|
||||
desc = getSBAppendDesc(cl);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||
if (el.isConst()) {
|
||||
Object cnst = el.getValue();
|
||||
mv.visitLdcInsn(cnst);
|
||||
desc = getSBAppendDesc(cnst.getClass());
|
||||
} else {
|
||||
Class<?> cl = arr[el.getArgPos()];
|
||||
mv.visitVarInsn(getLoadOpcode(cl), off);
|
||||
off += getParameterSize(cl);
|
||||
desc = getSBAppendDesc(cl);
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(
|
||||
INVOKEVIRTUAL,
|
||||
"java/lang/StringBuilder",
|
||||
@ -1279,26 +1253,19 @@ public final class StringConcatFactory {
|
||||
// call the usual String.length(). Primitive values string sizes can be estimated.
|
||||
int initial = 0;
|
||||
for (RecipeElement el : recipe.getElements()) {
|
||||
switch (el.getTag()) {
|
||||
case CONST: {
|
||||
Object cnst = el.getValue();
|
||||
initial += cnst.toString().length();
|
||||
break;
|
||||
if (el.isConst()) {
|
||||
Object cnst = el.getValue();
|
||||
initial += cnst.toString().length();
|
||||
} else {
|
||||
final int i = el.getArgPos();
|
||||
Class<?> type = ptypesList.get(i);
|
||||
if (type.isPrimitive()) {
|
||||
MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
|
||||
est = MethodHandles.dropArguments(est, 0, type);
|
||||
lengthers[i] = est;
|
||||
} else {
|
||||
lengthers[i] = STRING_LENGTH;
|
||||
}
|
||||
case ARG: {
|
||||
final int i = el.getArgPos();
|
||||
Class<?> type = ptypesList.get(i);
|
||||
if (type.isPrimitive()) {
|
||||
MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
|
||||
est = MethodHandles.dropArguments(est, 0, type);
|
||||
lengthers[i] = est;
|
||||
} else {
|
||||
lengthers[i] = STRING_LENGTH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1311,26 +1278,19 @@ public final class StringConcatFactory {
|
||||
for (int i = elements.size() - 1; i >= 0; i--) {
|
||||
RecipeElement el = elements.get(i);
|
||||
MethodHandle appender;
|
||||
switch (el.getTag()) {
|
||||
case CONST: {
|
||||
Object constant = el.getValue();
|
||||
MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
|
||||
appender = MethodHandles.insertArguments(mh, 1, constant);
|
||||
break;
|
||||
}
|
||||
case ARG: {
|
||||
int ac = el.getArgPos();
|
||||
appender = appender(ptypesList.get(ac));
|
||||
if (el.isConst()) {
|
||||
Object constant = el.getValue();
|
||||
MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
|
||||
appender = MethodHandles.insertArguments(mh, 1, constant);
|
||||
} else {
|
||||
int ac = el.getArgPos();
|
||||
appender = appender(ptypesList.get(ac));
|
||||
|
||||
// Insert dummy arguments to match the prefix in the signature.
|
||||
// The actual appender argument will be the ac-ith argument.
|
||||
if (ac != 0) {
|
||||
appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac));
|
||||
}
|
||||
break;
|
||||
// Insert dummy arguments to match the prefix in the signature.
|
||||
// The actual appender argument will be the ac-ith argument.
|
||||
if (ac != 0) {
|
||||
appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac));
|
||||
}
|
||||
default:
|
||||
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||
}
|
||||
builder = MethodHandles.foldArguments(builder, appender);
|
||||
}
|
||||
@ -1521,19 +1481,12 @@ public final class StringConcatFactory {
|
||||
// *ending* index.
|
||||
for (RecipeElement el : recipe.getElements()) {
|
||||
MethodHandle prepender;
|
||||
switch (el.getTag()) {
|
||||
case CONST: {
|
||||
Object cnst = el.getValue();
|
||||
prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
|
||||
break;
|
||||
}
|
||||
case ARG: {
|
||||
int pos = el.getArgPos();
|
||||
prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||
if (el.isConst()) {
|
||||
Object cnst = el.getValue();
|
||||
prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
|
||||
} else {
|
||||
int pos = el.getArgPos();
|
||||
prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos);
|
||||
}
|
||||
|
||||
// Remove "old" index from arguments
|
||||
@ -1573,43 +1526,36 @@ public final class StringConcatFactory {
|
||||
byte initialCoder = INITIAL_CODER;
|
||||
int initialLen = 0; // initial length, in characters
|
||||
for (RecipeElement el : recipe.getElements()) {
|
||||
switch (el.getTag()) {
|
||||
case CONST: {
|
||||
Object constant = el.getValue();
|
||||
String s = constant.toString();
|
||||
initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
|
||||
initialLen += s.length();
|
||||
break;
|
||||
}
|
||||
case ARG: {
|
||||
int ac = el.getArgPos();
|
||||
if (el.isConst()) {
|
||||
Object constant = el.getValue();
|
||||
String s = constant.toString();
|
||||
initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
|
||||
initialLen += s.length();
|
||||
} else {
|
||||
int ac = el.getArgPos();
|
||||
|
||||
Class<?> argClass = ptypesList.get(ac);
|
||||
MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
|
||||
lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
|
||||
lm = MethodHandles.dropArguments(lm, 2, byte.class);
|
||||
Class<?> argClass = ptypesList.get(ac);
|
||||
MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
|
||||
lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
|
||||
lm = MethodHandles.dropArguments(lm, 2, byte.class);
|
||||
|
||||
MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypesList, ac);
|
||||
cm = MethodHandles.dropArguments(cm, 0, int.class); // (**)
|
||||
MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypesList, ac);
|
||||
cm = MethodHandles.dropArguments(cm, 0, int.class); // (**)
|
||||
|
||||
// Read this bottom up:
|
||||
// Read this bottom up:
|
||||
|
||||
// 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
|
||||
mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
|
||||
// 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
|
||||
mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
|
||||
|
||||
// 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
|
||||
// Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
|
||||
mh = MethodHandles.foldArguments(mh, lm);
|
||||
// 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
|
||||
// Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
|
||||
mh = MethodHandles.foldArguments(mh, lm);
|
||||
|
||||
// 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
|
||||
// Coder mixer ignores the "old-index" arg due to dropArguments above (**)
|
||||
mh = MethodHandles.foldArguments(mh, cm);
|
||||
// 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
|
||||
// Coder mixer ignores the "old-index" arg due to dropArguments above (**)
|
||||
mh = MethodHandles.foldArguments(mh, cm);
|
||||
|
||||
// 1. The mh shape here is ("old-index", "old-coder", <args>)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||
// 1. The mh shape here is ("old-index", "old-coder", <args>)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
|
||||
super(Opcodes.ASM5, mv);
|
||||
}
|
||||
|
||||
private static final int NUM_WRAPPERS = Wrapper.values().length;
|
||||
private static final int NUM_WRAPPERS = Wrapper.COUNT;
|
||||
|
||||
private static final String NAME_OBJECT = "java/lang/Object";
|
||||
private static final String WRAPPER_PREFIX = "Ljava/lang/";
|
||||
|
@ -1057,57 +1057,11 @@ public abstract class VarHandle {
|
||||
Object addAndGet(Object... args);
|
||||
|
||||
enum AccessType {
|
||||
GET(Object.class) {
|
||||
@Override
|
||||
MethodType accessModeType(Class<?> receiver, Class<?> value,
|
||||
Class<?>... intermediate) {
|
||||
Class<?>[] ps = allocateParameters(0, receiver, intermediate);
|
||||
fillParameters(ps, receiver, intermediate);
|
||||
return MethodType.methodType(value, ps);
|
||||
}
|
||||
},
|
||||
SET(void.class) {
|
||||
@Override
|
||||
MethodType accessModeType(Class<?> receiver, Class<?> value,
|
||||
Class<?>... intermediate) {
|
||||
Class<?>[] ps = allocateParameters(1, receiver, intermediate);
|
||||
int i = fillParameters(ps, receiver, intermediate);
|
||||
ps[i] = value;
|
||||
return MethodType.methodType(void.class, ps);
|
||||
}
|
||||
},
|
||||
COMPARE_AND_SWAP(boolean.class) {
|
||||
@Override
|
||||
MethodType accessModeType(Class<?> receiver, Class<?> value,
|
||||
Class<?>... intermediate) {
|
||||
Class<?>[] ps = allocateParameters(2, receiver, intermediate);
|
||||
int i = fillParameters(ps, receiver, intermediate);
|
||||
ps[i++] = value;
|
||||
ps[i] = value;
|
||||
return MethodType.methodType(boolean.class, ps);
|
||||
}
|
||||
},
|
||||
COMPARE_AND_EXCHANGE(Object.class) {
|
||||
@Override
|
||||
MethodType accessModeType(Class<?> receiver, Class<?> value,
|
||||
Class<?>... intermediate) {
|
||||
Class<?>[] ps = allocateParameters(2, receiver, intermediate);
|
||||
int i = fillParameters(ps, receiver, intermediate);
|
||||
ps[i++] = value;
|
||||
ps[i] = value;
|
||||
return MethodType.methodType(value, ps);
|
||||
}
|
||||
},
|
||||
GET_AND_UPDATE(Object.class) {
|
||||
@Override
|
||||
MethodType accessModeType(Class<?> receiver, Class<?> value,
|
||||
Class<?>... intermediate) {
|
||||
Class<?>[] ps = allocateParameters(1, receiver, intermediate);
|
||||
int i = fillParameters(ps, receiver, intermediate);
|
||||
ps[i] = value;
|
||||
return MethodType.methodType(value, ps);
|
||||
}
|
||||
};
|
||||
GET(Object.class),
|
||||
SET(void.class),
|
||||
COMPARE_AND_SWAP(boolean.class),
|
||||
COMPARE_AND_EXCHANGE(Object.class),
|
||||
GET_AND_UPDATE(Object.class);
|
||||
|
||||
final Class<?> returnType;
|
||||
final boolean isMonomorphicInReturnType;
|
||||
@ -1117,8 +1071,41 @@ public abstract class VarHandle {
|
||||
isMonomorphicInReturnType = returnType != Object.class;
|
||||
}
|
||||
|
||||
abstract MethodType accessModeType(Class<?> receiver, Class<?> value,
|
||||
Class<?>... intermediate);
|
||||
MethodType accessModeType(Class<?> receiver, Class<?> value,
|
||||
Class<?>... intermediate) {
|
||||
Class<?>[] ps;
|
||||
int i;
|
||||
switch (this) {
|
||||
case GET:
|
||||
ps = allocateParameters(0, receiver, intermediate);
|
||||
fillParameters(ps, receiver, intermediate);
|
||||
return MethodType.methodType(value, ps);
|
||||
case SET:
|
||||
ps = allocateParameters(1, receiver, intermediate);
|
||||
i = fillParameters(ps, receiver, intermediate);
|
||||
ps[i] = value;
|
||||
return MethodType.methodType(void.class, ps);
|
||||
case COMPARE_AND_SWAP:
|
||||
ps = allocateParameters(2, receiver, intermediate);
|
||||
i = fillParameters(ps, receiver, intermediate);
|
||||
ps[i++] = value;
|
||||
ps[i] = value;
|
||||
return MethodType.methodType(boolean.class, ps);
|
||||
case COMPARE_AND_EXCHANGE:
|
||||
ps = allocateParameters(2, receiver, intermediate);
|
||||
i = fillParameters(ps, receiver, intermediate);
|
||||
ps[i++] = value;
|
||||
ps[i] = value;
|
||||
return MethodType.methodType(value, ps);
|
||||
case GET_AND_UPDATE:
|
||||
ps = allocateParameters(1, receiver, intermediate);
|
||||
i = fillParameters(ps, receiver, intermediate);
|
||||
ps[i] = value;
|
||||
return MethodType.methodType(value, ps);
|
||||
default:
|
||||
throw new InternalError("Unknown AccessType");
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?>[] allocateParameters(int values,
|
||||
Class<?> receiver, Class<?>... intermediate) {
|
||||
|
@ -29,27 +29,32 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.EnumMap;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
public class ValueConversions {
|
||||
private static final Class<?> THIS_CLASS = ValueConversions.class;
|
||||
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
|
||||
|
||||
/** Thread-safe canonicalized mapping from Wrapper to MethodHandle
|
||||
/**
|
||||
* Thread-safe canonicalized mapping from Wrapper to MethodHandle
|
||||
* with unsynchronized reads and synchronized writes.
|
||||
* It's safe to publish MethodHandles by data race because they are immutable. */
|
||||
* It's safe to publish MethodHandles by data race because they are immutable.
|
||||
*/
|
||||
private static class WrapperCache {
|
||||
/** EnumMap uses preconstructed array internally, which is constant during it's lifetime. */
|
||||
private final EnumMap<Wrapper, MethodHandle> map = new EnumMap<>(Wrapper.class);
|
||||
@Stable
|
||||
private final MethodHandle[] map = new MethodHandle[Wrapper.COUNT];
|
||||
|
||||
public MethodHandle get(Wrapper w) {
|
||||
return map.get(w);
|
||||
return map[w.ordinal()];
|
||||
}
|
||||
public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) {
|
||||
// Simulate CAS to avoid racy duplication
|
||||
MethodHandle prev = map.putIfAbsent(w, mh);
|
||||
if (prev != null) return prev;
|
||||
return mh;
|
||||
MethodHandle prev = map[w.ordinal()];
|
||||
if (prev != null) {
|
||||
return prev;
|
||||
} else {
|
||||
map[w.ordinal()] = mh;
|
||||
return mh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -623,7 +628,7 @@ public class ValueConversions {
|
||||
return (x ? (byte)1 : (byte)0);
|
||||
}
|
||||
|
||||
private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
|
||||
private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.COUNT);
|
||||
|
||||
public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
|
||||
WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
|
||||
|
@ -42,6 +42,8 @@ public enum Wrapper {
|
||||
VOID ( Void.class, void.class, 'V', null, Format.other( 0)),
|
||||
;
|
||||
|
||||
public static final int COUNT = 10;
|
||||
|
||||
private final Class<?> wrapperType;
|
||||
private final Class<?> primitiveType;
|
||||
private final char basicTypeChar;
|
||||
@ -160,7 +162,10 @@ public enum Wrapper {
|
||||
return true;
|
||||
}
|
||||
|
||||
static { assert(checkConvertibleFrom()); }
|
||||
static {
|
||||
assert(checkConvertibleFrom());
|
||||
assert(COUNT == Wrapper.values().length);
|
||||
}
|
||||
private static boolean checkConvertibleFrom() {
|
||||
// Check the matrix for correct classification of widening conversions.
|
||||
for (Wrapper w : values()) {
|
||||
|
Loading…
Reference in New Issue
Block a user