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 
main(String[] args)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         // Zero inputs
287         {
288             MethodType zero = MethodType.methodType(String.class);
289             CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, zero);
290             test("", (String) cs.getTarget().invokeExact());
291 
292             cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, zero, "");
293             test("", (String) cs.getTarget().invokeExact());
294         }
295 
296         // One input
297         {
298             MethodType zero = MethodType.methodType(String.class);
299             MethodType one = MethodType.methodType(String.class, String.class);
300             CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, one);
301             test("A", (String) cs.getTarget().invokeExact("A"));
302 
303             cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, one, "\1");
304             test("A", (String) cs.getTarget().invokeExact("A"));
305 
306             cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, zero, "\2", "A");
307             test("A", (String) cs.getTarget().invokeExact());
308         }
309     }
310 
ok(String msg, Callable runnable)311     public static void ok(String msg, Callable runnable) {
312         try {
313             runnable.call();
314         } catch (Throwable e) {
315             e.printStackTrace();
316             throw new IllegalStateException(msg + ", should have passed", e);
317         }
318     }
319 
fail(String msg, Callable runnable)320     public static void fail(String msg, Callable runnable) {
321         boolean expected = false;
322         try {
323             runnable.call();
324         } catch (StringConcatException e) {
325             expected = true;
326         } catch (Throwable e) {
327             e.printStackTrace();
328         }
329 
330         if (!expected) {
331             throw new IllegalStateException(msg + ", should have failed with StringConcatException");
332         }
333     }
334 
335 
failNPE(String msg, Callable runnable)336     public static void failNPE(String msg, Callable runnable) {
337         boolean expected = false;
338         try {
339             runnable.call();
340         } catch (NullPointerException e) {
341             expected = true;
342         } catch (Throwable e) {
343             e.printStackTrace();
344         }
345 
346         if (!expected) {
347             throw new IllegalStateException(msg + ", should have failed with NullPointerException");
348         }
349     }
350 
test(String expected, String actual)351     public static void test(String expected, String actual) {
352        // Fingers crossed: String concat should work.
353        if (!expected.equals(actual)) {
354            StringBuilder sb = new StringBuilder();
355            sb.append("Expected = ");
356            sb.append(expected);
357            sb.append(", actual = ");
358            sb.append(actual);
359            throw new IllegalStateException(sb.toString());
360        }
361     }
362 
363 }
364