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 }