8163370: Reduce number of classes loaded by common usage of java.lang.invoke

Reviewed-by: igerasim, psandoz
This commit is contained in:
Claes Redestad 2016-08-10 13:54:38 +02:00
parent 23853ed3e4
commit bb95ea6101
9 changed files with 224 additions and 293 deletions

View File

@ -515,7 +515,7 @@ class DirectMethodHandle extends MethodHandle {
// Enumerate the different field kinds using Wrapper, // Enumerate the different field kinds using Wrapper,
// with an extra case added for checked references. // with an extra case added for checked references.
private static final int private static final int
FT_LAST_WRAPPER = Wrapper.values().length-1, FT_LAST_WRAPPER = Wrapper.COUNT-1,
FT_UNCHECKED_REF = Wrapper.OBJECT.ordinal(), FT_UNCHECKED_REF = Wrapper.OBJECT.ordinal(),
FT_CHECKED_REF = FT_LAST_WRAPPER+1, FT_CHECKED_REF = FT_LAST_WRAPPER+1,
FT_LIMIT = FT_LAST_WRAPPER+2; FT_LIMIT = FT_LAST_WRAPPER+2;

View File

@ -60,7 +60,7 @@ class LambdaFormEditor {
} }
/** A description of a cached transform, possibly associated with the result of the transform. /** 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. * 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. * 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 long packedBytes;
final byte[] fullBytes; final byte[] fullBytes;
private enum Kind { // maybe add more for guard with test, catch exception, pointwise type conversions
NO_KIND, // necessary because ordinal must be greater than zero private static final byte
BIND_ARG, ADD_ARG, DUP_ARG, BIND_ARG = 1,
SPREAD_ARGS, ADD_ARG = 2,
FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO, DUP_ARG = 3,
COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY, SPREAD_ARGS = 4,
FOLD_ARGS, FOLD_ARGS_TO_VOID, FILTER_ARG = 5,
PERMUTE_ARGS, FILTER_RETURN = 6,
LOCAL_TYPES FILTER_RETURN_TO_ZERO = 7,
//maybe add more for guard with test, catch exception, pointwise type conversions 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 boolean STRESS_TEST = false; // turn on to disable most packing
private static final int private static final int
@ -131,20 +136,6 @@ class LambdaFormEditor {
return bytes; 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) { private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
super(result); super(result);
this.packedBytes = packedBytes; this.packedBytes = packedBytes;
@ -162,44 +153,39 @@ class LambdaFormEditor {
assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte
return (byte)b; return (byte)b;
} }
private static byte bval(Kind k) { static Transform of(byte k, int b1) {
return bval(k.ordinal());
}
static Transform of(Kind k, int b1) {
byte b0 = bval(k); byte b0 = bval(k);
if (inRange(b0 | b1)) if (inRange(b0 | b1))
return new Transform(packedBytes(b0, b1)); return new Transform(packedBytes(b0, b1));
else else
return new Transform(fullBytes(b0, b1)); return new Transform(fullBytes(b0, b1));
} }
static Transform of(Kind k, int b1, int b2) { static Transform of(byte b0, int b1, int b2) {
byte b0 = (byte) k.ordinal();
if (inRange(b0 | b1 | b2)) if (inRange(b0 | b1 | b2))
return new Transform(packedBytes(b0, b1, b2)); return new Transform(packedBytes(b0, b1, b2));
else else
return new Transform(fullBytes(b0, b1, b2)); return new Transform(fullBytes(b0, b1, b2));
} }
static Transform of(Kind k, int b1, int b2, int b3) { static Transform of(byte b0, int b1, int b2, int b3) {
byte b0 = (byte) k.ordinal();
if (inRange(b0 | b1 | b2 | b3)) if (inRange(b0 | b1 | b2 | b3))
return new Transform(packedBytes(b0, b1, b2, b3)); return new Transform(packedBytes(b0, b1, b2, b3));
else else
return new Transform(fullBytes(b0, b1, b2, b3)); return new Transform(fullBytes(b0, b1, b2, b3));
} }
private static final byte[] NO_BYTES = {}; private static final byte[] NO_BYTES = {};
static Transform of(Kind k, int... b123) { static Transform of(byte kind, int... b123) {
return ofBothArrays(k, b123, NO_BYTES); return ofBothArrays(kind, b123, NO_BYTES);
} }
static Transform of(Kind k, int b1, byte[] b234) { static Transform of(byte kind, int b1, byte[] b234) {
return ofBothArrays(k, new int[]{ b1 }, b234); return ofBothArrays(kind, new int[]{ b1 }, b234);
} }
static Transform of(Kind k, int b1, int b2, byte[] b345) { static Transform of(byte kind, int b1, int b2, byte[] b345) {
return ofBothArrays(k, new int[]{ b1, b2 }, 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]; byte[] fullBytes = new byte[1 + b123.length + b456.length];
int i = 0; int i = 0;
fullBytes[i++] = bval(k); fullBytes[i++] = bval(kind);
for (int bv : b123) { for (int bv : b123) {
fullBytes[i++] = bval(bv); 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. // Each editing method can (potentially) cache the edited LF so that it can be reused later.
LambdaForm bindArgumentForm(int pos) { 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); LambdaForm form = getInCache(key);
if (form != null) { if (form != null) {
assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos))); assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos)));
@ -484,7 +470,7 @@ class LambdaFormEditor {
} }
LambdaForm addArgumentForm(int pos, BasicType type) { 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); LambdaForm form = getInCache(key);
if (form != null) { if (form != null) {
assert(form.arity == lambdaForm.arity+1); assert(form.arity == lambdaForm.arity+1);
@ -501,7 +487,7 @@ class LambdaFormEditor {
} }
LambdaForm dupArgumentForm(int srcPos, int dstPos) { 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); LambdaForm form = getInCache(key);
if (form != null) { if (form != null) {
assert(form.arity == lambdaForm.arity-1); assert(form.arity == lambdaForm.arity-1);
@ -530,7 +516,7 @@ class LambdaFormEditor {
elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal(); 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); LambdaForm form = getInCache(key);
if (form != null) { if (form != null) {
assert(form.arity == lambdaForm.arity - arrayLength + 1); assert(form.arity == lambdaForm.arity - arrayLength + 1);
@ -569,9 +555,9 @@ class LambdaFormEditor {
return filterArgumentForm(pos, basicType(collectorType.parameterType(0))); return filterArgumentForm(pos, basicType(collectorType.parameterType(0)));
} }
byte[] newTypes = BasicType.basicTypesOrd(collectorType.parameterArray()); byte[] newTypes = BasicType.basicTypesOrd(collectorType.parameterArray());
Transform.Kind kind = (dropResult byte kind = (dropResult
? Transform.Kind.COLLECT_ARGS_TO_VOID ? Transform.COLLECT_ARGS_TO_VOID
: Transform.Kind.COLLECT_ARGS); : Transform.COLLECT_ARGS);
if (dropResult && collectorArity == 0) pos = 1; // pure side effect if (dropResult && collectorArity == 0) pos = 1; // pure side effect
Transform key = Transform.of(kind, pos, collectorArity, newTypes); Transform key = Transform.of(kind, pos, collectorArity, newTypes);
LambdaForm form = getInCache(key); LambdaForm form = getInCache(key);
@ -598,7 +584,7 @@ class LambdaFormEditor {
argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal(); argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
} }
assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType))); 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); Transform key = Transform.of(kind, pos, collectorArity, argTypeKey);
LambdaForm form = getInCache(key); LambdaForm form = getInCache(key);
if (form != null) { if (form != null) {
@ -634,7 +620,7 @@ class LambdaFormEditor {
} }
LambdaForm filterArgumentForm(int pos, BasicType newType) { 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); LambdaForm form = getInCache(key);
if (form != null) { if (form != null) {
assert(form.arity == lambdaForm.arity); assert(form.arity == lambdaForm.arity);
@ -710,7 +696,7 @@ class LambdaFormEditor {
} }
LambdaForm filterReturnForm(BasicType newType, boolean constantZero) { 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()); Transform key = Transform.of(kind, newType.ordinal());
LambdaForm form = getInCache(key); LambdaForm form = getInCache(key);
if (form != null) { if (form != null) {
@ -762,11 +748,11 @@ class LambdaFormEditor {
LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) { LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) {
int combinerArity = combinerType.parameterCount(); 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); Transform key = Transform.of(kind, foldPos, combinerArity);
LambdaForm form = getInCache(key); LambdaForm form = getInCache(key);
if (form != null) { 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; return form;
} }
form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult); form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult);
@ -786,7 +772,7 @@ class LambdaFormEditor {
} }
assert(skip + reorder.length == lambdaForm.arity); assert(skip + reorder.length == lambdaForm.arity);
if (nullPerm) return lambdaForm; // do not bother to cache 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); LambdaForm form = getInCache(key);
if (form != null) { if (form != null) {
assert(form.arity == skip+inTypes) : form; assert(form.arity == skip+inTypes) : form;
@ -855,7 +841,7 @@ class LambdaFormEditor {
int[] desc = BasicType.basicTypeOrds(localTypes); int[] desc = BasicType.basicTypeOrds(localTypes);
desc = Arrays.copyOf(desc, desc.length + 1); desc = Arrays.copyOf(desc, desc.length + 1);
desc[desc.length - 1] = pos; 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); LambdaForm form = getInCache(key);
if (form != null) { if (form != null) {
return form; return form;

View File

@ -1002,7 +1002,9 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
Collections.addAll(result, buf0); 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 // Signature matching is not the same as type matching, since
// one signature might correspond to several types. // one signature might correspond to several types.
// So if matchType is a Class or MethodType, refilter the results. // So if matchType is a Class or MethodType, refilter the results.

View File

@ -3115,7 +3115,7 @@ assert((int)twice.invokeExact(21) == 42);
return dropArguments(zero(type.returnType()), 0, type.parameterList()); 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) { private static MethodHandle makeIdentity(Class<?> ptype) {
MethodType mtype = methodType(ptype, ptype); MethodType mtype = methodType(ptype, ptype);
LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype)); LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype));
@ -3133,7 +3133,7 @@ assert((int)twice.invokeExact(21) == 42);
assert(btw == Wrapper.OBJECT); assert(btw == Wrapper.OBJECT);
return makeZero(rtype); 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) { private static MethodHandle makeZero(Class<?> rtype) {
MethodType mtype = methodType(rtype); MethodType mtype = methodType(rtype);
LambdaForm lform = LambdaForm.zeroForm(BasicType.basicType(rtype)); LambdaForm lform = LambdaForm.zeroForm(BasicType.basicType(rtype));

View File

@ -281,8 +281,7 @@ public final class StringConcatFactory {
if (c == TAG_CONST) { if (c == TAG_CONST) {
Object cnst = constants[constC++]; Object cnst = constants[constC++];
el.add(new RecipeElement(cnst)); el.add(new RecipeElement(cnst));
} } else if (c == TAG_ARG) {
if (c == TAG_ARG) {
el.add(new RecipeElement(argC++)); el.add(new RecipeElement(argC++));
} }
} else { } else {
@ -322,32 +321,30 @@ public final class StringConcatFactory {
private static final class RecipeElement { private static final class RecipeElement {
private final Object value; private final Object value;
private final int argPos; private final int argPos;
private final Tag tag;
public RecipeElement(Object cnst) { public RecipeElement(Object cnst) {
this.value = Objects.requireNonNull(cnst); this.value = Objects.requireNonNull(cnst);
this.argPos = -1; this.argPos = -1;
this.tag = Tag.CONST;
} }
public RecipeElement(int arg) { public RecipeElement(int arg) {
this.value = null; this.value = null;
assert (arg >= 0);
this.argPos = arg; this.argPos = arg;
this.tag = Tag.ARG;
} }
public Object getValue() { public Object getValue() {
assert (tag == Tag.CONST); assert (isConst());
return value; return value;
} }
public int getArgPos() { public int getArgPos() {
assert (tag == Tag.ARG); assert (!isConst());
return argPos; return argPos;
} }
public Tag getTag() { public boolean isConst() {
return tag; return argPos == -1;
} }
@Override @Override
@ -357,22 +354,19 @@ public final class StringConcatFactory {
RecipeElement that = (RecipeElement) o; RecipeElement that = (RecipeElement) o;
if (tag != that.tag) return false; boolean isConst = isConst();
if (tag == Tag.CONST && (!value.equals(that.value))) return false; if (isConst != that.isConst()) return false;
if (tag == Tag.ARG && (argPos != that.argPos)) return false; if (isConst && (!value.equals(that.value))) return false;
if (!isConst && (argPos != that.argPos)) return false;
return true; return true;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return tag.hashCode(); return argPos;
} }
} }
private enum Tag {
CONST, ARG
}
/** /**
* Facilitates the creation of optimized String concatenation methods, that * Facilitates the creation of optimized String concatenation methods, that
* can be used to efficiently concatenate a known number of arguments of * can be used to efficiently concatenate a known number of arguments of
@ -880,31 +874,24 @@ public final class StringConcatFactory {
int off = 0; int off = 0;
for (RecipeElement el : recipe.getElements()) { for (RecipeElement el : recipe.getElements()) {
switch (el.getTag()) { if (el.isConst()) {
case CONST: { // Guaranteed non-null, no null check required.
// Guaranteed non-null, no null check required. } else {
break; // 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: { off += getParameterSize(cl);
// 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());
} }
} }
} }
@ -925,37 +912,30 @@ public final class StringConcatFactory {
mv.visitInsn(ICONST_0); mv.visitInsn(ICONST_0);
for (RecipeElement el : recipe.getElements()) { for (RecipeElement el : recipe.getElements()) {
switch (el.getTag()) { if (el.isConst()) {
case CONST: { Object cnst = el.getValue();
Object cnst = el.getValue(); len += cnst.toString().length();
len += cnst.toString().length(); } else {
break; /*
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: { off += getParameterSize(cl);
/*
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());
} }
} }
@ -987,23 +967,17 @@ public final class StringConcatFactory {
int off = 0; int off = 0;
for (RecipeElement el : recipe.getElements()) { for (RecipeElement el : recipe.getElements()) {
String desc; String desc;
switch (el.getTag()) { if (el.isConst()) {
case CONST: { Object cnst = el.getValue();
Object cnst = el.getValue(); mv.visitLdcInsn(cnst);
mv.visitLdcInsn(cnst); desc = getSBAppendDesc(cnst.getClass());
desc = getSBAppendDesc(cnst.getClass()); } else {
break; Class<?> cl = arr[el.getArgPos()];
} mv.visitVarInsn(getLoadOpcode(cl), off);
case ARG: { off += getParameterSize(cl);
Class<?> cl = arr[el.getArgPos()]; desc = getSBAppendDesc(cl);
mv.visitVarInsn(getLoadOpcode(cl), off);
off += getParameterSize(cl);
desc = getSBAppendDesc(cl);
break;
}
default:
throw new StringConcatException("Unhandled tag: " + el.getTag());
} }
mv.visitMethodInsn( mv.visitMethodInsn(
INVOKEVIRTUAL, INVOKEVIRTUAL,
"java/lang/StringBuilder", "java/lang/StringBuilder",
@ -1279,26 +1253,19 @@ public final class StringConcatFactory {
// call the usual String.length(). Primitive values string sizes can be estimated. // call the usual String.length(). Primitive values string sizes can be estimated.
int initial = 0; int initial = 0;
for (RecipeElement el : recipe.getElements()) { for (RecipeElement el : recipe.getElements()) {
switch (el.getTag()) { if (el.isConst()) {
case CONST: { Object cnst = el.getValue();
Object cnst = el.getValue(); initial += cnst.toString().length();
initial += cnst.toString().length(); } else {
break; 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--) { for (int i = elements.size() - 1; i >= 0; i--) {
RecipeElement el = elements.get(i); RecipeElement el = elements.get(i);
MethodHandle appender; MethodHandle appender;
switch (el.getTag()) { if (el.isConst()) {
case CONST: { Object constant = el.getValue();
Object constant = el.getValue(); MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
MethodHandle mh = appender(adaptToStringBuilder(constant.getClass())); appender = MethodHandles.insertArguments(mh, 1, constant);
appender = MethodHandles.insertArguments(mh, 1, constant); } else {
break; int ac = el.getArgPos();
} appender = appender(ptypesList.get(ac));
case ARG: {
int ac = el.getArgPos();
appender = appender(ptypesList.get(ac));
// Insert dummy arguments to match the prefix in the signature. // Insert dummy arguments to match the prefix in the signature.
// The actual appender argument will be the ac-ith argument. // The actual appender argument will be the ac-ith argument.
if (ac != 0) { if (ac != 0) {
appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac)); appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac));
}
break;
} }
default:
throw new StringConcatException("Unhandled tag: " + el.getTag());
} }
builder = MethodHandles.foldArguments(builder, appender); builder = MethodHandles.foldArguments(builder, appender);
} }
@ -1521,19 +1481,12 @@ public final class StringConcatFactory {
// *ending* index. // *ending* index.
for (RecipeElement el : recipe.getElements()) { for (RecipeElement el : recipe.getElements()) {
MethodHandle prepender; MethodHandle prepender;
switch (el.getTag()) { if (el.isConst()) {
case CONST: { Object cnst = el.getValue();
Object cnst = el.getValue(); prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst); } else {
break; int pos = el.getArgPos();
} prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos);
case ARG: {
int pos = el.getArgPos();
prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos);
break;
}
default:
throw new StringConcatException("Unhandled tag: " + el.getTag());
} }
// Remove "old" index from arguments // Remove "old" index from arguments
@ -1573,43 +1526,36 @@ public final class StringConcatFactory {
byte initialCoder = INITIAL_CODER; byte initialCoder = INITIAL_CODER;
int initialLen = 0; // initial length, in characters int initialLen = 0; // initial length, in characters
for (RecipeElement el : recipe.getElements()) { for (RecipeElement el : recipe.getElements()) {
switch (el.getTag()) { if (el.isConst()) {
case CONST: { Object constant = el.getValue();
Object constant = el.getValue(); String s = constant.toString();
String s = constant.toString(); initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s); initialLen += s.length();
initialLen += s.length(); } else {
break; int ac = el.getArgPos();
}
case ARG: {
int ac = el.getArgPos();
Class<?> argClass = ptypesList.get(ac); Class<?> argClass = ptypesList.get(ac);
MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac); MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*) lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
lm = MethodHandles.dropArguments(lm, 2, byte.class); lm = MethodHandles.dropArguments(lm, 2, byte.class);
MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypesList, ac); MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypesList, ac);
cm = MethodHandles.dropArguments(cm, 0, int.class); // (**) 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>) // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class); mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
// 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>) // 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 (*) // Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
mh = MethodHandles.foldArguments(mh, lm); mh = MethodHandles.foldArguments(mh, lm);
// 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>) // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
// Coder mixer ignores the "old-index" arg due to dropArguments above (**) // Coder mixer ignores the "old-index" arg due to dropArguments above (**)
mh = MethodHandles.foldArguments(mh, cm); mh = MethodHandles.foldArguments(mh, cm);
// 1. The mh shape here is ("old-index", "old-coder", <args>) // 1. The mh shape here is ("old-index", "old-coder", <args>)
break;
}
default:
throw new StringConcatException("Unhandled tag: " + el.getTag());
} }
} }

View File

@ -38,7 +38,7 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
super(Opcodes.ASM5, mv); 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 NAME_OBJECT = "java/lang/Object";
private static final String WRAPPER_PREFIX = "Ljava/lang/"; private static final String WRAPPER_PREFIX = "Ljava/lang/";

View File

@ -1057,57 +1057,11 @@ public abstract class VarHandle {
Object addAndGet(Object... args); Object addAndGet(Object... args);
enum AccessType { enum AccessType {
GET(Object.class) { GET(Object.class),
@Override SET(void.class),
MethodType accessModeType(Class<?> receiver, Class<?> value, COMPARE_AND_SWAP(boolean.class),
Class<?>... intermediate) { COMPARE_AND_EXCHANGE(Object.class),
Class<?>[] ps = allocateParameters(0, receiver, intermediate); GET_AND_UPDATE(Object.class);
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);
}
};
final Class<?> returnType; final Class<?> returnType;
final boolean isMonomorphicInReturnType; final boolean isMonomorphicInReturnType;
@ -1117,8 +1071,41 @@ public abstract class VarHandle {
isMonomorphicInReturnType = returnType != Object.class; isMonomorphicInReturnType = returnType != Object.class;
} }
abstract MethodType accessModeType(Class<?> receiver, Class<?> value, MethodType accessModeType(Class<?> receiver, Class<?> value,
Class<?>... intermediate); 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, private static Class<?>[] allocateParameters(int values,
Class<?> receiver, Class<?>... intermediate) { Class<?> receiver, Class<?>... intermediate) {

View File

@ -29,27 +29,32 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.util.EnumMap; import jdk.internal.vm.annotation.Stable;
public class ValueConversions { public class ValueConversions {
private static final Class<?> THIS_CLASS = ValueConversions.class; private static final Class<?> THIS_CLASS = ValueConversions.class;
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); 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. * 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 { private static class WrapperCache {
/** EnumMap uses preconstructed array internally, which is constant during it's lifetime. */ @Stable
private final EnumMap<Wrapper, MethodHandle> map = new EnumMap<>(Wrapper.class); private final MethodHandle[] map = new MethodHandle[Wrapper.COUNT];
public MethodHandle get(Wrapper w) { public MethodHandle get(Wrapper w) {
return map.get(w); return map[w.ordinal()];
} }
public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) { public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) {
// Simulate CAS to avoid racy duplication MethodHandle prev = map[w.ordinal()];
MethodHandle prev = map.putIfAbsent(w, mh); if (prev != null) {
if (prev != null) return prev; return prev;
return mh; } else {
map[w.ordinal()] = mh;
return mh;
}
} }
} }
@ -623,7 +628,7 @@ public class ValueConversions {
return (x ? (byte)1 : (byte)0); 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) { public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()]; WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];

View File

@ -42,6 +42,8 @@ public enum Wrapper {
VOID ( Void.class, void.class, 'V', null, Format.other( 0)), VOID ( Void.class, void.class, 'V', null, Format.other( 0)),
; ;
public static final int COUNT = 10;
private final Class<?> wrapperType; private final Class<?> wrapperType;
private final Class<?> primitiveType; private final Class<?> primitiveType;
private final char basicTypeChar; private final char basicTypeChar;
@ -160,7 +162,10 @@ public enum Wrapper {
return true; return true;
} }
static { assert(checkConvertibleFrom()); } static {
assert(checkConvertibleFrom());
assert(COUNT == Wrapper.values().length);
}
private static boolean checkConvertibleFrom() { private static boolean checkConvertibleFrom() {
// Check the matrix for correct classification of widening conversions. // Check the matrix for correct classification of widening conversions.
for (Wrapper w : values()) { for (Wrapper w : values()) {