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