mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8222484: Specialize generation of simple String concatenation expressions
Reviewed-by: jrose, jlaskey
This commit is contained in:
parent
7d9e7e1e0b
commit
781fb29580
5 changed files with 177 additions and 96 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -67,13 +67,24 @@ public class HelloClasslist {
|
||||||
.forEach(System.out::println);
|
.forEach(System.out::println);
|
||||||
|
|
||||||
// Common concatenation patterns
|
// Common concatenation patterns
|
||||||
String const_I = "string" + args.length;
|
String SS = String.valueOf(args.length) + String.valueOf(args.length);
|
||||||
String const_S = "string" + String.valueOf(args.length);
|
String CS = "string" + String.valueOf(args.length);
|
||||||
String S_const = String.valueOf(args.length) + "string";
|
String SC = String.valueOf(args.length) + "string";
|
||||||
String S_S = String.valueOf(args.length) + String.valueOf(args.length);
|
String SCS = String.valueOf(args.length) + "string" + String.valueOf(args.length);
|
||||||
String const_J = "string" + System.currentTimeMillis();
|
String CSS = "string" + String.valueOf(args.length) + String.valueOf(args.length);
|
||||||
String I_const = args.length + "string";
|
String CSCS = "string" + String.valueOf(args.length) + "string" + String.valueOf(args.length);
|
||||||
String J_const = System.currentTimeMillis() + "string";
|
String SCSC = String.valueOf(args.length) + "string" + String.valueOf(args.length) + "string";
|
||||||
|
String CSCSC = "string" + String.valueOf(args.length) + "string" + String.valueOf(args.length) + "string";
|
||||||
|
String SCSCS = String.valueOf(args.length) + "string" + String.valueOf(args.length) + "string" + String.valueOf(args.length);
|
||||||
|
String CI = "string" + args.length;
|
||||||
|
String IC = args.length + "string";
|
||||||
|
String CIC = "string" + args.length + "string";
|
||||||
|
String CICI = "string" + args.length + "string" + args.length;
|
||||||
|
String CJ = "string" + System.currentTimeMillis();
|
||||||
|
String JC = System.currentTimeMillis() + "string";
|
||||||
|
String CJC = "string" + System.currentTimeMillis() + "string";
|
||||||
|
String CJCJ = "string" + System.currentTimeMillis() + "string" + System.currentTimeMillis();
|
||||||
|
String CJCJC = "string" + System.currentTimeMillis() + "string" + System.currentTimeMillis() + "string";
|
||||||
|
|
||||||
String newDate = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(
|
String newDate = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(
|
||||||
LocalDateTime.now(ZoneId.of("GMT")));
|
LocalDateTime.now(ZoneId.of("GMT")));
|
||||||
|
|
|
@ -1965,20 +1965,7 @@ public final class String
|
||||||
if (str.isEmpty()) {
|
if (str.isEmpty()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
if (coder() == str.coder()) {
|
return StringConcatHelper.simpleConcat(this, str);
|
||||||
byte[] val = this.value;
|
|
||||||
byte[] oval = str.value;
|
|
||||||
int len = val.length + oval.length;
|
|
||||||
byte[] buf = Arrays.copyOf(val, len);
|
|
||||||
System.arraycopy(oval, 0, buf, val.length, oval.length);
|
|
||||||
return new String(buf, coder);
|
|
||||||
}
|
|
||||||
int len = length();
|
|
||||||
int olen = str.length();
|
|
||||||
byte[] buf = StringUTF16.newBytesFor(len + olen);
|
|
||||||
getBytes(buf, 0, UTF16);
|
|
||||||
str.getBytes(buf, len, UTF16);
|
|
||||||
return new String(buf, UTF16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,6 +25,9 @@
|
||||||
|
|
||||||
package java.lang;
|
package java.lang;
|
||||||
|
|
||||||
|
import jdk.internal.misc.Unsafe;
|
||||||
|
import jdk.internal.vm.annotation.ForceInline;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for string concatenation. These methods are mostly looked up with private lookups
|
* Helper for string concatenation. These methods are mostly looked up with private lookups
|
||||||
* from {@link java.lang.invoke.StringConcatFactory}, and used in {@link java.lang.invoke.MethodHandle}
|
* from {@link java.lang.invoke.StringConcatFactory}, and used in {@link java.lang.invoke.MethodHandle}
|
||||||
|
@ -38,8 +41,10 @@ final class StringConcatHelper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for overflow, throw exception on overflow.
|
* Check for overflow, throw exception on overflow.
|
||||||
* @param lengthCoder String length and coder
|
*
|
||||||
* @return lengthCoder
|
* @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) {
|
private static long checkOverflow(long lengthCoder) {
|
||||||
if ((int)lengthCoder >= 0) {
|
if ((int)lengthCoder >= 0) {
|
||||||
|
@ -50,76 +55,83 @@ final class StringConcatHelper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix value length and coder into current length and coder.
|
* Mix value length and coder into current length and coder.
|
||||||
* @param current current length
|
* @param lengthCoder String length with coder packed into higher bits
|
||||||
* @param value value to mix in
|
* the upper word.
|
||||||
* @return new length and coder
|
* @param value value to mix in
|
||||||
|
* @return new length and coder
|
||||||
*/
|
*/
|
||||||
static long mix(long current, boolean value) {
|
static long mix(long lengthCoder, boolean value) {
|
||||||
return checkOverflow(current + (value ? 4 : 5));
|
return checkOverflow(lengthCoder + (value ? 4 : 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix value length and coder into current length and coder.
|
* Mix value length and coder into current length and coder.
|
||||||
* @param current current length
|
* @param lengthCoder String length with coder packed into higher bits
|
||||||
* @param value value to mix in
|
* the upper word.
|
||||||
* @return new length and coder
|
* @param value value to mix in
|
||||||
|
* @return new length and coder
|
||||||
*/
|
*/
|
||||||
static long mix(long current, byte value) {
|
static long mix(long lengthCoder, byte value) {
|
||||||
return mix(current, (int)value);
|
return mix(lengthCoder, (int)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix value length and coder into current length and coder.
|
* Mix value length and coder into current length and coder.
|
||||||
* @param current current length
|
* @param lengthCoder String length with coder packed into higher bits
|
||||||
* @param value value to mix in
|
* the upper word.
|
||||||
* @return new length and coder
|
* @param value value to mix in
|
||||||
|
* @return new length and coder
|
||||||
*/
|
*/
|
||||||
static long mix(long current, char value) {
|
static long mix(long lengthCoder, char value) {
|
||||||
return checkOverflow(current + 1) | (StringLatin1.canEncode(value) ? 0 : UTF16);
|
return checkOverflow(lengthCoder + 1) | (StringLatin1.canEncode(value) ? 0 : UTF16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix value length and coder into current length and coder.
|
* Mix value length and coder into current length and coder.
|
||||||
* @param current current length
|
* @param lengthCoder String length with coder packed into higher bits
|
||||||
* @param value value to mix in
|
* the upper word.
|
||||||
* @return new length and coder
|
* @param value value to mix in
|
||||||
|
* @return new length and coder
|
||||||
*/
|
*/
|
||||||
static long mix(long current, short value) {
|
static long mix(long lengthCoder, short value) {
|
||||||
return mix(current, (int)value);
|
return mix(lengthCoder, (int)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix value length and coder into current length and coder.
|
* Mix value length and coder into current length and coder.
|
||||||
* @param current current length
|
* @param lengthCoder String length with coder packed into higher bits
|
||||||
* @param value value to mix in
|
* the upper word.
|
||||||
* @return new length and coder
|
* @param value value to mix in
|
||||||
|
* @return new length and coder
|
||||||
*/
|
*/
|
||||||
static long mix(long current, int value) {
|
static long mix(long lengthCoder, int value) {
|
||||||
return checkOverflow(current + Integer.stringSize(value));
|
return checkOverflow(lengthCoder + Integer.stringSize(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix value length and coder into current length and coder.
|
* Mix value length and coder into current length and coder.
|
||||||
* @param current current length
|
* @param lengthCoder String length with coder packed into higher bits
|
||||||
* @param value value to mix in
|
* the upper word.
|
||||||
* @return new length and coder
|
* @param value value to mix in
|
||||||
|
* @return new length and coder
|
||||||
*/
|
*/
|
||||||
static long mix(long current, long value) {
|
static long mix(long lengthCoder, long value) {
|
||||||
return checkOverflow(current + Long.stringSize(value));
|
return checkOverflow(lengthCoder + Long.stringSize(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix value length and coder into current length and coder.
|
* Mix value length and coder into current length and coder.
|
||||||
* @param current current length
|
* @param lengthCoder String length with coder packed into higher bits
|
||||||
* @param value value to mix in
|
* the upper word.
|
||||||
* @return new length and coder
|
* @param value value to mix in
|
||||||
|
* @return new length and coder
|
||||||
*/
|
*/
|
||||||
static long mix(long current, String value) {
|
static long mix(long lengthCoder, String value) {
|
||||||
current += value.length();
|
lengthCoder += value.length();
|
||||||
if (value.coder() == String.UTF16) {
|
if (value.coder() == String.UTF16) {
|
||||||
current |= UTF16;
|
lengthCoder |= UTF16;
|
||||||
}
|
}
|
||||||
return checkOverflow(current);
|
return checkOverflow(lengthCoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -285,10 +297,62 @@ final class StringConcatHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a simple concatenation between two objects. Added for startup
|
||||||
|
* performance, but also demonstrates the code that would be emitted by
|
||||||
|
* {@code java.lang.invoke.StringConcatFactory$MethodHandleInlineCopyStrategy}
|
||||||
|
* for two Object arguments.
|
||||||
|
*
|
||||||
|
* @param first first argument
|
||||||
|
* @param second second argument
|
||||||
|
* @return String resulting string
|
||||||
|
*/
|
||||||
|
@ForceInline
|
||||||
|
static String simpleConcat(Object first, Object second) {
|
||||||
|
String s1 = stringOf(first);
|
||||||
|
String s2 = stringOf(second);
|
||||||
|
// start "mixing" in length and coder or arguments, order is not
|
||||||
|
// important
|
||||||
|
long indexCoder = mix(initialCoder(), s2);
|
||||||
|
indexCoder = mix(indexCoder, s1);
|
||||||
|
byte[] buf = newArray(indexCoder);
|
||||||
|
// prepend each argument in reverse order, since we prepending
|
||||||
|
// from the end of the byte array
|
||||||
|
indexCoder = prepend(indexCoder, buf, s2);
|
||||||
|
indexCoder = prepend(indexCoder, buf, s1);
|
||||||
|
return newString(buf, indexCoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need some additional conversion for Objects in general, because
|
||||||
|
* {@code String.valueOf(Object)} may return null. String conversion rules
|
||||||
|
* in Java state we need to produce "null" String in this case, so we
|
||||||
|
* provide a customized version that deals with this problematic corner case.
|
||||||
|
*/
|
||||||
|
static String stringOf(Object value) {
|
||||||
|
String s;
|
||||||
|
return (value == null || (s = value.toString()) == null) ? "null" : s;
|
||||||
|
}
|
||||||
|
|
||||||
private static final long LATIN1 = (long)String.LATIN1 << 32;
|
private static final long LATIN1 = (long)String.LATIN1 << 32;
|
||||||
|
|
||||||
private static final long UTF16 = (long)String.UTF16 << 32;
|
private static final long UTF16 = (long)String.UTF16 << 32;
|
||||||
|
|
||||||
|
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the initial coder for the String.
|
* Provides the initial coder for the String.
|
||||||
* @return initial coder, adjusted into the upper half
|
* @return initial coder, adjusted into the upper half
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
package java.lang.invoke;
|
package java.lang.invoke;
|
||||||
|
|
||||||
import jdk.internal.misc.Unsafe;
|
import jdk.internal.misc.Unsafe;
|
||||||
|
import jdk.internal.misc.VM;
|
||||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
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;
|
||||||
|
@ -191,6 +192,8 @@ public final class StringConcatFactory {
|
||||||
*/
|
*/
|
||||||
private static final ProxyClassesDumper DUMPER;
|
private static final ProxyClassesDumper DUMPER;
|
||||||
|
|
||||||
|
private static final Class<?> STRING_HELPER;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// In case we need to double-back onto the StringConcatFactory during this
|
// In case we need to double-back onto the StringConcatFactory during this
|
||||||
// static initialization, make sure we have the reasonable defaults to complete
|
// static initialization, make sure we have the reasonable defaults to complete
|
||||||
|
@ -202,15 +205,20 @@ public final class StringConcatFactory {
|
||||||
// DEBUG = false; // implied
|
// DEBUG = false; // implied
|
||||||
// DUMPER = null; // implied
|
// DUMPER = null; // implied
|
||||||
|
|
||||||
Properties props = GetPropertyAction.privilegedGetProperties();
|
try {
|
||||||
|
STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
|
||||||
final String strategy =
|
final String strategy =
|
||||||
props.getProperty("java.lang.invoke.stringConcat");
|
VM.getSavedProperty("java.lang.invoke.stringConcat");
|
||||||
CACHE_ENABLE = Boolean.parseBoolean(
|
CACHE_ENABLE = Boolean.parseBoolean(
|
||||||
props.getProperty("java.lang.invoke.stringConcat.cache"));
|
VM.getSavedProperty("java.lang.invoke.stringConcat.cache"));
|
||||||
DEBUG = Boolean.parseBoolean(
|
DEBUG = Boolean.parseBoolean(
|
||||||
props.getProperty("java.lang.invoke.stringConcat.debug"));
|
VM.getSavedProperty("java.lang.invoke.stringConcat.debug"));
|
||||||
final String dumpPath =
|
final String dumpPath =
|
||||||
props.getProperty("java.lang.invoke.stringConcat.dumpClasses");
|
VM.getSavedProperty("java.lang.invoke.stringConcat.dumpClasses");
|
||||||
|
|
||||||
STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
|
STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
|
||||||
CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
|
CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
|
||||||
|
@ -1519,6 +1527,33 @@ public final class StringConcatFactory {
|
||||||
|
|
||||||
static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
|
static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
|
||||||
|
|
||||||
|
// Fast-path two-argument Object + Object concatenations
|
||||||
|
if (recipe.getElements().size() == 2) {
|
||||||
|
// Two object arguments
|
||||||
|
if (mt.parameterCount() == 2 &&
|
||||||
|
!mt.parameterType(0).isPrimitive() &&
|
||||||
|
!mt.parameterType(1).isPrimitive()) {
|
||||||
|
return SIMPLE;
|
||||||
|
}
|
||||||
|
// One element is a constant
|
||||||
|
if (mt.parameterCount() == 1 && !mt.parameterType(0).isPrimitive()) {
|
||||||
|
MethodHandle mh = SIMPLE;
|
||||||
|
// Insert constant element
|
||||||
|
|
||||||
|
// First recipe element is a constant
|
||||||
|
if (recipe.getElements().get(0).getTag() == TAG_CONST &&
|
||||||
|
recipe.getElements().get(1).getTag() != TAG_CONST) {
|
||||||
|
return MethodHandles.insertArguments(mh, 0,
|
||||||
|
recipe.getElements().get(0).getValue());
|
||||||
|
} else if (recipe.getElements().get(1).getTag() == TAG_CONST &&
|
||||||
|
recipe.getElements().get(0).getTag() != TAG_CONST) {
|
||||||
|
return MethodHandles.insertArguments(mh, 1,
|
||||||
|
recipe.getElements().get(1).getValue());
|
||||||
|
}
|
||||||
|
// 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
|
||||||
// to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings).
|
// 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.
|
// The filtered argument type list is used all over in the combinators below.
|
||||||
|
@ -1626,13 +1661,6 @@ public final class StringConcatFactory {
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ForceInline
|
|
||||||
private static byte[] newArray(long indexCoder) {
|
|
||||||
byte coder = (byte)(indexCoder >> 32);
|
|
||||||
int index = (int)indexCoder;
|
|
||||||
return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MethodHandle prepender(Class<?> cl) {
|
private static MethodHandle prepender(Class<?> cl) {
|
||||||
return PREPENDERS.computeIfAbsent(cl, PREPEND);
|
return PREPENDERS.computeIfAbsent(cl, PREPEND);
|
||||||
}
|
}
|
||||||
|
@ -1659,16 +1687,15 @@ public final class StringConcatFactory {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final MethodHandle SIMPLE;
|
||||||
private static final MethodHandle NEW_STRING;
|
private static final MethodHandle NEW_STRING;
|
||||||
private static final MethodHandle NEW_ARRAY;
|
private static final MethodHandle NEW_ARRAY;
|
||||||
private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
|
private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
|
||||||
private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
|
private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
|
||||||
private static final long INITIAL_CODER;
|
private static final long INITIAL_CODER;
|
||||||
static final Class<?> STRING_HELPER;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
|
|
||||||
MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
|
MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
|
||||||
INITIAL_CODER = (long) initCoder.invoke();
|
INITIAL_CODER = (long) initCoder.invoke();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
@ -1678,8 +1705,9 @@ public final class StringConcatFactory {
|
||||||
PREPENDERS = new ConcurrentHashMap<>();
|
PREPENDERS = new ConcurrentHashMap<>();
|
||||||
MIXERS = new ConcurrentHashMap<>();
|
MIXERS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
SIMPLE = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class);
|
||||||
NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
|
NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
|
||||||
NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, long.class);
|
NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1692,22 +1720,8 @@ public final class StringConcatFactory {
|
||||||
// no instantiation
|
// no instantiation
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ObjectStringifier {
|
private static final MethodHandle OBJECT_INSTANCE =
|
||||||
|
lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class);
|
||||||
// We need some additional conversion for Objects in general, because String.valueOf(Object)
|
|
||||||
// may return null. String conversion rules in Java state we need to produce "null" String
|
|
||||||
// in this case, so we provide a customized version that deals with this problematic corner case.
|
|
||||||
private static String valueOf(Object value) {
|
|
||||||
String s;
|
|
||||||
return (value == null || (s = value.toString()) == null) ? "null" : s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could have used MethodHandles.lookup() instead of Lookup.IMPL_LOOKUP, if not for the fact
|
|
||||||
// java.lang.invoke Lookups are explicitly forbidden to be retrieved using that API.
|
|
||||||
private static final MethodHandle INSTANCE =
|
|
||||||
lookupStatic(Lookup.IMPL_LOOKUP, ObjectStringifier.class, "valueOf", String.class, Object.class);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FloatStringifiers {
|
private static class FloatStringifiers {
|
||||||
private static final MethodHandle FLOAT_INSTANCE =
|
private static final MethodHandle FLOAT_INSTANCE =
|
||||||
|
@ -1751,7 +1765,7 @@ public final class StringConcatFactory {
|
||||||
*/
|
*/
|
||||||
static MethodHandle forMost(Class<?> t) {
|
static MethodHandle forMost(Class<?> t) {
|
||||||
if (!t.isPrimitive()) {
|
if (!t.isPrimitive()) {
|
||||||
return ObjectStringifier.INSTANCE;
|
return OBJECT_INSTANCE;
|
||||||
} else if (t == float.class) {
|
} else if (t == float.class) {
|
||||||
return FloatStringifiers.FLOAT_INSTANCE;
|
return FloatStringifiers.FLOAT_INSTANCE;
|
||||||
} else if (t == double.class) {
|
} else if (t == double.class) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -62,6 +62,11 @@ public class StringConcat {
|
||||||
return "string" + stringValue;
|
return "string" + stringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public String concatMethodConstString() {
|
||||||
|
return "string".concat(stringValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
public String concatConstIntConstInt() {
|
public String concatConstIntConstInt() {
|
||||||
return "string" + intValue + "string" + intValue;
|
return "string" + intValue + "string" + intValue;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue