1 /*
2 * Copyright (c) 2022, 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 * @test
26 * @enablePreview
27 * @run junit/othervm -Xint -Djdk.value.recursion.threshold=100000 RecursiveValueClass
28 */
29
30 /*
31 * @ignore 8296056
32 * @enablePreview
33 * @test
34 * @run junit/othervm -XX:TieredStopAtLevel=1 -Djdk.value.recursion.threshold=100000 RecursiveValueClass
35 */
36
37 /*
38 * @ignore 8296056
39 * @enablePreview
40 * @test
41 * @run junit/othervm -Xcomp -Djdk.value.recursion.threshold=100000 RecursiveValueClass
42 */
43
44 import jdk.internal.value.ValueClass;
45 import org.junit.jupiter.api.Test;
46 import org.junit.jupiter.params.ParameterizedTest;
47 import org.junit.jupiter.params.provider.MethodSource;
48 import org.junit.jupiter.params.provider.Arguments;
49
50 import java.util.stream.Stream;
51
52 import static org.junit.jupiter.api.Assertions.*;
53
54 public class RecursiveValueClass {
55 static value class Node {
56 Node left;
57 Node right;
58
59 Node(Node l, Node r) {
60 this.left = l;
61 this.right = r;
62 }
63 }
64
65 static value class P {
66 Node node;
67 V v;
68 P(Node node, V v) {
69 this.node = node;
70 this.v = v;
71 }
72 }
73
74 static value class V {
75 P p;
76 V(P p) {
77 this.p = p;
78 }
79 }
80
81 static value class A {
82 B b;
83 E e;
84 A(B b, E e) {
85 this.b = b;
86 this.e = e;
87 }
88 }
89
90 static value class B {
91 C c;
92 D d;
93 B(C c, D d) {
94 this.c = c;
95 this.d = d;
96 }
97 }
98
99 static value class C {
100 A a;
101 C(A a) {
102 this.a = a;
103 }
104 }
105
106 static value class D {
107 int x;
108 D(int x) {
109 this.x = x;
110 }
111 }
112
113 static value class E {
114 F f;
115 E(F f) {
116 this.f = f;
117 }
118 }
119
120 static value class F {
121 E e;
122 F(E e) {
123 this.e = e;
124 }
125 }
126
127 static Stream<Arguments> objectsProvider() {
128 var n1 = new Node(null, null);
129 var n2 = new Node(n1, null);
130 var n3 = new Node(n2, n1);
131 var n4 = new Node(n2, n1);
132 var v1 = new V(null);
133 var p1 = new P(n3, v1);
134 var p2 = new P(n4, v1);
135 var v2 = new V(p1);
136 var v3 = new V(p2);
137 var p3 = new P(n3, v2);
138
139 var e1 = new E(new F(null));
140 var f1 = new F(e1);
141 var e2 = new E(f1);
142 var f2 = new F(e2);
143
144 var a = new A(new B(null, null), new E(null));
145
146 var d1 = new D(1);
147 var d2 = new D(2);
148 var c1 = new C(a);
149 var c2 = new C(a);
150
151 var b1 = new B(c1, d1);
152 var b2 = new B(c1, d2);
153 var b3 = new B(c2, d2);
154 var a1 = new A(b1, e1);
155 var a2 = new A(b2, e2);
156
157 return Stream.of(
158 // Node -> Node left & right
159 Arguments.of(n1, n1, true),
160 Arguments.of(n1, n2, false),
161 Arguments.of(n2, n3, false),
162 Arguments.of(n3, n4, true),
163 Arguments.of(null, n4, false),
164 Arguments.of(null, null, true),
165 Arguments.of(n1, "foo", false),
166
167 // value class P -> value class V -> P
168 Arguments.of(p1, p2, true),
169 Arguments.of(p1, p3, false),
170 Arguments.of(p3, p3, true),
171 Arguments.of(v2, v3, true),
172
173 // E -> F -> E
174 Arguments.of(e1, e2, false),
175 Arguments.of(e1, e1, true),
176 Arguments.of(f1, f2, false),
177 Arguments.of(f2, f2, true),
178
179 // two cyclic memberships from A
180 // A -> B -> C -> A and E -> F -> E
181 Arguments.of(a1, a2, false),
182 Arguments.of(a2, a2, true),
183 Arguments.of(b1, b2, false),
184 Arguments.of(b2, b3, true),
185 Arguments.of(c1, c2, true)
186 );
187 }
188
189 @ParameterizedTest
190 @MethodSource("objectsProvider")
191 public void testAcmp(Object o1, Object o2, boolean expected) {
192 var value = o1 == o2;
193 assertEquals(expected, value, o1 + " == " + o2);
194 }
195
196 static Stream<Arguments> hashCodeProvider() {
197 var n1 = new Node(null, null);
198 var n2 = new Node(n1, null);
199 var n3 = new Node(n2, n1);
200 var v1 = new V(null);
201 var p1 = new P(n3, v1);
202 var v2 = new V(p1);
203
204 var e1 = new E(new F(null));
205 var f1 = new F(e1);
206 var e2 = new E(f1);
207 var f2 = new F(e2);
208
209 var a = new A(new B(null, null), new E(null));
210
211 var d1 = new D(1);
212 var d2 = new D(2);
213 var c1 = new C(a);
214
215 var b1 = new B(c1, d1);
216 var b2 = new B(c1, d2);
217 var a1 = new A(b1, e1);
218 var a2 = new A(b2, e2);
219
220 return Stream.of(
221 // Node -> Node left & right
222 Arguments.of(n1),
223 Arguments.of(n2),
224 Arguments.of(n3),
225
226 // value class P -> value class V -> P
227 Arguments.of(p1),
228 Arguments.of(v2),
229
230 // E -> F -> E
231 Arguments.of(e1),
232 Arguments.of(e2),
233 Arguments.of(f1),
234 Arguments.of(f2),
235
236 // two cyclic memberships from A
237 // A -> B -> C -> A and E -> F -> E
238 Arguments.of(a1),
239 Arguments.of(a2),
240 Arguments.of(b1),
241 Arguments.of(b2),
242 Arguments.of(c1)
243 );
244 }
245
246 @ParameterizedTest
247 @MethodSource("hashCodeProvider")
248 public void testHashCode(Object o) {
249 int hc = o.hashCode();
250 assertEquals(System.identityHashCode(o), hc, o.toString());
251 }
252
253 static value class N {
254 N l;
255 N r;
256 int id;
257 N(N l, N r, int id) {
258 this.l = l;
259 this.r = r;
260 this.id = id;
261 }
262 }
263
264 private static N build() {
265 N n1 = new N(null, null, 0);
266 N n2 = new N(null, null, 0);
267 for (int id = 1; id < 100; ++id) {
268 N l = new N(n1, n2, id);
269 N r = new N(n1, n2, id);
270 n1 = l;
271 n2 = r;
272 }
273 return new N(n1, n2, 100);
274 }
275
276 /*
277 * Throw SOE for large graph
278 */
279 @Test
280 public void largeGraph() {
281 N node = build();
282 long start = System.nanoTime();
283 assertThrows(StackOverflowError.class, () -> { boolean v = node.l == node.r; });
284 assertThrows(StackOverflowError.class, () -> { int hc = node.hashCode(); });
285 System.out.format("testing large graph: %d ms%n", (System.nanoTime() - start) / 1000);
286 }
287 }