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 }