• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static java.lang.invoke.MethodHandles.lookup;
18 import static java.lang.invoke.MethodType.methodType;
19 
20 import java.lang.invoke.MethodHandle;
21 import java.lang.invoke.MethodHandles;
22 import java.lang.invoke.VarHandle;
23 import java.lang.invoke.WrongMethodTypeException;
24 
25 public final class Main {
26     static class TestSetupError extends Error {
TestSetupError(String message, Throwable cause)27         TestSetupError(String message, Throwable cause) {
28             super(message, cause);
29         }
30     }
31 
failAssertion(String message)32     private static void failAssertion(String message) {
33         StringBuilder sb = new StringBuilder();
34         sb.append("Test failure: ");
35         sb.append(message);
36         throw new AssertionError(sb.toString());
37     }
38 
assertUnreachable()39     private static void assertUnreachable() throws Throwable {
40         failAssertion("Unreachable");
41     }
42 
failAssertEquals(Object expected, Object actual)43     private static void failAssertEquals(Object expected, Object actual) {
44         StringBuilder sb = new StringBuilder();
45         sb.append(expected);
46         sb.append(" != ");
47         sb.append(actual);
48         failAssertion(sb.toString());
49     }
50 
assertEquals(boolean expected, boolean actual)51     private static void assertEquals(boolean expected, boolean actual) {
52         if (expected != actual) {
53             failAssertEquals(expected, actual);
54         }
55     }
56 
assertEquals(int expected, int actual)57     private static void assertEquals(int expected, int actual) {
58         if (expected != actual) {
59             failAssertEquals(expected, actual);
60         }
61     }
62 
assertEquals(long expected, long actual)63     private static void assertEquals(long expected, long actual) {
64         if (expected != actual) {
65             failAssertEquals(expected, actual);
66         }
67     }
68 
assertEquals(float expected, float actual)69     private static void assertEquals(float expected, float actual) {
70         if (expected != actual) {
71             failAssertEquals(expected, actual);
72         }
73     }
74 
75     static class FieldVarHandleExactInvokerTest {
76         private static final Class<?> THIS_CLASS = FieldVarHandleExactInvokerTest.class;
77         private static final VarHandle fieldVarHandle;
78 
79         int field;
80 
81         static {
82             try {
83                 fieldVarHandle = lookup().findVarHandle(THIS_CLASS, "field", int.class);
84             } catch (Exception e) {
85                 throw new TestSetupError("Failed to lookup of field", e);
86             }
87         }
88 
run()89         void run() throws Throwable {
90             System.out.println(THIS_CLASS.getName());
91 
92             MethodHandle invokerMethodHandle =
93                     MethodHandles.varHandleExactInvoker(
94                             VarHandle.AccessMode.GET_AND_SET,
95                             methodType(int.class, THIS_CLASS, int.class));
96 
97             field = 3;
98             assertEquals(3, (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 4));
99             assertEquals(4, field);
100 
101             //
102             // Check invocations with MethodHandle.invokeExact()
103             //
104             try {
105                 // Check for unboxing
106                 int i =
107                         (int)
108                                 invokerMethodHandle.invokeExact(
109                                         fieldVarHandle, this, Integer.valueOf(3));
110                 assertUnreachable();
111             } catch (WrongMethodTypeException expected) {
112                 assertEquals(4, field);
113             }
114             try {
115                 // Check for widening conversion
116                 int i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 3);
117                 assertUnreachable();
118             } catch (WrongMethodTypeException expected) {
119                 assertEquals(4, field);
120             }
121             try {
122                 // Check for acceptance of void return type
123                 invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
124                 assertUnreachable();
125             } catch (WrongMethodTypeException expected) {
126                 assertEquals(4, field);
127             }
128             try {
129                 // Check for wider return type
130                 long l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
131                 assertUnreachable();
132             } catch (WrongMethodTypeException expected) {
133                 assertEquals(4, field);
134             }
135             try {
136                 // Check null VarHandle instance fails
137                 VarHandle vhNull = null;
138                 int i = (int) invokerMethodHandle.invokeExact(vhNull, this, 777);
139                 assertUnreachable();
140             } catch (NullPointerException expected) {
141                 assertEquals(4, field);
142             }
143 
144             //
145             // Check invocations with MethodHandle.invoke()
146             //
147 
148             // Check for unboxing
149             int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3));
150             assertEquals(3, field);
151 
152             // Check for unboxing
153             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Short.valueOf((short) 4));
154             assertEquals(4, field);
155 
156             // Check for widening conversion
157             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 23);
158             assertEquals(23, field);
159 
160             // Check for acceptance of void return type
161             invokerMethodHandle.invoke(fieldVarHandle, this, 77);
162             assertEquals(77, field);
163 
164             // Check for wider return type
165             long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88);
166             assertEquals(88, field);
167 
168             try {
169                 // Check null VarHandle instance fails
170                 VarHandle vhNull = null;
171                 i = (int) invokerMethodHandle.invoke(vhNull, this, 888);
172                 assertUnreachable();
173             } catch (NullPointerException expected) {
174                 assertEquals(88, field);
175             }
176         }
177     }
178 
179     static class LongFieldVarHandleExactInvokerTest {
180         private static final Class<?> THIS_CLASS = LongFieldVarHandleExactInvokerTest.class;
181 
182         private static final VarHandle fieldVarHandle;
183 
184         private static final long CANARY = 0x0123456789abcdefL;
185 
186         long field = 0L;
187 
188         static {
189             try {
190                 fieldVarHandle = lookup().findVarHandle(THIS_CLASS, "field", long.class);
191             } catch (Exception e) {
192                 throw new TestSetupError("Failed to lookup of field", e);
193             }
194         }
195 
run()196         void run() throws Throwable {
197             System.out.println(THIS_CLASS.getName());
198 
199             MethodHandle invokerMethodHandle =
200                     MethodHandles.varHandleExactInvoker(
201                             VarHandle.AccessMode.COMPARE_AND_SET,
202                             methodType(boolean.class, THIS_CLASS, long.class, long.class));
203             checkCompareAndSet(invokerMethodHandle, 0L, CANARY);
204             checkCompareAndSet(invokerMethodHandle, 1L, 1L);
205             checkCompareAndSet(invokerMethodHandle, CANARY, ~CANARY);
206             checkCompareAndSet(invokerMethodHandle, ~CANARY, 0L);
207         }
208 
checkCompareAndSet(MethodHandle compareAndSet, long oldValue, long newValue)209         private void checkCompareAndSet(MethodHandle compareAndSet, long oldValue, long newValue)
210                 throws Throwable {
211             final boolean expectSuccess = (oldValue == field);
212             final long oldFieldValue = field;
213             assertEquals(
214                     expectSuccess,
215                     (boolean) compareAndSet.invoke(fieldVarHandle, this, oldValue, newValue));
216             assertEquals(expectSuccess ? newValue : oldFieldValue, field);
217         }
218     }
219 
220     static class FieldVarHandleInvokerTest {
221         private static final Class<?> THIS_CLASS = FieldVarHandleInvokerTest.class;
222         private static final VarHandle fieldVarHandle;
223         int field;
224 
225         static {
226             try {
227                 fieldVarHandle = lookup().findVarHandle(THIS_CLASS, "field", int.class);
228             } catch (Exception e) {
229                 throw new TestSetupError("Failed to lookup of field", e);
230             }
231         }
232 
run()233         void run() throws Throwable {
234             System.out.println("fieldVarHandleInvokerTest");
235             MethodHandle invokerMethodHandle =
236                     MethodHandles.varHandleInvoker(
237                             VarHandle.AccessMode.GET_AND_SET,
238                             methodType(int.class, THIS_CLASS, int.class));
239 
240             field = 3;
241             int oldField = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 4);
242             assertEquals(3, oldField);
243             assertEquals(4, field);
244 
245             //
246             // Check invocations with MethodHandle.invoke()
247             //
248 
249             // Check for unboxing
250             int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3));
251             assertEquals(3, field);
252 
253             // Check for widening conversion
254             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 33);
255             assertEquals(33, field);
256 
257             // Check for widening conversion
258             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Byte.valueOf((byte) 34));
259             assertEquals(34, field);
260 
261             // Check for acceptance of void return type
262             invokerMethodHandle.invoke(fieldVarHandle, this, 77);
263             assertEquals(77, field);
264 
265             // Check for wider return type
266             long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88);
267             assertEquals(88, field);
268             try {
269                 // Check narrowing conversion fails
270                 i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 3.0);
271                 assertUnreachable();
272             } catch (WrongMethodTypeException expected) {
273             }
274             try {
275                 // Check reference type fails
276                 i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, "Bad");
277                 assertUnreachable();
278             } catch (WrongMethodTypeException expected) {
279             }
280             try {
281                 // Check null VarHandle instance fails
282                 VarHandle vhNull = null;
283                 i = (int) invokerMethodHandle.invoke(vhNull, this, 888);
284                 assertUnreachable();
285             } catch (NullPointerException expected) {
286                 assertEquals(88, field);
287             }
288 
289             //
290             // Check invocations with MethodHandle.invokeExact()
291             //
292             field = -1;
293             try {
294                 // Check for unboxing
295                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, Integer.valueOf(3));
296                 assertUnreachable();
297             } catch (WrongMethodTypeException expected) {
298                 assertEquals(-1, field);
299             }
300             try {
301                 // Check for widening conversion
302                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 33);
303                 assertUnreachable();
304             } catch (WrongMethodTypeException expected) {
305                 assertEquals(-1, field);
306             }
307             try {
308                 // Check for acceptance of void return type
309                 invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
310                 assertUnreachable();
311             } catch (WrongMethodTypeException expected) {
312                 assertEquals(-1, field);
313             }
314             try {
315                 // Check for wider return type
316                 l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 78);
317                 assertUnreachable();
318             } catch (WrongMethodTypeException expected) {
319                 assertEquals(-1, field);
320             }
321             try {
322                 // Check narrowing conversion fails
323                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 3.0);
324                 assertUnreachable();
325             } catch (WrongMethodTypeException expected) {
326             }
327             try {
328                 // Check reference type fails
329                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, "Bad");
330                 assertUnreachable();
331             } catch (WrongMethodTypeException expected) {
332             }
333             try {
334                 // Check null VarHandle instance fails
335                 VarHandle vhNull = null;
336                 i = (int) invokerMethodHandle.invokeExact(vhNull, this, 888);
337                 assertUnreachable();
338             } catch (NullPointerException expected) {
339                 assertEquals(-1, field);
340             }
341         }
342     }
343 
344     static class DivergenceExactInvokerTest {
345         private static final VarHandle floatsArrayVarHandle;
346 
347         static {
348             try {
349                 floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class);
350             } catch (Exception e) {
351                 throw new TestSetupError("Failed to create VarHandle", e);
352             }
353         }
354 
run()355         void run() throws Throwable {
356             System.out.println("DivergenceExactInvokerTest");
357             float[] floatsArray = new float[4];
358             // Exact invoker of an accessor having the form:
359             //  float accessor(float[] values, int index, Float current, float replacement)
360             MethodHandle exactInvoker =
361                     MethodHandles.varHandleExactInvoker(
362                             VarHandle.AccessMode.COMPARE_AND_EXCHANGE,
363                             methodType(
364                                     float.class,
365                                     float[].class,
366                                     int.class,
367                                     Float.class,
368                                     float.class));
369             floatsArray[2] = Float.valueOf(4.0f);
370             // Callsite that is an exact match with exactInvoker.type().
371             try {
372                 // exactInvoker.type() is not compatible with floatsArrayVarHandle accessor.
373                 float old =
374                         (float)
375                                 exactInvoker.invoke(
376                                         floatsArrayVarHandle,
377                                         floatsArray,
378                                         2,
379                                         Float.valueOf(4.0f),
380                                         8.0f);
381                 assertUnreachable();
382             } catch (WrongMethodTypeException expected) {
383                 assertEquals(4.0f, floatsArray[2]);
384             }
385 
386             // Callsites that are exact matches with exactInvoker.type()
387             try {
388                 // Mismatch between exactInvoker.type() and VarHandle type (Float != float)
389                 float old =
390                         (float)
391                                 exactInvoker.invoke(
392                                         floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f);
393                 assertUnreachable();
394             } catch (WrongMethodTypeException expected) {
395                 assertEquals(4.0f, floatsArray[2]);
396             }
397             try {
398                 // short not convertible to Float
399                 float old =
400                         (float)
401                                 exactInvoker.invoke(
402                                         floatsArrayVarHandle, floatsArray, 2, (short) 4, 13.0f);
403                 assertUnreachable();
404             } catch (WrongMethodTypeException expected) {
405                 assertEquals(4.0f, floatsArray[2]);
406             }
407             try {
408                 // int not convertible to Float
409                 float old =
410                         (float) exactInvoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f);
411                 assertUnreachable();
412             } catch (WrongMethodTypeException expected) {
413                 assertEquals(4.0f, floatsArray[2]);
414             }
415         }
416     }
417 
418     static class DivergenceInvokerTest {
419         private static final VarHandle floatsArrayVarHandle;
420 
421         static {
422             try {
423                 floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class);
424             } catch (Exception e) {
425                 throw new TestSetupError("Failed to create VarHandle", e);
426             }
427         }
428 
run()429         void run() throws Throwable {
430             System.out.println("DivergenceInvokerTest");
431             float[] floatsArray = new float[4];
432             // Invoker of an accessor having the form:
433             //  float accessor(float[] values, int index, Float current, float replacement)
434             MethodHandle invoker =
435                     MethodHandles.varHandleInvoker(
436                             VarHandle.AccessMode.COMPARE_AND_EXCHANGE,
437                             methodType(
438                                     float.class,
439                                     float[].class,
440                                     int.class,
441                                     Float.class,
442                                     float.class));
443             floatsArray[2] = Float.valueOf(4.0f);
444             // Callsite that is an exact match with invoker.type()
445             float old =
446                     (float)
447                             invoker.invoke(
448                                     floatsArrayVarHandle,
449                                     floatsArray,
450                                     2,
451                                     Float.valueOf(4.0f),
452                                     8.0f);
453             assertEquals(4.0f, old);
454             assertEquals(8.0f, floatsArray[2]);
455 
456             // Callsite that is convertible match to invoker.type()
457             old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f);
458             assertEquals(8.0f, old);
459             assertEquals(16.0f, floatsArray[2]);
460 
461             // Callsites that are not convertible to invoker.type().
462             try {
463                 // short is not convertible to Float
464                 old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, (short) 4, 8.0f);
465                 assertUnreachable();
466             } catch (WrongMethodTypeException expected) {
467                 assertEquals(16.0f, floatsArray[2]);
468             }
469             try {
470                 // int is not convertible to Float
471                 old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f);
472                 assertUnreachable();
473             } catch (WrongMethodTypeException expected) {
474                 assertEquals(16.0f, floatsArray[2]);
475             }
476 
477             try {
478                 MethodHandle unsupportedInvoker =
479                         MethodHandles.varHandleInvoker(
480                                 VarHandle.AccessMode.GET_AND_BITWISE_OR,
481                                 methodType(float.class, float[].class, int.class, float.class));
482                 old =
483                         (float)
484                                 unsupportedInvoker.invoke(
485                                         floatsArrayVarHandle, floatsArray, 0, 2.71f);
486                 assertUnreachable();
487             } catch (UnsupportedOperationException expected) {
488             }
489         }
490     }
491 
main(String[] args)492     public static void main(String[] args) throws Throwable {
493         new FieldVarHandleExactInvokerTest().run();
494         new LongFieldVarHandleExactInvokerTest().run();
495         new FieldVarHandleInvokerTest().run();
496         new DivergenceExactInvokerTest().run();
497         new DivergenceInvokerTest().run();
498     }
499 }
500