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