1 /*
  2  * Copyright (c) 2018, 2025, 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 package runtime.valhalla.inlinetypes;
 25 
 26 /* @test
 27  * @summary test JNI functions with instances of value classes
 28  * @library /test/lib
 29  * @modules java.base/jdk.internal.vm.annotation
 30  *          java.base/jdk.internal.value
 31  * @enablePreview
 32  * @run main/othervm/native --enable-native-access=ALL-UNNAMED -XX:+UseNullableValueFlattening runtime.valhalla.inlinetypes.InlineWithJni
 33  */
 34 
 35 
 36 import jdk.internal.value.ValueClass;
 37 import jdk.internal.vm.annotation.NullRestricted;
 38 import jdk.internal.vm.annotation.Strict;
 39 
 40 import java.lang.reflect.Array;
 41 import java.lang.reflect.Method;
 42 
 43 import jdk.test.lib.Asserts;
 44 
 45 public value class InlineWithJni {
 46 
 47     static int returnValue = 999;
 48     static final int JNI_ERR = -1;  // from jni.h
 49 
 50     static {
 51         System.loadLibrary("InlineWithJni");
 52     }
 53 
 54     public static void main(String[] args) {
 55         testJniMonitorOps();
 56         testJniFieldAccess();
 57         testJniArrayAccess();
 58     }
 59 
 60     final int x;
 61 
 62     public InlineWithJni(int x) {
 63         this.x = x;
 64     }
 65 
 66     public native void doJniMonitorEnter();
 67     public native void doJniMonitorExit();
 68 
 69     public static void testJniMonitorOps() {
 70         boolean sawIe = false;
 71         boolean sawImse = false;
 72         try {
 73             new InlineWithJni(0).doJniMonitorEnter();
 74         } catch (IdentityException ie) {
 75             sawIe = true;
 76         }
 77         Asserts.assertTrue(sawIe, "Missing IdentityException");
 78         Asserts.assertEQ(returnValue, JNI_ERR);
 79         try {
 80             new InlineWithJni(0).doJniMonitorExit();
 81         } catch (IllegalMonitorStateException imse) {
 82             sawImse = true;
 83         }
 84         Asserts.assertTrue(sawImse, "Missing IllegalMonitorStateException");
 85     }
 86 
 87     public static native Object readInstanceField(Object obj, String name, String signature);
 88     public static native void writeInstanceField(Object obj, String name, String signature, Object value);
 89 
 90     public static native Object readArrayElement(Object[] array, int index);
 91     public static native void writeArrayElement(Object[] array, int index, Object value);
 92 
 93 
 94     static value class SmallValue {
 95         byte b;
 96         SmallValue() { b = 1; }
 97         SmallValue(byte b0) { b = b0; }
 98         static public SmallValue getValueA() { return new SmallValue((byte)42); }
 99         static public SmallValue getValueB() { return new SmallValue((byte)111); }
100     }
101 
102     static value class MediumValue {
103         int i0;
104         int i1;
105         MediumValue() {
106             i0 = 2;
107             i1 = 3;
108         }
109         MediumValue(int ia, int ib) {
110             i0 = ia;
111             i1 = ib;
112         }
113         static public MediumValue getValueA() { return new MediumValue(23, 64); }
114         static public MediumValue getValueB() { return new MediumValue(-51, -1023); }
115     }
116 
117     static value class BigValue {
118         long l0;
119         long l1;
120         long l2;
121         BigValue() {
122             l0 = 4L;
123             l1 = 5L;
124             l2 = 6L;
125         }
126         BigValue(long la, long lb, long lc) {
127             l0 = la;
128             l1 = lb;
129             l2 = lc;
130         }
131         static public BigValue getValueA() { return new BigValue(0L, 65525L, Long.MIN_VALUE); }
132         static public BigValue getValueB() { return new BigValue(Long.MIN_VALUE, 32000L, 0L); }
133     }
134 
135     static value class ValueWithOop {
136         String s;
137         byte b;
138         ValueWithOop() {
139             s = "Hello Duke!";
140             b = (byte)7;
141         }
142         ValueWithOop(String s0, byte b0) {
143             s = s0;
144             b = b0;
145         }
146         static public ValueWithOop getValueA() { return new ValueWithOop("Bretagne", (byte)123); }
147         static public ValueWithOop getValueB() { return new ValueWithOop("Alsace", (byte)-31); }
148     }
149 
150     // Container with nullable fields (potentially flattened)
151     static class Container0 {
152         SmallValue sv = new SmallValue();
153         MediumValue mv = new MediumValue();
154         BigValue bv = new BigValue();
155         ValueWithOop vwo = new ValueWithOop();
156     }
157 
158     // Container with null-restricted fields (potentially flattened)
159     static class Container1 {
160         @Strict
161         @NullRestricted
162         SmallValue sv = new SmallValue();
163         @Strict
164         @NullRestricted
165         MediumValue mv = new MediumValue();
166         @Strict
167         @NullRestricted
168         BigValue bv = new BigValue();
169         @Strict
170         @NullRestricted
171         ValueWithOop vwo = new ValueWithOop();
172     }
173 
174     static String getFieldSignature(Class c, String name) {
175         try {
176             return "L"+c.getDeclaredField(name).getType().getName().replaceAll("\\.", "/")+";";
177         } catch(NoSuchFieldException e) {
178             e.printStackTrace();
179             throw new RuntimeException(e);
180         }
181     }
182 
183     static void testJniFieldAccessHelper(Object c, boolean nullRestriction) {
184 
185         String smallSignature = getFieldSignature(c.getClass(), "sv");
186         String mediumSignature = getFieldSignature(c.getClass(), "mv");
187         String bigSignature = getFieldSignature(c.getClass(), "bv");
188         String withOopSignature = getFieldSignature(c.getClass(), "vwo");
189 
190 
191         // Reading nullable value fields
192         SmallValue sv = (SmallValue)readInstanceField(c, "sv", smallSignature);
193         Asserts.assertEQ(sv, new SmallValue());
194         Asserts.assertTrue(sv.b == 1);
195         MediumValue mv = (MediumValue)readInstanceField(c, "mv", mediumSignature);
196         Asserts.assertEQ(mv, new MediumValue());
197         Asserts.assertTrue(mv.i0 == 2);
198         Asserts.assertTrue(mv.i1 == 3);
199         BigValue bv = (BigValue)readInstanceField(c, "bv", bigSignature);
200         Asserts.assertEQ(bv, new BigValue());
201         Asserts.assertTrue(bv.l0 == 4);
202         Asserts.assertTrue(bv.l1 == 5);
203         Asserts.assertTrue(bv.l2 == 6);
204         ValueWithOop vwo = (ValueWithOop)readInstanceField(c, "vwo", withOopSignature);
205         Asserts.assertEQ(vwo, new ValueWithOop());
206         Asserts.assertTrue(vwo.s.equals("Hello Duke!"));
207         Asserts.assertTrue(vwo.b == 7);
208 
209 
210         // Writing non-null value to nullable field
211         SmallValue nsv = new SmallValue((byte)8);
212         writeInstanceField(c, "sv", smallSignature, nsv);
213         sv = (SmallValue)readInstanceField(c, "sv", smallSignature);
214         Asserts.assertTrue(sv == nsv);
215         MediumValue nmv = new MediumValue(9, 10);
216         writeInstanceField(c, "mv", mediumSignature, nmv);
217         mv = (MediumValue)readInstanceField(c, "mv", mediumSignature);
218         Asserts.assertTrue(mv == nmv);
219         BigValue nbv = new BigValue(11L, 12L, 13L);
220         writeInstanceField(c, "bv", bigSignature, nbv);
221         bv = (BigValue)readInstanceField(c, "bv", bigSignature);
222         Asserts.assertTrue(bv == nbv);
223         ValueWithOop nvwo = new ValueWithOop("Bye Duke!", (byte)14);
224         writeInstanceField(c, "vwo", withOopSignature, nvwo);
225         vwo = (ValueWithOop)readInstanceField(c, "vwo", withOopSignature);
226         Asserts.assertTrue(vwo == nvwo);
227 
228 
229         // Writing null to nullable field
230         Exception ex = null;
231         try {
232             writeInstanceField(c, "sv", smallSignature, null);
233             sv = (SmallValue)readInstanceField(c, "sv", smallSignature);
234             Asserts.assertTrue(sv == null);
235         } catch(NullPointerException npe) {
236             ex = npe;
237         }
238         Asserts.assertTrue((nullRestriction && ex != null) || (!nullRestriction && ex == null));
239         ex = null;
240         try {
241             writeInstanceField(c, "mv", mediumSignature, null);
242             mv = (MediumValue)readInstanceField(c, "mv", mediumSignature);
243             Asserts.assertTrue(mv == null);
244          } catch(NullPointerException npe) {
245             ex = npe;
246         }
247         System.out.println(ex + " / " + nullRestriction);
248         Asserts.assertTrue((nullRestriction && ex != null) || (!nullRestriction && ex == null));
249         ex = null;
250         try {
251             writeInstanceField(c, "bv", bigSignature, null);
252             bv = (BigValue)readInstanceField(c, "bv", bigSignature);
253             Asserts.assertTrue(bv == null);
254         } catch(NullPointerException npe) {
255             ex = npe;
256         }
257         Asserts.assertTrue((nullRestriction && ex != null) || (!nullRestriction && ex == null));
258         ex = null;
259         try {
260             writeInstanceField(c, "vwo", withOopSignature, null);
261             vwo = (ValueWithOop)readInstanceField(c, "vwo", withOopSignature);
262             Asserts.assertTrue(vwo == null);
263         } catch(NullPointerException npe) {
264             ex = npe;
265         }
266         Asserts.assertTrue((nullRestriction && ex != null) || (!nullRestriction && ex == null));
267     }
268 
269     static void testJniFieldAccess() {
270         // Reading nullable field
271         try {
272             Container0 c0 = new Container0();
273             testJniFieldAccessHelper(c0, false);
274             Container1 c1 = new Container1();
275             testJniFieldAccessHelper(c1, true);
276         } catch (Throwable t) {
277             t.printStackTrace();
278             throw new RuntimeException(t);
279         }
280     }
281 
282     static void testJniArrayAccessHelper(Object[] array, boolean nullRestriction) {
283         Object valueA = getValueA(array.getClass().getComponentType());
284         Object valueB = getValueB(array.getClass().getComponentType());
285         int length = array.length;
286 
287         // Reading elements
288         for (int i = 0; i < length; i++) {
289             Object element = readArrayElement(array, i);
290             Asserts.assertTrue(element == valueA);
291         }
292 
293         // Writing elements
294         for (int i = 0; i < length; i++) {
295             writeArrayElement(array, i, valueB);
296         }
297         for (int i = 0; i < length; i++) {
298             Object element = readArrayElement(array, i);
299             Asserts.assertTrue(element == valueB);
300         }
301 
302         // Writing null
303         for (int i = 0; i < length; i++) {
304             Exception ex = null;
305             try {
306                 writeArrayElement(array, i, null);
307                 Object element = readArrayElement(array, i);
308                 Asserts.assertTrue(element == null);
309             } catch(NullPointerException npe) {
310                 ex = npe;
311             }
312             Asserts.assertTrue((nullRestriction && ex != null) || (!nullRestriction && ex == null));
313         }
314     }
315 
316     static Object getValueA(Class c) {
317         try {
318             Method mA = c.getMethod("getValueA");
319             return mA.invoke(null);
320         } catch (Exception e) {
321             e.printStackTrace();
322             throw new RuntimeException(e);
323         }
324     }
325 
326     static Object getValueB(Class c) {
327         try {
328             Method mB = c.getMethod("getValueB");
329             return mB.invoke(null);
330         } catch (Exception e) {
331             e.printStackTrace();
332             throw new RuntimeException(e);
333         }
334     }
335 
336     static void fillArrayWithValueA(Object[] array) {
337         Object valueA = getValueA(array.getClass().getComponentType());
338         for (int i = 0; i < array.length; i++) {
339             array[i] = valueA;
340         }
341     }
342 
343     static void testJniArrayAccessHelper2(Class c) {
344 
345         Object[] array0 = (Object[])Array.newInstance(c, 10);
346         fillArrayWithValueA(array0);
347         testJniArrayAccessHelper(array0, false);
348 
349         Object[] array1 = ValueClass.newNullableAtomicArray(c, 31);
350         fillArrayWithValueA(array1);
351         testJniArrayAccessHelper(array1, false);
352 
353         Object[] array2 = ValueClass.newNullRestrictedAtomicArray(c, 127, getValueA(c));
354         fillArrayWithValueA(array2);
355         testJniArrayAccessHelper(array2, true);
356     }
357 
358     static void testJniArrayAccess() {
359         testJniArrayAccessHelper2(SmallValue.class);
360         testJniArrayAccessHelper2(MediumValue.class);
361         testJniArrayAccessHelper2(BigValue.class);
362         testJniArrayAccessHelper2(ValueWithOop.class);
363 
364     }
365 }