1 /*
  2  * Copyright (c) 2025, 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 import java.util.Map;
 26 import java.util.Set;
 27 import java.util.stream.Collectors;
 28 
 29 import jdk.internal.value.LayoutIteration;
 30 import jdk.internal.vm.annotation.LooselyConsistentValue;
 31 import jdk.internal.vm.annotation.NullRestricted;
 32 import org.junit.jupiter.api.Assertions;
 33 import org.junit.jupiter.api.Test;
 34 
 35 import static org.junit.jupiter.api.Assertions.assertEquals;
 36 
 37 /*
 38  * @test
 39  * @summary test LayoutIteration
 40  * @enablePreview
 41  * @requires vm.flagless
 42  * @modules java.base/jdk.internal.vm.annotation
 43  *          java.base/jdk.internal.value
 44  * @run junit/othervm LayoutIterationTest
 45  */
 46 class LayoutIterationTest {
 47 
 48     @LooselyConsistentValue
 49     static value class One {
 50         int a;
 51         short b;
 52 
 53         One(int a, short b) {
 54             this.a = a;
 55             this.b = b;
 56             super();
 57         }
 58     }
 59 
 60     @LooselyConsistentValue
 61     static value class Two {
 62         @NullRestricted
 63         One one = new One(5, (short) 3);
 64         One anotherOne = new One(4, (short) 2);
 65         long l = 5L;
 66     }
 67 
 68     @Test
 69     void testExample() {
 70         Two t = new Two();
 71         Set<Class<?>> classes = LayoutIteration.computeElementGetters(One.class).stream()
 72                 .map(mh -> mh.type().returnType()).collect(Collectors.toSet());
 73         assertEquals(Set.of(int.class, short.class), classes);
 74         Map<Class<?>, Object> values = LayoutIteration.computeElementGetters(Two.class).stream()
 75                 .collect(Collectors.toMap(mh -> mh.type().returnType(), mh -> {
 76                     try {
 77                         return (Object) mh.invoke(t);
 78                     } catch (Throwable ex) {
 79                         return Assertions.fail(ex);
 80                     }
 81                 }));
 82         assertEquals(Map.of(
 83                 int.class, t.one.a,
 84                 short.class, t.one.b,
 85                 One.class, t.anotherOne,
 86                 long.class, t.l
 87         ), values);
 88     }
 89 
 90     static value class IntValue {
 91         int value;
 92 
 93         static final int[] EDGE_CASES = {
 94                 0, -1, 1,
 95                 Integer.MIN_VALUE, Integer.MAX_VALUE
 96         };
 97 
 98         public IntValue(int index) {
 99             value = EDGE_CASES[index];
100         }
101 
102         public String toString() {
103             return "IntValue(" + value +
104                     ", bits=0x" + Integer.toHexString(value) + ")";
105         }
106 
107         static boolean cmp(int i, int j) {
108             return EDGE_CASES[i] == EDGE_CASES[j];
109         }
110     }
111 
112     static value class NestedValue {
113         SubstitutabilityTest.IntValue value;
114 
115         static final SubstitutabilityTest.IntValue[] EDGE_CASES = {
116                 null, new SubstitutabilityTest.IntValue(0), new SubstitutabilityTest.IntValue(1), new SubstitutabilityTest.IntValue(2),
117                 new SubstitutabilityTest.IntValue(3), new SubstitutabilityTest.IntValue(0)
118         };
119 
120         public NestedValue(int index) {
121             value = EDGE_CASES[index];
122         }
123 
124         public String toString() {
125             return "NestedValue(" + value + ")";
126         }
127 
128         static boolean cmp(int i, int j) {
129             return EDGE_CASES[i] == EDGE_CASES[j];
130         }
131     }
132 
133     @Test
134     void testNested() {
135         NestedValue v = new NestedValue(0);
136         Map<Class<?>, Object> values = LayoutIteration.computeElementGetters(NestedValue.class).stream()
137                 .collect(Collectors.toMap(mh -> mh.type().returnType(), mh -> {
138                     try {
139                         return (Object) mh.invoke(v);
140                     } catch (Throwable ex) {
141                         return Assertions.fail(ex);
142                     }
143                 }));
144         assertEquals(Map.of(
145                 int.class, 0,
146                 byte.class, (byte) 0 // null marker
147         ), values);
148 
149         NestedValue v1 = new NestedValue(2);
150         Map<Class<?>, Object> values1 = LayoutIteration.computeElementGetters(NestedValue.class).stream()
151                 .collect(Collectors.toMap(mh -> mh.type().returnType(), mh -> {
152                     try {
153                         return (Object) mh.invoke(v1);
154                     } catch (Throwable ex) {
155                         return Assertions.fail(ex);
156                     }
157                 }));
158         assertEquals(Map.of(
159                 int.class, NestedValue.EDGE_CASES[2].value,
160                 byte.class, (byte) 1 // null marker
161         ), values1);
162     }
163 }