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 Object[][] { 74 new String[] { "bar" }, 75 new Integer[] { 1 }, 76 new Short[] { 2 }, 77 new Long[] { 3L }, 78 new Boolean[] { true }, 79 new Character[] { 'a' }, 80 new Byte[] { -128 }, 81 new Class[] { String.class }, 82 new MethodHandle[] { MethodHandles.constant(String.class, "constant") }, 83 new MethodType[] { MethodType.methodType(String.class) } 84 }; 85 // The string representation that should end up if the corresponding 86 // Object[] in constants is used as an argument to makeConcatWithConstants 87 String[] constantString = new String[] { 88 "bar", 89 "1", 90 "2", 91 "3", 92 "true", 93 "a", 94 "-128", 95 "class java.lang.String", 96 "MethodHandle()String", 97 "()String" 98 }; 99 100 101 final int LIMIT = 200; 102 103 // Simple factory: check for dynamic arguments overflow 104 Class<?>[] underThreshold = new Class<?>[LIMIT - 1]; 105 Class<?>[] threshold = new Class<?>[LIMIT]; 106 Class<?>[] overThreshold = new Class<?>[LIMIT + 1]; 107 108 StringBuilder sbUnderThreshold = new StringBuilder(); 109 sbUnderThreshold.append(TAG_CONST); 110 for (int c = 0; c < LIMIT - 1; c++) { 111 underThreshold[c] = int.class; 112 threshold[c] = int.class; 113 overThreshold[c] = int.class; 114 sbUnderThreshold.append(TAG_ARG); 115 } 116 threshold[LIMIT - 1] = int.class; 117 overThreshold[LIMIT - 1] = int.class; 118 overThreshold[LIMIT] = int.class; 119 120 String recipeEmpty = ""; 121 String recipeUnderThreshold = sbUnderThreshold.toString(); 122 String recipeThreshold = sbUnderThreshold.append(TAG_ARG).toString(); 123 String recipeOverThreshold = sbUnderThreshold.append(TAG_ARG).toString(); 124 125 MethodType mtEmpty = MethodType.methodType(String.class); 126 MethodType mtUnderThreshold = MethodType.methodType(String.class, underThreshold); 127 MethodType mtThreshold = MethodType.methodType(String.class, threshold); 128 MethodType mtOverThreshold = MethodType.methodType(String.class, overThreshold); 129 130 131 // Check the basic functionality is working 132 { 133 CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, mt); 134 test("foo42", (String) cs.getTarget().invokeExact("foo", 42)); 135 } 136 137 { 138 for (int i = 0; i < constants.length; i++) { 139 CallSite cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, constants[i]); 140 test("foo42".concat(constantString[i]), (String) cs.getTarget().invokeExact("foo", 42)); 141 } 142 } 143 144 // Simple factory, check for nulls: 145 failNPE("Lookup is null", 146 () -> StringConcatFactory.makeConcat(null, methodName, mt)); 147 148 failNPE("Method name is null", 149 () -> StringConcatFactory.makeConcat(lookup, null, mt)); 150 151 failNPE("MethodType is null", 152 () -> StringConcatFactory.makeConcat(lookup, methodName, null)); 153 154 // Advanced factory, check for nulls: 155 for (int i = 0; i < constants.length; i++) { 156 final Object[] consts = constants[i]; 157 158 failNPE("Lookup is null", 159 () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, consts)); 160 161 failNPE("Method name is null", 162 () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, consts)); 163 164 failNPE("MethodType is null", 165 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, consts)); 166 167 failNPE("Recipe is null", 168 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, consts)); 169 } 170 171 failNPE("Constants vararg is null", 172 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, (Object[]) null)); 173 174 failNPE("Constant argument is null", 175 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, new Object[] { null })); 176 177 // Simple factory, check for return type 178 fail("Return type: void", 179 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(void.class, String.class, int.class))); 180 181 fail("Return type: int", 182 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(int.class, String.class, int.class))); 183 184 fail("Return type: StringBuilder", 185 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class))); 186 187 ok("Return type: Object", 188 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class))); 189 190 ok("Return type: CharSequence", 191 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class))); 192 193 ok("Return type: Serializable", 194 () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class))); 195 196 // Advanced factory, check for return types 197 for (int i = 0; i < constants.length; i++) { 198 final Object[] consts = constants[i]; 199 fail("Return type: void", 200 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, consts)); 201 202 fail("Return type: int", 203 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(int.class, String.class, int.class), recipe, consts)); 204 205 fail("Return type: StringBuilder", 206 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class), recipe, consts)); 207 208 ok("Return type: Object", 209 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, consts)); 210 211 ok("Return type: CharSequence", 212 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, consts)); 213 214 ok("Return type: Serializable", 215 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, consts)); 216 } 217 218 // Simple factory: check for dynamic arguments overflow 219 ok("Dynamic arguments is under limit", 220 () -> StringConcatFactory.makeConcat(lookup, methodName, mtUnderThreshold)); 221 222 ok("Dynamic arguments is at the limit", 223 () -> StringConcatFactory.makeConcat(lookup, methodName, mtThreshold)); 224 225 fail("Dynamic arguments is over the limit", 226 () -> StringConcatFactory.makeConcat(lookup, methodName, mtOverThreshold)); 227 228 // Advanced factory: check for dynamic arguments overflow 229 ok("Dynamic arguments is under limit", 230 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtUnderThreshold, recipeUnderThreshold, constants[0])); 231 232 ok("Dynamic arguments is at the limit", 233 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants[0])); 234 235 fail("Dynamic arguments is over the limit", 236 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtOverThreshold, recipeOverThreshold, constants[0])); 237 238 // Advanced factory: check for mismatched recipe and Constants 239 ok("Static arguments and recipe match", 240 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar")); 241 242 fail("Static arguments and recipe mismatch", 243 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar", "baz")); 244 245 // Advanced factory: check for mismatched recipe and dynamic arguments 246 fail("Dynamic arguments and recipe mismatch", 247 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeUnderThreshold, constants[0])); 248 249 ok("Dynamic arguments and recipe match", 250 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants[0])); 251 252 fail("Dynamic arguments and recipe mismatch", 253 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeOverThreshold, constants[0])); 254 255 // Test passing array as constant 256 { 257 Object[] arg = {"boo", "bar"}; 258 259 CallSite cs1 = StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST + TAG_CONST, arg); 260 test("42boobar", (String) cs1.getTarget().invokeExact(42)); 261 } 262 263 // Test passing null constant 264 ok("Can pass regular constants", 265 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, "foo")); 266 267 failNPE("Cannot pass null constants", 268 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, new Object[]{null})); 269 270 // Simple factory: test empty arguments 271 ok("Ok to pass empty arguments", 272 () -> StringConcatFactory.makeConcat(lookup, methodName, mtEmpty)); 273 274 // Advanced factory: test empty arguments 275 ok("Ok to pass empty arguments", 276 () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtEmpty, recipeEmpty)); 277 278 // Simple factory: public Lookup is rejected 279 fail("Passing public Lookup", 280 () -> StringConcatFactory.makeConcat(MethodHandles.publicLookup(), methodName, mtEmpty)); 281 282 // Advanced factory: public Lookup is rejected 283 fail("Passing public Lookup", 284 () -> StringConcatFactory.makeConcatWithConstants(MethodHandles.publicLookup(), methodName, mtEmpty, recipeEmpty)); 285 } 286 287 public static void ok(String msg, Callable runnable) { 288 try { 289 runnable.call(); 290 } catch (Throwable e) { 291 e.printStackTrace(); 292 throw new IllegalStateException(msg + ", should have passed", e); 293 } 294 } 295 296 public static void fail(String msg, Callable runnable) { 297 boolean expected = false; 298 try { 299 runnable.call(); 300 } catch (StringConcatException e) { 301 expected = true; 302 } catch (Throwable e) { 303 e.printStackTrace(); 304 } 305 306 if (!expected) { 307 throw new IllegalStateException(msg + ", should have failed with StringConcatException"); 308 } 309 } 310 311 312 public static void failNPE(String msg, Callable runnable) { 313 boolean expected = false; 314 try { 315 runnable.call(); 316 } catch (NullPointerException e) { 317 expected = true; 318 } catch (Throwable e) { 319 e.printStackTrace(); 320 } 321 322 if (!expected) { 323 throw new IllegalStateException(msg + ", should have failed with NullPointerException"); 324 } 325 } 326 327 public static void test(String expected, String actual) { 328 // Fingers crossed: String concat should work. 329 if (!expected.equals(actual)) { 330 StringBuilder sb = new StringBuilder(); 331 sb.append("Expected = "); 332 sb.append(expected); 333 sb.append(", actual = "); 334 sb.append(actual); 335 throw new IllegalStateException(sb.toString()); 336 } 337 } 338 339 }