< prev index next >
src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
Print this page
rev 15320 : 8163370: Reduce number of classes loaded by common usage of java.lang.invoke
Reviewed-by: igerasim, psandoz
@@ -279,12 +279,11 @@
acc.setLength(0);
}
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 {
// Not a special characters, this is a constant embedded into
// the recipe itself.
@@ -320,59 +319,54 @@
}
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
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
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
@@ -878,16 +872,13 @@
need to do null-checks early, not make the append chain shape simpler.
*/
int off = 0;
for (RecipeElement el : recipe.getElements()) {
- switch (el.getTag()) {
- case CONST: {
+ if (el.isConst()) {
// Guaranteed non-null, no null check required.
- break;
- }
- case ARG: {
+ } 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();
@@ -899,14 +890,10 @@
mv.visitLdcInsn("null");
mv.visitIntInsn(ASTORE, off);
mv.visitLabel(l0);
}
off += getParameterSize(cl);
- break;
- }
- default:
- throw new StringConcatException("Unhandled tag: " + el.getTag());
}
}
}
// Prepare StringBuilder instance
@@ -923,17 +910,14 @@
int off = 0;
mv.visitInsn(ICONST_0);
for (RecipeElement el : recipe.getElements()) {
- switch (el.getTag()) {
- case CONST: {
+ if (el.isConst()) {
Object cnst = el.getValue();
len += cnst.toString().length();
- break;
- }
- case ARG: {
+ } 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.
*/
@@ -950,14 +934,10 @@
mv.visitInsn(IADD);
} else if (cl.isPrimitive()) {
len += estimateSize(cl);
}
off += getParameterSize(cl);
- break;
- }
- default:
- throw new StringConcatException("Unhandled tag: " + el.getTag());
}
}
// Constants have non-zero length, mix in
if (len > 0) {
@@ -985,27 +965,21 @@
// At this point, we have a blank StringBuilder on stack, fill it in with .append calls.
{
int off = 0;
for (RecipeElement el : recipe.getElements()) {
String desc;
- switch (el.getTag()) {
- case CONST: {
+ if (el.isConst()) {
Object cnst = el.getValue();
mv.visitLdcInsn(cnst);
desc = getSBAppendDesc(cnst.getClass());
- break;
- }
- case ARG: {
+ } else {
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());
}
+
mv.visitMethodInsn(
INVOKEVIRTUAL,
"java/lang/StringBuilder",
"append",
desc,
@@ -1277,30 +1251,23 @@
// Figure out lengths: constants' lengths can be deduced on the spot.
// All reference arguments were filtered to String in the combinators below, so we can
// 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: {
+ if (el.isConst()) {
Object cnst = el.getValue();
initial += cnst.toString().length();
- break;
- }
- case ARG: {
+ } 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;
}
- break;
- }
- default:
- throw new StringConcatException("Unhandled tag: " + el.getTag());
}
}
// Create (StringBuilder, <args>) shape for appending:
MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypesList);
@@ -1309,30 +1276,23 @@
// reverse as well.
List<RecipeElement> elements = recipe.getElements();
for (int i = elements.size() - 1; i >= 0; i--) {
RecipeElement el = elements.get(i);
MethodHandle appender;
- switch (el.getTag()) {
- case CONST: {
+ if (el.isConst()) {
Object constant = el.getValue();
MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
appender = MethodHandles.insertArguments(mh, 1, constant);
- break;
- }
- case ARG: {
+ } 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;
- }
- default:
- throw new StringConcatException("Unhandled tag: " + el.getTag());
}
builder = MethodHandles.foldArguments(builder, appender);
}
// Build the sub-tree that adds the sizes and produces a StringBuilder:
@@ -1519,23 +1479,16 @@
// Mix in prependers. This happens when (int, byte[], byte) = (index, storage, coder) is already
// known from the combinators below. We are assembling the string backwards, so "index" is the
// *ending* index.
for (RecipeElement el : recipe.getElements()) {
MethodHandle prepender;
- switch (el.getTag()) {
- case CONST: {
+ if (el.isConst()) {
Object cnst = el.getValue();
prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
- break;
- }
- case ARG: {
+ } else {
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
mh = MethodHandles.dropArguments(mh, 1, int.class);
@@ -1571,19 +1524,16 @@
// The method handle shape after all length and coder mixers is:
// (int, byte, <args>)String = ("index", "coder", <args>)
byte initialCoder = INITIAL_CODER;
int initialLen = 0; // initial length, in characters
for (RecipeElement el : recipe.getElements()) {
- switch (el.getTag()) {
- case CONST: {
+ if (el.isConst()) {
Object constant = el.getValue();
String s = constant.toString();
initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
initialLen += s.length();
- break;
- }
- case ARG: {
+ } 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); // (*)
@@ -1604,14 +1554,10 @@
// 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());
}
}
// Insert initial lengths and coders here.
// The method handle shape here is (<args>).
< prev index next >