1 /*
  2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
  3  *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  *  This code is free software; you can redistribute it and/or modify it
  6  *  under the terms of the GNU General Public License version 2 only, as
  7  *  published by the Free Software Foundation.
  8  *
  9  *  This code is distributed in the hope that it will be useful, but WITHOUT
 10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  *  version 2 for more details (a copy is included in the LICENSE file that
 13  *  accompanied this code).
 14  *
 15  *  You should have received a copy of the GNU General Public License version
 16  *  2 along with this work; if not, write to the Free Software Foundation,
 17  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  *   Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  *  or visit www.oracle.com if you need additional information or have any
 21  *  questions.
 22  *
 23  */
 24 
 25 /*
 26  * @test
 27  * @run testng TestLayoutPaths
 28  */
 29 
 30 import jdk.incubator.foreign.GroupLayout;
 31 import jdk.incubator.foreign.MemoryLayouts;
 32 import jdk.incubator.foreign.MemoryLayout;
 33 import jdk.incubator.foreign.MemoryLayout.PathElement;
 34 import jdk.incubator.foreign.MemorySegment;
 35 import jdk.incubator.foreign.ResourceScope;
 36 import jdk.incubator.foreign.SequenceLayout;
 37 
 38 import org.testng.SkipException;
 39 import org.testng.annotations.*;
 40 
 41 import java.lang.invoke.MethodHandle;
 42 import java.nio.ByteOrder;
 43 import java.util.ArrayList;
 44 import java.util.List;
 45 
 46 import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
 47 import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
 48 import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
 49 import static org.testng.Assert.*;
 50 
 51 public class TestLayoutPaths {
 52 
 53     @Test(expectedExceptions = IllegalArgumentException.class)
 54     public void testBadBitSelectFromSeq() {
 55         SequenceLayout seq = MemoryLayout.sequenceLayout(JAVA_INT);
 56         seq.bitOffset(groupElement("foo"));
 57     }
 58 
 59     @Test(expectedExceptions = IllegalArgumentException.class)
 60     public void testBadByteSelectFromSeq() {
 61         SequenceLayout seq = MemoryLayout.sequenceLayout(JAVA_INT);
 62         seq.byteOffset(groupElement("foo"));
 63     }
 64 
 65     @Test(expectedExceptions = IllegalArgumentException.class)
 66     public void testBadBitSelectFromStruct() {
 67         GroupLayout g = MemoryLayout.structLayout(JAVA_INT);
 68         g.bitOffset(sequenceElement());
 69     }
 70 
 71     @Test(expectedExceptions = IllegalArgumentException.class)
 72     public void testBadByteSelectFromStruct() {
 73         GroupLayout g = MemoryLayout.structLayout(JAVA_INT);
 74         g.byteOffset(sequenceElement());
 75     }
 76 
 77     @Test(expectedExceptions = IllegalArgumentException.class)
 78     public void testBadBitSelectFromValue() {
 79         SequenceLayout seq = MemoryLayout.sequenceLayout(JAVA_INT);
 80         seq.bitOffset(sequenceElement(), sequenceElement());
 81     }
 82 
 83     @Test(expectedExceptions = IllegalArgumentException.class)
 84     public void testBadByteSelectFromValue() {
 85         SequenceLayout seq = MemoryLayout.sequenceLayout(JAVA_INT);
 86         seq.byteOffset(sequenceElement(), sequenceElement());
 87     }
 88 
 89     @Test(expectedExceptions = IllegalArgumentException.class)
 90     public void testUnknownBitStructField() {
 91         GroupLayout g = MemoryLayout.structLayout(JAVA_INT);
 92         g.bitOffset(groupElement("foo"));
 93     }
 94 
 95     @Test(expectedExceptions = IllegalArgumentException.class)
 96     public void testUnknownByteStructField() {
 97         GroupLayout g = MemoryLayout.structLayout(JAVA_INT);
 98         g.byteOffset(groupElement("foo"));
 99     }
100 
101     @Test(expectedExceptions = IllegalArgumentException.class)
102     public void testBitOutOfBoundsSeqIndex() {
103         SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT);
104         seq.bitOffset(sequenceElement(6));
105     }
106 
107     @Test(expectedExceptions = IllegalArgumentException.class)
108     public void testByteOutOfBoundsSeqIndex() {
109         SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT);
110         seq.byteOffset(sequenceElement(6));
111     }
112 
113     @Test(expectedExceptions = IllegalArgumentException.class)
114     public void testNegativeSeqIndex() {
115        sequenceElement(-2);
116     }
117 
118     @Test(expectedExceptions = IllegalArgumentException.class)
119     public void testBitNegativeSeqIndex() {
120         SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT);
121         seq.bitOffset(sequenceElement(-2));
122     }
123 
124     @Test(expectedExceptions = IllegalArgumentException.class)
125     public void testByteNegativeSeqIndex() {
126         SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT);
127         seq.byteOffset(sequenceElement(-2));
128     }
129 
130     @Test(expectedExceptions = IllegalArgumentException.class)
131     public void testOutOfBoundsSeqRange() {
132         SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT);
133         seq.bitOffset(sequenceElement(6, 2));
134     }
135 
136     @Test(expectedExceptions = IllegalArgumentException.class)
137     public void testNegativeSeqRange() {
138         sequenceElement(-2, 2);
139     }
140 
141     @Test(expectedExceptions = IllegalArgumentException.class)
142     public void testBitNegativeSeqRange() {
143         SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT);
144         seq.bitOffset(sequenceElement(-2, 2));
145     }
146 
147     @Test(expectedExceptions = IllegalArgumentException.class)
148     public void testByteNegativeSeqRange() {
149         SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT);
150         seq.byteOffset(sequenceElement(-2, 2));
151     }
152 
153     @Test(expectedExceptions = IllegalArgumentException.class)
154     public void testIncompleteAccess() {
155         SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT));
156         seq.varHandle(int.class, sequenceElement());
157     }
158 
159     @Test(expectedExceptions = IllegalArgumentException.class)
160     public void testBitOffsetHandleBadRange() {
161         SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT));
162         seq.bitOffsetHandle(sequenceElement(0, 1)); // ranges not accepted
163     }
164 
165     @Test(expectedExceptions = IllegalArgumentException.class)
166     public void testByteOffsetHandleBadRange() {
167         SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT));
168         seq.byteOffsetHandle(sequenceElement(0, 1)); // ranges not accepted
169     }
170 
171     @Test(expectedExceptions = UnsupportedOperationException.class)
172     public void testBadMultiple() {
173         GroupLayout g = MemoryLayout.structLayout(MemoryLayout.paddingLayout(3), JAVA_INT.withName("foo"));
174         g.byteOffset(groupElement("foo"));
175     }
176 
177     @Test(expectedExceptions = UnsupportedOperationException.class)
178     public void testBitOffsetBadUnboundedSequenceTraverse() {
179         MemoryLayout layout = MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(JAVA_INT));
180         layout.bitOffset(sequenceElement(1), sequenceElement(0));
181     }
182 
183     @Test(expectedExceptions = UnsupportedOperationException.class)
184     public void testByteOffsetBadUnboundedSequenceTraverse() {
185         MemoryLayout layout = MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(JAVA_INT));
186         layout.byteOffset(sequenceElement(1), sequenceElement(0));
187     }
188 
189     @Test(expectedExceptions = UnsupportedOperationException.class)
190     public void testBitOffsetHandleBadUnboundedSequenceTraverse() {
191         MemoryLayout layout = MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(JAVA_INT));
192         layout.bitOffsetHandle(sequenceElement(1), sequenceElement(0));
193     }
194 
195     @Test(expectedExceptions = UnsupportedOperationException.class)
196     public void testByteOffsetHandleBadUnboundedSequenceTraverse() {
197         MemoryLayout layout = MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(JAVA_INT));
198         layout.byteOffsetHandle(sequenceElement(1), sequenceElement(0));
199     }
200 
201     @Test(expectedExceptions = UnsupportedOperationException.class)
202     public void testBadByteOffsetNoMultipleOf8() {
203         MemoryLayout layout = MemoryLayout.structLayout(MemoryLayout.paddingLayout(7), JAVA_INT.withName("x"));
204         layout.byteOffset(groupElement("x"));
205     }
206 
207     @Test(expectedExceptions = UnsupportedOperationException.class)
208     public void testBadByteOffsetHandleNoMultipleOf8() throws Throwable {
209         MemoryLayout layout = MemoryLayout.structLayout(MemoryLayout.paddingLayout(7), JAVA_INT.withName("x"));
210         MethodHandle handle = layout.byteOffsetHandle(groupElement("x"));
211         handle.invoke();
212     }
213 
214     @Test
215     public void testBadContainerAlign() {
216         GroupLayout g = MemoryLayout.structLayout(JAVA_INT.withBitAlignment(16).withName("foo")).withBitAlignment(8);
217         try {
218             g.bitOffset(groupElement("foo"));
219             g.byteOffset(groupElement("foo"));
220         } catch (Throwable ex) {
221             throw new AssertionError(ex); // should be ok!
222         }
223         try {
224             g.varHandle(int.class, groupElement("foo")); //ok
225             assertTrue(false); //should fail!
226         } catch (UnsupportedOperationException ex) {
227             //ok
228         } catch (Throwable ex) {
229             throw new AssertionError(ex); //should fail!
230         }
231     }
232 
233     @Test
234     public void testBadAlignOffset() {
235         GroupLayout g = MemoryLayout.structLayout(MemoryLayouts.PAD_8, JAVA_INT.withBitAlignment(16).withName("foo"));
236         try {
237             g.bitOffset(groupElement("foo"));
238             g.byteOffset(groupElement("foo"));
239         } catch (Throwable ex) {
240             throw new AssertionError(ex); // should be ok!
241         }
242         try {
243             g.varHandle(int.class, groupElement("foo")); //ok
244             assertTrue(false); //should fail!
245         } catch (UnsupportedOperationException ex) {
246             //ok
247         } catch (Throwable ex) {
248             throw new AssertionError(ex); //should fail!
249         }
250     }
251 
252     @Test
253     public void testBadSequencePathInOffset() {
254         SequenceLayout seq = MemoryLayout.sequenceLayout(10, JAVA_INT);
255         // bad path elements
256         for (PathElement e : List.of( sequenceElement(), sequenceElement(0, 2) )) {
257             try {
258                 seq.bitOffset(e);
259                 fail();
260             } catch (IllegalArgumentException ex) {
261                 assertTrue(true);
262             }
263             try {
264                 seq.byteOffset(e);
265                 fail();
266             } catch (IllegalArgumentException ex) {
267                 assertTrue(true);
268             }
269         }
270     }
271 
272     @Test
273     public void testBadSequencePathInSelect() {
274         SequenceLayout seq = MemoryLayout.sequenceLayout(10, JAVA_INT);
275         for (PathElement e : List.of( sequenceElement(0), sequenceElement(0, 2) )) {
276             try {
277                 seq.select(e);
278                 fail();
279             } catch (IllegalArgumentException ex) {
280                 assertTrue(true);
281             }
282         }
283     }
284 
285     @Test
286     public void testBadSequencePathInMap() {
287         SequenceLayout seq = MemoryLayout.sequenceLayout(10, JAVA_INT);
288         for (PathElement e : List.of( sequenceElement(0), sequenceElement(0, 2) )) {
289             try {
290                 seq.map(l -> l, e);
291                 fail();
292             } catch (IllegalArgumentException ex) {
293                 assertTrue(true);
294             }
295         }
296     }
297 
298     @Test
299     public void testStructPaths() {
300         long[] offsets = { 0, 8, 24, 56 };
301         GroupLayout g = MemoryLayout.structLayout(
302                 MemoryLayouts.JAVA_BYTE.withName("1"),
303                 MemoryLayouts.JAVA_CHAR.withName("2"),
304                 MemoryLayouts.JAVA_FLOAT.withName("3"),
305                 MemoryLayouts.JAVA_LONG.withName("4")
306         );
307 
308         // test select
309 
310         for (int i = 1 ; i <= 4 ; i++) {
311             MemoryLayout selected = g.select(groupElement(String.valueOf(i)));
312             assertTrue(selected == g.memberLayouts().get(i - 1));
313         }
314 
315         // test offset
316 
317         for (int i = 1 ; i <= 4 ; i++) {
318             long bitOffset = g.bitOffset(groupElement(String.valueOf(i)));
319             assertEquals(offsets[i - 1], bitOffset);
320             long byteOffset = g.byteOffset(groupElement(String.valueOf(i)));
321             assertEquals((offsets[i - 1]) >>> 3, byteOffset);
322         }
323 
324         // test map
325 
326         for (int i = 1 ; i <= 4 ; i++) {
327             GroupLayout g2 = (GroupLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, groupElement(String.valueOf(i)));
328             assertTrue(g2.isStruct());
329             for (int j = 0 ; j < 4 ; j++) {
330                 if (j == i - 1) {
331                     assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE);
332                 } else {
333                     assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j));
334                 }
335             }
336         }
337     }
338 
339     @Test
340     public void testUnionPaths() {
341         long[] offsets = { 0, 0, 0, 0 };
342         GroupLayout g = MemoryLayout.unionLayout(
343                 MemoryLayouts.JAVA_BYTE.withName("1"),
344                 MemoryLayouts.JAVA_CHAR.withName("2"),
345                 MemoryLayouts.JAVA_FLOAT.withName("3"),
346                 MemoryLayouts.JAVA_LONG.withName("4")
347         );
348 
349         // test select
350 
351         for (int i = 1 ; i <= 4 ; i++) {
352             MemoryLayout selected = g.select(groupElement(String.valueOf(i)));
353             assertTrue(selected == g.memberLayouts().get(i - 1));
354         }
355 
356         // test offset
357 
358         for (int i = 1 ; i <= 4 ; i++) {
359             long bitOffset = g.bitOffset(groupElement(String.valueOf(i)));
360             assertEquals(offsets[i - 1], bitOffset);
361             long byteOffset = g.byteOffset(groupElement(String.valueOf(i)));
362             assertEquals((offsets[i - 1]) >>> 3, byteOffset);
363         }
364 
365         // test map
366 
367         for (int i = 1 ; i <= 4 ; i++) {
368             GroupLayout g2 = (GroupLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, groupElement(String.valueOf(i)));
369             assertTrue(g2.isUnion());
370             for (int j = 0 ; j < 4 ; j++) {
371                 if (j == i - 1) {
372                     assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE);
373                 } else {
374                     assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j));
375                 }
376             }
377         }
378     }
379 
380     @Test
381     public void testSequencePaths() {
382         long[] offsets = { 0, 8, 16, 24 };
383         SequenceLayout g = MemoryLayout.sequenceLayout(4, MemoryLayouts.JAVA_BYTE);
384 
385         // test select
386 
387         MemoryLayout selected = g.select(sequenceElement());
388         assertTrue(selected == MemoryLayouts.JAVA_BYTE);
389 
390         // test offset
391 
392         for (int i = 0 ; i < 4 ; i++) {
393             long bitOffset = g.bitOffset(sequenceElement(i));
394             assertEquals(offsets[i], bitOffset);
395             long byteOffset = g.byteOffset(sequenceElement(i));
396             assertEquals((offsets[i]) >>> 3, byteOffset);
397         }
398 
399         // test map
400 
401         SequenceLayout seq2 = (SequenceLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, sequenceElement());
402         assertTrue(seq2.elementLayout() == MemoryLayouts.JAVA_DOUBLE);
403     }
404 
405     @Test(dataProvider = "testLayouts")
406     public void testOffsetHandle(MemoryLayout layout, PathElement[] pathElements, long[] indexes,
407                                  long expectedBitOffset) throws Throwable {
408         MethodHandle bitOffsetHandle = layout.bitOffsetHandle(pathElements);
409         bitOffsetHandle = bitOffsetHandle.asSpreader(long[].class, indexes.length);
410         long actualBitOffset = (long) bitOffsetHandle.invokeExact(indexes);
411         assertEquals(actualBitOffset, expectedBitOffset);
412         if (expectedBitOffset % 8 == 0) {
413             MethodHandle byteOffsetHandle = layout.byteOffsetHandle(pathElements);
414             byteOffsetHandle = byteOffsetHandle.asSpreader(long[].class, indexes.length);
415             long actualByteOffset = (long) byteOffsetHandle.invokeExact(indexes);
416             assertEquals(actualByteOffset, expectedBitOffset / 8);
417         }
418     }
419 
420     @DataProvider
421     public static Object[][] testLayouts() {
422         List<Object[]> testCases = new ArrayList<>();
423 
424         testCases.add(new Object[] {
425             MemoryLayout.sequenceLayout(10, JAVA_INT),
426             new PathElement[] { sequenceElement() },
427             new long[] { 4 },
428             JAVA_INT.bitSize() * 4
429         });
430         testCases.add(new Object[] {
431             MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(JAVA_INT, JAVA_INT.withName("y"))),
432             new PathElement[] { sequenceElement(), groupElement("y") },
433             new long[] { 4 },
434             (JAVA_INT.bitSize() * 2) * 4 + JAVA_INT.bitSize()
435         });
436         testCases.add(new Object[] {
437             MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(5), JAVA_INT.withName("y"))),
438             new PathElement[] { sequenceElement(), groupElement("y") },
439             new long[] { 4 },
440             (JAVA_INT.bitSize() + 5) * 4 + 5
441         });
442         testCases.add(new Object[] {
443             MemoryLayout.sequenceLayout(10, JAVA_INT),
444             new PathElement[] { sequenceElement() },
445             new long[] { 4 },
446             JAVA_INT.bitSize() * 4
447         });
448         testCases.add(new Object[] {
449             MemoryLayout.structLayout(
450                 MemoryLayout.sequenceLayout(10, JAVA_INT).withName("data")
451             ),
452             new PathElement[] { groupElement("data"), sequenceElement() },
453             new long[] { 4 },
454             JAVA_INT.bitSize() * 4
455         });
456 
457         MemoryLayout complexLayout = MemoryLayout.structLayout(
458             MemoryLayout.sequenceLayout(10,
459                 MemoryLayout.sequenceLayout(10,
460                     MemoryLayout.structLayout(
461                         JAVA_INT.withName("x"),
462                         JAVA_INT.withName("y")
463                     )
464                 )
465             ).withName("data")
466         );
467 
468         testCases.add(new Object[] {
469             complexLayout,
470             new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("x") },
471             new long[] { 0, 1 },
472             (JAVA_INT.bitSize() * 2)
473         });
474         testCases.add(new Object[] {
475             complexLayout,
476             new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("x") },
477             new long[] { 1, 0 },
478             (JAVA_INT.bitSize() * 2) * 10
479         });
480         testCases.add(new Object[] {
481             complexLayout,
482             new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("y") },
483             new long[] { 0, 1 },
484             (JAVA_INT.bitSize() * 2) + JAVA_INT.bitSize()
485         });
486         testCases.add(new Object[] {
487             complexLayout,
488             new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("y") },
489             new long[] { 1, 0 },
490             (JAVA_INT.bitSize() * 2) * 10 + JAVA_INT.bitSize()
491         });
492 
493         return testCases.toArray(Object[][]::new);
494     }
495 
496     @Test(dataProvider = "testLayouts")
497     public void testSliceHandle(MemoryLayout layout, PathElement[] pathElements, long[] indexes,
498                                 long expectedBitOffset) throws Throwable {
499         if (expectedBitOffset % 8 != 0)
500             throw new SkipException("Offset not a multiple of 8");
501 
502         MemoryLayout selected = layout.select(pathElements);
503         MethodHandle sliceHandle = layout.sliceHandle(pathElements);
504         sliceHandle = sliceHandle.asSpreader(long[].class, indexes.length);
505 
506         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
507             MemorySegment segment = MemorySegment.allocateNative(layout, scope);
508             MemorySegment slice = (MemorySegment) sliceHandle.invokeExact(segment, indexes);
509             assertEquals(slice.address().segmentOffset(segment), expectedBitOffset / 8);
510             assertEquals(slice.byteSize(), selected.byteSize());
511         }
512     }
513 
514     @Test(expectedExceptions = UnsupportedOperationException.class)
515     public void testSliceHandleUOEInvalidSize() {
516         MemoryLayout layout = MemoryLayout.structLayout(
517             MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("x"),
518             MemoryLayout.valueLayout(31, ByteOrder.nativeOrder()).withName("y") // size not a multiple of 8
519         );
520 
521         layout.sliceHandle(groupElement("y")); // should throw
522     }
523 
524     @Test(expectedExceptions = UnsupportedOperationException.class)
525     public void testSliceHandleUOEInvalidOffsetEager() throws Throwable {
526         MemoryLayout layout = MemoryLayout.structLayout(
527             MemoryLayout.paddingLayout(5),
528             MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("y") // offset not a multiple of 8
529         );
530 
531         layout.sliceHandle(groupElement("y")); // should throw
532     }
533 
534     @Test(expectedExceptions = UnsupportedOperationException.class)
535     public void testSliceHandleUOEInvalidOffsetLate() throws Throwable {
536         MemoryLayout layout = MemoryLayout.sequenceLayout(3,
537             MemoryLayout.structLayout(
538                 MemoryLayout.paddingLayout(4),
539                 MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("y") // offset not a multiple of 8
540             )
541         );
542 
543         MethodHandle sliceHandle;
544         try {
545             sliceHandle = layout.sliceHandle(sequenceElement(), groupElement("y")); // should work
546         } catch (UnsupportedOperationException uoe) {
547             fail("Unexpected exception", uoe);
548             return;
549         }
550 
551         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
552             MemorySegment segment = MemorySegment.allocateNative(layout, scope);
553 
554             try {
555                 sliceHandle.invokeExact(segment, 1); // should work
556             } catch (UnsupportedOperationException uoe) {
557                 fail("Unexpected exception", uoe);
558                 return;
559             }
560 
561             sliceHandle.invokeExact(segment, 0); // should throw
562         }
563     }
564 }
565