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 { 25 TestSetupError(String message, Throwable cause) { 26 super(message, cause); 27 } 28 } 29 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 37 private static void assertUnreachable() throws Throwable { 38 failAssertion("Unreachable"); 39 } 40 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 49 private static void assertEquals(int expected, int actual) { 50 if (expected != actual) { 51 failAssertEquals(expected, actual); 52 } 53 } 54 55 private static void assertEquals(float expected, float actual) { 56 if (expected != actual) { 57 failAssertEquals(expected, actual); 58 } 59 } 60 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 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 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 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 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 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