1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.Serializable;
  25 import java.lang.invoke.*;
  26 import java.util.concurrent.Callable;
  27 
  28 /**
  29  * @test
  30  * @summary Test input invariants for StringConcatFactory
  31  *
  32  * @compile StringConcatFactoryInvariants.java
  33  *
  34  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB                                                                                                          StringConcatFactoryInvariants
  35  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                                                                    StringConcatFactoryInvariants
  36  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                                                                    StringConcatFactoryInvariants
  37  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                                                              StringConcatFactoryInvariants
  38  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                                                              StringConcatFactoryInvariants
  39  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                                                                          StringConcatFactoryInvariants
  40  *
  41  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  42  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  43  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  44  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  45  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  46  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  47  *
  48  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  49  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  50  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  51  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  52  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  53  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  54  *
  55  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  56  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  57  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  58  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  59  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  60  * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  61  *
  62 */
  63 public class StringConcatFactoryInvariants {
  64 
  65     private static final char TAG_ARG   = '\u0001';
  66     private static final char TAG_CONST = '\u0002';
  67 
  68     public static void main(String[] args) throws Throwable {
  69         MethodHandles.Lookup lookup = MethodHandles.lookup();
  70         String methodName = "foo";
  71         MethodType mt = MethodType.methodType(String.class, String.class, int.class);
  72         String recipe = "" + TAG_ARG + TAG_ARG + TAG_CONST;
  73         Object[] constants = new String[] { "bar" };
  74         Object[] intConstants = new Integer[] { 1 };
  75         Object[] shortConstants = new Short[] { 1 };
  76         Object[] longConstants = new Long[] { 1L };
  77         Object[] booleanConstants = new Boolean[] { true };
  78         Object[] charConstants = new Character[] { 'a' };
  79         Object[] byteConstants = new Byte[] { 1 };
  80 
  81         final int LIMIT = 200;
  82 
  83         // Simple factory: check for dynamic arguments overflow
  84         Class<?>[] underThreshold = new Class<?>[LIMIT - 1];
  85         Class<?>[] threshold      = new Class<?>[LIMIT];
  86         Class<?>[] overThreshold  = new Class<?>[LIMIT + 1];
  87 
  88         StringBuilder sbUnderThreshold = new StringBuilder();
  89         sbUnderThreshold.append(TAG_CONST);
  90         for (int c = 0; c < LIMIT - 1; c++) {
  91             underThreshold[c] = int.class;
  92             threshold[c] = int.class;
  93             overThreshold[c] = int.class;
  94             sbUnderThreshold.append(TAG_ARG);
  95         }
  96         threshold[LIMIT - 1] = int.class;
  97         overThreshold[LIMIT - 1] = int.class;
  98         overThreshold[LIMIT] = int.class;
  99 
 100         String recipeEmpty = "";
 101         String recipeUnderThreshold = sbUnderThreshold.toString();
 102         String recipeThreshold = sbUnderThreshold.append(TAG_ARG).toString();
 103         String recipeOverThreshold = sbUnderThreshold.append(TAG_ARG).toString();
 104 
 105         MethodType mtEmpty = MethodType.methodType(String.class);
 106         MethodType mtUnderThreshold = MethodType.methodType(String.class, underThreshold);
 107         MethodType mtThreshold = MethodType.methodType(String.class, threshold);
 108         MethodType mtOverThreshold = MethodType.methodType(String.class, overThreshold);
 109 
 110 
 111         // Check the basic functionality is working
 112         {
 113             CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, mt);
 114             test("foo42", (String) cs.getTarget().invokeExact("foo", 42));
 115         }
 116 
 117         {
 118             CallSite cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, constants);
 119             test("foo42bar", (String) cs.getTarget().invokeExact("foo", 42));
 120         }
 121 
 122         {
 123             CallSite cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, intConstants);
 124             test("foo421", (String) cs.getTarget().invokeExact("foo", 42));
 125         }
 126 
 127         {
 128             CallSite cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, booleanConstants);
 129             test("foo42true", (String) cs.getTarget().invokeExact("foo", 42));
 130         }
 131 
 132         // Simple factory, check for nulls:
 133         failNPE("Lookup is null",
 134                 () -> StringConcatFactory.makeConcat(null, methodName, mt));
 135 
 136         failNPE("Method name is null",
 137                 () -> StringConcatFactory.makeConcat(lookup, null, mt));
 138 
 139         failNPE("MethodType is null",
 140                 () -> StringConcatFactory.makeConcat(lookup, methodName, null));
 141 
 142         // Advanced factory, check for nulls:
 143         failNPE("Lookup is null",
 144                 () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, constants));
 145 
 146         failNPE("Lookup is null",
 147                 () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, shortConstants));
 148 
 149         failNPE("Lookup is null",
 150                 () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, intConstants));
 151 
 152         failNPE("Lookup is null",
 153                 () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, longConstants));
 154 
 155         failNPE("Lookup is null",
 156                 () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, booleanConstants));
 157 
 158         failNPE("Lookup is null",
 159                 () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, byteConstants));
 160 
 161         failNPE("Lookup is null",
 162                 () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, charConstants));
 163 
 164         failNPE("Method name is null",
 165                 () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, constants));
 166 
 167         failNPE("Method name is null",
 168                 () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, shortConstants));
 169 
 170         failNPE("Method name is null",
 171                 () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, intConstants));
 172 
 173         failNPE("Method name is null",
 174                 () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, longConstants));
 175 
 176         failNPE("Method name is null",
 177                 () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, booleanConstants));
 178 
 179         failNPE("Method name is null",
 180                 () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, byteConstants));
 181 
 182         failNPE("Method name is null",
 183                 () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, charConstants));
 184 
 185         failNPE("MethodType is null",
 186                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, constants));
 187 
 188         failNPE("MethodType is null",
 189                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, shortConstants));
 190 
 191         failNPE("MethodType is null",
 192                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, intConstants));
 193 
 194         failNPE("MethodType is null",
 195                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, longConstants));
 196 
 197         failNPE("MethodType is null",
 198                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, booleanConstants));
 199 
 200         failNPE("MethodType is null",
 201                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, byteConstants));
 202 
 203         failNPE("MethodType is null",
 204                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, charConstants));
 205 
 206         failNPE("Recipe is null",
 207                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, constants));
 208 
 209         failNPE("Recipe is null",
 210                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, shortConstants));
 211 
 212         failNPE("Recipe is null",
 213                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, intConstants));
 214 
 215         failNPE("Recipe is null",
 216                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, longConstants));
 217 
 218         failNPE("Recipe is null",
 219                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, booleanConstants));
 220 
 221         failNPE("Recipe is null",
 222                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, byteConstants));
 223 
 224         failNPE("Recipe is null",
 225                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, charConstants));
 226 
 227         failNPE("Constants vararg is null",
 228                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, (Object[])null));
 229 
 230         // Simple factory, check for return type
 231         fail("Return type: void",
 232                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(void.class, String.class, int.class)));
 233 
 234         fail("Return type: int",
 235                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(int.class, String.class, int.class)));
 236 
 237         fail("Return type: StringBuilder",
 238                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class)));
 239 
 240         ok("Return type: Object",
 241                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class)));
 242 
 243         ok("Return type: CharSequence",
 244                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class)));
 245 
 246         ok("Return type: Serializable",
 247                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class)));
 248 
 249         // Advanced factory, check for return types
 250         fail("Return type: void",
 251                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, constants));
 252 
 253         fail("Return type: void",
 254                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, intConstants));
 255 
 256         fail("Return type: void",
 257                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, shortConstants));
 258 
 259         fail("Return type: void",
 260                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, longConstants));
 261 
 262         fail("Return type: void",
 263                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, booleanConstants));
 264 
 265         fail("Return type: void",
 266                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, byteConstants));
 267 
 268         fail("Return type: void",
 269                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, charConstants));
 270 
 271         fail("Return type: int",
 272                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(int.class, String.class, int.class), recipe, constants));
 273 
 274         fail("Return type: StringBuilder",
 275                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class), recipe, constants));
 276 
 277         ok("Return type: Object",
 278                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, constants));
 279 
 280         ok("Return type: Object",
 281                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, intConstants));
 282 
 283         ok("Return type: Object",
 284                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, shortConstants));
 285 
 286         ok("Return type: Object",
 287                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, longConstants));
 288 
 289         ok("Return type: Object",
 290                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, booleanConstants));
 291 
 292         ok("Return type: Object",
 293                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, byteConstants));
 294 
 295         ok("Return type: Object",
 296                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, charConstants));
 297 
 298         ok("Return type: CharSequence",
 299                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, constants));
 300 
 301         ok("Return type: CharSequence",
 302                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, intConstants));
 303 
 304         ok("Return type: CharSequence",
 305                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, shortConstants));
 306 
 307         ok("Return type: CharSequence",
 308                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, longConstants));
 309 
 310         ok("Return type: CharSequence",
 311                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, booleanConstants));
 312 
 313         ok("Return type: CharSequence",
 314                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, byteConstants));
 315 
 316         ok("Return type: CharSequence",
 317                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, charConstants));
 318 
 319         ok("Return type: Serializable",
 320                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, constants));
 321 
 322         ok("Return type: Serializable",
 323                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, intConstants));
 324 
 325         ok("Return type: Serializable",
 326                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, shortConstants));
 327 
 328         ok("Return type: Serializable",
 329                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, longConstants));
 330 
 331         ok("Return type: Serializable",
 332                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, booleanConstants));
 333 
 334         ok("Return type: Serializable",
 335                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, byteConstants));
 336 
 337         ok("Return type: Serializable",
 338                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, charConstants));
 339 
 340         // Simple factory: check for dynamic arguments overflow
 341         ok("Dynamic arguments is under limit",
 342                 () -> StringConcatFactory.makeConcat(lookup, methodName, mtUnderThreshold));
 343 
 344         ok("Dynamic arguments is at the limit",
 345                 () -> StringConcatFactory.makeConcat(lookup, methodName, mtThreshold));
 346 
 347         fail("Dynamic arguments is over the limit",
 348                 () -> StringConcatFactory.makeConcat(lookup, methodName, mtOverThreshold));
 349 
 350         // Advanced factory: check for dynamic arguments overflow
 351         ok("Dynamic arguments is under limit",
 352                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtUnderThreshold, recipeUnderThreshold, constants));
 353 
 354         ok("Dynamic arguments is at the limit",
 355                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants));
 356 
 357         fail("Dynamic arguments is over the limit",
 358                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtOverThreshold, recipeOverThreshold, constants));
 359 
 360         // Advanced factory: check for mismatched recipe and Constants
 361         ok("Static arguments and recipe match",
 362                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar"));
 363 
 364         fail("Static arguments and recipe mismatch",
 365                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar", "baz"));
 366 
 367         // Advanced factory: check for mismatched recipe and dynamic arguments
 368         fail("Dynamic arguments and recipe mismatch",
 369                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeUnderThreshold, constants));
 370 
 371         ok("Dynamic arguments and recipe match",
 372                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants));
 373 
 374         fail("Dynamic arguments and recipe mismatch",
 375                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeOverThreshold, constants));
 376 
 377         // Test passing array as constant
 378         {
 379             Object[] arg = {"boo", "bar"};
 380 
 381             CallSite cs1 = StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST + TAG_CONST, arg);
 382             test("42boobar", (String) cs1.getTarget().invokeExact(42));
 383         }
 384 
 385         // Test passing null constant
 386         ok("Can pass regular constants",
 387                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, "foo"));
 388 
 389         failNPE("Cannot pass null constants",
 390                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, new Object[]{null}));
 391 
 392         // Simple factory: test empty arguments
 393         ok("Ok to pass empty arguments",
 394                 () -> StringConcatFactory.makeConcat(lookup, methodName, mtEmpty));
 395 
 396         // Advanced factory: test empty arguments
 397         ok("Ok to pass empty arguments",
 398                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtEmpty, recipeEmpty));
 399 
 400         // Simple factory: public Lookup is rejected
 401         fail("Passing public Lookup",
 402                 () -> StringConcatFactory.makeConcat(MethodHandles.publicLookup(), methodName, mtEmpty));
 403 
 404         // Advanced factory: public Lookup is rejected
 405         fail("Passing public Lookup",
 406                 () -> StringConcatFactory.makeConcatWithConstants(MethodHandles.publicLookup(), methodName, mtEmpty, recipeEmpty));
 407     }
 408 
 409     public static void ok(String msg, Callable runnable) {
 410         try {
 411             runnable.call();
 412         } catch (Throwable e) {
 413             e.printStackTrace();
 414             throw new IllegalStateException(msg + ", should have passed", e);
 415         }
 416     }
 417 
 418     public static void fail(String msg, Callable runnable) {
 419         boolean expected = false;
 420         try {
 421             runnable.call();
 422         } catch (StringConcatException e) {
 423             expected = true;
 424         } catch (Throwable e) {
 425             e.printStackTrace();
 426         }
 427 
 428         if (!expected) {
 429             throw new IllegalStateException(msg + ", should have failed with StringConcatException");
 430         }
 431     }
 432 
 433 
 434     public static void failNPE(String msg, Callable runnable) {
 435         boolean expected = false;
 436         try {
 437             runnable.call();
 438         } catch (NullPointerException e) {
 439             expected = true;
 440         } catch (Throwable e) {
 441             e.printStackTrace();
 442         }
 443 
 444         if (!expected) {
 445             throw new IllegalStateException(msg + ", should have failed with NullPointerException");
 446         }
 447     }
 448 
 449     public static void test(String expected, String actual) {
 450        // Fingers crossed: String concat should work.
 451        if (!expected.equals(actual)) {
 452            StringBuilder sb = new StringBuilder();
 453            sb.append("Expected = ");
 454            sb.append(expected);
 455            sb.append(", actual = ");
 456            sb.append(actual);
 457            throw new IllegalStateException(sb.toString());
 458        }
 459     }
 460 
 461 }