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