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,
// 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;

View File

@ -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;

View File

@ -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.

View File

@ -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));

View File

@ -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>)
}
}

View File

@ -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/";

View File

@ -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) {

View File

@ -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()];

View File

@ -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()) {