< 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,290 **** acc.setLength(0); } if (c == TAG_CONST) { Object cnst = constants[constC++]; el.add(new RecipeElement(cnst)); ! } ! if (c == TAG_ARG) { el.add(new RecipeElement(argC++)); } } else { // Not a special characters, this is a constant embedded into // the recipe itself. --- 279,289 ---- acc.setLength(0); } if (c == TAG_CONST) { Object cnst = constants[constC++]; el.add(new RecipeElement(cnst)); ! } 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,378 **** } 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; this.argPos = arg; - this.tag = Tag.ARG; } public Object getValue() { ! assert (tag == Tag.CONST); return value; } public int getArgPos() { ! assert (tag == Tag.ARG); return argPos; } ! public Tag getTag() { ! return tag; } @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; return true; } @Override public int hashCode() { ! return tag.hashCode(); ! } } - - 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 --- 319,372 ---- } private static final class RecipeElement { private final Object value; private final int argPos; public RecipeElement(Object cnst) { this.value = Objects.requireNonNull(cnst); this.argPos = -1; } public RecipeElement(int arg) { this.value = null; + assert (arg >= 0); this.argPos = arg; } public Object getValue() { ! assert (isConst()); return value; } public int getArgPos() { ! assert (!isConst()); return argPos; } ! 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; ! 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 argPos; } } /** * Facilitates the creation of optimized String concatenation methods, that * can be used to efficiently concatenate a known number of arguments of
*** 878,893 **** 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: { // Guaranteed non-null, no null check required. ! break; ! } ! 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(); --- 872,884 ---- need to do null-checks early, not make the append chain shape simpler. */ int off = 0; for (RecipeElement el : recipe.getElements()) { ! 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();
*** 899,912 **** 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 --- 890,899 ----
*** 923,939 **** int off = 0; mv.visitInsn(ICONST_0); for (RecipeElement el : recipe.getElements()) { ! switch (el.getTag()) { ! case CONST: { Object cnst = el.getValue(); len += cnst.toString().length(); ! break; ! } ! 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. */ --- 910,923 ---- int off = 0; mv.visitInsn(ICONST_0); for (RecipeElement el : recipe.getElements()) { ! 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. */
*** 950,963 **** 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) { --- 934,943 ----
*** 985,1011 **** // 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: { 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()); } mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/StringBuilder", "append", desc, --- 965,985 ---- // 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; ! 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", "append", desc,
*** 1277,1306 **** // 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: { Object cnst = el.getValue(); initial += cnst.toString().length(); ! break; ! } ! 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()); } } // Create (StringBuilder, <args>) shape for appending: MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypesList); --- 1251,1273 ---- // 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()) { ! 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; } } } // Create (StringBuilder, <args>) shape for appending: MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypesList);
*** 1309,1338 **** // 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: { 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)); // 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: --- 1276,1298 ---- // reverse as well. List<RecipeElement> elements = recipe.getElements(); for (int i = elements.size() - 1; i >= 0; i--) { RecipeElement el = elements.get(i); MethodHandle appender; ! 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)); } } builder = MethodHandles.foldArguments(builder, appender); } // Build the sub-tree that adds the sizes and produces a StringBuilder:
*** 1519,1541 **** // 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: { 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()); } // Remove "old" index from arguments mh = MethodHandles.dropArguments(mh, 1, int.class); --- 1479,1494 ---- // 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; ! 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 mh = MethodHandles.dropArguments(mh, 1, int.class);
*** 1571,1589 **** // 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: { 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(); Class<?> argClass = ptypesList.get(ac); MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac); lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*) --- 1524,1539 ---- // 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()) { ! 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); // (*)
*** 1604,1617 **** // 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>). --- 1554,1563 ----
< prev index next >