From e122f4dd0d00b6b7d95e5af118af72db2dfdcc85 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 23 Sep 2025 09:46:56 +0000 Subject: [PATCH] 8368024: Remove StringConcatFactory#generateMHInlineCopy Reviewed-by: redestad --- .../share/classes/java/lang/String.java | 2 +- .../classes/java/lang/StringConcatHelper.java | 306 +----------- .../share/classes/java/lang/System.java | 12 - .../java/lang/invoke/StringConcatFactory.java | 450 ------------------ .../jdk/internal/access/JavaLangAccess.java | 15 - 5 files changed, 3 insertions(+), 782 deletions(-) diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 24ead22e283..a18ac3250dc 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -3710,7 +3710,7 @@ public final class String if (len < 0L || (len <<= coder) != (int) len) { throw new OutOfMemoryError("Requested string length exceeds VM limit"); } - byte[] value = StringConcatHelper.newArray(len); + byte[] value = StringConcatHelper.newArray((int) len); int off = 0; prefix.getBytes(value, off, coder); off += prefix.length(); diff --git a/src/java.base/share/classes/java/lang/StringConcatHelper.java b/src/java.base/share/classes/java/lang/StringConcatHelper.java index a5f6abbfdf1..aeff494cabb 100644 --- a/src/java.base/share/classes/java/lang/StringConcatHelper.java +++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java @@ -141,262 +141,6 @@ final class StringConcatHelper { // no instantiation } - /** - * Return the coder for the character. - * @param value character - * @return coder - */ - static long coder(char value) { - return StringLatin1.canEncode(value) ? LATIN1 : UTF16; - } - - /** - * Check for overflow, throw exception on overflow. - * - * @param lengthCoder String length with coder packed into higher bits - * the upper word. - * @return the given parameter value, if valid - */ - private static long checkOverflow(long lengthCoder) { - if ((int)lengthCoder >= 0) { - return lengthCoder; - } - throw new OutOfMemoryError("Overflow: String length out of range"); - } - - /** - * Mix value length and coder into current length and coder. - * @param lengthCoder String length with coder packed into higher bits - * the upper word. - * @param value value to mix in - * @return new length and coder - */ - static long mix(long lengthCoder, boolean value) { - return checkOverflow(lengthCoder + (value ? 4 : 5)); - } - - /** - * Mix value length and coder into current length and coder. - * @param lengthCoder String length with coder packed into higher bits - * the upper word. - * @param value value to mix in - * @return new length and coder - */ - static long mix(long lengthCoder, char value) { - return checkOverflow(lengthCoder + 1) | coder(value); - } - - /** - * Mix value length and coder into current length and coder. - * @param lengthCoder String length with coder packed into higher bits - * the upper word. - * @param value value to mix in - * @return new length and coder - */ - static long mix(long lengthCoder, int value) { - return checkOverflow(lengthCoder + DecimalDigits.stringSize(value)); - } - - /** - * Mix value length and coder into current length and coder. - * @param lengthCoder String length with coder packed into higher bits - * the upper word. - * @param value value to mix in - * @return new length and coder - */ - static long mix(long lengthCoder, long value) { - return checkOverflow(lengthCoder + DecimalDigits.stringSize(value)); - } - - /** - * Mix value length and coder into current length and coder. - * @param lengthCoder String length with coder packed into higher bits - * the upper word. - * @param value value to mix in - * @return new length and coder - */ - static long mix(long lengthCoder, String value) { - lengthCoder += value.length(); - if (!value.isLatin1()) { - lengthCoder |= UTF16; - } - return checkOverflow(lengthCoder); - } - - /** - * Prepends constant and the stringly representation of value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to - * @param value boolean value to encode - * @param prefix a constant to prepend before value - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, boolean value, String prefix) { - int index = (int)indexCoder; - if (indexCoder < UTF16) { - if (value) { - index -= 4; - buf[index] = 't'; - buf[index + 1] = 'r'; - buf[index + 2] = 'u'; - buf[index + 3] = 'e'; - } else { - index -= 5; - buf[index] = 'f'; - buf[index + 1] = 'a'; - buf[index + 2] = 'l'; - buf[index + 3] = 's'; - buf[index + 4] = 'e'; - } - index -= prefix.length(); - prefix.getBytes(buf, index, String.LATIN1); - return index; - } else { - if (value) { - index -= 4; - StringUTF16.putChar(buf, index, 't'); - StringUTF16.putChar(buf, index + 1, 'r'); - StringUTF16.putChar(buf, index + 2, 'u'); - StringUTF16.putChar(buf, index + 3, 'e'); - } else { - index -= 5; - StringUTF16.putChar(buf, index, 'f'); - StringUTF16.putChar(buf, index + 1, 'a'); - StringUTF16.putChar(buf, index + 2, 'l'); - StringUTF16.putChar(buf, index + 3, 's'); - StringUTF16.putChar(buf, index + 4, 'e'); - } - index -= prefix.length(); - prefix.getBytes(buf, index, String.UTF16); - return index | UTF16; - } - } - - /** - * Prepends constant and the stringly representation of value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to - * @param value char value to encode - * @param prefix a constant to prepend before value - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, char value, String prefix) { - int index = (int)indexCoder; - if (indexCoder < UTF16) { - buf[--index] = (byte) (value & 0xFF); - index -= prefix.length(); - prefix.getBytes(buf, index, String.LATIN1); - return index; - } else { - StringUTF16.putChar(buf, --index, value); - index -= prefix.length(); - prefix.getBytes(buf, index, String.UTF16); - return index | UTF16; - } - } - - /** - * Prepends constant and the stringly representation of value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to - * @param value int value to encode - * @param prefix a constant to prepend before value - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, int value, String prefix) { - int index = (int)indexCoder; - if (indexCoder < UTF16) { - index = DecimalDigits.uncheckedGetCharsLatin1(value, index, buf); - index -= prefix.length(); - prefix.getBytes(buf, index, String.LATIN1); - return index; - } else { - index = DecimalDigits.uncheckedGetCharsUTF16(value, index, buf); - index -= prefix.length(); - prefix.getBytes(buf, index, String.UTF16); - return index | UTF16; - } - } - - /** - * Prepends constant and the stringly representation of value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to - * @param value long value to encode - * @param prefix a constant to prepend before value - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, long value, String prefix) { - int index = (int)indexCoder; - if (indexCoder < UTF16) { - index = DecimalDigits.uncheckedGetCharsLatin1(value, index, buf); - index -= prefix.length(); - prefix.getBytes(buf, index, String.LATIN1); - return index; - } else { - index = DecimalDigits.uncheckedGetCharsUTF16(value, index, buf); - index -= prefix.length(); - prefix.getBytes(buf, index, String.UTF16); - return index | UTF16; - } - } - - /** - * Prepends constant and the stringly representation of value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to - * @param value boolean value to encode - * @param prefix a constant to prepend before value - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, String value, String prefix) { - int index = ((int)indexCoder) - value.length(); - if (indexCoder < UTF16) { - value.getBytes(buf, index, String.LATIN1); - index -= prefix.length(); - prefix.getBytes(buf, index, String.LATIN1); - return index; - } else { - value.getBytes(buf, index, String.UTF16); - index -= prefix.length(); - prefix.getBytes(buf, index, String.UTF16); - return index | UTF16; - } - } - - /** - * Instantiates the String with given buffer and coder - * @param buf buffer to use - * @param indexCoder remaining index (should be zero) and coder - * @return String resulting string - */ - static String newString(byte[] buf, long indexCoder) { - // Use the private, non-copying constructor (unsafe!) - if (indexCoder == LATIN1) { - return new String(buf, String.LATIN1); - } else if (indexCoder == UTF16) { - return new String(buf, String.UTF16); - } else { - throw new InternalError("Storage is not completely initialized, " + - (int)indexCoder + " bytes left"); - } - } - /** * Perform a simple concatenation between two objects. Added for startup * performance, but also demonstrates the code that would be emitted by @@ -466,10 +210,6 @@ final class StringConcatHelper { return (value == null || (s = value.toString()) == null) ? "null" : s; } - private static final long LATIN1 = (long)String.LATIN1 << 32; - - private static final long UTF16 = (long)String.UTF16 << 32; - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); static String stringOf(float value) { @@ -530,41 +270,6 @@ final class StringConcatHelper { return checkOverflow(length + value.length()); } - /** - * Allocates an uninitialized byte array based on the length and coder - * information, then prepends the given suffix string at the end of the - * byte array before returning it. The calling code must adjust the - * indexCoder so that it's taken the coder of the suffix into account, but - * subtracted the length of the suffix. - * - * @param suffix - * @param indexCoder - * @return the newly allocated byte array - */ - @ForceInline - static byte[] newArrayWithSuffix(String suffix, long indexCoder) { - byte[] buf = newArray(indexCoder + suffix.length()); - if (indexCoder < UTF16) { - suffix.getBytes(buf, (int)indexCoder, String.LATIN1); - } else { - suffix.getBytes(buf, (int)indexCoder, String.UTF16); - } - return buf; - } - - /** - * Allocates an uninitialized byte array based on the length and coder information - * in indexCoder - * @param indexCoder - * @return the newly allocated byte array - */ - @ForceInline - static byte[] newArray(long indexCoder) { - byte coder = (byte)(indexCoder >> 32); - int index = ((int)indexCoder) << coder; - return newArray(index); - } - /** * Allocates an uninitialized byte array based on the length * @param length @@ -578,14 +283,6 @@ final class StringConcatHelper { return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length); } - /** - * Provides the initial coder for the String. - * @return initial coder, adjusted into the upper half - */ - static long initialCoder() { - return String.COMPACT_STRINGS ? LATIN1 : UTF16; - } - static MethodHandle lookupStatic(String name, MethodType methodType) { try { return MethodHandles.lookup() @@ -603,7 +300,8 @@ final class StringConcatHelper { * subtracted the length of the suffix. * * @param suffix - * @param indexCoder + * @param index final char index in the buffer + * @param coder coder of the buffer * @return the newly allocated byte array */ @ForceInline diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index bb1775fbc6b..c88cf4ac797 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2185,18 +2185,6 @@ public final class System { return StringConcatHelper.lookupStatic(name, methodType); } - public long stringConcatInitialCoder() { - return StringConcatHelper.initialCoder(); - } - - public long stringConcatMix(long lengthCoder, String constant) { - return StringConcatHelper.mix(lengthCoder, constant); - } - - public long stringConcatMix(long lengthCoder, char value) { - return StringConcatHelper.mix(lengthCoder, value); - } - public Object uncheckedStringConcat1(String[] constants) { return new StringConcatHelper.Concat1(constants); } diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java index 1c7995c4ec7..733714b5786 100644 --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -37,7 +37,6 @@ import jdk.internal.util.ReferenceKey; import jdk.internal.util.ReferencedKeyMap; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; -import sun.invoke.util.Wrapper; import java.lang.classfile.Annotation; import java.lang.classfile.ClassBuilder; @@ -119,14 +118,10 @@ import static java.lang.invoke.MethodType.methodType; */ @AOTSafeClassInitializer public final class StringConcatFactory { - private static final int HIGH_ARITY_THRESHOLD; private static final int CACHE_THRESHOLD; private static final int FORCE_INLINE_THRESHOLD; static { - String highArity = VM.getSavedProperty("java.lang.invoke.StringConcat.highArityThreshold"); - HIGH_ARITY_THRESHOLD = highArity != null ? Integer.parseInt(highArity) : 0; - String cacheThreshold = VM.getSavedProperty("java.lang.invoke.StringConcat.cacheThreshold"); CACHE_THRESHOLD = cacheThreshold != null ? Integer.parseInt(cacheThreshold) : 256; @@ -391,9 +386,6 @@ public final class StringConcatFactory { try { MethodHandle mh = makeSimpleConcat(concatType, constantStrings); - if (mh == null && concatType.parameterCount() <= HIGH_ARITY_THRESHOLD) { - mh = generateMHInlineCopy(concatType, constantStrings); - } if (mh == null) { mh = InlineHiddenClassStrategy.generate(lookup, concatType, constantStrings); @@ -518,385 +510,6 @@ public final class StringConcatFactory { return null; } - /** - *

