1
+ /*
2
+ * Copyright (c) 2020, 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
+ /**
25
+ * @test
26
+ * @run testng Reflection
27
+ * @summary Test virtual threads using core reflection
28
+ */
29
+
30
+ import java .lang .reflect .Constructor ;
31
+ import java .lang .reflect .InvocationTargetException ;
32
+ import java .lang .reflect .Method ;
33
+ import java .util .concurrent .ExecutorService ;
34
+ import java .util .concurrent .Executors ;
35
+ import java .util .concurrent .locks .LockSupport ;
36
+
37
+ import org .testng .annotations .Test ;
38
+ import static org .testng .Assert .*;
39
+
40
+ @ Test
41
+ public class Reflection {
42
+
43
+ // -- invoke static method --
44
+
45
+ public void testInvokeStatic1 () throws Exception {
46
+ TestHelper .runInVirtualThread (() -> {
47
+ int result = (int ) divideMethod ().invoke (null , 20 , 2 );
48
+ assertTrue (result == 10 );
49
+ });
50
+ }
51
+
52
+ // InvocationTargetException with cause
53
+ public void testInvokeStatic2 () throws Exception {
54
+ TestHelper .runInVirtualThread (() -> {
55
+ try {
56
+ divideMethod ().invoke (null , 20 , 0 );
57
+ assertTrue (false );
58
+ } catch (InvocationTargetException e ) {
59
+ assertTrue (e .getCause () instanceof ArithmeticException );
60
+ }
61
+ });
62
+ }
63
+
64
+ // IllegalArgumentException
65
+ public void testInvokeStatic3 () throws Exception {
66
+ TestHelper .runInVirtualThread (() -> {
67
+ assertThrows (IllegalArgumentException .class ,
68
+ () -> divideMethod ().invoke (null ));
69
+ assertThrows (IllegalArgumentException .class ,
70
+ () -> divideMethod ().invoke (null , 1 ));
71
+ assertThrows (IllegalArgumentException .class ,
72
+ () -> divideMethod ().invoke (null , 1 , 2 , 3 ));
73
+ assertThrows (IllegalArgumentException .class ,
74
+ () -> divideMethod ().invoke (new Object ()));
75
+ assertThrows (IllegalArgumentException .class ,
76
+ () -> divideMethod ().invoke (new Object (), 1 ));
77
+ assertThrows (IllegalArgumentException .class ,
78
+ () -> divideMethod ().invoke (new Object (), 1 , 2 , 3 ));
79
+ });
80
+ }
81
+
82
+ // ExceptionInInitializerError
83
+ public void testInvokeStatic4 () throws Exception {
84
+ TestHelper .runInVirtualThread (() -> {
85
+ Method foo = BadClass1 .class .getDeclaredMethod ("foo" );
86
+ try {
87
+ foo .invoke (null );
88
+ assertTrue (false );
89
+ } catch (ExceptionInInitializerError e ) {
90
+ assertTrue (e .getCause () instanceof ArithmeticException );
91
+ }
92
+ });
93
+ }
94
+
95
+ static class BadClass1 {
96
+ static {
97
+ if (1 ==1 ) throw new ArithmeticException ();
98
+ }
99
+ static void foo () { }
100
+ }
101
+
102
+
103
+ // <clinit> throws Error, specified behavior?
104
+ public void testInvokeStatic5 () throws Exception {
105
+ TestHelper .runInVirtualThread (() -> {
106
+ Method foo = BadClass2 .class .getDeclaredMethod ("foo" );
107
+ assertThrows (AbstractMethodError .class , () -> foo .invoke (null ));
108
+ });
109
+ }
110
+
111
+ static class BadClass2 {
112
+ static {
113
+ if (1 ==1 ) throw new AbstractMethodError ();
114
+ }
115
+ static void foo () { }
116
+ }
117
+
118
+ // test that invoke does not pin the carrier thread
119
+ public void testInvokeStatic6 () throws Exception {
120
+ Method parkMethod = Parker .class .getDeclaredMethod ("park" );
121
+ try (ExecutorService scheduler = Executors .newFixedThreadPool (1 )) {
122
+ Thread vthread = Thread .builder ().virtual (scheduler ).task (() -> {
123
+ try {
124
+ parkMethod .invoke (null ); // blocks
125
+ } catch (Exception e ) { }
126
+ }).start ();
127
+
128
+ Thread .sleep (100 ); // give thread time to be scheduled
129
+
130
+ // unpark with another virtual thread, runs on same carrier thread
131
+ Thread .builder ().virtual (scheduler ).task (() -> LockSupport .unpark (vthread ));
132
+ }
133
+ }
134
+
135
+
136
+ // -- invoke instance method --
137
+
138
+ public void testInvokeInstance1 () throws Exception {
139
+ TestHelper .runInVirtualThread (() -> {
140
+ var adder = new Adder ();
141
+ Adder .addMethod ().invoke (adder , 5 );
142
+ assertTrue (adder .sum == 5 );
143
+ });
144
+ }
145
+
146
+ // test exception throw by method
147
+ public void testInvokeInstance2 () throws Exception {
148
+ TestHelper .runInVirtualThread (() -> {
149
+ var adder = new Adder ();
150
+ try {
151
+ Adder .addMethod ().invoke (adder , -5 );
152
+ assertTrue (false );
153
+ } catch (InvocationTargetException e ) {
154
+ assertTrue (e .getCause () instanceof IllegalArgumentException );
155
+ }
156
+ });
157
+ }
158
+
159
+ // NullPointerException/IllegalArgumentException
160
+ public void testInvokeInstance3 () throws Exception {
161
+ TestHelper .runInVirtualThread (() -> {
162
+ var adder = new Adder ();
163
+ Method addMethod = Adder .addMethod ();
164
+ assertThrows (NullPointerException .class ,
165
+ () -> addMethod .invoke (null ));
166
+ assertThrows (IllegalArgumentException .class ,
167
+ () -> addMethod .invoke (adder ));
168
+ assertThrows (IllegalArgumentException .class ,
169
+ () -> addMethod .invoke (adder , 1 , 2 ));
170
+ assertThrows (IllegalArgumentException .class ,
171
+ () -> addMethod .invoke (adder , 1 , "hi" ));
172
+ assertThrows (IllegalArgumentException .class ,
173
+ () -> addMethod .invoke (adder , "hi" ));
174
+ });
175
+ }
176
+
177
+
178
+ // -- newInstance to construct objects --
179
+
180
+ public void testNewInstance1 () throws Exception {
181
+ TestHelper .runInVirtualThread (() -> {
182
+ Constructor <?> ctor = Adder .class .getDeclaredConstructor (long .class );
183
+ Adder adder = (Adder ) ctor .newInstance (10 );
184
+ assertTrue (adder .sum == 10 );
185
+ });
186
+ }
187
+
188
+ // InvocationTargetException with cause
189
+ public void testNewInstance2 () throws Exception {
190
+ TestHelper .runInVirtualThread (() -> {
191
+ Constructor <?> ctor = Adder .class .getDeclaredConstructor (long .class );
192
+ try {
193
+ ctor .newInstance (-10 );
194
+ assertTrue (false );
195
+ } catch (InvocationTargetException e ) {
196
+ assertTrue (e .getCause () instanceof IllegalArgumentException );
197
+ }
198
+ });
199
+ }
200
+
201
+ // IllegalArgumentException
202
+ public void testNewInstance3 () throws Exception {
203
+ TestHelper .runInVirtualThread (() -> {
204
+ var adder = new Adder ();
205
+ Constructor <?> ctor = Adder .class .getDeclaredConstructor (long .class );
206
+ assertThrows (IllegalArgumentException .class ,
207
+ () -> ctor .newInstance ((Object [])null ));
208
+ assertThrows (IllegalArgumentException .class ,
209
+ () -> ctor .newInstance (adder ));
210
+ assertThrows (IllegalArgumentException .class ,
211
+ () -> ctor .newInstance (adder , null ));
212
+ assertThrows (IllegalArgumentException .class ,
213
+ () -> ctor .newInstance (adder , "foo" ));
214
+ assertThrows (IllegalArgumentException .class ,
215
+ () -> ctor .newInstance (adder , 1 , 2 ));
216
+ });
217
+ }
218
+
219
+ // ExceptionInInitializerError
220
+ public void testNewInstance4 () throws Exception {
221
+ TestHelper .runInVirtualThread (() -> {
222
+ Constructor <?> ctor = BadClass3 .class .getDeclaredConstructor ();
223
+ try {
224
+ ctor .newInstance ((Object [])null );
225
+ assertTrue (false );
226
+ } catch (ExceptionInInitializerError e ) {
227
+ assertTrue (e .getCause () instanceof ArithmeticException );
228
+ }
229
+ });
230
+ }
231
+
232
+ static class BadClass3 {
233
+ static {
234
+ if (1 ==1 ) throw new ArithmeticException ();
235
+ }
236
+ static void foo () { }
237
+ }
238
+
239
+ // <clinit> throws Error, specified behavior?
240
+ public void testNewInstance5 () throws Exception {
241
+ TestHelper .runInVirtualThread (() -> {
242
+ Constructor <?> ctor = BadClass4 .class .getDeclaredConstructor ();
243
+ assertThrows (AbstractMethodError .class , () -> ctor .newInstance ((Object [])null ));
244
+ });
245
+ }
246
+
247
+ static class BadClass4 {
248
+ static {
249
+ if (1 ==1 ) throw new AbstractMethodError ();
250
+ }
251
+ static void foo () { }
252
+ }
253
+
254
+ // test that newInstance does not pin the carrier thread
255
+ public void testNewInstance6 () throws Exception {
256
+ Constructor <?> ctor = Parker .class .getDeclaredConstructor ();
257
+ try (ExecutorService scheduler = Executors .newFixedThreadPool (1 )) {
258
+ Thread vthread = Thread .builder ().virtual (scheduler ).task (() -> {
259
+ try {
260
+ ctor .newInstance ();
261
+ } catch (Exception e ) { }
262
+ }).start ();
263
+
264
+ Thread .sleep (100 ); // give thread time to be scheduled
265
+
266
+ // unpark with another virtual thread, runs on same carrier thread
267
+ Thread .builder ().virtual (scheduler ).task (() -> LockSupport .unpark (vthread ));
268
+ }
269
+ }
270
+
271
+
272
+ // -- support classes and methods --
273
+
274
+ static int divide (int x , int y ) {
275
+ return x / y ;
276
+ }
277
+
278
+ static Method divideMethod () throws NoSuchMethodException {
279
+ return Reflection .class .getDeclaredMethod ("divide" , int .class , int .class );
280
+ }
281
+
282
+ static class Adder {
283
+ long sum ;
284
+ Adder () { }
285
+ Adder (long x ) {
286
+ if (x < 0 )
287
+ throw new IllegalArgumentException ();
288
+ sum = x ;
289
+ }
290
+ Adder add (long x ) {
291
+ if (x < 0 )
292
+ throw new IllegalArgumentException ();
293
+ sum += x ;
294
+ return this ;
295
+ }
296
+ static Method addMethod () throws NoSuchMethodException {
297
+ return Adder .class .getDeclaredMethod ("add" , long .class );
298
+ }
299
+ long sum () {
300
+ return sum ;
301
+ }
302
+ }
303
+
304
+ static class Parker {
305
+ Parker () {
306
+ LockSupport .park ();
307
+ }
308
+ static void park () {
309
+ LockSupport .park ();
310
+ }
311
+ }
312
+
313
+ }
0 commit comments