1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.lang.invoke.MethodHandle;
18 import java.lang.invoke.MethodHandles;
19 import java.lang.invoke.MethodType;
20 import java.lang.invoke.VarHandle;
21 import java.lang.invoke.WrongMethodTypeException;
22 
23 public final class Main {
24     static class TestSetupError extends Error {
TestSetupError(String message, Throwable cause)25         TestSetupError(String message, Throwable cause) {
26             super(message, cause);
27         }
28     }
29 
failAssertion(String message)30     private static void failAssertion(String message) {
31         StringBuilder sb = new StringBuilder();
32         sb.append("Test failure: ");
33         sb.append(message);
34         throw new AssertionError(sb.toString());
35     }
36 
assertUnreachable()37     private static void assertUnreachable() throws Throwable {
38         failAssertion("Unreachable");
39     }
40 
failAssertEquals(Object expected, Object actual)41     private static void failAssertEquals(Object expected, Object actual) {
42         StringBuilder sb = new StringBuilder();
43         sb.append(expected);
44         sb.append(" != ");
45         sb.append(actual);
46         failAssertion(sb.toString());
47     }
48 
assertEquals(int expected, int actual)49     private static void assertEquals(int expected, int actual) {
50         if (expected != actual) {
51             failAssertEquals(expected, actual);
52         }
53     }
54 
assertEquals(float expected, float actual)55     private static void assertEquals(float expected, float actual) {
56         if (expected != actual) {
57             failAssertEquals(expected, actual);
58         }
59     }
60 
assertEquals(double expected, double actual)61     private static void assertEquals(double expected, double actual) {
62         if (expected != actual) {
63             failAssertEquals(expected, actual);
64         }
65     }
66 
67     static class FieldVarHandleExactInvokerTest {
68         private static final VarHandle fieldVarHandle;
69         int field;
70 
71         static {
72             try {
73                 fieldVarHandle =
74                         MethodHandles.lookup()
75                                 .findVarHandle(
76                                         FieldVarHandleExactInvokerTest.class, "field", int.class);
77             } catch (Exception e) {
78                 throw new TestSetupError("Failed to lookup of field", e);
79             }
80         }
81 
run()82         void run() throws Throwable {
83             System.out.println("fieldVarHandleExactInvokerTest");
84 
85             MethodHandle invokerMethodHandle =
86                     MethodHandles.varHandleExactInvoker(
87                             VarHandle.AccessMode.GET_AND_SET,
88                             MethodType.methodType(
89                                     int.class, FieldVarHandleExactInvokerTest.class, int.class));
90 
91             field = 3;
92             assertEquals(3, (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 4));
93             assertEquals(4, field);
94 
95             //
96             // Check invocations with MethodHandle.invokeExact()
97             //
98             try {
99                 // Check for unboxing
100                 int i =
101                         (int)
102                                 invokerMethodHandle.invokeExact(
103                                         fieldVarHandle, this, Integer.valueOf(3));
104                 assertUnreachable();
105             } catch (WrongMethodTypeException expected) {
106                 assertEquals(4, field);
107             }
108             try {
109                 // Check for widening conversion
110                 int i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 3);
111                 assertUnreachable();
112             } catch (WrongMethodTypeException expected) {
113                 assertEquals(4, field);
114             }
115             try {
116                 // Check for acceptance of void return type
117                 invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
118                 assertUnreachable();
119             } catch (WrongMethodTypeException expected) {
120                 assertEquals(4, field);
121             }
122             try {
123                 // Check for wider return type
124                 long l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
125                 assertUnreachable();
126             } catch (WrongMethodTypeException expected) {
127                 assertEquals(4, field);
128             }
129             try {
130                 // Check null VarHandle instance fails
131                 VarHandle vhNull = null;
132                 int i = (int) invokerMethodHandle.invokeExact(vhNull, this, 777);
133                 assertUnreachable();
134             } catch (NullPointerException expected) {
135                 assertEquals(4, field);
136             }
137 
138             //
139             // Check invocations with MethodHandle.invoke()
140             //
141 
142             // Check for unboxing
143             int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3));
144             assertEquals(3, field);
145 
146             // Check for unboxing
147             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Short.valueOf((short) 4));
148             assertEquals(4, field);
149 
150             // Check for widening conversion
151             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 23);
152             assertEquals(23, field);
153 
154             // Check for acceptance of void return type
155             invokerMethodHandle.invoke(fieldVarHandle, this, 77);
156             assertEquals(77, field);
157 
158             // Check for wider return type
159             long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88);
160             assertEquals(88, field);
161 
162             try {
163                 // Check null VarHandle instance fails
164                 VarHandle vhNull = null;
165                 i = (int) invokerMethodHandle.invoke(vhNull, this, 888);
166                 assertUnreachable();
167             } catch (NullPointerException expected) {
168                 assertEquals(88, field);
169             }
170         }
171     }
172 
173     static class FieldVarHandleInvokerTest {
174         private static final VarHandle fieldVarHandle;
175         int field;
176 
177         static {
178             try {
179                 fieldVarHandle =
180                         MethodHandles.lookup()
181                                 .findVarHandle(FieldVarHandleInvokerTest.class, "field", int.class);
182             } catch (Exception e) {
183                 throw new TestSetupError("Failed to lookup of field", e);
184             }
185         }
186 
run()187         void run() throws Throwable {
188             System.out.println("fieldVarHandleInvokerTest");
189             MethodHandle invokerMethodHandle =
190                     MethodHandles.varHandleInvoker(
191                             VarHandle.AccessMode.GET_AND_SET,
192                             MethodType.methodType(
193                                     int.class, FieldVarHandleInvokerTest.class, int.class));
194 
195             field = 3;
196             int oldField = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 4);
197             assertEquals(3, oldField);
198             assertEquals(4, field);
199 
200             //
201             // Check invocations with MethodHandle.invoke()
202             //
203 
204             // Check for unboxing
205             int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3));
206             assertEquals(3, field);
207 
208             // Check for widening conversion
209             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 33);
210             assertEquals(33, field);
211 
212             // Check for widening conversion
213             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Byte.valueOf((byte) 34));
214             assertEquals(34, field);
215 
216             // Check for acceptance of void return type
217             invokerMethodHandle.invoke(fieldVarHandle, this, 77);
218             assertEquals(77, field);
219 
220             // Check for wider return type
221             long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88);
222             assertEquals(88, field);
223             try {
224                 // Check narrowing conversion fails
225                 i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 3.0);
226                 assertUnreachable();
227             } catch (WrongMethodTypeException expected) {
228             }
229             try {
230                 // Check reference type fails
231                 i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, "Bad");
232                 assertUnreachable();
233             } catch (WrongMethodTypeException expected) {
234             }
235             try {
236                 // Check null VarHandle instance fails
237                 VarHandle vhNull = null;
238                 i = (int) invokerMethodHandle.invoke(vhNull, this, 888);
239                 assertUnreachable();
240             } catch (NullPointerException expected) {
241                 assertEquals(88, field);
242             }
243 
244             //
245             // Check invocations with MethodHandle.invokeExact()
246             //
247             field = -1;
248             try {
249                 // Check for unboxing
250                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, Integer.valueOf(3));
251                 assertUnreachable();
252             } catch (WrongMethodTypeException expected) {
253                 assertEquals(-1, field);
254             }
255             try {
256                 // Check for widening conversion
257                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 33);
258                 assertUnreachable();
259             } catch (WrongMethodTypeException expected) {
260                 assertEquals(-1, field);
261             }
262             try {
263                 // Check for acceptance of void return type
264                 invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
265                 assertUnreachable();
266             } catch (WrongMethodTypeException expected) {
267                 assertEquals(-1, field);
268             }
269             try {
270                 // Check for wider return type
271                 l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 78);
272                 assertUnreachable();
273             } catch (WrongMethodTypeException expected) {
274                 assertEquals(-1, field);
275             }
276             try {
277                 // Check narrowing conversion fails
278                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 3.0);
279                 assertUnreachable();
280             } catch (WrongMethodTypeException expected) {
281             }
282             try {
283                 // Check reference type fails
284                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, "Bad");
285                 assertUnreachable();
286             } catch (WrongMethodTypeException expected) {
287             }
288             try {
289                 // Check null VarHandle instance fails
290                 VarHandle vhNull = null;
291                 i = (int) invokerMethodHandle.invokeExact(vhNull, this, 888);
292                 assertUnreachable();
293             } catch (NullPointerException expected) {
294                 assertEquals(-1, field);
295             }
296         }
297     }
298 
299     static class DivergenceExactInvokerTest {
300         private static final VarHandle floatsArrayVarHandle;
301 
302         static {
303             try {
304                 floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class);
305             } catch (Exception e) {
306                 throw new TestSetupError("Failed to create VarHandle", e);
307             }
308         }
309 
run()310         void run() throws Throwable {
311             System.out.println("DivergenceExactInvokerTest");
312             float[] floatsArray = new float[4];
313             // Exact invoker of an accessor having the form:
314             //  float accessor(float[] values, int index, Float current, float replacement)
315             MethodHandle exactInvoker =
316                     MethodHandles.varHandleExactInvoker(
317                             VarHandle.AccessMode.COMPARE_AND_EXCHANGE,
318                             MethodType.methodType(
319                                     float.class,
320                                     float[].class,
321                                     int.class,
322                                     Float.class,
323                                     float.class));
324             floatsArray[2] = Float.valueOf(4.0f);
325             // Callsite that is an exact match with exactInvoker.type().
326             try {
327                 // exactInvoker.type() is not compatible with floatsArrayVarHandle accessor.
328                 float old =
329                         (float)
330                                 exactInvoker.invoke(
331                                         floatsArrayVarHandle,
332                                         floatsArray,
333                                         2,
334                                         Float.valueOf(4.0f),
335                                         8.0f);
336                 assertUnreachable();
337             } catch (WrongMethodTypeException expected) {
338                 assertEquals(4.0f, floatsArray[2]);
339             }
340 
341             // Callsites that are exact matches with exactInvoker.type()
342             try {
343                 // Mismatch between exactInvoker.type() and VarHandle type (Float != float)
344                 float old =
345                         (float)
346                                 exactInvoker.invoke(
347                                         floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f);
348                 assertUnreachable();
349             } catch (WrongMethodTypeException expected) {
350                 assertEquals(4.0f, floatsArray[2]);
351             }
352             try {
353                 // short not convertible to Float
354                 float old =
355                         (float)
356                                 exactInvoker.invoke(
357                                         floatsArrayVarHandle, floatsArray, 2, (short) 4, 13.0f);
358                 assertUnreachable();
359             } catch (WrongMethodTypeException expected) {
360                 assertEquals(4.0f, floatsArray[2]);
361             }
362             try {
363                 // int not convertible to Float
364                 float old =
365                         (float) exactInvoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f);
366                 assertUnreachable();
367             } catch (WrongMethodTypeException expected) {
368                 assertEquals(4.0f, floatsArray[2]);
369             }
370         }
371     }
372 
373     static class DivergenceInvokerTest {
374         private static final VarHandle floatsArrayVarHandle;
375 
376         static {
377             try {
378                 floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class);
379             } catch (Exception e) {
380                 throw new TestSetupError("Failed to create VarHandle", e);
381             }
382         }
383 
run()384         void run() throws Throwable {
385             System.out.println("DivergenceInvokerTest");
386             float[] floatsArray = new float[4];
387             // Invoker of an accessor having the form:
388             //  float accessor(float[] values, int index, Float current, float replacement)
389             MethodHandle invoker =
390                     MethodHandles.varHandleInvoker(
391                             VarHandle.AccessMode.COMPARE_AND_EXCHANGE,
392                             MethodType.methodType(
393                                     float.class,
394                                     float[].class,
395                                     int.class,
396                                     Float.class,
397                                     float.class));
398             floatsArray[2] = Float.valueOf(4.0f);
399             // Callsite that is an exact match with invoker.type()
400             float old =
401                     (float)
402                             invoker.invoke(
403                                     floatsArrayVarHandle,
404                                     floatsArray,
405                                     2,
406                                     Float.valueOf(4.0f),
407                                     8.0f);
408             assertEquals(4.0f, old);
409             assertEquals(8.0f, floatsArray[2]);
410 
411             // Callsite that is convertible match to invoker.type()
412             old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f);
413             assertEquals(8.0f, old);
414             assertEquals(16.0f, floatsArray[2]);
415 
416             // Callsites that are not convertible to invoker.type().
417             try {
418                 // short is not convertible to Float
419                 old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, (short) 4, 8.0f);
420                 assertUnreachable();
421             } catch (WrongMethodTypeException expected) {
422                 assertEquals(16.0f, floatsArray[2]);
423             }
424             try {
425                 // int is not convertible to Float
426                 old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f);
427                 assertUnreachable();
428             } catch (WrongMethodTypeException expected) {
429                 assertEquals(16.0f, floatsArray[2]);
430             }
431         }
432     }
433 
main(String[] args)434     public static void main(String[] args) throws Throwable {
435         new FieldVarHandleExactInvokerTest().run();
436         new FieldVarHandleInvokerTest().run();
437         new DivergenceExactInvokerTest().run();
438         new DivergenceInvokerTest().run();
439     }
440 }
441