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.LooselyConsistentValue;
 33 import jdk.internal.vm.annotation.Strict;
 34 
 35 
 36 
 37 
 38 
 39 /*
 40  * @test
 41  * @summary Test of NullRestricted, Strict 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:+UseNullableValueFlattening");
 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         @Strict
 77         @NullRestricted
 78         String s = new String("bad");
 79     }
 80 
 81     // Test detection of illegal usage of NullRestricted on an identity field
 82     void test_0() {
 83         Throwable exception = null;
 84         try {
 85             BadClass0 bc = new BadClass0();
 86         } catch (IncompatibleClassChangeError e) {
 87             exception = e;
 88             System.out.println("Received " + e);
 89         }
 90         Asserts.assertNotNull(exception, "Failed to detect illegal use of @NullRestricted");
 91     }
 92 
 93     // Test detection of non-static field annotated with @NullRestricted but not @Strict
 94     static value class ValueClass1 {
 95         int i = 0;
 96         int j = 0;
 97     }
 98 
 99     static class BadClass1 {
100         @NullRestricted
101         ValueClass1 vc;
102     }
103 
104     void test_1() {
105         Throwable exception = null;
106         try {
107             BadClass1 tc = new BadClass1();
108         } catch (ClassFormatError e) {
109             exception = e;
110             System.out.println("Received " + e);
111         }
112         Asserts.assertNotNull(exception, "Failed to detect illegal use of @NullRestricted without @Strict on a non-static field");
113     }
114 
115     // Invalid usage of @NullRestricted on a non-strict non-static field
116     static value class ValueClass2 {
117         int i = 0;
118         int j = 0;
119     }
120 
121     static class BadClass2 {
122         @NullRestricted
123         static ValueClass2 val;
124     }
125 
126     void test_2() {
127         Throwable exception = null;
128         try {
129             BadClass2 tc = new BadClass2();
130         } catch (ClassFormatError e) {
131             exception = e;
132             System.out.println("Received " + e);
133         }
134         Asserts.assertNotNull(exception, "FFailed to detect illegal use of @NullRestricted without @Strict on a static field");
135     }
136 
137     // Test invalid usage of @LooselyConsistentValue on an identity class
138     @LooselyConsistentValue
139     static class BadClass4 {
140 
141     }
142 
143     void test_4() {
144         Throwable exception = null;
145         try {
146             BadClass4 tc = new BadClass4();
147         } catch (ClassFormatError e) {
148             exception = e;
149             System.out.println("Received " + e);
150         }
151         Asserts.assertNotNull(exception, "Failed to detect illegal use of @LooselyConsistentValue");
152     }
153 
154     // Test field flattening of @NullRestricted annotated fields
155 
156     @LooselyConsistentValue
157     static value class ValueClass5 {
158         int i = 0;
159     }
160 
161     static class GoodClass5 {
162       ValueClass5 f0 = new ValueClass5();
163 
164         @Strict
165         @NullRestricted
166         ValueClass5 f1 = new ValueClass5();
167     }
168 
169     void test_5() {
170         Throwable exception = null;
171         try {
172             GoodClass5 vc = new GoodClass5();
173             Field f0 = vc.getClass().getDeclaredField("f0");
174             if (nullableLayoutEnabled) {
175                 Asserts.assertTrue(UNSAFE.isFlatField(f0), "Flat field expected, but field is not flat");
176             } else {
177                 Asserts.assertFalse(UNSAFE.isFlatField(f0), "Unexpected flat field");
178             }
179             Field f1 = vc.getClass().getDeclaredField("f1");
180             Asserts.assertTrue(UNSAFE.isFlatField(f1), "Flat field expected, but field is not flat");
181         } catch (IncompatibleClassChangeError e) {
182             exception = e;
183             System.out.println("Received " + e);
184         } catch(NoSuchFieldException e) {
185             Asserts.fail("Test error");
186         }
187         Asserts.assertNull(exception, "Unexpected exception: " + exception);
188     }
189 
190 
191     // Test detection/handling of circularity
192 
193     static value class ValueClass6a {
194         @Strict
195         @NullRestricted
196         ValueClass6b val = new ValueClass6b();
197     }
198 
199     static value class ValueClass6b {
200         @Strict
201         @NullRestricted
202         ValueClass6a val = new ValueClass6a();
203     }
204 
205     static class BadClass6 {
206         @Strict
207         @NullRestricted
208         ValueClass6a val = new ValueClass6a();
209     }
210 
211     void test_6() {
212         Throwable exception = null;
213         try {
214             BadClass6 bc = new BadClass6();
215         } catch (ClassCircularityError e) {
216             exception = e;
217             System.out.println("Received " + e);
218         }
219         Asserts.assertNotNull(exception, "Failed to detect circularity");
220     }
221 
222     // Test null restricted static field
223     static value class ValueClass7 {
224         int i = 0;
225     }
226 
227     static class GoodClass7 {
228         @Strict
229         @NullRestricted
230         static ValueClass7 sval = new ValueClass7();
231     }
232 
233     void test_7() {
234         Throwable exception = null;
235         try {
236             ValueClass7 val = GoodClass7.sval;
237             Asserts.assertNotNull(val, "Unexpected null value");
238         } catch (Throwable e) {
239             exception = e;
240             System.out.println("Received " + e);
241         }
242         Asserts.assertNull(exception, "Unexpected exception: " + exception);
243     }
244 
245     // Test circularity on static fields
246     static value class ValueClass8 {
247         @Strict
248         @NullRestricted
249         static ValueClass8 sval = new ValueClass8();
250     }
251 
252     void test_8() {
253         Throwable exception = null;
254         try {
255             ValueClass8 val = ValueClass8.sval;
256             Asserts.assertNotNull(val, "Unexpected null value");
257         } catch (Throwable e) {
258             exception = e;
259             System.out.println("Received " + e);
260         }
261         Asserts.assertNull(exception, "Unexpected exception: " + exception);
262     }
263 
264     // Test that writing null to a @NullRestricted non-static field throws an exception
265     static value class ValueClass9 {
266         int i = 0;
267     }
268 
269     static class GoodClass9 {
270         @Strict
271         @NullRestricted
272         ValueClass9 val = new ValueClass9();
273     }
274 
275     void test_9() {
276         Throwable exception = null;
277         try {
278             GoodClass9 gc = new GoodClass9();
279             gc.val = null;
280         } catch(NullPointerException e) {
281             exception = e;
282             System.out.println("Received " + e);
283         }
284         Asserts.assertNotNull(exception, "Expected NullPointerException not received");
285     }
286 
287     // Test that writing null to a @NullRestricted static field throws an exception
288     static value class ValueClass10 {
289         @Strict
290         @NullRestricted
291         static ValueClass10 sval = new ValueClass10();
292     }
293 
294     void test_10() {
295         Throwable exception = null;
296         try {
297             ValueClass10.sval = null;
298         } catch(NullPointerException e) {
299             exception = e;
300             System.out.println("Received " + e);
301         }
302         Asserts.assertNotNull(exception, "Expected NullPointerException not received");
303     }
304 
305     // Test static null restricted field that is not declared strict
306     static value class ValueClass11 {
307         int i = 0;
308         int j = 0;
309     }
310 
311     static class BadClass11 {
312         @NullRestricted
313         static ValueClass11 val = new ValueClass11();
314     }
315 
316     void test_11() {
317         Throwable exception = null;
318         try {
319             ValueClass11 val = BadClass11.val;
320             System.out.println(val);
321         } catch(ClassFormatError e) {
322             exception = e;
323             System.out.println("Received " + e);
324         }
325         Asserts.assertNotNull(exception, "Expected ClassFormatError not received");
326     }
327 
328     // Test illegal use of @NullRestricted on a primitive field
329     static class BadClass12 {
330         @Strict
331         @NullRestricted
332         int i = 0;
333     }
334     void test_12() {
335         Throwable exception = null;
336         try {
337             BadClass12 val = new BadClass12();
338             System.out.println(val);
339         } catch(ClassFormatError e) {
340             exception = e;
341             System.out.println("Received " + e);
342         }
343         Asserts.assertNotNull(exception, "Expected ClassFormatError not received");
344     }
345 
346     // Test illegal use of @NullRestricted on an array field
347     static class BadClass13 {
348         @Strict
349         @NullRestricted
350         int[] intArray = new int[1];
351     }
352     void test_13() {
353         Throwable exception = null;
354         try {
355             BadClass13 val = new BadClass13();
356             System.out.println(val);
357         } catch(ClassFormatError e) {
358             exception = e;
359             System.out.println("Received " + e);
360         }
361         Asserts.assertNotNull(exception, "Expected ClassFormatError not received");
362     }
363 
364  }
365