This strategy replicates what StringBuilders are doing: it builds the - * byte[] array on its own and passes that byte[] array to String - * constructor. This strategy requires access to some private APIs in JDK, - * most notably, the private String constructor that accepts byte[] arrays - * without copying. - */ - private static MethodHandle generateMHInlineCopy(MethodType mt, String[] constants) { - int paramCount = mt.parameterCount(); - String suffix = constants[paramCount]; - - - // else... fall-through to slow-path - - // Create filters and obtain filtered parameter types. Filters would be used in the beginning - // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings). - // The filtered argument type list is used all over in the combinators below. - - Class[] ptypes = mt.erase().parameterArray(); - MethodHandle[] objFilters = null; - MethodHandle[] floatFilters = null; - MethodHandle[] doubleFilters = null; - for (int i = 0; i < ptypes.length; i++) { - Class cl = ptypes[i]; - // Use int as the logical type for subword integral types - // (byte and short). char and boolean require special - // handling so don't change the logical type of those - ptypes[i] = promoteToIntType(ptypes[i]); - // Object, float and double will be eagerly transformed - // into a (non-null) String as a first step after invocation. - // Set up to use String as the logical type for such arguments - // internally. - if (cl == Object.class) { - if (objFilters == null) { - objFilters = new MethodHandle[ptypes.length]; - } - objFilters[i] = objectStringifier(); - ptypes[i] = String.class; - } else if (cl == float.class) { - if (floatFilters == null) { - floatFilters = new MethodHandle[ptypes.length]; - } - floatFilters[i] = floatStringifier(); - ptypes[i] = String.class; - } else if (cl == double.class) { - if (doubleFilters == null) { - doubleFilters = new MethodHandle[ptypes.length]; - } - doubleFilters[i] = doubleStringifier(); - ptypes[i] = String.class; - } - } - - // Start building the combinator tree. The tree "starts" with ()String, and "finishes" - // with the (byte[], long)String shape to invoke newString in StringConcatHelper. The combinators are - // assembled bottom-up, which makes the code arguably hard to read. - - // Drop all remaining parameter types, leave only helper arguments: - MethodHandle mh = MethodHandles.dropArgumentsTrusted(newString(), 2, ptypes); - - // Calculate the initialLengthCoder value by looking at all constant values and summing up - // their lengths and adjusting the encoded coder bit if needed - long initialLengthCoder = INITIAL_CODER; - - for (String constant : constants) { - if (constant != null) { - initialLengthCoder = JLA.stringConcatMix(initialLengthCoder, constant); - } - } - - // Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already - // known from the combinators below. We are assembling the string backwards, so the index coded - // into indexCoder is the *ending* index. - mh = filterInPrependers(mh, constants, ptypes); - - // Fold in byte[] instantiation at argument 0 - MethodHandle newArrayCombinator; - if (suffix == null || suffix.isEmpty()) { - suffix = ""; - } - // newArray variant that deals with prepending any trailing constant - // - // initialLengthCoder is adjusted to have the correct coder - // and length: The newArrayWithSuffix method expects only the coder of the - // suffix to be encoded into indexCoder - initialLengthCoder -= suffix.length(); - newArrayCombinator = newArrayWithSuffix(suffix); - - mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, newArrayCombinator, - 1 // index - ); - - // Start combining length and coder mixers. - // - // Length is easy: constant lengths can be computed on the spot, and all non-constant - // shapes have been either converted to Strings, or explicit methods for getting the - // string length out of primitives are provided. - // - // Coders are more interesting. Only Object, String and char arguments (and constants) - // can have non-Latin1 encoding. It is easier to blindly convert constants to String, - // and deduce the coder from there. Arguments would be either converted to Strings - // during the initial filtering, or handled by specializations in MIXERS. - // - // The method handle shape before all mixers are combined in is: - // (long, )String = ("indexCoder", ) - // - // We will bind the initialLengthCoder value to the last mixer (the one that will be - // executed first), then fold that in. This leaves the shape after all mixers are - // combined in as: - // ()String = () - - mh = filterAndFoldInMixers(mh, initialLengthCoder, ptypes); - - // The method handle shape here is (). - - // Apply filters, converting the arguments: - if (objFilters != null) { - mh = MethodHandles.filterArguments(mh, 0, objFilters); - } - if (floatFilters != null) { - mh = MethodHandles.filterArguments(mh, 0, floatFilters); - } - if (doubleFilters != null) { - mh = MethodHandles.filterArguments(mh, 0, doubleFilters); - } - - return mh; - } - - // We need one prepender per argument, but also need to fold in constants. We do so by greedily - // creating prependers that fold in surrounding constants into the argument prepender. This reduces - // the number of unique MH combinator tree shapes we'll create in an application. - // Additionally we do this in chunks to reduce the number of combinators bound to the root tree, - // which simplifies the shape and makes construction of similar trees use less unique LF classes - private static MethodHandle filterInPrependers(MethodHandle mh, String[] constants, Class[] ptypes) { - int pos; - int[] argPositions = null; - MethodHandle prepend; - for (pos = 0; pos < ptypes.length - 3; pos += 4) { - prepend = prepender(pos, constants, ptypes, 4); - argPositions = filterPrependArgPositions(argPositions, pos, 4); - mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepend, argPositions); - } - if (pos < ptypes.length) { - int count = ptypes.length - pos; - prepend = prepender(pos, constants, ptypes, count); - argPositions = filterPrependArgPositions(argPositions, pos, count); - mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepend, argPositions); - } - return mh; - } - - static int[] filterPrependArgPositions(int[] argPositions, int pos, int count) { - if (argPositions == null || argPositions.length != count + 2) { - argPositions = new int[count + 2]; - argPositions[0] = 1; // indexCoder - argPositions[1] = 0; // storage - } - int limit = count + 2; - for (int i = 2; i < limit; i++) { - argPositions[i] = i + pos; - } - return argPositions; - } - - - // We need one mixer per argument. - private static MethodHandle filterAndFoldInMixers(MethodHandle mh, long initialLengthCoder, Class[] ptypes) { - int pos; - int[] argPositions = null; - for (pos = 0; pos < ptypes.length - 4; pos += 4) { - // Compute new "index" in-place pairwise using old value plus the appropriate arguments. - MethodHandle mix = mixer(ptypes[pos], ptypes[pos + 1], ptypes[pos + 2], ptypes[pos + 3]); - argPositions = filterMixerArgPositions(argPositions, pos, 4); - mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, - mix, argPositions); - } - - if (pos < ptypes.length) { - // Mix in the last 1 to 4 parameters, insert the initialLengthCoder into the final mixer and - // fold the result into the main combinator - mh = foldInLastMixers(mh, initialLengthCoder, pos, ptypes, ptypes.length - pos); - } else if (ptypes.length == 0) { - // No mixer (constants only concat), insert initialLengthCoder directly - mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder); - } - return mh; - } - - static int[] filterMixerArgPositions(int[] argPositions, int pos, int count) { - if (argPositions == null || argPositions.length != count + 2) { - argPositions = new int[count + 1]; - argPositions[0] = 0; // indexCoder - } - int limit = count + 1; - for (int i = 1; i < limit; i++) { - argPositions[i] = i + pos; - } - return argPositions; - } - - private static MethodHandle foldInLastMixers(MethodHandle mh, long initialLengthCoder, int pos, Class[] ptypes, int count) { - MethodHandle mix = switch (count) { - case 1 -> mixer(ptypes[pos]); - case 2 -> mixer(ptypes[pos], ptypes[pos + 1]); - case 3 -> mixer(ptypes[pos], ptypes[pos + 1], ptypes[pos + 2]); - case 4 -> mixer(ptypes[pos], ptypes[pos + 1], ptypes[pos + 2], ptypes[pos + 3]); - default -> throw new IllegalArgumentException("Unexpected count: " + count); - }; - mix = MethodHandles.insertArguments(mix,0, initialLengthCoder); - // apply selected arguments on the 1-4 arg mixer and fold in the result - return switch (count) { - case 1 -> MethodHandles.foldArgumentsWithCombiner(mh, 0, mix, - 1 + pos); - case 2 -> MethodHandles.foldArgumentsWithCombiner(mh, 0, mix, - 1 + pos, 2 + pos); - case 3 -> MethodHandles.foldArgumentsWithCombiner(mh, 0, mix, - 1 + pos, 2 + pos, 3 + pos); - case 4 -> MethodHandles.foldArgumentsWithCombiner(mh, 0, mix, - 1 + pos, 2 + pos, 3 + pos, 4 + pos); - default -> throw new IllegalArgumentException(); - }; - } - - // Simple prependers, single argument. May be used directly or as a - // building block for complex prepender combinators. - private static MethodHandle prepender(String prefix, Class cl) { - if (prefix == null || prefix.isEmpty()) { - return noPrefixPrepender(cl); - } else { - return MethodHandles.insertArguments( - prepender(cl), 3, prefix); - } - } - - private static MethodHandle prepender(Class cl) { - int idx = classIndex(cl); - MethodHandle prepend = PREPENDERS[idx]; - if (prepend == null) { - PREPENDERS[idx] = prepend = JLA.stringConcatHelper("prepend", - methodType(long.class, long.class, byte[].class, - Wrapper.asPrimitiveType(cl), String.class)).rebind(); - } - return prepend; - } - - private static MethodHandle noPrefixPrepender(Class cl) { - int idx = classIndex(cl); - MethodHandle prepend = NO_PREFIX_PREPENDERS[idx]; - if (prepend == null) { - NO_PREFIX_PREPENDERS[idx] = prepend = MethodHandles.insertArguments(prepender(cl), 3, ""); - } - return prepend; - } - - private static final int INT_IDX = 0, - CHAR_IDX = 1, - LONG_IDX = 2, - BOOLEAN_IDX = 3, - STRING_IDX = 4, - TYPE_COUNT = 5; - private static int classIndex(Class cl) { - if (cl == String.class) return STRING_IDX; - if (cl == int.class) return INT_IDX; - if (cl == boolean.class) return BOOLEAN_IDX; - if (cl == char.class) return CHAR_IDX; - if (cl == long.class) return LONG_IDX; - throw new IllegalArgumentException("Unexpected class: " + cl); - } - - // Constant argument lists used by the prepender MH builders - private static final int[] PREPEND_FILTER_FIRST_ARGS = new int[] { 0, 1, 2 }; - private static final int[] PREPEND_FILTER_SECOND_ARGS = new int[] { 0, 1, 3 }; - private static final int[] PREPEND_FILTER_THIRD_ARGS = new int[] { 0, 1, 4 }; - private static final int[] PREPEND_FILTER_FIRST_PAIR_ARGS = new int[] { 0, 1, 2, 3 }; - private static final int[] PREPEND_FILTER_SECOND_PAIR_ARGS = new int[] { 0, 1, 4, 5 }; - - // Base MH for complex prepender combinators. - private static @Stable MethodHandle PREPEND_BASE; - private static MethodHandle prependBase() { - MethodHandle base = PREPEND_BASE; - if (base == null) { - base = PREPEND_BASE = MethodHandles.dropArguments( - MethodHandles.identity(long.class), 1, byte[].class); - } - return base; - } - - private static final @Stable MethodHandle[][] DOUBLE_PREPENDERS = new MethodHandle[TYPE_COUNT][TYPE_COUNT]; - - private static MethodHandle prepender(String prefix, Class cl, String prefix2, Class cl2) { - int idx1 = classIndex(cl); - int idx2 = classIndex(cl2); - MethodHandle prepend = DOUBLE_PREPENDERS[idx1][idx2]; - if (prepend == null) { - prepend = DOUBLE_PREPENDERS[idx1][idx2] = - MethodHandles.dropArguments(prependBase(), 2, cl, cl2); - } - prepend = MethodHandles.filterArgumentsWithCombiner(prepend, 0, prepender(prefix, cl), - PREPEND_FILTER_FIRST_ARGS); - return MethodHandles.filterArgumentsWithCombiner(prepend, 0, prepender(prefix2, cl2), - PREPEND_FILTER_SECOND_ARGS); - } - - private static MethodHandle prepender(int pos, String[] constants, Class[] ptypes, int count) { - // build the simple cases directly - if (count == 1) { - return prepender(constants[pos], ptypes[pos]); - } - if (count == 2) { - return prepender(constants[pos], ptypes[pos], constants[pos + 1], ptypes[pos + 1]); - } - // build a tree from an unbound prepender, allowing us to bind the constants in a batch as a final step - MethodHandle prepend = prependBase(); - if (count == 3) { - prepend = MethodHandles.dropArguments(prepend, 2, - ptypes[pos], ptypes[pos + 1], ptypes[pos + 2]); - prepend = MethodHandles.filterArgumentsWithCombiner(prepend, 0, - prepender(constants[pos], ptypes[pos], constants[pos + 1], ptypes[pos + 1]), - PREPEND_FILTER_FIRST_PAIR_ARGS); - return MethodHandles.filterArgumentsWithCombiner(prepend, 0, - prepender(constants[pos + 2], ptypes[pos + 2]), - PREPEND_FILTER_THIRD_ARGS); - } else if (count == 4) { - prepend = MethodHandles.dropArguments(prepend, 2, - ptypes[pos], ptypes[pos + 1], ptypes[pos + 2], ptypes[pos + 3]); - prepend = MethodHandles.filterArgumentsWithCombiner(prepend, 0, - prepender(constants[pos], ptypes[pos], constants[pos + 1], ptypes[pos + 1]), - PREPEND_FILTER_FIRST_PAIR_ARGS); - return MethodHandles.filterArgumentsWithCombiner(prepend, 0, - prepender(constants[pos + 2], ptypes[pos + 2], constants[pos + 3], ptypes[pos + 3]), - PREPEND_FILTER_SECOND_PAIR_ARGS); - } else { - throw new IllegalArgumentException("Unexpected count: " + count); - } - } - - // Constant argument lists used by the mixer MH builders - private static final int[] MIX_FILTER_SECOND_ARGS = new int[] { 0, 2 }; - private static final int[] MIX_FILTER_THIRD_ARGS = new int[] { 0, 3 }; - private static final int[] MIX_FILTER_SECOND_PAIR_ARGS = new int[] { 0, 3, 4 }; - private static MethodHandle mixer(Class cl) { - int index = classIndex(cl); - MethodHandle mix = MIXERS[index]; - if (mix == null) { - MIXERS[index] = mix = JLA.stringConcatHelper("mix", - methodType(long.class, long.class, Wrapper.asPrimitiveType(cl))).rebind(); - } - return mix; - } - - private static final @Stable MethodHandle[][] DOUBLE_MIXERS = new MethodHandle[TYPE_COUNT][TYPE_COUNT]; - private static MethodHandle mixer(Class cl, Class cl2) { - int idx1 = classIndex(cl); - int idx2 = classIndex(cl2); - MethodHandle mix = DOUBLE_MIXERS[idx1][idx2]; - if (mix == null) { - mix = mixer(cl); - mix = MethodHandles.dropArguments(mix, 2, cl2); - DOUBLE_MIXERS[idx1][idx2] = mix = MethodHandles.filterArgumentsWithCombiner(mix, 0, - mixer(cl2), MIX_FILTER_SECOND_ARGS); - } - return mix; - } - - private static MethodHandle mixer(Class cl, Class cl2, Class cl3) { - MethodHandle mix = mixer(cl, cl2); - mix = MethodHandles.dropArguments(mix, 3, cl3); - return MethodHandles.filterArgumentsWithCombiner(mix, 0, - mixer(cl3), MIX_FILTER_THIRD_ARGS); - } - - private static MethodHandle mixer(Class cl, Class cl2, Class cl3, Class cl4) { - MethodHandle mix = mixer(cl, cl2); - mix = MethodHandles.dropArguments(mix, 3, cl3, cl4); - return MethodHandles.filterArgumentsWithCombiner(mix, 0, - mixer(cl3, cl4), MIX_FILTER_SECOND_PAIR_ARGS); - } - private @Stable static MethodHandle SIMPLE_CONCAT; private static MethodHandle simpleConcat() { MethodHandle mh = SIMPLE_CONCAT; @@ -908,42 +521,11 @@ public final class StringConcatFactory { return mh; } - private @Stable static MethodHandle NEW_STRING; - private static MethodHandle newString() { - MethodHandle mh = NEW_STRING; - if (mh == null) { - MethodHandle newString = JLA.stringConcatHelper("newString", - methodType(String.class, byte[].class, long.class)); - NEW_STRING = mh = newString.rebind(); - } - return mh; - } - - private @Stable static MethodHandle NEW_ARRAY_SUFFIX; - private static MethodHandle newArrayWithSuffix(String suffix) { - MethodHandle mh = NEW_ARRAY_SUFFIX; - if (mh == null) { - MethodHandle newArrayWithSuffix = JLA.stringConcatHelper("newArrayWithSuffix", - methodType(byte[].class, String.class, long.class)); - NEW_ARRAY_SUFFIX = mh = newArrayWithSuffix.rebind(); - } - return MethodHandles.insertArguments(mh, 0, suffix); - } - /** * Public gateways to public "stringify" methods. These methods have the * form String apply(T obj), and normally delegate to {@code String.valueOf}, * depending on argument's type. */ - private @Stable static MethodHandle OBJECT_STRINGIFIER; - private static MethodHandle objectStringifier() { - MethodHandle mh = OBJECT_STRINGIFIER; - if (mh == null) { - OBJECT_STRINGIFIER = mh = JLA.stringConcatHelper("stringOf", - methodType(String.class, Object.class)); - } - return mh; - } private @Stable static MethodHandle FLOAT_STRINGIFIER; private static MethodHandle floatStringifier() { MethodHandle mh = FLOAT_STRINGIFIER; @@ -1027,38 +609,6 @@ public final class StringConcatFactory { } } - private static final @Stable MethodHandle[] NO_PREFIX_PREPENDERS = new MethodHandle[TYPE_COUNT]; - private static final @Stable MethodHandle[] PREPENDERS = new MethodHandle[TYPE_COUNT]; - private static final @Stable MethodHandle[] MIXERS = new MethodHandle[TYPE_COUNT]; - private static final long INITIAL_CODER = JLA.stringConcatInitialCoder(); - - /** - * Promote integral types to int. - */ - private static Class promoteToIntType(Class t) { - // use int for subword integral types; still need special mixers - // and prependers for char, boolean - return t == byte.class || t == short.class ? int.class : t; - } - - /** - * Returns a stringifier for references and floats/doubles only. - * Always returns null for other primitives. - * - * @param t class to stringify - * @return stringifier; null, if not available - */ - private static MethodHandle stringifierFor(Class t) { - if (t == Object.class) { - return objectStringifier(); - } else if (t == float.class) { - return floatStringifier(); - } else if (t == double.class) { - return doubleStringifier(); - } - return null; - } - private static MethodHandle stringValueOf(Class ptype) { try { return MethodHandles.publicLookup() diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index e529e8ba350..fa6e5b4aac3 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -452,21 +452,6 @@ public interface JavaLangAccess { */ MethodHandle stringConcatHelper(String name, MethodType methodType); - /** - * Get the string concat initial coder - */ - long stringConcatInitialCoder(); - - /** - * Update lengthCoder for constant - */ - long stringConcatMix(long lengthCoder, String constant); - - /** - * Mix value length and coder into current length and coder. - */ - long stringConcatMix(long lengthCoder, char value); - /** * Creates helper for string concatenation. *