8222852: Reduce String concat combinator tree shapes by folding constants into prependers
Co-authored-by: Peter Levart <peter.levart@gmail.com> Reviewed-by: shade, plevart, forax
This commit is contained in:
parent
e3aa6f7907
commit
e7a6cbbd38
@ -144,7 +144,7 @@ final class StringConcatHelper {
|
|||||||
* @param value boolean value to encode
|
* @param value boolean value to encode
|
||||||
* @return updated index (coder value retained)
|
* @return updated index (coder value retained)
|
||||||
*/
|
*/
|
||||||
static long prepend(long indexCoder, byte[] buf, boolean value) {
|
private static long prepend(long indexCoder, byte[] buf, boolean value) {
|
||||||
int index = (int)indexCoder;
|
int index = (int)indexCoder;
|
||||||
if (indexCoder < UTF16) {
|
if (indexCoder < UTF16) {
|
||||||
if (value) {
|
if (value) {
|
||||||
@ -178,17 +178,41 @@ final class StringConcatHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepends the stringly representation of byte value into buffer,
|
* Prepends constant and the stringly representation of value into buffer,
|
||||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
* 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
|
* @param indexCoder final char index in the buffer, along with coder packed
|
||||||
* into higher bits.
|
* into higher bits.
|
||||||
* @param buf buffer to append to
|
* @param buf buffer to append to
|
||||||
* @param value byte value to encode
|
* @param prefix a constant to prepend before value
|
||||||
|
* @param value boolean value to encode
|
||||||
|
* @param suffix a constant to prepend after value
|
||||||
* @return updated index (coder value retained)
|
* @return updated index (coder value retained)
|
||||||
*/
|
*/
|
||||||
static long prepend(long indexCoder, byte[] buf, byte value) {
|
static long prepend(long indexCoder, byte[] buf, String prefix, boolean value, String suffix) {
|
||||||
return prepend(indexCoder, buf, (int)value);
|
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||||
|
indexCoder = prepend(indexCoder, buf, value);
|
||||||
|
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||||
|
return indexCoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 prefix a constant to prepend before value
|
||||||
|
* @param value boolean value to encode
|
||||||
|
* @param suffix a constant to prepend after value
|
||||||
|
* @return updated index (coder value retained)
|
||||||
|
*/
|
||||||
|
static long prepend(long indexCoder, byte[] buf, String prefix, byte value, String suffix) {
|
||||||
|
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||||
|
indexCoder = prepend(indexCoder, buf, (int)value);
|
||||||
|
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||||
|
return indexCoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -201,7 +225,7 @@ final class StringConcatHelper {
|
|||||||
* @param value char value to encode
|
* @param value char value to encode
|
||||||
* @return updated index (coder value retained)
|
* @return updated index (coder value retained)
|
||||||
*/
|
*/
|
||||||
static long prepend(long indexCoder, byte[] buf, char value) {
|
private static long prepend(long indexCoder, byte[] buf, char value) {
|
||||||
if (indexCoder < UTF16) {
|
if (indexCoder < UTF16) {
|
||||||
buf[(int)(--indexCoder)] = (byte) (value & 0xFF);
|
buf[(int)(--indexCoder)] = (byte) (value & 0xFF);
|
||||||
} else {
|
} else {
|
||||||
@ -211,17 +235,41 @@ final class StringConcatHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepends the stringly representation of short value into buffer,
|
* Prepends constant and the stringly representation of value into buffer,
|
||||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
* 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
|
* @param indexCoder final char index in the buffer, along with coder packed
|
||||||
* into higher bits.
|
* into higher bits.
|
||||||
* @param buf buffer to append to
|
* @param buf buffer to append to
|
||||||
* @param value short value to encode
|
* @param prefix a constant to prepend before value
|
||||||
|
* @param value boolean value to encode
|
||||||
|
* @param suffix a constant to prepend after value
|
||||||
* @return updated index (coder value retained)
|
* @return updated index (coder value retained)
|
||||||
*/
|
*/
|
||||||
static long prepend(long indexCoder, byte[] buf, short value) {
|
static long prepend(long indexCoder, byte[] buf, String prefix, char value, String suffix) {
|
||||||
return prepend(indexCoder, buf, (int)value);
|
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||||
|
indexCoder = prepend(indexCoder, buf, value);
|
||||||
|
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||||
|
return indexCoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 prefix a constant to prepend before value
|
||||||
|
* @param value boolean value to encode
|
||||||
|
* @param suffix a constant to prepend after value
|
||||||
|
* @return updated index (coder value retained)
|
||||||
|
*/
|
||||||
|
static long prepend(long indexCoder, byte[] buf, String prefix, short value, String suffix) {
|
||||||
|
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||||
|
indexCoder = prepend(indexCoder, buf, (int)value);
|
||||||
|
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||||
|
return indexCoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,7 +282,7 @@ final class StringConcatHelper {
|
|||||||
* @param value integer value to encode
|
* @param value integer value to encode
|
||||||
* @return updated index (coder value retained)
|
* @return updated index (coder value retained)
|
||||||
*/
|
*/
|
||||||
static long prepend(long indexCoder, byte[] buf, int value) {
|
private static long prepend(long indexCoder, byte[] buf, int value) {
|
||||||
if (indexCoder < UTF16) {
|
if (indexCoder < UTF16) {
|
||||||
return Integer.getChars(value, (int)indexCoder, buf);
|
return Integer.getChars(value, (int)indexCoder, buf);
|
||||||
} else {
|
} else {
|
||||||
@ -242,6 +290,25 @@ final class StringConcatHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 prefix a constant to prepend before value
|
||||||
|
* @param value boolean value to encode
|
||||||
|
* @param suffix a constant to prepend after value
|
||||||
|
* @return updated index (coder value retained)
|
||||||
|
*/
|
||||||
|
static long prepend(long indexCoder, byte[] buf, String prefix, int value, String suffix) {
|
||||||
|
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||||
|
indexCoder = prepend(indexCoder, buf, value);
|
||||||
|
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||||
|
return indexCoder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepends the stringly representation of long value into buffer,
|
* Prepends the stringly representation of long value into buffer,
|
||||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||||
@ -252,7 +319,7 @@ final class StringConcatHelper {
|
|||||||
* @param value long value to encode
|
* @param value long value to encode
|
||||||
* @return updated index (coder value retained)
|
* @return updated index (coder value retained)
|
||||||
*/
|
*/
|
||||||
static long prepend(long indexCoder, byte[] buf, long value) {
|
private static long prepend(long indexCoder, byte[] buf, long value) {
|
||||||
if (indexCoder < UTF16) {
|
if (indexCoder < UTF16) {
|
||||||
return Long.getChars(value, (int)indexCoder, buf);
|
return Long.getChars(value, (int)indexCoder, buf);
|
||||||
} else {
|
} else {
|
||||||
@ -260,6 +327,25 @@ final class StringConcatHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 prefix a constant to prepend before value
|
||||||
|
* @param value boolean value to encode
|
||||||
|
* @param suffix a constant to prepend after value
|
||||||
|
* @return updated index (coder value retained)
|
||||||
|
*/
|
||||||
|
static long prepend(long indexCoder, byte[] buf, String prefix, long value, String suffix) {
|
||||||
|
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||||
|
indexCoder = prepend(indexCoder, buf, value);
|
||||||
|
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||||
|
return indexCoder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepends the stringly representation of String value into buffer,
|
* Prepends the stringly representation of String value into buffer,
|
||||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||||
@ -270,7 +356,7 @@ final class StringConcatHelper {
|
|||||||
* @param value String value to encode
|
* @param value String value to encode
|
||||||
* @return updated index (coder value retained)
|
* @return updated index (coder value retained)
|
||||||
*/
|
*/
|
||||||
static long prepend(long indexCoder, byte[] buf, String value) {
|
private static long prepend(long indexCoder, byte[] buf, String value) {
|
||||||
indexCoder -= value.length();
|
indexCoder -= value.length();
|
||||||
if (indexCoder < UTF16) {
|
if (indexCoder < UTF16) {
|
||||||
value.getBytes(buf, (int)indexCoder, String.LATIN1);
|
value.getBytes(buf, (int)indexCoder, String.LATIN1);
|
||||||
@ -280,6 +366,25 @@ final class StringConcatHelper {
|
|||||||
return indexCoder;
|
return indexCoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 prefix a constant to prepend before value
|
||||||
|
* @param value boolean value to encode
|
||||||
|
* @param suffix a constant to prepend after value
|
||||||
|
* @return updated index (coder value retained)
|
||||||
|
*/
|
||||||
|
static long prepend(long indexCoder, byte[] buf, String prefix, String value, String suffix) {
|
||||||
|
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||||
|
indexCoder = prepend(indexCoder, buf, value);
|
||||||
|
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||||
|
return indexCoder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates the String with given buffer and coder
|
* Instantiates the String with given buffer and coder
|
||||||
* @param buf buffer to use
|
* @param buf buffer to use
|
||||||
|
@ -31,16 +31,13 @@ import jdk.internal.org.objectweb.asm.ClassWriter;
|
|||||||
import jdk.internal.org.objectweb.asm.Label;
|
import jdk.internal.org.objectweb.asm.Label;
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||||
import jdk.internal.vm.annotation.ForceInline;
|
|
||||||
import sun.invoke.util.Wrapper;
|
import sun.invoke.util.Wrapper;
|
||||||
import sun.security.action.GetPropertyAction;
|
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles.Lookup;
|
import java.lang.invoke.MethodHandles.Lookup;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@ -1531,27 +1528,33 @@ public final class StringConcatFactory {
|
|||||||
if (recipe.getElements().size() == 2) {
|
if (recipe.getElements().size() == 2) {
|
||||||
// Two object arguments
|
// Two object arguments
|
||||||
if (mt.parameterCount() == 2 &&
|
if (mt.parameterCount() == 2 &&
|
||||||
!mt.parameterType(0).isPrimitive() &&
|
!mt.parameterType(0).isPrimitive() &&
|
||||||
!mt.parameterType(1).isPrimitive()) {
|
!mt.parameterType(1).isPrimitive() &&
|
||||||
return SIMPLE;
|
recipe.getElements().get(0).getTag() == TAG_ARG &&
|
||||||
}
|
recipe.getElements().get(1).getTag() == TAG_ARG) {
|
||||||
// One element is a constant
|
|
||||||
if (mt.parameterCount() == 1 && !mt.parameterType(0).isPrimitive()) {
|
return SIMPLE;
|
||||||
MethodHandle mh = SIMPLE;
|
|
||||||
// Insert constant element
|
} else if (mt.parameterCount() == 1 &&
|
||||||
|
!mt.parameterType(0).isPrimitive()) {
|
||||||
|
// One Object argument, one constant
|
||||||
|
MethodHandle mh = SIMPLE;
|
||||||
|
|
||||||
// First recipe element is a constant
|
|
||||||
if (recipe.getElements().get(0).getTag() == TAG_CONST &&
|
if (recipe.getElements().get(0).getTag() == TAG_CONST &&
|
||||||
recipe.getElements().get(1).getTag() != TAG_CONST) {
|
recipe.getElements().get(1).getTag() == TAG_ARG) {
|
||||||
|
// First recipe element is a constant
|
||||||
return MethodHandles.insertArguments(mh, 0,
|
return MethodHandles.insertArguments(mh, 0,
|
||||||
recipe.getElements().get(0).getValue());
|
recipe.getElements().get(0).getValue());
|
||||||
|
|
||||||
} else if (recipe.getElements().get(1).getTag() == TAG_CONST &&
|
} else if (recipe.getElements().get(1).getTag() == TAG_CONST &&
|
||||||
recipe.getElements().get(0).getTag() != TAG_CONST) {
|
recipe.getElements().get(0).getTag() == TAG_ARG) {
|
||||||
|
// Second recipe element is a constant
|
||||||
return MethodHandles.insertArguments(mh, 1,
|
return MethodHandles.insertArguments(mh, 1,
|
||||||
recipe.getElements().get(1).getValue());
|
recipe.getElements().get(1).getValue());
|
||||||
|
|
||||||
}
|
}
|
||||||
// else... fall-through to slow-path
|
|
||||||
}
|
}
|
||||||
|
// else... fall-through to slow-path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create filters and obtain filtered parameter types. Filters would be used in the beginning
|
// Create filters and obtain filtered parameter types. Filters would be used in the beginning
|
||||||
@ -1579,26 +1582,49 @@ public final class StringConcatFactory {
|
|||||||
|
|
||||||
mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
|
mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
|
||||||
|
|
||||||
|
long initialLengthCoder = INITIAL_CODER;
|
||||||
|
|
||||||
// Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already
|
// 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
|
// known from the combinators below. We are assembling the string backwards, so the index coded
|
||||||
// into indexCoder is the *ending* index.
|
// into indexCoder is the *ending* index.
|
||||||
|
|
||||||
|
// We need one prepender per argument, but also need to fold in constants. We do so by greedily
|
||||||
|
// create 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.
|
||||||
|
String prefixConstant = null, suffixConstant = null;
|
||||||
|
int pos = -1;
|
||||||
for (RecipeElement el : recipe.getElements()) {
|
for (RecipeElement el : recipe.getElements()) {
|
||||||
// Do the prepend, and put "new" index at index 1
|
// Do the prepend, and put "new" index at index 1
|
||||||
switch (el.getTag()) {
|
switch (el.getTag()) {
|
||||||
case TAG_CONST: {
|
case TAG_CONST: {
|
||||||
MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, el.getValue());
|
String constantValue = el.getValue();
|
||||||
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
|
|
||||||
1, 0 // indexCoder, storage
|
// Eagerly update the initialLengthCoder value
|
||||||
);
|
initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constantValue);
|
||||||
|
|
||||||
|
if (pos < 0) {
|
||||||
|
// Collecting into prefixConstant
|
||||||
|
prefixConstant = prefixConstant == null ? constantValue : prefixConstant + constantValue;
|
||||||
|
} else {
|
||||||
|
// Collecting into suffixConstant
|
||||||
|
suffixConstant = suffixConstant == null ? constantValue : suffixConstant + constantValue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TAG_ARG: {
|
case TAG_ARG: {
|
||||||
int pos = el.getArgPos();
|
|
||||||
MethodHandle prepender = prepender(ptypes[pos]);
|
if (pos >= 0) {
|
||||||
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
|
// Flush the previous non-constant arg with any prefix/suffix constant
|
||||||
|
mh = MethodHandles.filterArgumentsWithCombiner(
|
||||||
|
mh, 1,
|
||||||
|
prepender(prefixConstant, ptypes[pos], suffixConstant),
|
||||||
1, 0, // indexCoder, storage
|
1, 0, // indexCoder, storage
|
||||||
2 + pos // selected argument
|
2 + pos // selected argument
|
||||||
);
|
);
|
||||||
|
prefixConstant = suffixConstant = null;
|
||||||
|
}
|
||||||
|
// Mark the pos of next non-constant arg
|
||||||
|
pos = el.getArgPos();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -1606,6 +1632,24 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert any trailing args, constants
|
||||||
|
if (pos >= 0) {
|
||||||
|
mh = MethodHandles.filterArgumentsWithCombiner(
|
||||||
|
mh, 1,
|
||||||
|
prepender(prefixConstant, ptypes[pos], suffixConstant),
|
||||||
|
1, 0, // indexCoder, storage
|
||||||
|
2 + pos // selected argument
|
||||||
|
);
|
||||||
|
} else if (prefixConstant != null) {
|
||||||
|
assert (suffixConstant == null);
|
||||||
|
// Sole prefixConstant can only happen if there were no non-constant arguments
|
||||||
|
mh = MethodHandles.filterArgumentsWithCombiner(
|
||||||
|
mh, 1,
|
||||||
|
MethodHandles.insertArguments(prepender(null, String.class, null), 2, prefixConstant),
|
||||||
|
1, 0 // indexCoder, storage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Fold in byte[] instantiation at argument 0
|
// Fold in byte[] instantiation at argument 0
|
||||||
mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
|
mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
|
||||||
1 // index
|
1 // index
|
||||||
@ -1624,12 +1668,11 @@ public final class StringConcatFactory {
|
|||||||
//
|
//
|
||||||
// The method handle shape before and after all mixers are combined in is:
|
// The method handle shape before and after all mixers are combined in is:
|
||||||
// (long, <args>)String = ("indexCoder", <args>)
|
// (long, <args>)String = ("indexCoder", <args>)
|
||||||
long initialLengthCoder = INITIAL_CODER;
|
|
||||||
for (RecipeElement el : recipe.getElements()) {
|
for (RecipeElement el : recipe.getElements()) {
|
||||||
switch (el.getTag()) {
|
switch (el.getTag()) {
|
||||||
case TAG_CONST:
|
case TAG_CONST:
|
||||||
String constant = el.getValue();
|
// Constants already handled in the code above
|
||||||
initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constant);
|
|
||||||
break;
|
break;
|
||||||
case TAG_ARG:
|
case TAG_ARG:
|
||||||
int ac = el.getArgPos();
|
int ac = el.getArgPos();
|
||||||
@ -1661,8 +1704,10 @@ public final class StringConcatFactory {
|
|||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodHandle prepender(Class<?> cl) {
|
private static MethodHandle prepender(String prefix, Class<?> cl, String suffix) {
|
||||||
return PREPENDERS.computeIfAbsent(cl, PREPEND);
|
return MethodHandles.insertArguments(
|
||||||
|
MethodHandles.insertArguments(
|
||||||
|
PREPENDERS.computeIfAbsent(cl, PREPEND),2, prefix), 3, suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodHandle mixer(Class<?> cl) {
|
private static MethodHandle mixer(Class<?> cl) {
|
||||||
@ -1670,16 +1715,16 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This one is deliberately non-lambdified to optimize startup time:
|
// This one is deliberately non-lambdified to optimize startup time:
|
||||||
private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
|
private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
|
||||||
@Override
|
@Override
|
||||||
public MethodHandle apply(Class<?> c) {
|
public MethodHandle apply(Class<?> c) {
|
||||||
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
|
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
|
||||||
Wrapper.asPrimitiveType(c));
|
String.class, Wrapper.asPrimitiveType(c), String.class);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This one is deliberately non-lambdified to optimize startup time:
|
// This one is deliberately non-lambdified to optimize startup time:
|
||||||
private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() {
|
private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
|
||||||
@Override
|
@Override
|
||||||
public MethodHandle apply(Class<?> c) {
|
public MethodHandle apply(Class<?> c) {
|
||||||
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
|
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.lang.invoke.*;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
* @summary StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
|
|
||||||
* @bug 8148787
|
|
||||||
*
|
|
||||||
* @compile StringConcatFactoryEmptyMethods.java
|
|
||||||
*
|
|
||||||
* @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true StringConcatFactoryEmptyMethods
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class StringConcatFactoryEmptyMethods {
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Throwable {
|
|
||||||
StringConcatFactory.makeConcat(
|
|
||||||
MethodHandles.lookup(),
|
|
||||||
"foo",
|
|
||||||
MethodType.methodType(String.class)
|
|
||||||
);
|
|
||||||
|
|
||||||
StringConcatFactory.makeConcatWithConstants(
|
|
||||||
MethodHandles.lookup(),
|
|
||||||
"foo",
|
|
||||||
MethodType.methodType(String.class),
|
|
||||||
""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.invoke.CallSite;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.lang.invoke.StringConcatFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @summary StringConcatFactory allow recipes with repeated constants, but this
|
||||||
|
* is not expressible with java code and needs an explicit sanity test
|
||||||
|
* @bug 8222852
|
||||||
|
*
|
||||||
|
* @compile StringConcatFactoryRepeatedConstants.java
|
||||||
|
*
|
||||||
|
* @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB StringConcatFactoryRepeatedConstants
|
||||||
|
* @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED StringConcatFactoryRepeatedConstants
|
||||||
|
* @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED StringConcatFactoryRepeatedConstants
|
||||||
|
* @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT StringConcatFactoryRepeatedConstants
|
||||||
|
* @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT StringConcatFactoryRepeatedConstants
|
||||||
|
* @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT StringConcatFactoryRepeatedConstants
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class StringConcatFactoryRepeatedConstants {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
|
||||||
|
CallSite site = StringConcatFactory.makeConcatWithConstants(
|
||||||
|
MethodHandles.lookup(),
|
||||||
|
"foo",
|
||||||
|
MethodType.methodType(String.class),
|
||||||
|
"\u0002\u0002",
|
||||||
|
"foo", "bar"
|
||||||
|
);
|
||||||
|
String string = (String)site.dynamicInvoker().invoke();
|
||||||
|
if (!"foobar".equals(string)) {
|
||||||
|
throw new IllegalStateException("Expected: foobar, got: " + string);
|
||||||
|
}
|
||||||
|
|
||||||
|
site = StringConcatFactory.makeConcatWithConstants(
|
||||||
|
MethodHandles.lookup(),
|
||||||
|
"foo",
|
||||||
|
MethodType.methodType(String.class),
|
||||||
|
"\u0002\u0002test\u0002\u0002",
|
||||||
|
"foo", 17.0f, 4711L, "bar"
|
||||||
|
);
|
||||||
|
string = (String)site.dynamicInvoker().invoke();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("foo").append(17.0f).append("test").append(4711L).append("bar");
|
||||||
|
if (!sb.toString().equals(string)) {
|
||||||
|
throw new IllegalStateException("Expected: " + sb.toString() + ", got: " + string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -26,6 +26,7 @@ import org.openjdk.jmh.annotations.Benchmark;
|
|||||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||||
import org.openjdk.jmh.annotations.Mode;
|
import org.openjdk.jmh.annotations.Mode;
|
||||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||||
|
import org.openjdk.jmh.annotations.Param;
|
||||||
import org.openjdk.jmh.annotations.Scope;
|
import org.openjdk.jmh.annotations.Scope;
|
||||||
import org.openjdk.jmh.annotations.Setup;
|
import org.openjdk.jmh.annotations.Setup;
|
||||||
import org.openjdk.jmh.annotations.State;
|
import org.openjdk.jmh.annotations.State;
|
||||||
@ -42,7 +43,8 @@ import java.util.concurrent.TimeUnit;
|
|||||||
@State(Scope.Thread)
|
@State(Scope.Thread)
|
||||||
public class StringConcat {
|
public class StringConcat {
|
||||||
|
|
||||||
public int intValue = 4711;
|
@Param("4711")
|
||||||
|
public int intValue;
|
||||||
|
|
||||||
public String stringValue = String.valueOf(intValue);
|
public String stringValue = String.valueOf(intValue);
|
||||||
|
|
||||||
@ -77,11 +79,26 @@ public class StringConcat {
|
|||||||
return "string" + stringValue + "string" + intValue;
|
return "string" + stringValue + "string" + intValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public String concatMix4String() {
|
||||||
|
// Investigate "profile pollution" between shared LFs that might eliminate some JIT optimizations
|
||||||
|
String s1 = "string" + stringValue + stringValue + stringValue + stringValue;
|
||||||
|
String s2 = "string" + stringValue + "string" + stringValue + stringValue + stringValue;
|
||||||
|
String s3 = stringValue + stringValue + "string" + stringValue + "string" + stringValue + "string";
|
||||||
|
String s4 = "string" + stringValue + "string" + stringValue + "string" + stringValue + "string" + stringValue + "string";
|
||||||
|
return s1 + s2 + s3 + s4;
|
||||||
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
public String concatConst4String() {
|
public String concatConst4String() {
|
||||||
return "string" + stringValue + stringValue + stringValue + stringValue;
|
return "string" + stringValue + stringValue + stringValue + stringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public String concat4String() {
|
||||||
|
return stringValue + stringValue + stringValue + stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
public String concatConst2String() {
|
public String concatConst2String() {
|
||||||
return "string" + stringValue + stringValue;
|
return "string" + stringValue + stringValue;
|
||||||
@ -97,6 +114,11 @@ public class StringConcat {
|
|||||||
return "string" + stringValue + stringValue + stringValue + stringValue + stringValue + stringValue;
|
return "string" + stringValue + stringValue + stringValue + stringValue + stringValue + stringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public String concat6String() {
|
||||||
|
return stringValue + stringValue + stringValue + stringValue + stringValue + stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
public String concatConst6Object() {
|
public String concatConst6Object() {
|
||||||
return "string" + objectValue + objectValue + objectValue + objectValue + objectValue + objectValue;
|
return "string" + objectValue + objectValue + objectValue + objectValue + objectValue + objectValue;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user