# HG changeset patch # User psandoz # Date 1516656448 28800 # Node ID 5375d426822a59fba8ec7ba9a6ac222d235e52ef # Parent 99b6276379119211ba6d2dad6a2d6f76d1d0130b 8194233: Improve support for array handles Reviewed-by: jrose, vlivanov, ahgross, rhalade diff -r 99b627637911 -r 5375d426822a src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template Fri Jan 19 11:24:39 2018 -0800 +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template Mon Jan 22 13:27:28 2018 -0800 @@ -592,6 +592,28 @@ return accessMode.at.accessModeType({#if[Object]?arrayType:$type$[].class}, {#if[Object]?arrayType.getComponentType():$type$.class}, int.class); } +#if[Object] + @ForceInline + static Object runtimeTypeCheck(Array handle, Object[] oarray, Object value) { + if (handle.arrayType == oarray.getClass()) { + // Fast path: static array type same as argument array type + return handle.componentType.cast(value); + } else { + // Slow path: check value against argument array component type + return reflectiveTypeCheck(oarray, value); + } + } + + @ForceInline + static Object reflectiveTypeCheck(Object[] oarray, Object value) { + try { + return oarray.getClass().getComponentType().cast(value); + } catch (ClassCastException e) { + throw new ArrayStoreException(); + } + } +#end[Object] + @ForceInline static $type$ get(Array handle, Object oarray, int index) { #if[Object] @@ -632,7 +654,7 @@ #end[Object] UNSAFE.put$Type$Volatile(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -655,7 +677,7 @@ #end[Object] UNSAFE.put$Type$Opaque(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -678,7 +700,7 @@ #end[Object] UNSAFE.put$Type$Release(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } #if[CAS] @@ -692,7 +714,7 @@ return UNSAFE.compareAndSet$Type$(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, {#if[Object]?handle.componentType.cast(expected):expected}, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -705,7 +727,7 @@ return UNSAFE.compareAndExchange$Type$(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, {#if[Object]?handle.componentType.cast(expected):expected}, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -718,7 +740,7 @@ return UNSAFE.compareAndExchange$Type$Acquire(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, {#if[Object]?handle.componentType.cast(expected):expected}, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -731,7 +753,7 @@ return UNSAFE.compareAndExchange$Type$Release(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, {#if[Object]?handle.componentType.cast(expected):expected}, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -744,7 +766,7 @@ return UNSAFE.weakCompareAndSet$Type$Plain(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, {#if[Object]?handle.componentType.cast(expected):expected}, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -757,7 +779,7 @@ return UNSAFE.weakCompareAndSet$Type$(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, {#if[Object]?handle.componentType.cast(expected):expected}, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -770,7 +792,7 @@ return UNSAFE.weakCompareAndSet$Type$Acquire(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, {#if[Object]?handle.componentType.cast(expected):expected}, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -783,7 +805,7 @@ return UNSAFE.weakCompareAndSet$Type$Release(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, {#if[Object]?handle.componentType.cast(expected):expected}, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -795,7 +817,7 @@ #end[Object] return UNSAFE.getAndSet$Type$(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -807,7 +829,7 @@ #end[Object] return UNSAFE.getAndSet$Type$Acquire(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } @ForceInline @@ -819,7 +841,7 @@ #end[Object] return UNSAFE.getAndSet$Type$Release(array, (((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase, - {#if[Object]?handle.componentType.cast(value):value}); + {#if[Object]?runtimeTypeCheck(handle, array, value):value}); } #end[CAS] #if[AtomicAdd] diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleBaseTest.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseTest.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseTest.java Mon Jan 22 13:27:28 2018 -0800 @@ -70,6 +70,14 @@ checkWithThrowable(IndexOutOfBoundsException.class, message, r); } + static void checkASE(ThrowingRunnable r) { + checkWithThrowable(ArrayStoreException.class, null, r); + } + + static void checkASE(Object message, ThrowingRunnable r) { + checkWithThrowable(ArrayStoreException.class, message, r); + } + static void checkISE(ThrowingRunnable r) { checkWithThrowable(IllegalStateException.class, null, r); } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,7 @@ VarHandle vhArray; + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -210,7 +211,6 @@ cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccessBoolean::testArrayIndexOutOfBounds, false)); - // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1255,5 +1255,6 @@ }); } } + } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,7 @@ VarHandle vhArray; + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -210,7 +211,6 @@ cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccessByte::testArrayIndexOutOfBounds, false)); - // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1292,5 +1292,6 @@ }); } } + } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,7 @@ VarHandle vhArray; + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -210,7 +211,6 @@ cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccessChar::testArrayIndexOutOfBounds, false)); - // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1292,5 +1292,6 @@ }); } } + } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,7 @@ VarHandle vhArray; + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -210,7 +211,6 @@ cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccessDouble::testArrayIndexOutOfBounds, false)); - // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1183,5 +1183,6 @@ } } + } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,7 @@ VarHandle vhArray; + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -210,7 +211,6 @@ cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccessFloat::testArrayIndexOutOfBounds, false)); - // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1183,5 +1183,6 @@ } } + } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,7 @@ VarHandle vhArray; + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -210,7 +211,6 @@ cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccessInt::testArrayIndexOutOfBounds, false)); - // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1292,5 +1292,6 @@ }); } } + } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,7 @@ VarHandle vhArray; + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -210,7 +211,6 @@ cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccessLong::testArrayIndexOutOfBounds, false)); - // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1292,5 +1292,6 @@ }); } } + } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,7 @@ VarHandle vhArray; + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -210,7 +211,6 @@ cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccessShort::testArrayIndexOutOfBounds, false)); - // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1292,5 +1292,6 @@ }); } } + } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessString.java --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessString.java Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessString.java Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,8 @@ VarHandle vhArray; + VarHandle vhArrayObject; + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -75,6 +77,7 @@ VarHandleTestAccessString.class, "static_v", String.class); vhArray = MethodHandles.arrayElementVarHandle(String[].class); + vhArrayObject = MethodHandles.arrayElementVarHandle(Object[].class); } @@ -204,13 +207,17 @@ cases.add(new VarHandleAccessTestCase("Array", vhArray, VarHandleTestAccessString::testArray)); + cases.add(new VarHandleAccessTestCase("Array Object[]", + vhArrayObject, VarHandleTestAccessString::testArray)); cases.add(new VarHandleAccessTestCase("Array unsupported", vhArray, VarHandleTestAccessString::testArrayUnsupported, false)); cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccessString::testArrayIndexOutOfBounds, false)); - + cases.add(new VarHandleAccessTestCase("Array store exception", + vhArrayObject, VarHandleTestAccessString::testArrayStoreException, + false)); // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1146,5 +1153,86 @@ } } + + static void testArrayStoreException(VarHandle vh) throws Throwable { + Object[] array = new String[10]; + Arrays.fill(array, "foo"); + Object value = new Object(); + + // Set + checkASE(() -> { + vh.set(array, 0, value); + }); + + // SetVolatile + checkASE(() -> { + vh.setVolatile(array, 0, value); + }); + + // SetOpaque + checkASE(() -> { + vh.setOpaque(array, 0, value); + }); + + // SetRelease + checkASE(() -> { + vh.setRelease(array, 0, value); + }); + + // CompareAndSet + checkASE(() -> { // receiver reference class + boolean r = vh.compareAndSet(array, 0, "foo", value); + }); + + // WeakCompareAndSet + checkASE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetPlain(array, 0, "foo", value); + }); + + // WeakCompareAndSetVolatile + checkASE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSet(array, 0, "foo", value); + }); + + // WeakCompareAndSetAcquire + checkASE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetAcquire(array, 0, "foo", value); + }); + + // WeakCompareAndSetRelease + checkASE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetRelease(array, 0, "foo", value); + }); + + // CompareAndExchange + checkASE(() -> { // receiver reference class + String x = (String) vh.compareAndExchange(array, 0, "foo", value); + }); + + // CompareAndExchangeAcquire + checkASE(() -> { // receiver reference class + String x = (String) vh.compareAndExchangeAcquire(array, 0, "foo", value); + }); + + // CompareAndExchangeRelease + checkASE(() -> { // receiver reference class + String x = (String) vh.compareAndExchangeRelease(array, 0, "foo", value); + }); + + // GetAndSet + checkASE(() -> { // receiver reference class + String x = (String) vh.getAndSet(array, 0, value); + }); + + // GetAndSetAcquire + checkASE(() -> { // receiver reference class + String x = (String) vh.getAndSetAcquire(array, 0, value); + }); + + // GetAndSetRelease + checkASE(() -> { // receiver reference class + String x = (String) vh.getAndSetRelease(array, 0, value); + }); + } } diff -r 99b627637911 -r 5375d426822a test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template --- a/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template Fri Jan 19 11:24:39 2018 -0800 +++ b/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template Mon Jan 22 13:27:28 2018 -0800 @@ -60,6 +60,10 @@ VarHandle vhArray; +#if[String] + VarHandle vhArrayObject; +#end[String] + @BeforeClass public void setup() throws Exception { vhFinalField = MethodHandles.lookup().findVarHandle( @@ -75,6 +79,9 @@ VarHandleTestAccess$Type$.class, "static_v", $type$.class); vhArray = MethodHandles.arrayElementVarHandle($type$[].class); +#if[String] + vhArrayObject = MethodHandles.arrayElementVarHandle(Object[].class); +#end[String] } @@ -236,13 +243,21 @@ cases.add(new VarHandleAccessTestCase("Array", vhArray, VarHandleTestAccess$Type$::testArray)); +#if[String] + cases.add(new VarHandleAccessTestCase("Array Object[]", + vhArrayObject, VarHandleTestAccess$Type$::testArray)); +#end[String] cases.add(new VarHandleAccessTestCase("Array unsupported", vhArray, VarHandleTestAccess$Type$::testArrayUnsupported, false)); cases.add(new VarHandleAccessTestCase("Array index out of bounds", vhArray, VarHandleTestAccess$Type$::testArrayIndexOutOfBounds, false)); - +#if[String] + cases.add(new VarHandleAccessTestCase("Array store exception", + vhArrayObject, VarHandleTestAccess$Type$::testArrayStoreException, + false)); +#end[String] // Work around issue with jtreg summary reporting which truncates // the String result of Object.toString to 30 characters, hence // the first dummy argument @@ -1823,5 +1838,88 @@ #end[Bitwise] } } + +#if[String] + static void testArrayStoreException(VarHandle vh) throws Throwable { + Object[] array = new $type$[10]; + Arrays.fill(array, $value1$); + Object value = new Object(); + + // Set + checkASE(() -> { + vh.set(array, 0, value); + }); + + // SetVolatile + checkASE(() -> { + vh.setVolatile(array, 0, value); + }); + + // SetOpaque + checkASE(() -> { + vh.setOpaque(array, 0, value); + }); + + // SetRelease + checkASE(() -> { + vh.setRelease(array, 0, value); + }); + + // CompareAndSet + checkASE(() -> { // receiver reference class + boolean r = vh.compareAndSet(array, 0, $value1$, value); + }); + + // WeakCompareAndSet + checkASE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetPlain(array, 0, $value1$, value); + }); + + // WeakCompareAndSetVolatile + checkASE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSet(array, 0, $value1$, value); + }); + + // WeakCompareAndSetAcquire + checkASE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetAcquire(array, 0, $value1$, value); + }); + + // WeakCompareAndSetRelease + checkASE(() -> { // receiver reference class + boolean r = vh.weakCompareAndSetRelease(array, 0, $value1$, value); + }); + + // CompareAndExchange + checkASE(() -> { // receiver reference class + $type$ x = ($type$) vh.compareAndExchange(array, 0, $value1$, value); + }); + + // CompareAndExchangeAcquire + checkASE(() -> { // receiver reference class + $type$ x = ($type$) vh.compareAndExchangeAcquire(array, 0, $value1$, value); + }); + + // CompareAndExchangeRelease + checkASE(() -> { // receiver reference class + $type$ x = ($type$) vh.compareAndExchangeRelease(array, 0, $value1$, value); + }); + + // GetAndSet + checkASE(() -> { // receiver reference class + $type$ x = ($type$) vh.getAndSet(array, 0, value); + }); + + // GetAndSetAcquire + checkASE(() -> { // receiver reference class + $type$ x = ($type$) vh.getAndSetAcquire(array, 0, value); + }); + + // GetAndSetRelease + checkASE(() -> { // receiver reference class + $type$ x = ($type$) vh.getAndSetRelease(array, 0, value); + }); + } +#end[String] }