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 }