| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import static java.lang.invoke.MethodHandles.lookup; |
| import static java.lang.invoke.MethodType.methodType; |
| |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.VarHandle; |
| import java.lang.invoke.WrongMethodTypeException; |
| |
| public final class Main { |
| static class TestSetupError extends Error { |
| TestSetupError(String message, Throwable cause) { |
| super(message, cause); |
| } |
| } |
| |
| private static void failAssertion(String message) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Test failure: "); |
| sb.append(message); |
| throw new AssertionError(sb.toString()); |
| } |
| |
| private static void assertUnreachable() throws Throwable { |
| failAssertion("Unreachable"); |
| } |
| |
| private static void failAssertEquals(Object expected, Object actual) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(expected); |
| sb.append(" != "); |
| sb.append(actual); |
| failAssertion(sb.toString()); |
| } |
| |
| private static void assertEquals(boolean expected, boolean actual) { |
| if (expected != actual) { |
| failAssertEquals(expected, actual); |
| } |
| } |
| |
| private static void assertEquals(int expected, int actual) { |
| if (expected != actual) { |
| failAssertEquals(expected, actual); |
| } |
| } |
| |
| private static void assertEquals(long expected, long actual) { |
| if (expected != actual) { |
| failAssertEquals(expected, actual); |
| } |
| } |
| |
| private static void assertEquals(float expected, float actual) { |
| if (expected != actual) { |
| failAssertEquals(expected, actual); |
| } |
| } |
| |
| static class FieldVarHandleExactInvokerTest { |
| private static final Class<?> THIS_CLASS = FieldVarHandleExactInvokerTest.class; |
| private static final VarHandle fieldVarHandle; |
| |
| int field; |
| |
| static { |
| try { |
| fieldVarHandle = lookup().findVarHandle(THIS_CLASS, "field", int.class); |
| } catch (Exception e) { |
| throw new TestSetupError("Failed to lookup of field", e); |
| } |
| } |
| |
| void run() throws Throwable { |
| System.out.println(THIS_CLASS.getName()); |
| |
| MethodHandle invokerMethodHandle = |
| MethodHandles.varHandleExactInvoker( |
| VarHandle.AccessMode.GET_AND_SET, |
| methodType(int.class, THIS_CLASS, int.class)); |
| |
| field = 3; |
| assertEquals(3, (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 4)); |
| assertEquals(4, field); |
| |
| // |
| // Check invocations with MethodHandle.invokeExact() |
| // |
| try { |
| // Check for unboxing |
| int i = |
| (int) |
| invokerMethodHandle.invokeExact( |
| fieldVarHandle, this, Integer.valueOf(3)); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(4, field); |
| } |
| try { |
| // Check for widening conversion |
| int i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 3); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(4, field); |
| } |
| try { |
| // Check for acceptance of void return type |
| invokerMethodHandle.invokeExact(fieldVarHandle, this, 77); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(4, field); |
| } |
| try { |
| // Check for wider return type |
| long l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 77); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(4, field); |
| } |
| try { |
| // Check null VarHandle instance fails |
| VarHandle vhNull = null; |
| int i = (int) invokerMethodHandle.invokeExact(vhNull, this, 777); |
| assertUnreachable(); |
| } catch (NullPointerException expected) { |
| assertEquals(4, field); |
| } |
| |
| // |
| // Check invocations with MethodHandle.invoke() |
| // |
| |
| // Check for unboxing |
| int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3)); |
| assertEquals(3, field); |
| |
| // Check for unboxing |
| i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Short.valueOf((short) 4)); |
| assertEquals(4, field); |
| |
| // Check for widening conversion |
| i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 23); |
| assertEquals(23, field); |
| |
| // Check for acceptance of void return type |
| invokerMethodHandle.invoke(fieldVarHandle, this, 77); |
| assertEquals(77, field); |
| |
| // Check for wider return type |
| long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88); |
| assertEquals(88, field); |
| |
| try { |
| // Check null VarHandle instance fails |
| VarHandle vhNull = null; |
| i = (int) invokerMethodHandle.invoke(vhNull, this, 888); |
| assertUnreachable(); |
| } catch (NullPointerException expected) { |
| assertEquals(88, field); |
| } |
| } |
| } |
| |
| static class LongFieldVarHandleExactInvokerTest { |
| private static final Class<?> THIS_CLASS = LongFieldVarHandleExactInvokerTest.class; |
| |
| private static final VarHandle fieldVarHandle; |
| |
| private static final long CANARY = 0x0123456789abcdefL; |
| |
| long field = 0L; |
| |
| static { |
| try { |
| fieldVarHandle = lookup().findVarHandle(THIS_CLASS, "field", long.class); |
| } catch (Exception e) { |
| throw new TestSetupError("Failed to lookup of field", e); |
| } |
| } |
| |
| void run() throws Throwable { |
| System.out.println(THIS_CLASS.getName()); |
| |
| MethodHandle invokerMethodHandle = |
| MethodHandles.varHandleExactInvoker( |
| VarHandle.AccessMode.COMPARE_AND_SET, |
| methodType(boolean.class, THIS_CLASS, long.class, long.class)); |
| checkCompareAndSet(invokerMethodHandle, 0L, CANARY); |
| checkCompareAndSet(invokerMethodHandle, 1L, 1L); |
| checkCompareAndSet(invokerMethodHandle, CANARY, ~CANARY); |
| checkCompareAndSet(invokerMethodHandle, ~CANARY, 0L); |
| } |
| |
| private void checkCompareAndSet(MethodHandle compareAndSet, long oldValue, long newValue) |
| throws Throwable { |
| final boolean expectSuccess = (oldValue == field); |
| final long oldFieldValue = field; |
| assertEquals( |
| expectSuccess, |
| (boolean) compareAndSet.invoke(fieldVarHandle, this, oldValue, newValue)); |
| assertEquals(expectSuccess ? newValue : oldFieldValue, field); |
| } |
| } |
| |
| static class FieldVarHandleInvokerTest { |
| private static final Class<?> THIS_CLASS = FieldVarHandleInvokerTest.class; |
| private static final VarHandle fieldVarHandle; |
| int field; |
| |
| static { |
| try { |
| fieldVarHandle = lookup().findVarHandle(THIS_CLASS, "field", int.class); |
| } catch (Exception e) { |
| throw new TestSetupError("Failed to lookup of field", e); |
| } |
| } |
| |
| void run() throws Throwable { |
| System.out.println("fieldVarHandleInvokerTest"); |
| MethodHandle invokerMethodHandle = |
| MethodHandles.varHandleInvoker( |
| VarHandle.AccessMode.GET_AND_SET, |
| methodType(int.class, THIS_CLASS, int.class)); |
| |
| field = 3; |
| int oldField = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 4); |
| assertEquals(3, oldField); |
| assertEquals(4, field); |
| |
| // |
| // Check invocations with MethodHandle.invoke() |
| // |
| |
| // Check for unboxing |
| int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3)); |
| assertEquals(3, field); |
| |
| // Check for widening conversion |
| i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 33); |
| assertEquals(33, field); |
| |
| // Check for widening conversion |
| i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Byte.valueOf((byte) 34)); |
| assertEquals(34, field); |
| |
| // Check for acceptance of void return type |
| invokerMethodHandle.invoke(fieldVarHandle, this, 77); |
| assertEquals(77, field); |
| |
| // Check for wider return type |
| long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88); |
| assertEquals(88, field); |
| try { |
| // Check narrowing conversion fails |
| i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 3.0); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| try { |
| // Check reference type fails |
| i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, "Bad"); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| try { |
| // Check null VarHandle instance fails |
| VarHandle vhNull = null; |
| i = (int) invokerMethodHandle.invoke(vhNull, this, 888); |
| assertUnreachable(); |
| } catch (NullPointerException expected) { |
| assertEquals(88, field); |
| } |
| |
| // |
| // Check invocations with MethodHandle.invokeExact() |
| // |
| field = -1; |
| try { |
| // Check for unboxing |
| i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, Integer.valueOf(3)); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(-1, field); |
| } |
| try { |
| // Check for widening conversion |
| i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 33); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(-1, field); |
| } |
| try { |
| // Check for acceptance of void return type |
| invokerMethodHandle.invokeExact(fieldVarHandle, this, 77); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(-1, field); |
| } |
| try { |
| // Check for wider return type |
| l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 78); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(-1, field); |
| } |
| try { |
| // Check narrowing conversion fails |
| i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 3.0); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| try { |
| // Check reference type fails |
| i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, "Bad"); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| try { |
| // Check null VarHandle instance fails |
| VarHandle vhNull = null; |
| i = (int) invokerMethodHandle.invokeExact(vhNull, this, 888); |
| assertUnreachable(); |
| } catch (NullPointerException expected) { |
| assertEquals(-1, field); |
| } |
| } |
| } |
| |
| static class DivergenceExactInvokerTest { |
| private static final VarHandle floatsArrayVarHandle; |
| |
| static { |
| try { |
| floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class); |
| } catch (Exception e) { |
| throw new TestSetupError("Failed to create VarHandle", e); |
| } |
| } |
| |
| void run() throws Throwable { |
| System.out.println("DivergenceExactInvokerTest"); |
| float[] floatsArray = new float[4]; |
| // Exact invoker of an accessor having the form: |
| // float accessor(float[] values, int index, Float current, float replacement) |
| MethodHandle exactInvoker = |
| MethodHandles.varHandleExactInvoker( |
| VarHandle.AccessMode.COMPARE_AND_EXCHANGE, |
| methodType( |
| float.class, |
| float[].class, |
| int.class, |
| Float.class, |
| float.class)); |
| floatsArray[2] = Float.valueOf(4.0f); |
| // Callsite that is an exact match with exactInvoker.type(). |
| try { |
| // exactInvoker.type() is not compatible with floatsArrayVarHandle accessor. |
| float old = |
| (float) |
| exactInvoker.invoke( |
| floatsArrayVarHandle, |
| floatsArray, |
| 2, |
| Float.valueOf(4.0f), |
| 8.0f); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(4.0f, floatsArray[2]); |
| } |
| |
| // Callsites that are exact matches with exactInvoker.type() |
| try { |
| // Mismatch between exactInvoker.type() and VarHandle type (Float != float) |
| float old = |
| (float) |
| exactInvoker.invoke( |
| floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(4.0f, floatsArray[2]); |
| } |
| try { |
| // short not convertible to Float |
| float old = |
| (float) |
| exactInvoker.invoke( |
| floatsArrayVarHandle, floatsArray, 2, (short) 4, 13.0f); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(4.0f, floatsArray[2]); |
| } |
| try { |
| // int not convertible to Float |
| float old = |
| (float) exactInvoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(4.0f, floatsArray[2]); |
| } |
| } |
| } |
| |
| static class DivergenceInvokerTest { |
| private static final VarHandle floatsArrayVarHandle; |
| |
| static { |
| try { |
| floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class); |
| } catch (Exception e) { |
| throw new TestSetupError("Failed to create VarHandle", e); |
| } |
| } |
| |
| void run() throws Throwable { |
| System.out.println("DivergenceInvokerTest"); |
| float[] floatsArray = new float[4]; |
| // Invoker of an accessor having the form: |
| // float accessor(float[] values, int index, Float current, float replacement) |
| MethodHandle invoker = |
| MethodHandles.varHandleInvoker( |
| VarHandle.AccessMode.COMPARE_AND_EXCHANGE, |
| methodType( |
| float.class, |
| float[].class, |
| int.class, |
| Float.class, |
| float.class)); |
| floatsArray[2] = Float.valueOf(4.0f); |
| // Callsite that is an exact match with invoker.type() |
| float old = |
| (float) |
| invoker.invoke( |
| floatsArrayVarHandle, |
| floatsArray, |
| 2, |
| Float.valueOf(4.0f), |
| 8.0f); |
| assertEquals(4.0f, old); |
| assertEquals(8.0f, floatsArray[2]); |
| |
| // Callsite that is convertible match to invoker.type() |
| old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f); |
| assertEquals(8.0f, old); |
| assertEquals(16.0f, floatsArray[2]); |
| |
| // Callsites that are not convertible to invoker.type(). |
| try { |
| // short is not convertible to Float |
| old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, (short) 4, 8.0f); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(16.0f, floatsArray[2]); |
| } |
| try { |
| // int is not convertible to Float |
| old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f); |
| assertUnreachable(); |
| } catch (WrongMethodTypeException expected) { |
| assertEquals(16.0f, floatsArray[2]); |
| } |
| |
| try { |
| MethodHandle unsupportedInvoker = |
| MethodHandles.varHandleInvoker( |
| VarHandle.AccessMode.GET_AND_BITWISE_OR, |
| methodType(float.class, float[].class, int.class, float.class)); |
| old = |
| (float) |
| unsupportedInvoker.invoke( |
| floatsArrayVarHandle, floatsArray, 0, 2.71f); |
| assertUnreachable(); |
| } catch (UnsupportedOperationException expected) { |
| } |
| } |
| } |
| |
| public static void main(String[] args) throws Throwable { |
| new FieldVarHandleExactInvokerTest().run(); |
| new LongFieldVarHandleExactInvokerTest().run(); |
| new FieldVarHandleInvokerTest().run(); |
| new DivergenceExactInvokerTest().run(); |
| new DivergenceInvokerTest().run(); |
| } |
| } |