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 }