1 /*
  2  * Copyright (c) 2019, 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 package runtime.valhalla.inlinetypes;
 24 
 25 import jdk.test.lib.Asserts;
 26 
 27 import java.lang.reflect.Field;
 28 
 29 import jdk.internal.value.ValueClass;
 30 import jdk.internal.vm.annotation.ImplicitlyConstructible;
 31 import jdk.internal.vm.annotation.LooselyConsistentValue;
 32 import jdk.internal.vm.annotation.NullRestricted;
 33 
 34 
 35 /*
 36  * @test
 37  * @summary Test support for empty inline types (no instance fields)
 38  * @library /test/lib
 39  * @modules java.base/jdk.internal.vm.annotation
 40  *          java.base/jdk.internal.value
 41  * @enablePreview
 42  * @compile EmptyInlineTest.java
 43  * @run main/othervm -XX:InlineFieldMaxFlatSize=128 runtime.valhalla.inlinetypes.EmptyInlineTest
 44  */
 45 
 46 public class EmptyInlineTest {
 47 
 48     @ImplicitlyConstructible
 49     @LooselyConsistentValue
 50     static value class EmptyInline {
 51         public boolean isEmpty() {
 52             return true;
 53         }
 54     }
 55 
 56     @ImplicitlyConstructible
 57     @LooselyConsistentValue
 58     static value class EmptyField {
 59         @NullRestricted
 60         EmptyInline empty;
 61 
 62         EmptyField() {
 63             this.empty = new EmptyInline();
 64         }
 65     }
 66 
 67     static class WithInt {
 68         int i;
 69     }
 70 
 71     static class WithEmptyField extends WithInt  {
 72         // With current layout strategy for reference classs, the empty
 73         // inline field would be placed between the int and the Object
 74         // fields, along with some padding.
 75         Object o;
 76         @NullRestricted
 77         EmptyInline empty;
 78     }
 79 
 80     public static void main(String[] args) {
 81         // Create an empty inline
 82         EmptyInline empty = new EmptyInline();
 83         Asserts.assertTrue(empty.isEmpty());
 84 
 85         // Create an inline with an empty inline field
 86         EmptyField emptyField = new EmptyField();
 87         Asserts.assertEquals(emptyField.empty.getClass(), EmptyInline.class);
 88         Asserts.assertTrue(emptyField.empty.isEmpty());
 89         System.out.println(emptyField.empty.isEmpty());
 90 
 91         // Regular instance with an empty field inside
 92         WithEmptyField w = new WithEmptyField();
 93         Asserts.assertEquals(w.empty.getClass(), EmptyInline.class);
 94         Asserts.assertTrue(w.empty.isEmpty());
 95         w.empty = new EmptyInline();
 96         Asserts.assertEquals(w.empty.getClass(), EmptyInline.class);
 97         Asserts.assertTrue(w.empty.isEmpty());
 98 
 99         // Create an array of empty inlines
100         EmptyInline[] emptyArray = (EmptyInline[])ValueClass.newNullRestrictedArray(EmptyInline.class, 100);
101         for(EmptyInline element : emptyArray) {
102             Asserts.assertEquals(element.getClass(), EmptyInline.class);
103             Asserts.assertTrue(element.isEmpty());
104         }
105 
106         // Testing arrayCopy
107         EmptyInline[] array2 = (EmptyInline[])ValueClass.newNullRestrictedArray(EmptyInline.class, 100);
108         // with two arrays
109         System.arraycopy(emptyArray, 10, array2, 20, 50);
110         for(EmptyInline element : array2) {
111             Asserts.assertEquals(element.getClass(), EmptyInline.class);
112             Asserts.assertTrue(element.isEmpty());
113         }
114         // single array, no overlap
115         System.arraycopy(emptyArray, 10, emptyArray, 50, 20);
116         for(EmptyInline element : emptyArray) {
117             Asserts.assertEquals(element.getClass(), EmptyInline.class);
118             Asserts.assertTrue(element.isEmpty());
119         }
120         // single array with overlap
121         System.arraycopy(emptyArray, 10, emptyArray, 20, 50);
122         for(EmptyInline element : emptyArray) {
123             Asserts.assertEquals(element.getClass(), EmptyInline.class);
124             Asserts.assertTrue(element.isEmpty());
125         }
126 
127         // Passing an empty inline in argument
128         assert isEmpty(empty);
129 
130         // Returning an empty inline
131         assert getEmpty().isEmpty();
132 
133         // Checking fields with reflection
134         Class<?> c = empty.getClass();
135         try {
136             Field[] fields = c.getDeclaredFields();
137             Asserts.assertTrue(fields.length == 0);
138         } catch (Throwable t) {
139             t.printStackTrace();
140             throw t;
141         }
142         WithEmptyField w0 = new WithEmptyField();
143         Class<?> c2 = w0.getClass();
144         try {
145             Field emptyfield = c2.getDeclaredField("empty");
146             EmptyInline e = (EmptyInline)emptyfield.get(w0);
147             Asserts.assertEquals(e.getClass(), EmptyInline.class);
148             Asserts.assertTrue(e.isEmpty());
149             emptyfield.set(w0, new EmptyInline());
150             e = (EmptyInline)emptyfield.get(w0);
151             Asserts.assertEquals(e.getClass(), EmptyInline.class);
152             Asserts.assertTrue(e.isEmpty());
153         } catch(Throwable t) {
154             t.printStackTrace();
155             throw new RuntimeException("Reflection tests failed: " + t);
156         }
157 
158         // Testing JIT compiler
159         // for(int i=0; i < 100000; i++) {
160         //     test();
161         // }
162     }
163 
164     static boolean isEmpty(EmptyInline empty) {
165         return empty.isEmpty();
166     }
167 
168     static EmptyInline getEmpty() {
169         return new EmptyInline();
170     }
171 
172     static void test() {
173         for(int i=0; i < 10000; i++) {
174             Asserts.assertTrue(getEmpty().isEmpty());
175         }
176     }
177 }