1 /*
2 * Copyright (c) 2025, 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.classfile.ClassFile;
25 import java.lang.classfile.MethodModel;
26 import java.lang.classfile.instruction.LookupSwitchInstruction;
27 import java.lang.classfile.instruction.TableSwitchInstruction;
28 import jdk.incubator.code.Reflect;
29 import jdk.incubator.code.Op;
30 import jdk.incubator.code.bytecode.BytecodeGenerator;
31 import jdk.incubator.code.dialect.core.CoreOp;
32 import org.junit.jupiter.api.Assertions;
33 import org.junit.jupiter.params.ParameterizedTest;
34 import org.junit.jupiter.params.provider.ValueSource;
35
36 import java.lang.invoke.MethodHandle;
37 import java.lang.invoke.MethodHandles;
38 import java.lang.reflect.Method;
39 import java.util.Optional;
40 import java.util.stream.Stream;
41
42 /*
43 * @test
44 * @modules jdk.incubator.code/jdk.incubator.code.bytecode.impl
45 * @run junit TestSwitch
46 */
47 public class TestSwitch {
48
49 @Reflect
50 static String lookupSwitchExpression(int i) {
51 return switch (i) {
52 case 7,8 -> "magic number";
53 case 42 -> "Answer to the Ultimate Question of Life, the Universe, and Everything";
54 case 101 -> "introduction to a subject";
55 default -> "not important";
56 };
57 }
58
59 @Reflect
60 static String tableSwitchExpression(int i) {
61 return switch (i) {
62 case -1 -> "?";
63 case 0 -> "none";
64 case 1 -> "one";
65 case 2 -> "two";
66 case 3 -> "three";
67 default -> "many";
68 };
69 }
70
71 @Reflect
72 static String lookupSwitchStatement(int i) {
73 String ret = null;
74 switch (i) {
75 case 7 : ret = "magic number"; break;
76 case 42 : return "Answer to the Ultimate Question of Life, the Universe, and Everything";
77 case 101 : ret = "introduction to a subject"; break;
78 default : return "not important";
79 }
80 return ret;
81 }
82
83 @Reflect
84 static String tableSwitchStatement(int i) {
85 String ret = null;
86 switch (i) {
87 case -1 : ret = "?"; break;
88 case 0 : return "none";
89 case 1 : ret = "one"; break;
90 case 2 : return "two";
91 case 3 : ret ="three"; break;
92 default : return"many";
93 }
94 return ret;
95 }
96
97 @Reflect
98 static String outOfOrderFallThrought(int i) {
99 String ret = "";
100 switch (i) {
101 default : ret += "? ";
102 case 4 : ret += "four ";
103 case 2 : ret += "two ";
104 case 3 : ret += "three ";
105 case 1 : ret += "one";
106 }
107 return ret;
108 }
109
110 @Reflect
111 static String nestedExpressions(int i) {
112 return switch (i) {
113 case -1 -> "?";
114 case 0 -> "none";
115 case 1 -> "one";
116 case 2 -> "two";
117 case 3 -> "three";
118 default -> switch (i) {
119 case 7,8 -> "magic number";
120 case 42 -> "Answer to the Ultimate Question of Life, the Universe, and Everything";
121 case 101 -> "introduction to a subject";
122 default -> "not important";
123 };
124 };
125 }
126
127 @ParameterizedTest
128 @ValueSource(strings = {
129 "lookupSwitchExpression",
130 "tableSwitchExpression",
131 "lookupSwitchStatement",
132 "tableSwitchStatement",
133 "outOfOrderFallThrought",
134 "nestedExpressions"
135 })
136
137 public void testSwitch(String methodName) throws Throwable {
138 Method m = getMethod(methodName);
139 CoreOp.FuncOp f = Op.ofMethod(m).orElseThrow();
140
141 Assertions.assertTrue(getModel(f).code().get().elementStream()
142 .anyMatch(i -> i instanceof TableSwitchInstruction || i instanceof LookupSwitchInstruction));
143
144 MethodHandle mh = generate(f);
145 for (int i = -1; i < 110; i++) {
146 Assertions.assertEquals((String)m.invoke(null, i), (String)mh.invokeExact(i));
147 }
148 }
149
150 static MethodHandle generate(CoreOp.FuncOp f) {
151 System.out.println(f.toText());
152
153 return BytecodeGenerator.generate(MethodHandles.lookup(), f);
154 }
155
156 static MethodModel getModel(CoreOp.FuncOp f) {
157 var clm = ClassFile.of().parse(BytecodeGenerator.generateClassData(MethodHandles.lookup(), f));
158 var mm = clm.methods().getFirst();
159 System.out.println(mm.toDebugString());
160 return mm;
161 }
162
163 static Method getMethod(String name) {
164 Optional<Method> om = Stream.of(TestSwitch.class.getDeclaredMethods())
165 .filter(m -> m.getName().equals(name))
166 .findFirst();
167
168 return om.get();
169 }
170 }