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