119119 */
120120public final class StringConcatFactory {
121121 private static final int HIGH_ARITY_THRESHOLD ;
122+ private static final int CACHE_THRESHOLD ;
122123 private static final int FORCE_INLINE_THRESHOLD ;
123124
124125 static {
125126 String highArity = VM .getSavedProperty ("java.lang.invoke.StringConcat.highArityThreshold" );
126127 HIGH_ARITY_THRESHOLD = highArity != null ? Integer .parseInt (highArity ) : 0 ;
127128
129+ String cacheThreshold = VM .getSavedProperty ("java.lang.invoke.StringConcat.cacheThreshold" );
130+ CACHE_THRESHOLD = cacheThreshold != null ? Integer .parseInt (cacheThreshold ) : 256 ;
131+
128132 String inlineThreshold = VM .getSavedProperty ("java.lang.invoke.StringConcat.inlineThreshold" );
129133 FORCE_INLINE_THRESHOLD = inlineThreshold != null ? Integer .parseInt (inlineThreshold ) : 16 ;
130134 }
@@ -1179,17 +1183,21 @@ private static MethodType erasedArgs(MethodType args) {
11791183 * int arg0, long arg1, boolean arg2, char arg3, String arg5)
11801184 * </pre></blockquote>
11811185 */
1182- private static MethodTypeDesc prependArgs (MethodType concatArgs ) {
1186+ private static MethodTypeDesc prependArgs (MethodType concatArgs , boolean staticConcat ) {
11831187 int parameterCount = concatArgs .parameterCount ();
1184- var paramTypes = new ClassDesc [parameterCount + 4 ];
1188+ int prefixArgs = staticConcat ? 3 : 4 ;
1189+ var paramTypes = new ClassDesc [parameterCount + prefixArgs ];
11851190 paramTypes [0 ] = CD_int ; // length
11861191 paramTypes [1 ] = CD_byte ; // coder
11871192 paramTypes [2 ] = CD_Array_byte ; // buff
1188- paramTypes [3 ] = CD_Array_String ; // constants
1193+
1194+ if (!staticConcat ) {
1195+ paramTypes [3 ] = CD_Array_String ; // constants
1196+ }
11891197
11901198 for (int i = 0 ; i < parameterCount ; i ++) {
11911199 var cl = concatArgs .parameterType (i );
1192- paramTypes [i + 4 ] = needStringOf (cl ) ? CD_String : ConstantUtils .classDesc (cl );
1200+ paramTypes [i + prefixArgs ] = needStringOf (cl ) ? CD_String : ConstantUtils .classDesc (cl );
11931201 }
11941202 return MethodTypeDescImpl .ofValidated (CD_int , paramTypes );
11951203 }
@@ -1254,32 +1262,42 @@ private static MethodHandle generate(Lookup lookup, MethodType args, String[] co
12541262 return handle .bindTo (concat1 );
12551263 }
12561264
1257- var weakConstructorHandle = CACHE .get (concatArgs );
1258- if (weakConstructorHandle != null ) {
1259- MethodHandlePair handlePair = weakConstructorHandle .get ();
1260- if (handlePair != null ) {
1261- try {
1262- var instance = handlePair .constructor .invokeBasic ((Object )constants );
1263- return handlePair .concatenator .bindTo (instance );
1264- } catch (Throwable e ) {
1265- throw new StringConcatException ("Exception while utilizing the hidden class" , e );
1265+ boolean forceInline = concatArgs .parameterCount () < FORCE_INLINE_THRESHOLD ;
1266+ boolean staticConcat = concatArgs .parameterCount () >= CACHE_THRESHOLD ;
1267+
1268+ if (!staticConcat ) {
1269+ var weakConstructorHandle = CACHE .get (concatArgs );
1270+ if (weakConstructorHandle != null ) {
1271+ MethodHandlePair handlePair = weakConstructorHandle .get ();
1272+ if (handlePair != null ) {
1273+ try {
1274+ var instance = handlePair .constructor .invokeBasic ((Object )constants );
1275+ return handlePair .concatenator .bindTo (instance );
1276+ } catch (Throwable e ) {
1277+ throw new StringConcatException ("Exception while utilizing the hidden class" , e );
1278+ }
12661279 }
12671280 }
12681281 }
1282+
12691283 MethodTypeDesc lengthArgs = lengthArgs (concatArgs ),
12701284 coderArgs = coderArgsIfMaybeUTF16 (concatArgs ),
1271- prependArgs = prependArgs (concatArgs );
1285+ prependArgs = prependArgs (concatArgs , staticConcat );
12721286
12731287 byte [] classBytes = ClassFile .of ().build (CD_CONCAT ,
12741288 new Consumer <ClassBuilder >() {
1275- final boolean forceInline = concatArgs .parameterCount () < FORCE_INLINE_THRESHOLD ;
1276-
12771289 @ Override
12781290 public void accept (ClassBuilder clb ) {
1279- clb .withSuperclass (CD_StringConcatBase )
1280- .withFlags (ACC_FINAL | ACC_SUPER | ACC_SYNTHETIC )
1281- .withMethodBody (INIT_NAME , MTD_INIT , 0 , CONSTRUCTOR_BUILDER )
1282- .withMethod ("length" ,
1291+ if (staticConcat ) {
1292+ clb .withSuperclass (CD_Object )
1293+ .withFlags (ACC_ABSTRACT | ACC_SUPER | ACC_SYNTHETIC );
1294+ } else {
1295+ clb .withSuperclass (CD_StringConcatBase )
1296+ .withFlags (ACC_FINAL | ACC_SUPER | ACC_SYNTHETIC )
1297+ .withMethodBody (INIT_NAME , MTD_INIT , 0 , CONSTRUCTOR_BUILDER );
1298+ }
1299+
1300+ clb .withMethod ("length" ,
12831301 lengthArgs ,
12841302 ACC_STATIC | ACC_PRIVATE ,
12851303 new Consumer <MethodBuilder >() {
@@ -1298,18 +1316,20 @@ public void accept(MethodBuilder mb) {
12981316 if (forceInline ) {
12991317 mb .with (FORCE_INLINE );
13001318 }
1301- mb .withCode (generatePrependMethod (prependArgs ));
1319+ mb .withCode (generatePrependMethod (prependArgs , staticConcat , constants ));
13021320 }
13031321 })
13041322 .withMethod (METHOD_NAME ,
13051323 ConstantUtils .methodTypeDesc (concatArgs ),
1306- ACC_FINAL ,
1324+ staticConcat ? ACC_STATIC | ACC_FINAL : ACC_FINAL ,
13071325 new Consumer <MethodBuilder >() {
13081326 public void accept (MethodBuilder mb ) {
13091327 if (forceInline ) {
13101328 mb .with (FORCE_INLINE );
13111329 }
13121330 mb .withCode (generateConcatMethod (
1331+ staticConcat ,
1332+ constants ,
13131333 CD_CONCAT ,
13141334 concatArgs ,
13151335 lengthArgs ,
@@ -1335,6 +1355,11 @@ public void accept(MethodBuilder mb) {
13351355 try {
13361356 var hiddenClass = lookup .makeHiddenClassDefiner (CLASS_NAME , classBytes , Set .of (), DUMPER )
13371357 .defineClass (true , null );
1358+
1359+ if (staticConcat ) {
1360+ return lookup .findStatic (hiddenClass , METHOD_NAME , concatArgs );
1361+ }
1362+
13381363 var constructor = lookup .findConstructor (hiddenClass , CONSTRUCTOR_METHOD_TYPE );
13391364 var concatenator = lookup .findVirtual (hiddenClass , METHOD_NAME , concatArgs );
13401365 CACHE .put (concatArgs , new SoftReference <>(new MethodHandlePair (constructor , concatenator )));
@@ -1410,6 +1435,8 @@ public void accept(MethodBuilder mb) {
14101435 * </pre></blockquote>
14111436 */
14121437 private static Consumer <CodeBuilder > generateConcatMethod (
1438+ boolean staticConcat ,
1439+ String [] constants ,
14131440 ClassDesc concatClass ,
14141441 MethodType concatArgs ,
14151442 MethodTypeDesc lengthArgs ,
@@ -1421,7 +1448,7 @@ private static Consumer<CodeBuilder> generateConcatMethod(
14211448 public void accept (CodeBuilder cb ) {
14221449 // Compute parameter variable slots
14231450 int paramCount = concatArgs .parameterCount (),
1424- thisSlot = cb .receiverSlot (),
1451+ thisSlot = staticConcat ? 0 : cb .receiverSlot (),
14251452 lengthSlot = cb .allocateLocal (TypeKind .INT ),
14261453 coderSlot = cb .allocateLocal (TypeKind .BYTE ),
14271454 bufSlot = cb .allocateLocal (TypeKind .REFERENCE ),
@@ -1457,11 +1484,30 @@ public void accept(CodeBuilder cb) {
14571484 }
14581485 }
14591486
1487+ int coder = JLA .stringInitCoder (),
1488+ length = 0 ;
1489+ if (staticConcat ) {
1490+ for (var constant : constants ) {
1491+ coder |= JLA .stringCoder (constant );
1492+ length += constant .length ();
1493+ }
1494+ }
1495+
14601496 /*
14611497 * coder = coder(this.coder, arg0, arg1, ... argN);
14621498 */
1463- cb .aload (thisSlot )
1464- .getfield (concatClass , "coder" , CD_byte );
1499+ if (staticConcat ) {
1500+ // coder can only be 0 or 1
1501+ if (coder == 0 ) {
1502+ cb .iconst_0 ();
1503+ } else {
1504+ cb .iconst_1 ();
1505+ }
1506+ } else {
1507+ cb .aload (thisSlot )
1508+ .getfield (concatClass , "coder" , CD_byte );
1509+ }
1510+
14651511 if (coderArgs != null ) {
14661512 for (int i = 0 ; i < paramCount ; i ++) {
14671513 var cl = concatArgs .parameterType (i );
@@ -1480,8 +1526,13 @@ public void accept(CodeBuilder cb) {
14801526 /*
14811527 * length = length(this.length, arg0, arg1, ..., argN);
14821528 */
1483- cb .aload (thisSlot )
1484- .getfield (concatClass , "length" , CD_int );
1529+ if (staticConcat ) {
1530+ cb .ldc (length );
1531+ } else {
1532+ cb .aload (thisSlot )
1533+ .getfield (concatClass , "length" , CD_int );
1534+ }
1535+
14851536 for (int i = 0 ; i < paramCount ; i ++) {
14861537 var cl = concatArgs .parameterType (i );
14871538 int paramSlot = cb .parameterSlot (i );
@@ -1498,25 +1549,35 @@ public void accept(CodeBuilder cb) {
14981549 * suffix = constants[paramCount];
14991550 * length -= suffix.length();
15001551 */
1501- cb .aload (thisSlot )
1502- .getfield (concatClass , "constants" , CD_Array_String )
1503- .dup ()
1504- .astore (constantsSlot )
1505- .ldc (paramCount )
1506- .aaload ()
1507- .dup ()
1508- .astore (suffixSlot )
1509- .invokevirtual (CD_String , "length" , MTD_int )
1510- .isub ()
1511- .istore (lengthSlot );
1552+ if (staticConcat ) {
1553+ cb .ldc (constants [paramCount ].length ())
1554+ .isub ()
1555+ .istore (lengthSlot );
1556+ } else {
1557+ cb .aload (thisSlot )
1558+ .getfield (concatClass , "constants" , CD_Array_String )
1559+ .dup ()
1560+ .astore (constantsSlot )
1561+ .ldc (paramCount )
1562+ .aaload ()
1563+ .dup ()
1564+ .astore (suffixSlot )
1565+ .invokevirtual (CD_String , "length" , MTD_int )
1566+ .isub ()
1567+ .istore (lengthSlot );
1568+ }
15121569
15131570 /*
15141571 * Allocate buffer :
15151572 *
15161573 * buf = newArrayWithSuffix(suffix, length, coder)
15171574 */
1518- cb .aload (suffixSlot )
1519- .iload (lengthSlot )
1575+ if (staticConcat ) {
1576+ cb .ldc (constants [paramCount ]);
1577+ } else {
1578+ cb .aload (suffixSlot );
1579+ }
1580+ cb .iload (lengthSlot )
15201581 .iload (coderSlot )
15211582 .invokestatic (CD_StringConcatHelper , "newArrayWithSuffix" , MTD_NEW_ARRAY_SUFFIX )
15221583 .astore (bufSlot );
@@ -1526,8 +1587,10 @@ public void accept(CodeBuilder cb) {
15261587 */
15271588 cb .iload (lengthSlot )
15281589 .iload (coderSlot )
1529- .aload (bufSlot )
1530- .aload (constantsSlot );
1590+ .aload (bufSlot );
1591+ if (!staticConcat ) {
1592+ cb .aload (constantsSlot );
1593+ }
15311594 for (int i = 0 ; i < paramCount ; i ++) {
15321595 var cl = concatArgs .parameterType (i );
15331596 int paramSlot = cb .parameterSlot (i );
@@ -1651,7 +1714,10 @@ public void accept(CodeBuilder cb) {
16511714 * }
16521715 * </pre></blockquote>
16531716 */
1654- private static Consumer <CodeBuilder > generatePrependMethod (MethodTypeDesc prependArgs ) {
1717+ private static Consumer <CodeBuilder > generatePrependMethod (
1718+ MethodTypeDesc prependArgs ,
1719+ boolean staticConcat , String [] constants
1720+ ) {
16551721 return new Consumer <CodeBuilder >() {
16561722 @ Override
16571723 public void accept (CodeBuilder cb ) {
@@ -1670,7 +1736,7 @@ public void accept(CodeBuilder cb) {
16701736 * buf, arg1, constant[1]), buf, arg0, constant[0]);
16711737 */
16721738 cb .iload (lengthSlot );
1673- for (int i = prependArgs .parameterCount () - 1 ; i >= 4 ; i --) {
1739+ for (int i = prependArgs .parameterCount () - 1 , end = staticConcat ? 3 : 4 ; i >= end ; i --) {
16741740 var cl = prependArgs .parameterType (i );
16751741 var kind = TypeKind .from (cl );
16761742
@@ -1691,11 +1757,17 @@ public void accept(CodeBuilder cb) {
16911757
16921758 cb .iload (coderSlot )
16931759 .aload (bufSlot )
1694- .loadLocal (kind , cb .parameterSlot (i ))
1695- .aload (constantsSlot )
1696- .ldc (i - 4 )
1697- .aaload ()
1698- .invokestatic (CD_StringConcatHelper , "prepend" , methodTypeDesc );
1760+ .loadLocal (kind , cb .parameterSlot (i ));
1761+
1762+ if (staticConcat ) {
1763+ cb .ldc (constants [i - 3 ]);
1764+ } else {
1765+ cb .aload (constantsSlot )
1766+ .ldc (i - 4 )
1767+ .aaload ();
1768+ }
1769+
1770+ cb .invokestatic (CD_StringConcatHelper , "prepend" , methodTypeDesc );
16991771 }
17001772 cb .ireturn ();
17011773 }
0 commit comments