1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import annotations.BootstrapMethod;
18 import annotations.CalledByIndy;
19 import annotations.Constant;
20 import java.lang.invoke.CallSite;
21 import java.lang.invoke.ConstantCallSite;
22 import java.lang.invoke.MethodHandle;
23 import java.lang.invoke.MethodHandles;
24 import java.lang.invoke.MethodType;
25 import java.util.Arrays;
26 
27 public class TestVariableArityLinkerMethod extends TestBase {
printBsmArgs(String method, Object... args)28     private static void printBsmArgs(String method, Object... args) {
29         System.out.print(method);
30         System.out.print("(");
31         for (int i = 0; i < args.length; ++i) {
32             if (i != 0) {
33                 System.out.print(", ");
34             }
35             if (args[i] != null && args[i].getClass().isArray()) {
36                 Object array = args[i];
37                 if (array.getClass() == int[].class) {
38                     System.out.print(Arrays.toString((int[]) array));
39                 } else if (array.getClass() == long[].class) {
40                     System.out.print(Arrays.toString((long[]) array));
41                 } else if (array.getClass() == float[].class) {
42                     System.out.print(Arrays.toString((float[]) array));
43                 } else if (array.getClass() == double[].class) {
44                     System.out.print(Arrays.toString((double[]) array));
45                 } else {
46                     System.out.print(Arrays.toString((Object[]) array));
47                 }
48             } else {
49                 System.out.print(args[i]);
50             }
51         }
52         System.out.println(");");
53     }
54 
bsmWithStringArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, String... arityArgs)55     private static CallSite bsmWithStringArray(
56             MethodHandles.Lookup lookup,
57             String methodName,
58             MethodType methodType,
59             String... arityArgs)
60             throws Throwable {
61         printBsmArgs("bsmWithStringArray", lookup, methodName, methodType, arityArgs);
62         MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
63         return new ConstantCallSite(mh);
64     }
65 
66     @CalledByIndy(
67         bootstrapMethod =
68                 @BootstrapMethod(
69                     enclosingType = TestVariableArityLinkerMethod.class,
70                     name = "bsmWithStringArray",
71                     parameterTypes = {
72                         MethodHandles.Lookup.class,
73                         String.class,
74                         MethodType.class,
75                         String[].class
76                     }
77                 ),
78         fieldOrMethodName = "methodA",
79         constantArgumentsForBootstrapMethod = {
80             @Constant(stringValue = "Aachen"),
81             @Constant(stringValue = "Aalborg"),
82             @Constant(stringValue = "Aalto")
83         }
84     )
methodA()85     private static void methodA() {
86         System.out.println("methodA");
87     }
88 
89     @CalledByIndy(
90         bootstrapMethod =
91                 @BootstrapMethod(
92                     enclosingType = TestVariableArityLinkerMethod.class,
93                     name = "bsmWithStringArray",
94                     parameterTypes = {
95                         MethodHandles.Lookup.class,
96                         String.class,
97                         MethodType.class,
98                         String[].class
99                     }
100                 ),
101         fieldOrMethodName = "methodB",
102         constantArgumentsForBootstrapMethod = {@Constant(stringValue = "barium")}
103     )
methodB()104     private static void methodB() {
105         System.out.println("methodB");
106     }
107 
108     @CalledByIndy(
109         bootstrapMethod =
110                 @BootstrapMethod(
111                     enclosingType = TestVariableArityLinkerMethod.class,
112                     name = "bsmWithStringArray",
113                     parameterTypes = {
114                         MethodHandles.Lookup.class,
115                         String.class,
116                         MethodType.class,
117                         String[].class
118                     }
119                 ),
120         fieldOrMethodName = "methodC"
121     )
methodC()122     private static void methodC() {
123         System.out.println("methodC");
124     }
125 
bsmWithIntAndStringArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, int extraInt, String... extraArityArgs)126     private static CallSite bsmWithIntAndStringArray(
127             MethodHandles.Lookup lookup,
128             String methodName,
129             MethodType methodType,
130             int extraInt,
131             String... extraArityArgs)
132             throws Throwable {
133         printBsmArgs(
134                 "bsmWithIntAndStringArray",
135                 lookup,
136                 methodName,
137                 methodType,
138                 extraInt,
139                 extraArityArgs);
140         MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
141         return new ConstantCallSite(mh);
142     }
143 
144     @CalledByIndy(
145         bootstrapMethod =
146                 @BootstrapMethod(
147                     enclosingType = TestVariableArityLinkerMethod.class,
148                     name = "bsmWithIntAndStringArray",
149                     parameterTypes = {
150                         MethodHandles.Lookup.class,
151                         String.class,
152                         MethodType.class,
153                         int.class,
154                         String[].class
155                     }
156                 ),
157         fieldOrMethodName = "methodD",
158         constantArgumentsForBootstrapMethod = {
159             @Constant(intValue = 101),
160             @Constant(stringValue = "zoo"),
161             @Constant(stringValue = "zoogene"),
162             @Constant(stringValue = "zoogenic")
163         }
164     )
methodD()165     private static void methodD() {
166         System.out.println("methodD");
167     }
168 
169     @CalledByIndy(
170         bootstrapMethod =
171                 @BootstrapMethod(
172                     enclosingType = TestVariableArityLinkerMethod.class,
173                     name = "bsmWithIntAndStringArray",
174                     parameterTypes = {
175                         MethodHandles.Lookup.class,
176                         String.class,
177                         MethodType.class,
178                         int.class,
179                         String[].class
180                     }
181                 ),
182         fieldOrMethodName = "methodE",
183         constantArgumentsForBootstrapMethod = {
184             @Constant(intValue = 102),
185             @Constant(stringValue = "zonic")
186         }
187     )
methodE()188     private static void methodE() {
189         System.out.println("methodE");
190     }
191 
192     @CalledByIndy(
193         bootstrapMethod =
194                 @BootstrapMethod(
195                     enclosingType = TestVariableArityLinkerMethod.class,
196                     name = "bsmWithIntAndStringArray",
197                     parameterTypes = {
198                         MethodHandles.Lookup.class,
199                         String.class,
200                         MethodType.class,
201                         int.class,
202                         String[].class
203                     }
204                 ),
205         fieldOrMethodName = "methodF",
206         constantArgumentsForBootstrapMethod = {@Constant(intValue = 103)}
207     )
methodF()208     private static void methodF() {
209         System.out.println("methodF");
210     }
211 
bsmWithLongAndIntArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, long extraArg, int... arityArgs)212     private static CallSite bsmWithLongAndIntArray(
213             MethodHandles.Lookup lookup,
214             String methodName,
215             MethodType methodType,
216             long extraArg,
217             int... arityArgs)
218             throws Throwable {
219         printBsmArgs("bsmWithLongAndIntArray", lookup, methodName, methodType, extraArg, arityArgs);
220         MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
221         return new ConstantCallSite(mh);
222     }
223 
224     @CalledByIndy(
225         bootstrapMethod =
226                 @BootstrapMethod(
227                     enclosingType = TestVariableArityLinkerMethod.class,
228                     name = "bsmWithLongAndIntArray",
229                     parameterTypes = {
230                         MethodHandles.Lookup.class,
231                         String.class,
232                         MethodType.class,
233                         long.class,
234                         int[].class
235                     }
236                 ),
237         fieldOrMethodName = "methodG",
238         constantArgumentsForBootstrapMethod = {
239             @Constant(longValue = 0x123456789abcdefl),
240             @Constant(intValue = +1),
241             @Constant(intValue = -1),
242             @Constant(intValue = +2),
243             @Constant(intValue = -2)
244         }
245     )
methodG()246     private static void methodG() {
247         System.out.println("methodG");
248     }
249 
bsmWithFloatAndLongArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, float extraArg, long... arityArgs)250     private static CallSite bsmWithFloatAndLongArray(
251             MethodHandles.Lookup lookup,
252             String methodName,
253             MethodType methodType,
254             float extraArg,
255             long... arityArgs)
256             throws Throwable {
257         printBsmArgs(
258                 "bsmWithFloatAndLongArray", lookup, methodName, methodType, extraArg, arityArgs);
259         MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
260         return new ConstantCallSite(mh);
261     }
262 
263     @CalledByIndy(
264         bootstrapMethod =
265                 @BootstrapMethod(
266                     enclosingType = TestVariableArityLinkerMethod.class,
267                     name = "bsmWithFloatAndLongArray",
268                     parameterTypes = {
269                         MethodHandles.Lookup.class,
270                         String.class,
271                         MethodType.class,
272                         float.class,
273                         long[].class
274                     }
275                 ),
276         fieldOrMethodName = "methodH",
277         constantArgumentsForBootstrapMethod = {
278             @Constant(floatValue = (float) -Math.E),
279             @Constant(longValue = 999999999999l),
280             @Constant(longValue = -8888888888888l)
281         }
282     )
methodH()283     private static void methodH() {
284         System.out.println("methodH");
285     }
286 
bsmWithClassAndFloatArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, Class<?> extraArg, float... arityArgs)287     private static CallSite bsmWithClassAndFloatArray(
288             MethodHandles.Lookup lookup,
289             String methodName,
290             MethodType methodType,
291             Class<?> extraArg,
292             float... arityArgs)
293             throws Throwable {
294         printBsmArgs(
295                 "bsmWithClassAndFloatArray", lookup, methodName, methodType, extraArg, arityArgs);
296         MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
297         return new ConstantCallSite(mh);
298     }
299 
300     @CalledByIndy(
301         bootstrapMethod =
302                 @BootstrapMethod(
303                     enclosingType = TestVariableArityLinkerMethod.class,
304                     name = "bsmWithClassAndFloatArray",
305                     parameterTypes = {
306                         MethodHandles.Lookup.class,
307                         String.class,
308                         MethodType.class,
309                         Class.class,
310                         float[].class
311                     }
312                 ),
313         fieldOrMethodName = "methodI",
314         constantArgumentsForBootstrapMethod = {
315             @Constant(classValue = Throwable.class),
316             @Constant(floatValue = Float.MAX_VALUE),
317             @Constant(floatValue = Float.MIN_VALUE),
318             @Constant(floatValue = (float) Math.PI),
319             @Constant(floatValue = (float) -Math.PI)
320         }
321     )
methodI()322     private static void methodI() {
323         System.out.println("methodI");
324     }
325 
bsmWithDoubleArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, double... arityArgs)326     private static CallSite bsmWithDoubleArray(
327             MethodHandles.Lookup lookup,
328             String methodName,
329             MethodType methodType,
330             double... arityArgs)
331             throws Throwable {
332         printBsmArgs("bsmWithDoubleArray", lookup, methodName, methodType, arityArgs);
333         MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
334         return new ConstantCallSite(mh);
335     }
336 
337     @CalledByIndy(
338         bootstrapMethod =
339                 @BootstrapMethod(
340                     enclosingType = TestVariableArityLinkerMethod.class,
341                     name = "bsmWithDoubleArray",
342                     parameterTypes = {
343                         MethodHandles.Lookup.class,
344                         String.class,
345                         MethodType.class,
346                         double[].class
347                     }
348                 ),
349         fieldOrMethodName = "methodJ",
350         constantArgumentsForBootstrapMethod = {
351             @Constant(doubleValue = Double.MAX_VALUE),
352             @Constant(doubleValue = Double.MIN_VALUE),
353             @Constant(doubleValue = Math.E),
354             @Constant(doubleValue = -Math.PI)
355         }
356     )
methodJ()357     private static void methodJ() {
358         System.out.println("methodJ");
359     }
360 
bsmWithClassArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, Class... arityArgs)361     private static CallSite bsmWithClassArray(
362             MethodHandles.Lookup lookup,
363             String methodName,
364             MethodType methodType,
365             Class... arityArgs)
366             throws Throwable {
367         printBsmArgs("bsmWithClassArray", lookup, methodName, methodType, arityArgs);
368         MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
369         return new ConstantCallSite(mh);
370     }
371 
372     @CalledByIndy(
373         bootstrapMethod =
374                 @BootstrapMethod(
375                     enclosingType = TestVariableArityLinkerMethod.class,
376                     name = "bsmWithClassArray",
377                     parameterTypes = {
378                         MethodHandles.Lookup.class,
379                         String.class,
380                         MethodType.class,
381                         Class[].class
382                     }
383                 ),
384         fieldOrMethodName = "methodK",
385         constantArgumentsForBootstrapMethod = {
386             @Constant(classValue = Integer.class),
387             @Constant(classValue = MethodHandles.class),
388             @Constant(classValue = Arrays.class)
389         }
390     )
methodK()391     private static void methodK() {
392         System.out.println("methodK");
393     }
394 
395     @CalledByIndy(
396         bootstrapMethod =
397                 @BootstrapMethod(
398                     enclosingType = TestVariableArityLinkerMethod.class,
399                     name = "bsmWithIntAndStringArray",
400                     parameterTypes = {
401                         MethodHandles.Lookup.class,
402                         String.class,
403                         MethodType.class,
404                         int.class,
405                         String[].class
406                     }
407                 ),
408         fieldOrMethodName = "methodO",
409         constantArgumentsForBootstrapMethod = {@Constant(intValue = 103), @Constant(intValue = 104)}
410     )
methodO()411     private static void methodO() {
412         // Arguments are not compatible
413         assertNotReached();
414     }
415 
416     @CalledByIndy(
417         bootstrapMethod =
418                 @BootstrapMethod(
419                     enclosingType = TestVariableArityLinkerMethod.class,
420                     name = "bsmWithIntAndStringArray",
421                     parameterTypes = {
422                         MethodHandles.Lookup.class,
423                         String.class,
424                         MethodType.class,
425                         int.class,
426                         String[].class
427                     }
428                 ),
429         fieldOrMethodName = "methodP",
430         constantArgumentsForBootstrapMethod = {
431             @Constant(intValue = 103),
432             @Constant(stringValue = "A"),
433             @Constant(stringValue = "B"),
434             @Constant(intValue = 42)
435         }
436     )
methodP()437     private static void methodP() {
438         // Arguments are not compatible - specifically, the third
439         // component of potential collector array is an integer
440         // argument (42).
441         assertNotReached();
442     }
443 
bsmWithWiderArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, long[] extraArgs)444     private static CallSite bsmWithWiderArray(
445             MethodHandles.Lookup lookup, String methodName, MethodType methodType, long[] extraArgs)
446             throws Throwable {
447         printBsmArgs("bsmWithWiderArray", lookup, methodName, methodType, extraArgs);
448         MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
449         return new ConstantCallSite(mh);
450     }
451 
452     @CalledByIndy(
453         bootstrapMethod =
454                 @BootstrapMethod(
455                     enclosingType = TestVariableArityLinkerMethod.class,
456                     name = "bsmWithWiderArray",
457                     parameterTypes = {
458                         MethodHandles.Lookup.class,
459                         String.class,
460                         MethodType.class,
461                         long[].class
462                     }
463                 ),
464         fieldOrMethodName = "methodQ",
465         constantArgumentsForBootstrapMethod = {@Constant(intValue = 103), @Constant(intValue = 42)}
466     )
methodQ()467     private static void methodQ() {
468         assertNotReached();
469     }
470 
bsmWithBoxedArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, Integer[] extraArgs)471     private static CallSite bsmWithBoxedArray(
472             MethodHandles.Lookup lookup,
473             String methodName,
474             MethodType methodType,
475             Integer[] extraArgs)
476             throws Throwable {
477         printBsmArgs("bsmWithBoxedArray", lookup, methodName, methodType, extraArgs);
478         MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
479         return new ConstantCallSite(mh);
480     }
481 
482     @CalledByIndy(
483         bootstrapMethod =
484                 @BootstrapMethod(
485                     enclosingType = TestVariableArityLinkerMethod.class,
486                     name = "bsmWithBoxedArray",
487                     parameterTypes = {
488                         MethodHandles.Lookup.class,
489                         String.class,
490                         MethodType.class,
491                         Integer[].class
492                     }
493                 ),
494         fieldOrMethodName = "methodR",
495         constantArgumentsForBootstrapMethod = {
496             @Constant(intValue = 1030),
497             @Constant(intValue = 420)
498         }
499     )
methodR()500     private static void methodR() {
501         assertNotReached();
502     }
503 
test()504     static void test() {
505         // Happy cases
506         for (int i = 0; i < 2; ++i) {
507             methodA();
508             methodB();
509             methodC();
510         }
511         for (int i = 0; i < 2; ++i) {
512             methodD();
513             methodE();
514             methodF();
515         }
516         methodG();
517         methodH();
518         methodI();
519         methodJ();
520         methodK();
521 
522         // Broken cases
523         try {
524             // bsm has incompatible static methods. Collector
525             // component type is String, the corresponding static
526             // arguments are int values.
527             methodO();
528             assertNotReached();
529         } catch (BootstrapMethodError expected) {
530             System.out.print("methodO => ");
531             System.out.print(expected.getClass());
532             System.out.print(" => ");
533             System.out.println(expected.getCause().getClass());
534         }
535         try {
536             // bsm has a trailing String array for the collector array.
537             // There is an int value amongst the String values.
538             methodP();
539             assertNotReached();
540         } catch (BootstrapMethodError expected) {
541             System.out.print("methodP => ");
542             System.out.print(expected.getClass());
543             System.out.print(" => ");
544             System.out.println(expected.getCause().getClass());
545         }
546         try {
547             // bsm has as trailing long[] element for the collector array.
548             // The corresponding static bsm arguments are of type int.
549             methodQ();
550             assertNotReached();
551         } catch (BootstrapMethodError expected) {
552             System.out.print("methodQ => ");
553             System.out.print(expected.getClass());
554             System.out.print(" => ");
555             System.out.println(expected.getCause().getClass());
556         }
557         try {
558             // bsm has as trailing Integer[] element for the collector array.
559             // The corresponding static bsm arguments are of type int.
560             methodR();
561             assertNotReached();
562         } catch (BootstrapMethodError expected) {
563             System.out.print("methodR => ");
564             System.out.print(expected.getClass());
565             System.out.print(" => ");
566             System.out.println(expected.getCause().getClass());
567         }
568     }
569 }
570