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