1 /*
   2  * Copyright (c) 2015, 2015, 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 -Djava.lang.invoke.stringConcat=BC_SB                                                                                                          StringConcatFactoryInvariants
  35  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                                                                    StringConcatFactoryInvariants
  36  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                                                                    StringConcatFactoryInvariants
  37  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                                                              StringConcatFactoryInvariants
  38  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                                                              StringConcatFactoryInvariants
  39  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                                                                          StringConcatFactoryInvariants
  40  *
  41  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  42  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  43  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  44  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  45  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  46  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
  47  *
  48  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  49  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  50  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  51  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  52  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  53  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  54  *
  55  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  56  * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  57  * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
  58  * @run main/othervm -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 -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 -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.publicLookup();
  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         String[] constants = new String[]{"bar"};
  74 
  75         final int LIMIT = 200;
  76 
  77         // Simple factory: check for dynamic arguments overflow
  78         Class<?>[] underThreshold = new Class<?>[LIMIT - 1];
  79         Class<?>[] threshold      = new Class<?>[LIMIT];
  80         Class<?>[] overThreshold  = new Class<?>[LIMIT + 1];
  81 
  82         StringBuilder sbUnderThreshold = new StringBuilder();
  83         sbUnderThreshold.append(TAG_CONST);
  84         for (int c = 0; c < LIMIT - 1; c++) {
  85             underThreshold[c] = int.class;
  86             threshold[c] = int.class;
  87             overThreshold[c] = int.class;
  88             sbUnderThreshold.append(TAG_ARG);
  89         }
  90         threshold[LIMIT - 1] = int.class;
  91         overThreshold[LIMIT - 1] = int.class;
  92         overThreshold[LIMIT] = int.class;
  93 
  94         String recipeEmpty = "";
  95         String recipeUnderThreshold = sbUnderThreshold.toString();
  96         String recipeThreshold = sbUnderThreshold.append(TAG_ARG).toString();
  97         String recipeOverThreshold = sbUnderThreshold.append(TAG_ARG).toString();
  98 
  99         MethodType mtEmpty = MethodType.methodType(String.class);
 100         MethodType mtUnderThreshold = MethodType.methodType(String.class, underThreshold);
 101         MethodType mtThreshold = MethodType.methodType(String.class, threshold);
 102         MethodType mtOverThreshold = MethodType.methodType(String.class, overThreshold);
 103 
 104 
 105         // Check the basic functionality is working
 106         {
 107             CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, mt);
 108             test("foo42", (String) cs.getTarget().invokeExact("foo", 42));
 109         }
 110 
 111         {
 112             CallSite cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, constants);
 113             test("foo42bar", (String) cs.getTarget().invokeExact("foo", 42));
 114         }
 115 
 116         // Simple factory, check for nulls:
 117         failNPE("Lookup is null",
 118                 () -> StringConcatFactory.makeConcat(null, methodName, mt));
 119 
 120         failNPE("Method name is null",
 121                 () -> StringConcatFactory.makeConcat(lookup, null, mt));
 122 
 123         failNPE("MethodType is null",
 124                 () -> StringConcatFactory.makeConcat(lookup, methodName, null));
 125 
 126         // Advanced factory, check for nulls:
 127         failNPE("Lookup is null",
 128                 () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, constants));
 129 
 130         failNPE("Method name is null",
 131                 () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, constants));
 132 
 133         failNPE("MethodType is null",
 134                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, constants));
 135 
 136         failNPE("Recipe is null",
 137                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, constants));
 138 
 139         failNPE("Constants vararg is null",
 140                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, null));
 141 
 142         // Simple factory, check for return type
 143         fail("Return type: void",
 144                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(void.class, String.class, int.class)));
 145 
 146         fail("Return type: int",
 147                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(int.class, String.class, int.class)));
 148 
 149         fail("Return type: StringBuilder",
 150                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class)));
 151 
 152         ok("Return type: Object",
 153                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class)));
 154 
 155         ok("Return type: CharSequence",
 156                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class)));
 157 
 158         ok("Return type: Serializable",
 159                 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class)));
 160 
 161         // Advanced factory, check for return types
 162         fail("Return type: void",
 163                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, constants));
 164 
 165         fail("Return type: int",
 166                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(int.class, String.class, int.class), recipe, constants));
 167 
 168         fail("Return type: StringBuilder",
 169                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class), recipe, constants));
 170 
 171         ok("Return type: Object",
 172                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, constants));
 173 
 174         ok("Return type: CharSequence",
 175                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, constants));
 176 
 177         ok("Return type: Serializable",
 178                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, constants));
 179 
 180         // Simple factory: check for dynamic arguments overflow
 181         ok("Dynamic arguments is under limit",
 182                 () -> StringConcatFactory.makeConcat(lookup, methodName, mtUnderThreshold));
 183 
 184         ok("Dynamic arguments is at the limit",
 185                 () -> StringConcatFactory.makeConcat(lookup, methodName, mtThreshold));
 186 
 187         fail("Dynamic arguments is over the limit",
 188                 () -> StringConcatFactory.makeConcat(lookup, methodName, mtOverThreshold));
 189 
 190         // Advanced factory: check for dynamic arguments overflow
 191         ok("Dynamic arguments is under limit",
 192                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtUnderThreshold, recipeUnderThreshold, constants));
 193 
 194         ok("Dynamic arguments is at the limit",
 195                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants));
 196 
 197         fail("Dynamic arguments is over the limit",
 198                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtOverThreshold, recipeOverThreshold, constants));
 199 
 200         // Advanced factory: check for mismatched recipe and Constants
 201         ok("Static arguments and recipe match",
 202                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar"));
 203 
 204         fail("Static arguments and recipe mismatch",
 205                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar", "baz"));
 206 
 207         // Advanced factory: check for mismatched recipe and dynamic arguments
 208         fail("Dynamic arguments and recipe mismatch",
 209                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeUnderThreshold, constants));
 210 
 211         ok("Dynamic arguments and recipe match",
 212                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants));
 213 
 214         fail("Dynamic arguments and recipe mismatch",
 215                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeOverThreshold, constants));
 216 
 217         // Test passing array as constant
 218         {
 219             String[] arg = {"boo", "bar"};
 220 
 221             CallSite cs1 = StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST + TAG_CONST, arg);
 222             test("42boobar", (String) cs1.getTarget().invokeExact(42));
 223         }
 224 
 225         // Test passing null constant
 226         ok("Can pass regular constants",
 227                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, "foo"));
 228 
 229         failNPE("Cannot pass null constants",
 230                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, new String[]{null}));
 231 
 232         // Simple factory: test empty arguments
 233         ok("Ok to pass empty arguments",
 234                 () -> StringConcatFactory.makeConcat(lookup, methodName, mtEmpty));
 235 
 236         // Advanced factory: test empty arguments
 237         ok("Ok to pass empty arguments",
 238                 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtEmpty, recipeEmpty));
 239     }
 240 
 241     public static void ok(String msg, Callable runnable) {
 242         try {
 243             runnable.call();
 244         } catch (Throwable e) {
 245             e.printStackTrace();
 246             throw new IllegalStateException(msg + ", should have passed", e);
 247         }
 248     }
 249 
 250     public static void fail(String msg, Callable runnable) {
 251         boolean expected = false;
 252         try {
 253             runnable.call();
 254         } catch (StringConcatException e) {
 255             expected = true;
 256         } catch (Throwable e) {
 257             e.printStackTrace();
 258         }
 259 
 260         if (!expected) {
 261             throw new IllegalStateException(msg + ", should have failed with StringConcatException");
 262         }
 263     }
 264 
 265 
 266     public static void failNPE(String msg, Callable runnable) {
 267         boolean expected = false;
 268         try {
 269             runnable.call();
 270         } catch (NullPointerException e) {
 271             expected = true;
 272         } catch (Throwable e) {
 273             e.printStackTrace();
 274         }
 275 
 276         if (!expected) {
 277             throw new IllegalStateException(msg + ", should have failed with NullPointerException");
 278         }
 279     }
 280 
 281     public static void test(String expected, String actual) {
 282        // Fingers crossed: String concat should work.
 283        if (!expected.equals(actual)) {
 284            StringBuilder sb = new StringBuilder();
 285            sb.append("Expected = ");
 286            sb.append(expected);
 287            sb.append(", actual = ");
 288            sb.append(actual);
 289            throw new IllegalStateException(sb.toString());
 290        }
 291     }
 292 
 293 }