• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package test.java.lang.runtime;
27 
28 import java.io.Serializable;
29 import java.lang.Enum.EnumDesc;
30 import java.lang.constant.ClassDesc;
31 import java.lang.invoke.CallSite;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.lang.runtime.SwitchBootstraps;
36 import java.util.concurrent.atomic.AtomicBoolean;
37 
38 import org.testng.annotations.Test;
39 
40 
41 import static org.testng.Assert.assertEquals;
42 import static org.testng.Assert.assertFalse;
43 import static org.testng.Assert.assertTrue;
44 import static org.testng.Assert.fail;
45 
46 /**
47  * @test
48  * @bug 8318144
49  * @enablePreview
50  * @compile SwitchBootstrapsTest.java
51  * @run testng/othervm SwitchBootstrapsTest
52  */
53 @Test
54 public class SwitchBootstrapsTest {
55 
56     public static final MethodHandle BSM_TYPE_SWITCH;
57     public static final MethodHandle BSM_ENUM_SWITCH;
58 
59     static {
60         try {
61             BSM_TYPE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "typeSwitch",
62                                                                 MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class));
63             BSM_ENUM_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "enumSwitch",
64                                                                 MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class));
65         }
66         catch (ReflectiveOperationException e) {
67             throw new AssertionError("Should not happen", e);
68         }
69     }
70 
testType(Object target, int start, int result, Object... labels)71     private void testType(Object target, int start, int result, Object... labels) throws Throwable {
72         MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
73         MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker();
74         assertEquals((int) indy.invoke(target, start), result);
75         assertEquals(-1, (int) indy.invoke(null, start));
76     }
77 
testEnum(Enum<?> target, int start, int result, Object... labels)78     private void testEnum(Enum<?> target, int start, int result, Object... labels) throws Throwable {
79         testEnum(target.getClass(), target, start, result, labels);
80     }
81 
testEnum(Class<?> targetClass, Enum<?> target, int start, int result, Object... labels)82     private void testEnum(Class<?> targetClass, Enum<?> target, int start, int result, Object... labels) throws Throwable {
83         MethodType switchType = MethodType.methodType(int.class, targetClass, int.class);
84         MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker();
85         assertEquals((int) indy.invoke(target, start), result);
86         assertEquals(-1, (int) indy.invoke(null, start));
87     }
88 
89     public enum E1 {
90         A,
91         B;
92     }
93 
94     public enum E2 {
95         C;
96     }
97 
testTypes()98     public void testTypes() throws Throwable {
99         testType("", 0, 0, String.class, Object.class);
100         testType("", 0, 0, Object.class);
101         testType("", 0, 1, Integer.class);
102         testType("", 0, 1, Integer.class, Serializable.class);
103         testType(E1.A, 0, 0, E1.class, Object.class);
104         testType(E2.C, 0, 1, E1.class, Object.class);
105         testType(new Serializable() { }, 0, 1, Comparable.class, Serializable.class);
106         testType("", 0, 0, "", String.class);
107         testType("", 1, 1, "", String.class);
108         testType("a", 0, 1, "", String.class);
109         testType(1, 0, 0, 1, Integer.class);
110         testType(2, 0, 1, 1, Integer.class);
111         testType(Byte.valueOf((byte) 1), 0, 0, 1, Integer.class);
112         testType(Short.valueOf((short) 1), 0, 0, 1, Integer.class);
113         testType(Character.valueOf((char) 1), 0, 0, 1, Integer.class);
114         testType(Integer.valueOf((int) 1), 0, 0, 1, Integer.class);
115         try {
116             testType(1, 0, 1, 1.0, Integer.class);
117             fail("Didn't get the expected exception.");
118         } catch (IllegalArgumentException ex) {
119             //OK
120         }
121         testType("", 0, 0, String.class, String.class, String.class);
122         testType("", 1, 1, String.class, String.class, String.class);
123         testType("", 2, 2, String.class, String.class, String.class);
124         testType("", 0, 0);
125     }
126 
testEnums()127     public void testEnums() throws Throwable {
128         testEnum(E1.A, 0, 2, "B", "C", "A", E1.class);
129         testEnum(E1.B, 0, 0, "B", "C", "A", E1.class);
130         testEnum(E1.B, 1, 3, "B", "C", "A", E1.class);
131         try {
132             testEnum(E1.B, 1, 3, "B", "C", "A", E2.class);
133             fail("Didn't get the expected exception.");
134         } catch (IllegalArgumentException ex) {
135             //OK
136         }
137         try {
138             testEnum(E1.B, 1, 3, "B", "C", "A", String.class);
139             fail("Didn't get the expected exception.");
140         } catch (IllegalArgumentException ex) {
141             //OK
142         }
143         testEnum(E1.B, 0, 0, "B", "A");
144         testEnum(E1.A, 0, 1, "B", "A");
145         testEnum(E1.A, 0, 0, "A", "A", "B");
146         testEnum(E1.A, 1, 1, "A", "A", "B");
147         testEnum(E1.A, 2, 3, "A", "A", "B");
148         testEnum(E1.A, 0, 0);
149     }
150 
testEnumsWithConstants()151     public void testEnumsWithConstants() throws Throwable {
152         enum E {
153             A {},
154             B {},
155             C {}
156         }
157         ClassDesc eDesc = E.class.describeConstable().get();
158         Object[] typeParams = new Object[] {
159             EnumDesc.of(eDesc, "A"),
160             EnumDesc.of(eDesc, "B"),
161             EnumDesc.of(eDesc, "C"),
162             "a",
163             String.class
164         };
165         testType(E.A, 0, 0, typeParams);
166         testType(E.B, 0, 1, typeParams);
167         testType(E.C, 0, 2, typeParams);
168         testType("a", 0, 3, typeParams);
169         testType("x", 0, 4, typeParams);
170         testType('a', 0, 5, typeParams);
171         testEnum(E.class, E.A, 0, 0, "A", "B", "C");
172         testEnum(E.class, E.B, 0, 1, "A", "B", "C");
173         testEnum(E.class, E.C, 0, 2, "A", "B", "C");
174     }
175 
testWrongSwitchTypes()176     public void testWrongSwitchTypes() throws Throwable {
177         MethodType[] switchTypes = new MethodType[] {
178             MethodType.methodType(int.class, Object.class),
179             MethodType.methodType(int.class, double.class, int.class),
180             MethodType.methodType(int.class, Object.class, Integer.class)
181         };
182         for (MethodType switchType : switchTypes) {
183             try {
184                 BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType);
185                 fail("Didn't get the expected exception.");
186             } catch (IllegalArgumentException ex) {
187                 //OK, expected
188             }
189         }
190         MethodType[] enumSwitchTypes = new MethodType[] {
191             MethodType.methodType(int.class, Enum.class),
192             MethodType.methodType(int.class, Object.class, int.class),
193             MethodType.methodType(int.class, double.class, int.class),
194             MethodType.methodType(int.class, Enum.class, Integer.class)
195         };
196         for (MethodType enumSwitchType : enumSwitchTypes) {
197             try {
198                 BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType);
199                 fail("Didn't get the expected exception.");
200             } catch (IllegalArgumentException ex) {
201                 //OK, expected
202             }
203         }
204     }
205 
testSwitchLabelTypes()206     public void testSwitchLabelTypes() throws Throwable {
207         enum E {A}
208         try {
209             testType(E.A, 0, -1, E.A);
210             fail("Didn't get the expected exception.");
211         } catch (IllegalArgumentException ex) {
212             //OK, expected
213         }
214     }
215 
testSwitchQualifiedEnum()216     public void testSwitchQualifiedEnum() throws Throwable {
217         enum E {A, B, C}
218         Object[] labels = new Object[] {
219             EnumDesc.of(ClassDesc.of(E.class.getName()), "A"),
220             EnumDesc.of(ClassDesc.of(E.class.getName()), "B"),
221             EnumDesc.of(ClassDesc.of(E.class.getName()), "C")
222         };
223         testType(E.A, 0, 0, labels);
224         testType(E.B, 0, 1, labels);
225         testType(E.C, 0, 2, labels);
226     }
227 
testNullLabels()228     public void testNullLabels() throws Throwable {
229         MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
230         try {
231             BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, (Object[]) null);
232             fail("Didn't get the expected exception.");
233         } catch (NullPointerException ex) {
234             //OK
235         }
236         try {
237             BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType,
238                                    new Object[] {1, null, String.class});
239             fail("Didn't get the expected exception.");
240         } catch (IllegalArgumentException ex) {
241             //OK
242         }
243         MethodType enumSwitchType = MethodType.methodType(int.class, E1.class, int.class);
244         try {
245             BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null);
246             fail("Didn't get the expected exception.");
247         } catch (NullPointerException ex) {
248             //OK
249         }
250         try {
251             BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType,
252                                    new Object[] {1, null, String.class});
253             fail("Didn't get the expected exception.");
254         } catch (IllegalArgumentException ex) {
255             //OK
256         }
257     }
258 
259     private static AtomicBoolean enumInitialized = new AtomicBoolean();
testEnumInitialization1()260     public void testEnumInitialization1() throws Throwable {
261         enumInitialized.set(false);
262 
263         enum E {
264             A;
265 
266             static {
267                 enumInitialized.set(true);
268             }
269         }
270 
271         MethodType enumSwitchType = MethodType.methodType(int.class, E.class, int.class);
272 
273         CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"});
274         assertFalse(enumInitialized.get());
275         assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1);
276         assertFalse(enumInitialized.get());
277         E e = E.A;
278         assertTrue(enumInitialized.get());
279         assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0);
280     }
281 
testEnumInitialization2()282     public void testEnumInitialization2() throws Throwable {
283         enumInitialized.set(false);
284 
285         enum E {
286             A;
287 
288             static {
289                 enumInitialized.set(true);
290             }
291         }
292 
293         MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
294         Object[] labels = new Object[] {
295             EnumDesc.of(ClassDesc.of(E.class.getName()), "A"),
296             "test"
297         };
298         CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels);
299         assertFalse(enumInitialized.get());
300         assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1);
301         assertFalse(enumInitialized.get());
302         assertEquals(invocation.dynamicInvoker().invoke("test", 0), 1);
303         assertFalse(enumInitialized.get());
304         E e = E.A;
305         assertTrue(enumInitialized.get());
306         assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0);
307     }
308 
testIncorrectEnumLabels()309     public void testIncorrectEnumLabels() throws Throwable {
310         try {
311             testEnum(E1.B, 0, -1, "B", 1);
312             fail("Didn't get the expected exception.");
313         } catch (IllegalArgumentException ex) {
314             //OK
315         }
316         try {
317             testEnum(E1.B, 0, -1, "B", null);
318             fail("Didn't get the expected exception.");
319         } catch (IllegalArgumentException ex) {
320             //OK
321         }
322     }
323 
testIncorrectEnumStartIndex()324     public void testIncorrectEnumStartIndex() throws Throwable {
325         try {
326             testEnum(E1.B, -1, -1, "B");
327             fail("Didn't get the expected exception.");
328         } catch (IndexOutOfBoundsException ex) {
329             //OK
330         }
331         try {
332             testEnum(E1.B, 2, -1, "B");
333             fail("Didn't get the expected exception.");
334         } catch (IndexOutOfBoundsException ex) {
335             //OK
336         }
337     }
338 
testIncorrectTypeStartIndex()339     public void testIncorrectTypeStartIndex() throws Throwable {
340         try {
341             testType("", -1, -1, "");
342             fail("Didn't get the expected exception.");
343         } catch (IndexOutOfBoundsException ex) {
344             //OK
345         }
346         try {
347             testType("", 2, -1, "");
348             fail("Didn't get the expected exception.");
349         } catch (IndexOutOfBoundsException ex) {
350             //OK
351         }
352     }
353 
354 }
355