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 }