| /* |
| * Copyright (C) 2021 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 java.lang.invoke.MethodHandles; |
| import java.lang.invoke.VarHandle; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| |
| // These tests cover DoVarHandleInvokeCommon in interpreter_common.cc. |
| |
| public class VarHandleArrayTests { |
| public static class ArrayStoreTest extends VarHandleUnitTest { |
| private static final Integer ZERO = Integer.valueOf(0); |
| private static final Integer ONE = Integer.valueOf(1); |
| private static final Integer TWO = Integer.valueOf(2); |
| |
| private final Integer[] values = new Integer[10]; |
| |
| private void testIntegerArrayVarHandle() { |
| final VarHandle vh = MethodHandles.arrayElementVarHandle(Integer[].class); |
| |
| // AccessModeTemplate::kSet |
| vh.set(values, 0, ZERO); |
| assertEquals(0, values[0].intValue()); |
| vh.set((Object[]) values, 1, ONE); |
| assertEquals(ONE, values[1]); |
| assertThrowsAIOBE(() -> vh.set(values, values.length, null)); |
| assertThrowsCCE(() -> vh.set(values, 6, new Object())); |
| assertThrowsCCE(() -> vh.set((Object[]) values, 6, new Object())); |
| assertThrowsNPE(() -> vh.set((Integer[]) null, 6, ONE)); |
| assertThrowsWMTE(() -> vh.set(values, 'c')); |
| assertThrowsWMTE(() -> vh.set((Object[]) values, 5, 'c')); |
| |
| // AccessModeTemplate::kGetAndUpdate |
| assertEquals(ZERO, (Integer) vh.getAndSet(values, 0, ONE)); |
| assertEquals(ONE, values[0]); |
| assertThrowsAIOBE(() -> vh.getAndSet(values, values.length, null)); |
| assertThrowsCCE(() -> vh.getAndSet(values, 6, new Object())); |
| assertThrowsCCE(() -> vh.getAndSet((Object[]) values, 6, new Object())); |
| assertThrowsNPE(() -> vh.getAndSet((Integer[]) null, 6, ONE)); |
| assertThrowsWMTE(() -> vh.getAndSet(values, 'c')); |
| assertThrowsWMTE(() -> vh.getAndSet((Object[]) values, 5, 'c')); |
| |
| // AccessModeTemplate::kCompareAndExchange |
| assertEquals(ONE, (Integer) vh.compareAndExchange(values, 0, ONE, TWO)); |
| assertEquals(TWO, values[0]); |
| assertEquals(TWO, (Integer) vh.compareAndExchange(values, 0, ONE, ZERO)); |
| assertEquals(TWO, values[0]); |
| assertThrowsAIOBE(() -> vh.compareAndExchange(values, values.length, null, null)); |
| assertThrowsCCE(() -> vh.compareAndExchange(values, 6, 6, new Object())); |
| assertThrowsCCE(() -> vh.compareAndExchange((Object[]) values, 6, 6, new Object())); |
| assertThrowsNPE(() -> vh.compareAndExchange((Integer[]) null, 6, ONE, ONE)); |
| assertThrowsWMTE(() -> vh.compareAndExchange(values, null, 'c')); |
| assertThrowsWMTE(() -> vh.compareAndExchange((Object[]) values, 5, null, 'c')); |
| |
| // AccessModeTemplate::kCompareAndSet |
| assertEquals(true, (boolean) vh.compareAndSet(values, 0, TWO, ONE)); |
| assertEquals(ONE, values[0]); |
| assertEquals(false, (boolean) vh.compareAndSet(values, 0, ZERO, TWO)); |
| assertEquals(ONE, values[0]); |
| assertThrowsAIOBE(() -> vh.compareAndSet(values, values.length, null, null)); |
| assertThrowsCCE(() -> vh.compareAndSet(values, 6, 6, new Object())); |
| assertThrowsCCE(() -> vh.compareAndSet((Object[]) values, 6, 6, new Object())); |
| assertThrowsNPE(() -> vh.compareAndSet((Integer[]) null, 6, ONE, ONE)); |
| assertThrowsWMTE(() -> vh.compareAndSet(values, null, 'c')); |
| assertThrowsWMTE(() -> vh.compareAndSet((Object[]) values, 5, null, 'c')); |
| } |
| |
| private void testObjectArrayVarHandle() { |
| final VarHandle vho = MethodHandles.arrayElementVarHandle(Object[].class); |
| |
| // AccessModeTemplate::kSet |
| vho.set(values, 0, ONE); |
| assertEquals(ONE, values[0]); |
| assertThrowsAIOBE(() -> vho.set(values, values.length, null)); |
| assertThrowsASE(() -> vho.set(values, 0, new Object())); |
| assertThrowsASE(() -> vho.set(values, 0, "hello")); |
| assertThrowsNPE(() -> vho.set(null, 0, ZERO)); |
| assertThrowsWMTE(() -> vho.set(0, ZERO)); |
| assertThrowsWMTE(() -> vho.set(values, ZERO)); |
| |
| // AccessModeTemplate::kGetAndUpdate |
| assertEquals(ONE, vho.getAndSetAcquire(values, 0, TWO)); |
| assertThrowsAIOBE(() -> vho.getAndSetRelease(values, values.length, null)); |
| assertThrowsASE(() -> vho.getAndSet(values, 0, new Object())); |
| assertThrowsASE(() -> vho.getAndSet(values, 0, "hello")); |
| assertThrowsNPE(() -> vho.getAndSet(null, 0, ZERO)); |
| assertThrowsWMTE(() -> vho.getAndSet(0, ZERO)); |
| assertThrowsWMTE(() -> vho.getAndSet(values, ZERO)); |
| |
| // AccessModeTemplate::kCompareAndExchange |
| assertEquals(TWO, vho.compareAndExchange(values, 0, TWO, ZERO)); |
| assertThrowsAIOBE(() -> vho.compareAndExchange(values, values.length, ONE, TWO)); |
| assertThrowsASE(() -> vho.compareAndExchange(values, 0, ONE, new Object())); |
| assertThrowsASE(() -> vho.compareAndExchange(values, 0, ONE, "hello")); |
| assertThrowsNPE(() -> vho.compareAndExchange(null, 0, ONE, ZERO)); |
| assertThrowsWMTE(() -> vho.compareAndExchange(0, ZERO, ONE)); |
| assertThrowsWMTE(() -> vho.compareAndExchange(values, ONE, ZERO)); |
| |
| // AccessModeTemplate::kCompareAndSet |
| assertEquals(true, (boolean) vho.compareAndSet(values, 0, ZERO, ONE)); |
| assertThrowsAIOBE(() -> vho.compareAndSet(values, values.length, ONE, TWO)); |
| assertThrowsASE(() -> vho.compareAndSet(values, 0, ONE, new Object())); |
| assertThrowsASE(() -> vho.compareAndSet(values, 0, ONE, "hello")); |
| assertThrowsNPE(() -> vho.compareAndSet(null, 0, ONE, ZERO)); |
| assertThrowsWMTE(() -> vho.compareAndSet(0, ZERO, ONE)); |
| assertThrowsWMTE(() -> vho.compareAndSet(values, ONE, ZERO)); |
| } |
| |
| private short toHost(ByteOrder order, byte b0, byte b1) { |
| final int u0 = Byte.toUnsignedInt(b0); |
| final int u1 = Byte.toUnsignedInt(b1); |
| if (order == ByteOrder.LITTLE_ENDIAN) { |
| return (short) (u0 + (u1 << 8)); |
| } else { |
| return (short) (u1 + (u0 << 8)); |
| } |
| } |
| |
| private int toHost(ByteOrder order, byte b0, byte b1, byte b2, byte b3) { |
| final int u0 = Byte.toUnsignedInt(b0); |
| final int u1 = Byte.toUnsignedInt(b1); |
| final int u2 = Byte.toUnsignedInt(b2); |
| final int u3 = Byte.toUnsignedInt(b3); |
| if (order == ByteOrder.LITTLE_ENDIAN) { |
| return u0 + (u1 << 8) + (u2 << 16) + (u3 << 24); |
| } else { |
| return u3 + (u2 << 8) + (u1 << 16) + (u0 << 24); |
| } |
| } |
| |
| private void testByteArrayViewVarHandle() { |
| final int BITS_PER_BYTE = 8; |
| byte[] array = new byte[32]; |
| |
| final ByteOrder[] byteOrders = |
| new ByteOrder[] {ByteOrder.LITTLE_ENDIAN, ByteOrder.BIG_ENDIAN}; |
| |
| for (ByteOrder order : byteOrders) { |
| { |
| final VarHandle vhShort = |
| MethodHandles.byteArrayViewVarHandle(short[].class, order); |
| assertThrowsIOOBE(() -> vhShort.get(array, -1)); |
| assertThrowsIOOBE(() -> vhShort.get(array, Integer.MIN_VALUE)); |
| assertThrowsIOOBE(() -> vhShort.get(array, array.length)); |
| assertThrowsIOOBE(() -> vhShort.get(array, array.length - 1)); |
| assertThrowsIOOBE(() -> vhShort.get(array, Integer.MAX_VALUE)); |
| |
| for (int i = 0; i < array.length - 1; ++i) { |
| final boolean isAligned = (i % 2) == 0; |
| final short value = (short) ((i + 1) * 0xff); |
| vhShort.set(array, i, value); |
| assertEquals(value, (short) vhShort.get(array, i)); |
| assertEquals(value, toHost(order, array[i], array[i + 1])); |
| for (int j = 0; j < array.length; ++j) { |
| if (j < i || j > i + 1) { |
| assertEquals((byte) 0, array[j]); |
| } |
| } |
| if (isAligned) { |
| vhShort.getAcquire(array, i); |
| vhShort.setRelease(array, i, (short) 0); |
| } else { |
| final int fi = i; |
| assertThrowsISE(() -> vhShort.getAcquire(array, fi)); |
| assertThrowsISE(() -> vhShort.setRelease(array, fi, (short) 0)); |
| } |
| vhShort.set(array, i, (short) 0); |
| } |
| } |
| { |
| final VarHandle vhInt = |
| MethodHandles.byteArrayViewVarHandle(int[].class, order); |
| assertThrowsIOOBE(() -> vhInt.get(array, -1)); |
| assertThrowsIOOBE(() -> vhInt.get(array, Integer.MIN_VALUE)); |
| assertThrowsIOOBE(() -> vhInt.get(array, array.length)); |
| assertThrowsIOOBE(() -> vhInt.get(array, array.length - 1)); |
| assertThrowsIOOBE(() -> vhInt.get(array, array.length - 2)); |
| assertThrowsIOOBE(() -> vhInt.get(array, array.length - 3)); |
| assertThrowsIOOBE(() -> vhInt.get(array, Integer.MAX_VALUE)); |
| for (int i = 0; i < array.length - 3; ++i) { |
| final boolean isAligned = (i % 4) == 0; |
| final int value = (i + 1) * 0x11223344; |
| vhInt.set(array, i, value); |
| assertEquals(value, vhInt.get(array, i)); |
| assertEquals( |
| value, |
| toHost(order, array[i], array[i + 1], array[i + 2], array[i + 3])); |
| for (int j = 0; j < array.length; ++j) { |
| if (j < i || j > i + 3) { |
| assertEquals((byte) 0, array[j]); |
| } |
| } |
| if (isAligned) { |
| vhInt.getAcquire(array, i); |
| vhInt.setRelease(array, i, (int) 0); |
| } else { |
| final int fi = i; |
| assertThrowsISE(() -> vhInt.getAcquire(array, fi)); |
| assertThrowsISE(() -> vhInt.setRelease(array, fi, (int) 0)); |
| } |
| vhInt.set(array, i, 0); |
| } |
| } |
| } |
| } |
| |
| private void testByteBufferVarHandle() { |
| final ByteOrder[] byteOrders = |
| new ByteOrder[] {ByteOrder.LITTLE_ENDIAN, ByteOrder.BIG_ENDIAN}; |
| |
| for (final ByteOrder byteOrder : byteOrders) { |
| final ByteBuffer heapBuffer = ByteBuffer.allocate(32); |
| final ByteBuffer directBuffer = ByteBuffer.allocateDirect(32); |
| final ByteBuffer arrayBuffer = ByteBuffer.wrap(new byte[32]); |
| final ByteBuffer anotherArrayBuffer = ByteBuffer.wrap(new byte[32], 3, 23); |
| final ByteBuffer[] buffers = { |
| heapBuffer, |
| ((ByteBuffer) heapBuffer.duplicate().position(1)).slice(), |
| directBuffer, |
| ((ByteBuffer) directBuffer.duplicate().position(1)).slice(), |
| arrayBuffer, |
| ((ByteBuffer) arrayBuffer.duplicate().position(1)).slice(), |
| anotherArrayBuffer, |
| ((ByteBuffer) anotherArrayBuffer.duplicate().position(1)).slice() |
| }; |
| for (final ByteBuffer buffer : buffers) { |
| { |
| final VarHandle vhShort = |
| MethodHandles.byteBufferViewVarHandle(short[].class, byteOrder); |
| assertThrowsIOOBE(() -> vhShort.get(buffer, -1)); |
| assertThrowsIOOBE(() -> vhShort.get(buffer, Integer.MIN_VALUE)); |
| assertThrowsIOOBE(() -> vhShort.get(buffer, Integer.MAX_VALUE)); |
| assertThrowsIOOBE(() -> vhShort.get(buffer, buffer.limit())); |
| assertThrowsIOOBE(() -> vhShort.get(buffer, buffer.limit() - 1)); |
| final int zeroAlignment = buffer.alignmentOffset(0, Short.BYTES); |
| for (int i = 0; i < buffer.limit() - 1; ++i) { |
| boolean isAligned = (zeroAlignment + i) % Short.BYTES == 0; |
| final short value = (short) ((i + 1) * 0xff); |
| vhShort.set(buffer, i, value); |
| assertEquals(value, (short) vhShort.get(buffer, i)); |
| assertEquals( |
| value, toHost(byteOrder, buffer.get(i), buffer.get(i + 1))); |
| for (int j = 0; j < buffer.limit(); ++j) { |
| if (j < i || j > i + 1) { |
| assertEquals((byte) 0, buffer.get(j)); |
| } |
| } |
| if (isAligned) { |
| vhShort.getAcquire(buffer, i); |
| vhShort.setRelease(buffer, i, (short) 0); |
| } else { |
| final int fi = i; |
| assertThrowsISE(() -> vhShort.getAcquire(buffer, fi)); |
| assertThrowsISE(() -> vhShort.setRelease(buffer, fi, (short) 0)); |
| } |
| vhShort.set(buffer, i, (short) 0); |
| } |
| } |
| { |
| final VarHandle vhInt = |
| MethodHandles.byteBufferViewVarHandle(int[].class, byteOrder); |
| assertThrowsIOOBE(() -> vhInt.get(buffer, -1)); |
| assertThrowsIOOBE(() -> vhInt.get(buffer, Integer.MIN_VALUE)); |
| assertThrowsIOOBE(() -> vhInt.get(buffer, Integer.MAX_VALUE)); |
| assertThrowsIOOBE(() -> vhInt.get(buffer, buffer.limit())); |
| assertThrowsIOOBE(() -> vhInt.get(buffer, buffer.limit() - 1)); |
| assertThrowsIOOBE(() -> vhInt.get(buffer, buffer.limit() - 2)); |
| assertThrowsIOOBE(() -> vhInt.get(buffer, buffer.limit() - 3)); |
| final int zeroAlignment = buffer.alignmentOffset(0, Integer.BYTES); |
| for (int i = 0; i < buffer.limit() - 3; ++i) { |
| boolean isAligned = (zeroAlignment + i) % Integer.BYTES == 0; |
| final int value = (i + 1) * 0x11223344; |
| vhInt.set(buffer, i, value); |
| assertEquals(value, vhInt.get(buffer, i)); |
| assertEquals( |
| value, |
| toHost( |
| byteOrder, |
| buffer.get(i), |
| buffer.get(i + 1), |
| buffer.get(i + 2), |
| buffer.get(i + 3))); |
| for (int j = 0; j < buffer.limit(); ++j) { |
| if (j < i || j > i + 3) { |
| assertEquals((byte) 0, buffer.get(j)); |
| } |
| } |
| if (isAligned) { |
| vhInt.getAcquire(buffer, i); |
| vhInt.setRelease(buffer, i, (int) 0); |
| } else { |
| final int fi = i; |
| assertThrowsISE(() -> vhInt.getAcquire(buffer, fi)); |
| assertThrowsISE(() -> vhInt.setRelease(buffer, fi, (int) 0)); |
| } |
| vhInt.set(buffer, i, 0); |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| protected void doTest() throws Exception { |
| testIntegerArrayVarHandle(); |
| testObjectArrayVarHandle(); |
| testByteArrayViewVarHandle(); |
| testByteBufferVarHandle(); |
| } |
| |
| public static void main(String[] args) { |
| new ArrayStoreTest().run(); |
| } |
| } |
| |
| public static void main(String[] args) { |
| ArrayStoreTest.main(args); |
| } |
| } |