8163370: Reduce number of classes loaded by common usage of java.lang.invoke
Reviewed-by: igerasim, psandoz
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Wed Aug 10 13:54:38 2016 +0200
@@ -515,7 +515,7 @@
// 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;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java Wed Aug 10 13:54:38 2016 +0200
@@ -60,7 +60,7 @@
}
/** 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 @@
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 @@
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 @@
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 @@
// 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 @@
}
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 @@
}
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
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 @@
}
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 @@
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;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Wed Aug 10 13:54:38 2016 +0200
@@ -1002,7 +1002,9 @@
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.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Aug 10 13:54:38 2016 +0200
@@ -3115,7 +3115,7 @@
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(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));
--- a/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Wed Aug 10 13:54:38 2016 +0200
@@ -281,8 +281,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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;
+ 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));
}
- case ARG: {
- 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;
- }
- default:
- throw new StringConcatException("Unhandled tag: " + el.getTag());
}
builder = MethodHandles.foldArguments(builder, appender);
}
@@ -1521,19 +1481,12 @@
// *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 @@
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);
-
- MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypesList, ac);
- cm = MethodHandles.dropArguments(cm, 0, int.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);
- // Read this bottom up:
+ MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypesList, ac);
+ cm = MethodHandles.dropArguments(cm, 0, int.class); // (**)
- // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
- mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
+ // Read this bottom up:
- // 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);
+ // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
+ mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
- // 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);
+ // 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);
- // 1. The mh shape here is ("old-index", "old-coder", <args>)
- break;
- }
- default:
- throw new StringConcatException("Unhandled tag: " + el.getTag());
+ // 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>)
}
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Wed Aug 10 13:54:38 2016 +0200
@@ -38,7 +38,7 @@
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/";
--- a/jdk/src/java.base/share/classes/java/lang/invoke/VarHandle.java Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/VarHandle.java Wed Aug 10 13:54:38 2016 +0200
@@ -1057,57 +1057,11 @@
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 @@
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) {
--- a/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java Wed Aug 10 13:54:38 2016 +0200
@@ -29,27 +29,32 @@
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 @@
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()];
--- a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java Wed Aug 10 13:54:38 2016 +0200
@@ -42,6 +42,8 @@
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 @@
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()) {