1 /*
2  * Copyright (C) 2021 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.MethodHandles;
18 import java.lang.invoke.VarHandle;
19 import java.nio.ByteBuffer;
20 import java.nio.ByteOrder;
21 
22 // These tests cover DoVarHandleInvokeCommon in interpreter_common.cc.
23 
24 public class VarHandleArrayTests {
25     public static class ArrayStoreTest extends VarHandleUnitTest {
26         private static final Integer ZERO = Integer.valueOf(0);
27         private static final Integer ONE = Integer.valueOf(1);
28         private static final Integer TWO = Integer.valueOf(2);
29 
30         private final Integer[] values = new Integer[10];
31 
testIntegerArrayVarHandle()32         private void testIntegerArrayVarHandle() {
33             final VarHandle vh = MethodHandles.arrayElementVarHandle(Integer[].class);
34 
35             // AccessModeTemplate::kSet
36             vh.set(values, 0, ZERO);
37             assertEquals(0, values[0].intValue());
38             vh.set((Object[]) values, 1, ONE);
39             assertEquals(ONE, values[1]);
40             assertThrowsAIOBE(() -> vh.set(values, values.length, null));
41             assertThrowsCCE(() -> vh.set(values, 6, new Object()));
42             assertThrowsCCE(() -> vh.set((Object[]) values, 6, new Object()));
43             assertThrowsNPE(() -> vh.set((Integer[]) null, 6, ONE));
44             assertThrowsWMTE(() -> vh.set(values, 'c'));
45             assertThrowsWMTE(() -> vh.set((Object[]) values, 5, 'c'));
46 
47             // AccessModeTemplate::kGetAndUpdate
48             assertEquals(ZERO, (Integer) vh.getAndSet(values, 0, ONE));
49             assertEquals(ONE, values[0]);
50             assertThrowsAIOBE(() -> vh.getAndSet(values, values.length, null));
51             assertThrowsCCE(() -> vh.getAndSet(values, 6, new Object()));
52             assertThrowsCCE(() -> vh.getAndSet((Object[]) values, 6, new Object()));
53             assertThrowsNPE(() -> vh.getAndSet((Integer[]) null, 6, ONE));
54             assertThrowsWMTE(() -> vh.getAndSet(values, 'c'));
55             assertThrowsWMTE(() -> vh.getAndSet((Object[]) values, 5, 'c'));
56 
57             // AccessModeTemplate::kCompareAndExchange
58             assertEquals(ONE, (Integer) vh.compareAndExchange(values, 0, ONE, TWO));
59             assertEquals(TWO, values[0]);
60             assertEquals(TWO, (Integer) vh.compareAndExchange(values, 0, ONE, ZERO));
61             assertEquals(TWO, values[0]);
62             assertThrowsAIOBE(() -> vh.compareAndExchange(values, values.length, null, null));
63             assertThrowsCCE(() -> vh.compareAndExchange(values, 6, 6, new Object()));
64             assertThrowsCCE(() -> vh.compareAndExchange((Object[]) values, 6, 6, new Object()));
65             assertThrowsNPE(() -> vh.compareAndExchange((Integer[]) null, 6, ONE, ONE));
66             assertThrowsWMTE(() -> vh.compareAndExchange(values, null, 'c'));
67             assertThrowsWMTE(() -> vh.compareAndExchange((Object[]) values, 5, null, 'c'));
68 
69             // AccessModeTemplate::kCompareAndSet
70             assertEquals(true, (boolean) vh.compareAndSet(values, 0, TWO, ONE));
71             assertEquals(ONE, values[0]);
72             assertEquals(false, (boolean) vh.compareAndSet(values, 0, ZERO, TWO));
73             assertEquals(ONE, values[0]);
74             assertThrowsAIOBE(() -> vh.compareAndSet(values, values.length, null, null));
75             assertThrowsCCE(() -> vh.compareAndSet(values, 6, 6, new Object()));
76             assertThrowsCCE(() -> vh.compareAndSet((Object[]) values, 6, 6, new Object()));
77             assertThrowsNPE(() -> vh.compareAndSet((Integer[]) null, 6, ONE, ONE));
78             assertThrowsWMTE(() -> vh.compareAndSet(values, null, 'c'));
79             assertThrowsWMTE(() -> vh.compareAndSet((Object[]) values, 5, null, 'c'));
80         }
81 
testObjectArrayVarHandle()82         private void testObjectArrayVarHandle() {
83             final VarHandle vho = MethodHandles.arrayElementVarHandle(Object[].class);
84 
85             // AccessModeTemplate::kSet
86             vho.set(values, 0, ONE);
87             assertEquals(ONE, values[0]);
88             assertThrowsAIOBE(() -> vho.set(values, values.length, null));
89             assertThrowsASE(() -> vho.set(values, 0, new Object()));
90             assertThrowsASE(() -> vho.set(values, 0, "hello"));
91             assertThrowsNPE(() -> vho.set(null, 0, ZERO));
92             assertThrowsWMTE(() -> vho.set(0, ZERO));
93             assertThrowsWMTE(() -> vho.set(values, ZERO));
94 
95             // AccessModeTemplate::kGetAndUpdate
96             assertEquals(ONE, vho.getAndSetAcquire(values, 0, TWO));
97             assertThrowsAIOBE(() -> vho.getAndSetRelease(values, values.length, null));
98             assertThrowsASE(() -> vho.getAndSet(values, 0, new Object()));
99             assertThrowsASE(() -> vho.getAndSet(values, 0, "hello"));
100             assertThrowsNPE(() -> vho.getAndSet(null, 0, ZERO));
101             assertThrowsWMTE(() -> vho.getAndSet(0, ZERO));
102             assertThrowsWMTE(() -> vho.getAndSet(values, ZERO));
103 
104             // AccessModeTemplate::kCompareAndExchange
105             assertEquals(TWO, vho.compareAndExchange(values, 0, TWO, ZERO));
106             assertThrowsAIOBE(() -> vho.compareAndExchange(values, values.length, ONE, TWO));
107             assertThrowsASE(() -> vho.compareAndExchange(values, 0, ONE, new Object()));
108             assertThrowsASE(() -> vho.compareAndExchange(values, 0, ONE, "hello"));
109             assertThrowsNPE(() -> vho.compareAndExchange(null, 0, ONE, ZERO));
110             assertThrowsWMTE(() -> vho.compareAndExchange(0, ZERO, ONE));
111             assertThrowsWMTE(() -> vho.compareAndExchange(values, ONE, ZERO));
112 
113             // AccessModeTemplate::kCompareAndSet
114             assertEquals(true, (boolean) vho.compareAndSet(values, 0, ZERO, ONE));
115             assertThrowsAIOBE(() -> vho.compareAndSet(values, values.length, ONE, TWO));
116             assertThrowsASE(() -> vho.compareAndSet(values, 0, ONE, new Object()));
117             assertThrowsASE(() -> vho.compareAndSet(values, 0, ONE, "hello"));
118             assertThrowsNPE(() -> vho.compareAndSet(null, 0, ONE, ZERO));
119             assertThrowsWMTE(() -> vho.compareAndSet(0, ZERO, ONE));
120             assertThrowsWMTE(() -> vho.compareAndSet(values, ONE, ZERO));
121         }
122 
toHost(ByteOrder order, byte b0, byte b1)123         private short toHost(ByteOrder order, byte b0, byte b1) {
124             final int u0 = Byte.toUnsignedInt(b0);
125             final int u1 = Byte.toUnsignedInt(b1);
126             if (order == ByteOrder.LITTLE_ENDIAN) {
127                 return (short) (u0 + (u1 << 8));
128             } else {
129                 return (short) (u1 + (u0 << 8));
130             }
131         }
132 
toHost(ByteOrder order, byte b0, byte b1, byte b2, byte b3)133         private int toHost(ByteOrder order, byte b0, byte b1, byte b2, byte b3) {
134             final int u0 = Byte.toUnsignedInt(b0);
135             final int u1 = Byte.toUnsignedInt(b1);
136             final int u2 = Byte.toUnsignedInt(b2);
137             final int u3 = Byte.toUnsignedInt(b3);
138             if (order == ByteOrder.LITTLE_ENDIAN) {
139                 return u0 + (u1 << 8) + (u2 << 16) + (u3 << 24);
140             } else {
141                 return u3 + (u2 << 8) + (u1 << 16) + (u0 << 24);
142             }
143         }
144 
testByteArrayViewVarHandle()145         private void testByteArrayViewVarHandle() {
146             final int BITS_PER_BYTE = 8;
147             byte[] array = new byte[32];
148 
149             final ByteOrder[] byteOrders =
150                     new ByteOrder[] {ByteOrder.LITTLE_ENDIAN, ByteOrder.BIG_ENDIAN};
151 
152             for (ByteOrder order : byteOrders) {
153                 {
154                     final VarHandle vhShort =
155                             MethodHandles.byteArrayViewVarHandle(short[].class, order);
156                     assertThrowsIOOBE(() -> vhShort.get(array, -1));
157                     assertThrowsIOOBE(() -> vhShort.get(array, Integer.MIN_VALUE));
158                     assertThrowsIOOBE(() -> vhShort.get(array, array.length));
159                     assertThrowsIOOBE(() -> vhShort.get(array, array.length - 1));
160                     assertThrowsIOOBE(() -> vhShort.get(array, Integer.MAX_VALUE));
161 
162                     for (int i = 0; i < array.length - 1; ++i) {
163                         final boolean isAligned = (i % 2) == 0;
164                         final short value = (short) ((i + 1) * 0xff);
165                         vhShort.set(array, i, value);
166                         assertEquals(value, (short) vhShort.get(array, i));
167                         assertEquals(value, toHost(order, array[i], array[i + 1]));
168                         for (int j = 0; j < array.length; ++j) {
169                             if (j < i || j > i + 1) {
170                                 assertEquals((byte) 0, array[j]);
171                             }
172                         }
173                         if (isAligned) {
174                             vhShort.getAcquire(array, i);
175                             vhShort.setRelease(array, i, (short) 0);
176                         } else {
177                             final int fi = i;
178                             assertThrowsISE(() -> vhShort.getAcquire(array, fi));
179                             assertThrowsISE(() -> vhShort.setRelease(array, fi, (short) 0));
180                         }
181                         vhShort.set(array, i, (short) 0);
182                     }
183                 }
184                 {
185                     final VarHandle vhInt =
186                             MethodHandles.byteArrayViewVarHandle(int[].class, order);
187                     assertThrowsIOOBE(() -> vhInt.get(array, -1));
188                     assertThrowsIOOBE(() -> vhInt.get(array, Integer.MIN_VALUE));
189                     assertThrowsIOOBE(() -> vhInt.get(array, array.length));
190                     assertThrowsIOOBE(() -> vhInt.get(array, array.length - 1));
191                     assertThrowsIOOBE(() -> vhInt.get(array, array.length - 2));
192                     assertThrowsIOOBE(() -> vhInt.get(array, array.length - 3));
193                     assertThrowsIOOBE(() -> vhInt.get(array, Integer.MAX_VALUE));
194                     for (int i = 0; i < array.length - 3; ++i) {
195                         final boolean isAligned = (i % 4) == 0;
196                         final int value = (i + 1) * 0x11223344;
197                         vhInt.set(array, i, value);
198                         assertEquals(value, vhInt.get(array, i));
199                         assertEquals(
200                                 value,
201                                 toHost(order, array[i], array[i + 1], array[i + 2], array[i + 3]));
202                         for (int j = 0; j < array.length; ++j) {
203                             if (j < i || j > i + 3) {
204                                 assertEquals((byte) 0, array[j]);
205                             }
206                         }
207                         if (isAligned) {
208                             vhInt.getAcquire(array, i);
209                             vhInt.setRelease(array, i, (int) 0);
210                         } else {
211                             final int fi = i;
212                             assertThrowsISE(() -> vhInt.getAcquire(array, fi));
213                             assertThrowsISE(() -> vhInt.setRelease(array, fi, (int) 0));
214                         }
215                         vhInt.set(array, i, 0);
216                     }
217                 }
218             }
219         }
220 
testByteBufferVarHandle()221         private void testByteBufferVarHandle() {
222             final ByteOrder[] byteOrders =
223                     new ByteOrder[] {ByteOrder.LITTLE_ENDIAN, ByteOrder.BIG_ENDIAN};
224 
225             for (final ByteOrder byteOrder : byteOrders) {
226                 final ByteBuffer heapBuffer = ByteBuffer.allocate(32);
227                 final ByteBuffer directBuffer = ByteBuffer.allocateDirect(32);
228                 final ByteBuffer arrayBuffer = ByteBuffer.wrap(new byte[32]);
229                 final ByteBuffer anotherArrayBuffer = ByteBuffer.wrap(new byte[32], 3, 23);
230                 final ByteBuffer[] buffers = {
231                     heapBuffer,
232                     ((ByteBuffer) heapBuffer.duplicate().position(1)).slice(),
233                     directBuffer,
234                     ((ByteBuffer) directBuffer.duplicate().position(1)).slice(),
235                     arrayBuffer,
236                     ((ByteBuffer) arrayBuffer.duplicate().position(1)).slice(),
237                     anotherArrayBuffer,
238                     ((ByteBuffer) anotherArrayBuffer.duplicate().position(1)).slice()
239                 };
240                 for (final ByteBuffer buffer : buffers) {
241                     {
242                         final VarHandle vhShort =
243                                 MethodHandles.byteBufferViewVarHandle(short[].class, byteOrder);
244                         assertThrowsIOOBE(() -> vhShort.get(buffer, -1));
245                         assertThrowsIOOBE(() -> vhShort.get(buffer, Integer.MIN_VALUE));
246                         assertThrowsIOOBE(() -> vhShort.get(buffer, Integer.MAX_VALUE));
247                         assertThrowsIOOBE(() -> vhShort.get(buffer, buffer.limit()));
248                         assertThrowsIOOBE(() -> vhShort.get(buffer, buffer.limit() - 1));
249                         final int zeroAlignment = buffer.alignmentOffset(0, Short.BYTES);
250                         for (int i = 0; i < buffer.limit() - 1; ++i) {
251                             boolean isAligned = (zeroAlignment + i) % Short.BYTES == 0;
252                             final short value = (short) ((i + 1) * 0xff);
253                             vhShort.set(buffer, i, value);
254                             assertEquals(value, (short) vhShort.get(buffer, i));
255                             assertEquals(
256                                     value, toHost(byteOrder, buffer.get(i), buffer.get(i + 1)));
257                             for (int j = 0; j < buffer.limit(); ++j) {
258                                 if (j < i || j > i + 1) {
259                                     assertEquals((byte) 0, buffer.get(j));
260                                 }
261                             }
262                             if (isAligned) {
263                                 vhShort.getAcquire(buffer, i);
264                                 vhShort.setRelease(buffer, i, (short) 0);
265                             } else {
266                                 final int fi = i;
267                                 assertThrowsISE(() -> vhShort.getAcquire(buffer, fi));
268                                 assertThrowsISE(() -> vhShort.setRelease(buffer, fi, (short) 0));
269                             }
270                             vhShort.set(buffer, i, (short) 0);
271                         }
272                     }
273                     {
274                         final VarHandle vhInt =
275                                 MethodHandles.byteBufferViewVarHandle(int[].class, byteOrder);
276                         assertThrowsIOOBE(() -> vhInt.get(buffer, -1));
277                         assertThrowsIOOBE(() -> vhInt.get(buffer, Integer.MIN_VALUE));
278                         assertThrowsIOOBE(() -> vhInt.get(buffer, Integer.MAX_VALUE));
279                         assertThrowsIOOBE(() -> vhInt.get(buffer, buffer.limit()));
280                         assertThrowsIOOBE(() -> vhInt.get(buffer, buffer.limit() - 1));
281                         assertThrowsIOOBE(() -> vhInt.get(buffer, buffer.limit() - 2));
282                         assertThrowsIOOBE(() -> vhInt.get(buffer, buffer.limit() - 3));
283                         final int zeroAlignment = buffer.alignmentOffset(0, Integer.BYTES);
284                         for (int i = 0; i < buffer.limit() - 3; ++i) {
285                             boolean isAligned = (zeroAlignment + i) % Integer.BYTES == 0;
286                             final int value = (i + 1) * 0x11223344;
287                             vhInt.set(buffer, i, value);
288                             assertEquals(value, vhInt.get(buffer, i));
289                             assertEquals(
290                                     value,
291                                     toHost(
292                                             byteOrder,
293                                             buffer.get(i),
294                                             buffer.get(i + 1),
295                                             buffer.get(i + 2),
296                                             buffer.get(i + 3)));
297                             for (int j = 0; j < buffer.limit(); ++j) {
298                                 if (j < i || j > i + 3) {
299                                     assertEquals((byte) 0, buffer.get(j));
300                                 }
301                             }
302                             if (isAligned) {
303                                 vhInt.getAcquire(buffer, i);
304                                 vhInt.setRelease(buffer, i, (int) 0);
305                             } else {
306                                 final int fi = i;
307                                 assertThrowsISE(() -> vhInt.getAcquire(buffer, fi));
308                                 assertThrowsISE(() -> vhInt.setRelease(buffer, fi, (int) 0));
309                             }
310                             vhInt.set(buffer, i, 0);
311                         }
312                     }
313                 }
314             }
315         }
316 
317         @Override
doTest()318         protected void doTest() throws Exception {
319             testIntegerArrayVarHandle();
320             testObjectArrayVarHandle();
321             testByteArrayViewVarHandle();
322             testByteBufferVarHandle();
323         }
324 
main(String[] args)325         public static void main(String[] args) {
326             new ArrayStoreTest().run();
327         }
328     }
329 
main(String[] args)330     public static void main(String[] args) {
331         ArrayStoreTest.main(args);
332     }
333 }
334