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