1 /*
  2  * Copyright (c) 2024, 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.lang.runtime.CodeReflection;
 25 import java.util.function.Consumer;
 26 import java.util.function.Supplier;
 27 
 28 /*
 29  * @test
 30  * @summary Smoke test for code reflection with switch expressions.
 31  * @enablePreview
 32  * @build SwitchExpressionTest
 33  * @build CodeReflectionTester
 34  * @run main CodeReflectionTester SwitchExpressionTest
 35  */
 36 
 37 public class SwitchExpressionTest {
 38 
 39     @CodeReflection
 40     @IR("""
 41             func @"constantCaseLabelRule" (%0 : java.lang.String)java.lang.Object -> {
 42                 %1 : Var<java.lang.String> = var %0 @"r";
 43                 %2 : java.lang.String = var.load %1;
 44                 %3 : java.lang.Object = java.switch.expression %2
 45                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
 46                         %5 : java.lang.String = constant @"FOO";
 47                         %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
 48                         yield %6;
 49                     }
 50                     ()java.lang.Object -> {
 51                         %7 : java.lang.String = constant @"FOO";
 52                         yield %7;
 53                     }
 54                     ^constantCaseLabel(%8 : java.lang.String)boolean -> {
 55                         %9 : java.lang.String = constant @"BAR";
 56                         %10 : boolean = invoke %8 %9 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
 57                         yield %10;
 58                     }
 59                     ()java.lang.Object -> {
 60                         %11 : java.lang.String = constant @"FOO";
 61                         yield %11;
 62                     }
 63                     ^constantCaseLabel(%12 : java.lang.String)boolean -> {
 64                         %13 : java.lang.String = constant @"BAZ";
 65                         %14 : boolean = invoke %12 %13 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
 66                         yield %14;
 67                     }
 68                     ()java.lang.Object -> {
 69                         %15 : java.lang.String = constant @"FOO";
 70                         yield %15;
 71                     }
 72                     ^defaultCaseLabel()void -> {
 73                         yield;
 74                     }
 75                     ()java.lang.Object -> {
 76                         %16 : java.lang.String = constant @"";
 77                         yield %16;
 78                     };
 79                 return %3;
 80             };
 81             """)
 82     public static Object constantCaseLabelRule(String r) {
 83         return switch (r) {
 84             case "FOO" -> "FOO";
 85             case "BAR" -> "FOO";
 86             case "BAZ" -> "FOO";
 87             default -> "";
 88         };
 89     }
 90 
 91     @CodeReflection
 92     @IR("""
 93             func @"constantCaseLabelsRule" (%0 : java.lang.String)java.lang.Object -> {
 94                 %1 : Var<java.lang.String> = var %0 @"r";
 95                 %2 : java.lang.String = var.load %1;
 96                 %3 : java.lang.Object = java.switch.expression %2
 97                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
 98                         %5 : boolean = java.cor
 99                             ()boolean -> {
100                                 %6 : java.lang.String = constant @"FOO";
101                                 %7 : boolean = invoke %4 %6 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
102                                 yield %7;
103                             }
104                             ()boolean -> {
105                                 %8 : java.lang.String = constant @"BAR";
106                                 %9 : boolean = invoke %4 %8 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
107                                 yield %9;
108                             }
109                             ()boolean -> {
110                                 %10 : java.lang.String = constant @"BAZ";
111                                 %11 : boolean = invoke %4 %10 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
112                                 yield %11;
113                             };
114                         yield %5;
115                     }
116                     ()java.lang.Object -> {
117                         %12 : java.lang.String = constant @"FOO";
118                         yield %12;
119                     }
120                     ^defaultCaseLabel()void -> {
121                         yield;
122                     }
123                     ()java.lang.Object -> {
124                         %13 : java.lang.String = constant @"";
125                         java.yield %13;
126                     };
127                 return %3;
128             };
129             """)
130     public static Object constantCaseLabelsRule(String r) {
131         return switch (r) {
132             case "FOO", "BAR", "BAZ" -> "FOO";
133             default -> {
134                 yield "";
135             }
136         };
137     }
138 
139     @CodeReflection
140     @IR("""
141             func @"constantCaseLabelStatement" (%0 : java.lang.String)java.lang.Object -> {
142                 %1 : Var<java.lang.String> = var %0 @"r";
143                 %2 : java.lang.String = var.load %1;
144                 %3 : java.lang.Object = java.switch.expression %2
145                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
146                         %5 : java.lang.String = constant @"FOO";
147                         %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
148                         yield %6;
149                     }
150                     ()java.lang.Object -> {
151                         %7 : java.lang.String = constant @"FOO";
152                         java.yield %7;
153                     }
154                     ^constantCaseLabel(%8 : java.lang.String)boolean -> {
155                         %9 : java.lang.String = constant @"BAR";
156                         %10 : boolean = invoke %8 %9 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
157                         yield %10;
158                     }
159                     ()java.lang.Object -> {
160                         %11 : java.lang.String = constant @"FOO";
161                         java.yield %11;
162                     }
163                     ^constantCaseLabel(%12 : java.lang.String)boolean -> {
164                         %13 : java.lang.String = constant @"BAZ";
165                         %14 : boolean = invoke %12 %13 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
166                         yield %14;
167                     }
168                     ()java.lang.Object -> {
169                         %15 : java.lang.String = constant @"FOO";
170                         java.yield %15;
171                     }
172                     ^defaultCaseLabel()void -> {
173                         yield;
174                     }
175                     ()java.lang.Object -> {
176                         %16 : java.lang.String = constant @"";
177                         java.yield %16;
178                     };
179                 return %3;
180             };
181             """)
182     public static Object constantCaseLabelStatement(String r) {
183         return switch (r) {
184             case "FOO" : yield "FOO";
185             case "BAR" : yield "FOO";
186             case "BAZ" : yield "FOO";
187             default : yield "";
188         };
189     }
190 
191     @CodeReflection
192     @IR("""
193             func @"constantCaseLabelsStatement" (%0 : java.lang.String)java.lang.Object -> {
194                 %1 : Var<java.lang.String> = var %0 @"r";
195                 %2 : java.lang.String = var.load %1;
196                 %3 : java.lang.Object = java.switch.expression %2
197                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
198                         %5 : boolean = java.cor
199                             ()boolean -> {
200                                 %6 : java.lang.String = constant @"FOO";
201                                 %7 : boolean = invoke %4 %6 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
202                                 yield %7;
203                             }
204                             ()boolean -> {
205                                 %8 : java.lang.String = constant @"BAR";
206                                 %9 : boolean = invoke %4 %8 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
207                                 yield %9;
208                             }
209                             ()boolean -> {
210                                 %10 : java.lang.String = constant @"BAZ";
211                                 %11 : boolean = invoke %4 %10 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
212                                 yield %11;
213                             };
214                         yield %5;
215                     }
216                     ()java.lang.Object -> {
217                         %12 : java.lang.String = constant @"FOO";
218                         java.yield %12;
219                     }
220                     ^defaultCaseLabel()void -> {
221                         yield;
222                     }
223                     ()java.lang.Object -> {
224                         java.block ()void -> {
225                             %13 : java.lang.String = constant @"";
226                             java.yield %13;
227                         };
228                         unreachable;
229                     };
230                 return %3;
231             };
232             """)
233     public static Object constantCaseLabelsStatement(String r) {
234         return switch (r) {
235             case "FOO", "BAR", "BAZ" : yield "FOO";
236             default : { yield ""; }
237         };
238     }
239 
240     @CodeReflection
241     @IR("""
242             func @"constantCaseLabelStatements" (%0 : java.lang.String)java.lang.Object -> {
243                 %1 : Var<java.lang.String> = var %0 @"r";
244                 %2 : java.lang.String = var.load %1;
245                 %3 : java.lang.Object = java.switch.expression %2
246                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
247                         %5 : java.lang.String = constant @"FOO";
248                         %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
249                         yield %6;
250                     }
251                     ()java.lang.Object -> {
252                         java.block ()void -> {
253                             %7 : java.io.PrintStream = field.load @"java.lang.System::out()java.io.PrintStream";
254                             %8 : java.lang.String = constant @"FOO";
255                             invoke %7 %8 @"java.io.PrintStream::println(java.lang.String)void";
256                             yield;
257                         };
258                         java.block ()void -> {
259                             %9 : java.lang.String = constant @"FOO";
260                             java.yield %9;
261                         };
262                         unreachable;
263                     }
264                     ^defaultCaseLabel()void -> {
265                         yield;
266                     }
267                     ()java.lang.Object -> {
268                         %10 : java.lang.String = constant @"";
269                         java.yield %10;
270                     };
271                 return %3;
272             };
273             """)
274     public static Object constantCaseLabelStatements(String r) {
275         return switch (r) {
276             case "FOO" : {
277                 System.out.println("FOO");
278             }
279             {
280                 yield "FOO";
281             }
282             default : yield "";
283         };
284     }
285 
286     @CodeReflection
287     @IR("""
288             func @"constantCaseLabelFallthrough" (%0 : java.lang.String)java.lang.Object -> {
289                 %1 : Var<java.lang.String> = var %0 @"r";
290                 %2 : java.lang.String = var.load %1;
291                 %3 : java.lang.Object = java.switch.expression %2
292                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
293                         %5 : java.lang.String = constant @"FOO";
294                         %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
295                         yield %6;
296                     }
297                     ()java.lang.Object -> {
298                         java.block ()void -> {
299                             %7 : java.io.PrintStream = field.load @"java.lang.System::out()java.io.PrintStream";
300                             %8 : java.lang.String = constant @"FOO";
301                             invoke %7 %8 @"java.io.PrintStream::println(java.lang.String)void";
302                             yield;
303                         };
304                         java.switch.fallthrough;
305                     }
306                     ^defaultCaseLabel()void -> {
307                         yield;
308                     }
309                     ()java.lang.Object -> {
310                         %9 : java.lang.String = constant @"";
311                         java.yield %9;
312                     };
313                 return %3;
314             };
315             """)
316     public static Object constantCaseLabelFallthrough(String r) {
317         return switch (r) {
318             case "FOO" : {
319                 System.out.println("FOO");
320             }
321             default : yield "";
322         };
323     }
324 
325     record A(Number n) {
326     }
327 
328     @CodeReflection
329     @IR("""
330             func @"patternCaseLabel" (%0 : java.lang.Object)java.lang.Object -> {
331                 %1 : Var<java.lang.Object> = var %0 @"r";
332                 %2 : java.lang.Object = var.load %1;
333                 %3 : java.lang.Number = constant @null;
334                 %4 : Var<java.lang.Number> = var %3 @"n";
335                 %5 : java.lang.String = constant @null;
336                 %6 : Var<java.lang.String> = var %5 @"s";
337                 %7 : java.lang.Object = java.switch.expression %2
338                     ^patternCaseLabel(%8 : java.lang.Object)boolean -> {
339                         %9 : boolean = pattern.match %8
340                             ^pattern()java.lang.reflect.code.ExtendedOp$Pattern$Record<SwitchExpressionTest$A> -> {
341                                 %10 : java.lang.reflect.code.ExtendedOp$Pattern$Binding<java.lang.Number> = pattern.binding @"n";
342                                 %11 : java.lang.reflect.code.ExtendedOp$Pattern$Record<SwitchExpressionTest$A> = pattern.record %10 @"(java.lang.Number n)SwitchExpressionTest$A";
343                                 yield %11;
344                             }
345                             ^match(%12 : java.lang.Number)void -> {
346                                 var.store %4 %12;
347                                 yield;
348                             };
349                         yield %9;
350                     }
351                     ()java.lang.Object -> {
352                         %13 : java.lang.Number = var.load %4;
353                         java.yield %13;
354                     }
355                     ^patternCaseLabel(%14 : java.lang.Object)boolean -> {
356                         %15 : boolean = pattern.match %14
357                             ^pattern()java.lang.reflect.code.ExtendedOp$Pattern$Binding<java.lang.String> -> {
358                                 %16 : java.lang.reflect.code.ExtendedOp$Pattern$Binding<java.lang.String> = pattern.binding @"s";
359                                 yield %16;
360                             }
361                             ^match(%17 : java.lang.String)void -> {
362                                 var.store %6 %17;
363                                 yield;
364                             };
365                         yield %15;
366                     }
367                     ()java.lang.Object -> {
368                         %18 : java.lang.String = var.load %6;
369                         java.yield %18;
370                     }
371                     ^defaultCaseLabel()void -> {
372                         yield;
373                     }
374                     ()java.lang.Object -> {
375                         %19 : java.lang.String = constant @"";
376                         java.yield %19;
377                     };
378                 return %7;
379             };
380             """)
381     public static Object patternCaseLabel(Object r) {
382         return switch (r) {
383             case A(Number n) -> {
384                 yield n;
385             }
386             case String s -> {
387                 yield s;
388             }
389             default -> {
390                 yield "";
391             }
392         };
393     }
394 
395     @CodeReflection
396     @IR("""
397             func @"patternCaseLabelGuard" (%0 : java.lang.Object)java.lang.Object -> {
398                 %1 : Var<java.lang.Object> = var %0 @"r";
399                 %2 : java.lang.Object = var.load %1;
400                 %3 : java.lang.Number = constant @null;
401                 %4 : Var<java.lang.Number> = var %3 @"n";
402                 %5 : java.lang.String = constant @null;
403                 %6 : Var<java.lang.String> = var %5 @"s";
404                 %7 : java.lang.String = constant @null;
405                 %8 : Var<java.lang.String> = var %7 @"s";
406                 %9 : java.lang.Object = java.switch.expression %2
407                     ^patternCaseLabel(%10 : java.lang.Object)boolean -> {
408                         %11 : boolean = pattern.match %10
409                             ^pattern()java.lang.reflect.code.ExtendedOp$Pattern$Record<SwitchExpressionTest$A> -> {
410                                 %12 : java.lang.reflect.code.ExtendedOp$Pattern$Binding<java.lang.Number> = pattern.binding @"n";
411                                 %13 : java.lang.reflect.code.ExtendedOp$Pattern$Record<SwitchExpressionTest$A> = pattern.record %12 @"(java.lang.Number n)SwitchExpressionTest$A";
412                                 yield %13;
413                             }
414                             ^match(%14 : java.lang.Number)void -> {
415                                 var.store %4 %14;
416                                 yield;
417                             };
418                         yield %11;
419                     }
420                     ()java.lang.Object -> {
421                         %15 : java.lang.Number = var.load %4;
422                         java.yield %15;
423                     }
424                     ^patternCaseLabel(%16 : java.lang.Object)boolean -> {
425                         %17 : boolean = java.cand
426                             ()boolean -> {
427                                 %18 : boolean = pattern.match %16
428                                     ^pattern()java.lang.reflect.code.ExtendedOp$Pattern$Binding<java.lang.String> -> {
429                                         %19 : java.lang.reflect.code.ExtendedOp$Pattern$Binding<java.lang.String> = pattern.binding @"s";
430                                         yield %19;
431                                     }
432                                     ^match(%20 : java.lang.String)void -> {
433                                         var.store %6 %20;
434                                         yield;
435                                     };
436                                 yield %18;
437                             }
438                             ()boolean -> {
439                                 %21 : java.lang.String = var.load %6;
440                                 %22 : int = invoke %21 @"java.lang.String::length()int";
441                                 %23 : int = constant @"5";
442                                 %24 : boolean = lt %22 %23;
443                                 yield %24;
444                             };
445                         yield %17;
446                     }
447                     ()java.lang.Object -> {
448                         %25 : java.lang.String = var.load %6;
449                         java.yield %25;
450                     }
451                     ^patternCaseLabel(%26 : java.lang.Object)boolean -> {
452                         %27 : boolean = java.cand
453                             ()boolean -> {
454                                 %28 : boolean = pattern.match %26
455                                     ^pattern()java.lang.reflect.code.ExtendedOp$Pattern$Binding<java.lang.String> -> {
456                                         %29 : java.lang.reflect.code.ExtendedOp$Pattern$Binding<java.lang.String> = pattern.binding @"s";
457                                         yield %29;
458                                     }
459                                     ^match(%30 : java.lang.String)void -> {
460                                         var.store %8 %30;
461                                         yield;
462                                     };
463                                 yield %28;
464                             }
465                             ()boolean -> {
466                                 %31 : java.lang.String = var.load %8;
467                                 %32 : int = invoke %31 @"java.lang.String::length()int";
468                                 %33 : int = constant @"10";
469                                 %34 : boolean = lt %32 %33;
470                                 yield %34;
471                             };
472                         yield %27;
473                     }
474                     ()java.lang.Object -> {
475                         %35 : java.lang.String = var.load %8;
476                         java.yield %35;
477                     }
478                     ^defaultCaseLabel()void -> {
479                         yield;
480                     }
481                     ()java.lang.Object -> {
482                         %36 : java.lang.String = constant @"";
483                         java.yield %36;
484                     };
485                 return %9;
486             };
487             """)
488     public static Object patternCaseLabelGuard(Object r) {
489         return switch (r) {
490             case A(Number n) -> {
491                 yield n;
492             }
493             case String s when s.length() < 5 -> {
494                 yield s;
495             }
496             case String s when s.length() < 10 -> {
497                 yield s;
498             }
499             default -> {
500                 yield "";
501             }
502         };
503     }
504 }