1 /* 2 * Copyright (c) 2023, 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 import jdk.test.lib.Asserts; 25 import java.lang.management.ManagementFactory; 26 import java.lang.management.RuntimeMXBean; 27 import java.lang.reflect.Method; 28 import java.lang.reflect.Field; 29 import java.util.List; 30 import jdk.internal.misc.Unsafe; 31 import jdk.internal.vm.annotation.NullRestricted; 32 import jdk.internal.vm.annotation.ImplicitlyConstructible; 33 import jdk.internal.vm.annotation.LooselyConsistentValue; 34 35 36 37 38 39 /* 40 * @test 41 * @summary Test of ImplicitlyConstructible, NullRestricted and LooselyConsistentValue annotations 42 * @modules java.base/jdk.internal.misc 43 * java.base/jdk.internal.vm.annotation 44 * @library /test/lib 45 * @enablePreview 46 * @compile AnnotationsTests.java 47 * @run main/othervm AnnotationsTests 48 */ 49 50 51 public class AnnotationsTests { 52 53 private static final Unsafe UNSAFE = Unsafe.getUnsafe(); 54 static boolean nullableLayoutEnabled; 55 56 public static void main(String[] args) { 57 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); 58 List<String> arguments = runtimeMxBean.getInputArguments(); 59 nullableLayoutEnabled = arguments.contains("-XX:+NullableFieldFlattening"); 60 AnnotationsTests tests = new AnnotationsTests(); 61 Class c = tests.getClass(); 62 for (Method m : c.getDeclaredMethods()) { 63 if (m.getName().startsWith("test_")) { 64 try { 65 System.out.println("Running " + m.getName()); 66 m.invoke(tests); 67 } catch (Throwable t) { 68 t.printStackTrace(); 69 throw new RuntimeException(t); 70 } 71 } 72 } 73 } 74 75 static class BadClass0 { 76 @NullRestricted 77 String s; 78 } 79 80 // Test detection of illegal usage of NullRestricted on an identity field 81 void test_0() { 82 Throwable exception = null; 83 try { 84 BadClass0 bc = new BadClass0(); 85 } catch (IncompatibleClassChangeError e) { 86 exception = e; 87 System.out.println("Received " + e); 88 } 89 Asserts.assertNotNull(exception, "Failed to detect illegal use of @NullRestricted"); 90 } 91 92 // Test detection of mismatch between a @NullRestricted field and its class that is not @ImplicitlyConstructible 93 static value class ValueClass1 { 94 int i = 0; 95 int j = 0; 96 } 97 98 static class BadClass1 { 99 @NullRestricted 100 ValueClass1 vc; 101 } 102 103 void test_1() { 104 Throwable exception = null; 105 try { 106 BadClass1 tc = new BadClass1(); 107 } catch (IncompatibleClassChangeError e) { 108 exception = e; 109 System.out.println("Received " + e); 110 } 111 Asserts.assertNotNull(exception, "Failed to detect illegal use of @NullRestricted"); 112 } 113 114 // Test a valid @NullRestricted field with a class that is @ImplicitlyConstructible 115 @ImplicitlyConstructible 116 static value class ValueClass2 { 117 int i = 0; 118 int j = 0; 119 } 120 121 static class GoodClass2 { 122 @NullRestricted 123 ValueClass2 vc; 124 } 125 126 void test_2() { 127 Throwable exception = null; 128 try { 129 GoodClass2 tc = new GoodClass2(); 130 } catch (IncompatibleClassChangeError e) { 131 exception = e; 132 System.out.println("Received " + e); 133 } 134 Asserts.assertNull(exception, "Unexpected exception: " + exception); 135 } 136 137 // Invalid usage of @ImplicitlyConstructible on an identity class 138 @ImplicitlyConstructible 139 static class BadClass3 { 140 141 } 142 143 void test_3() { 144 Throwable exception = null; 145 try { 146 BadClass3 tc = new BadClass3(); 147 } catch (ClassFormatError e) { 148 exception = e; 149 System.out.println("Received " + e); 150 } 151 Asserts.assertNotNull(exception, "Failed to detect illegal use of @ImplicitlyConstructible"); 152 } 153 154 // Test invalid usage of @LooselyConsistentValue on an identity class 155 @LooselyConsistentValue 156 static class BadClass4 { 157 158 } 159 160 void test_4() { 161 Throwable exception = null; 162 try { 163 BadClass4 tc = new BadClass4(); 164 } catch (ClassFormatError e) { 165 exception = e; 166 System.out.println("Received " + e); 167 } 168 Asserts.assertNotNull(exception, "Failed to detect illegal use of @LooselyConsistentValue"); 169 } 170 171 // Test field flattening of @NullRestricted annotated fields 172 173 @ImplicitlyConstructible 174 @LooselyConsistentValue 175 static value class ValueClass5 { 176 int i = 0; 177 } 178 179 static class GoodClass5 { 180 ValueClass5 f0 = new ValueClass5(); 181 182 @NullRestricted 183 ValueClass5 f1 = new ValueClass5(); 184 } 185 186 void test_5() { 187 Throwable exception = null; 188 try { 189 GoodClass5 vc = new GoodClass5(); 190 Field f0 = vc.getClass().getDeclaredField("f0"); 191 if (nullableLayoutEnabled) { 192 Asserts.assertTrue(UNSAFE.isFlatField(f0), "Flat field expected, but field is not flat"); 193 } else { 194 Asserts.assertFalse(UNSAFE.isFlatField(f0), "Unexpected flat field"); 195 } 196 Field f1 = vc.getClass().getDeclaredField("f1"); 197 Asserts.assertTrue(UNSAFE.isFlatField(f1), "Flat field expected, but field is not flat"); 198 } catch (IncompatibleClassChangeError e) { 199 exception = e; 200 System.out.println("Received " + e); 201 } catch(NoSuchFieldException e) { 202 Asserts.fail("Test error"); 203 } 204 Asserts.assertNull(exception, "Unexpected exception: " + exception); 205 } 206 207 208 // Test detection/handling of circularity 209 210 @ImplicitlyConstructible 211 static value class ValueClass6a { 212 @NullRestricted 213 ValueClass6b val = new ValueClass6b(); 214 } 215 216 @ImplicitlyConstructible 217 static value class ValueClass6b { 218 @NullRestricted 219 ValueClass6a val = new ValueClass6a(); 220 } 221 222 static class BadClass6 { 223 @NullRestricted 224 ValueClass6a val = new ValueClass6a(); 225 } 226 227 void test_6() { 228 Throwable exception = null; 229 try { 230 BadClass6 bc = new BadClass6(); 231 } catch (ClassCircularityError e) { 232 exception = e; 233 System.out.println("Received " + e); 234 } 235 Asserts.assertNotNull(exception, "Failed to detect circularity"); 236 } 237 238 // Test null restricted static field 239 @ImplicitlyConstructible 240 static value class ValueClass7 { 241 int i = 0; 242 } 243 244 static class GoodClass7 { 245 @NullRestricted 246 static ValueClass7 sval; 247 } 248 249 void test_7() { 250 Throwable exception = null; 251 try { 252 ValueClass7 val = GoodClass7.sval; 253 Asserts.assertNotNull(val, "Unexpected null value"); 254 } catch (Throwable e) { 255 exception = e; 256 System.out.println("Received " + e); 257 } 258 Asserts.assertNull(exception, "Unexpected exception: " + exception); 259 } 260 261 // Test circularity on static fields 262 @ImplicitlyConstructible 263 static value class ValueClass8 { 264 @NullRestricted 265 static ValueClass8 sval; 266 } 267 268 void test_8() { 269 Throwable exception = null; 270 try { 271 ValueClass8 val = ValueClass8.sval; 272 Asserts.assertNotNull(val, "Unexpected null value"); 273 } catch (Throwable e) { 274 exception = e; 275 System.out.println("Received " + e); 276 } 277 Asserts.assertNull(exception, "Unexpected exception: " + exception); 278 } 279 280 // Test that writing null to a @NullRestricted non-static field throws an exception 281 @ImplicitlyConstructible 282 static value class ValueClass9 { 283 int i = 0; 284 } 285 286 static class GoodClass9 { 287 @NullRestricted 288 ValueClass9 val = new ValueClass9(); 289 } 290 291 void test_9() { 292 Throwable exception = null; 293 try { 294 GoodClass9 gc = new GoodClass9(); 295 gc.val = null; 296 } catch(NullPointerException e) { 297 exception = e; 298 System.out.println("Received " + e); 299 } 300 Asserts.assertNotNull(exception, "Expected NullPointerException not received"); 301 } 302 303 // Test that writing null to a @NullRestricted static field throws an exception 304 @ImplicitlyConstructible 305 static value class ValueClass10 { 306 @NullRestricted 307 static ValueClass10 sval; 308 } 309 310 void test_10() { 311 Throwable exception = null; 312 try { 313 ValueClass10.sval = null; 314 } catch(NullPointerException e) { 315 exception = e; 316 System.out.println("Received " + e); 317 } 318 Asserts.assertNotNull(exception, "Expected NullPointerException not received"); 319 } 320 321 // Test uninitialized static null restricted field with a class not implicitly constructible 322 static value class ValueClass11 { 323 int i = 0; 324 int j = 0; 325 } 326 327 static class BadClass11 { 328 @NullRestricted 329 static ValueClass11 val; 330 } 331 332 void test_11() { 333 Throwable exception = null; 334 try { 335 ValueClass11 val = BadClass11.val; 336 System.out.println(val); 337 } catch(IncompatibleClassChangeError e) { 338 exception = e; 339 System.out.println("Received " + e); 340 } 341 Asserts.assertNotNull(exception, "Expected IncompatibleClassChangerError not received"); 342 } 343 344 // Test illegal use of @NullRestricted on a primitive field 345 static class BadClass12 { 346 @NullRestricted 347 int i; 348 } 349 void test_12() { 350 Throwable exception = null; 351 try { 352 BadClass12 val = new BadClass12(); 353 System.out.println(val); 354 } catch(ClassFormatError e) { 355 exception = e; 356 System.out.println("Received " + e); 357 } 358 Asserts.assertNotNull(exception, "Expected ClassFormatError not received"); 359 } 360 361 // Test illegal use of @NullRestricted on an array field 362 static class BadClass13 { 363 @NullRestricted 364 int Integer[]; 365 } 366 void test_13() { 367 Throwable exception = null; 368 try { 369 BadClass13 val = new BadClass13(); 370 System.out.println(val); 371 } catch(ClassFormatError e) { 372 exception = e; 373 System.out.println("Received " + e); 374 } 375 Asserts.assertNotNull(exception, "Expected ClassFormatError not received"); 376 } 377 378 379 // Test that a value class annotated with @ImplicitlyConstructible but extending 380 // an abstract value class not annotated with @ImplicitlyConstructible is not 381 // considered as implicitely constructible 382 383 static abstract value class AbstractValue14 { } 384 @ImplicitlyConstructible 385 static value class Value14 extends AbstractValue14 { } 386 387 static class Test14 { 388 @NullRestricted 389 Value14 val; 390 } 391 392 void test_14() { 393 Throwable exception = null; 394 try { 395 Test14 t14 = new Test14(); 396 } catch(IncompatibleClassChangeError e) { 397 exception = e; 398 System.out.println("Received "+ e); 399 } 400 Asserts.assertNotNull(exception, "Expected IncompatibleClassChangeError not received"); 401 } 402 403 // Test that a value class annotated with @ImplicitlyConstructible but extending 404 // an abstract value class also annotated with @ImplicitlyConstructible is 405 // considered as implicitely constructible 406 407 @ImplicitlyConstructible 408 static abstract value class AbstractValue15 { } 409 @ImplicitlyConstructible 410 static value class Value15 extends AbstractValue15 { } 411 412 static class Test15 { 413 @NullRestricted 414 Value15 val; 415 } 416 417 void test_15() { 418 Throwable exception = null; 419 try { 420 Test15 t15 = new Test15(); 421 } catch(IncompatibleClassChangeError e) { 422 exception = e; 423 System.out.println("Received "+ e); 424 } 425 Asserts.assertNull(exception, "Unexpected IncompatibleClassChangeError received"); 426 } 427 428 } 429