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