11/*
2- * Copyright (c) 2018, 2024 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2018, 2025 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
2626/* @test
2727 * @summary test JNI functions with instances of value classes
2828 * @library /test/lib
29+ * @modules java.base/jdk.internal.vm.annotation
30+ * java.base/jdk.internal.value
2931 * @enablePreview
30- * @run main/othervm/native runtime.valhalla.inlinetypes.InlineWithJni
32+ * @run main/othervm/native --enable-native-access=ALL-UNNAMED -XX:+UseNullableValueFlattening runtime.valhalla.inlinetypes.InlineWithJni
3133 */
3234
33- import jdk .test .lib .Asserts ;
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 ;
3444
3545public value class InlineWithJni {
3646
@@ -43,6 +53,8 @@ public value class InlineWithJni {
4353
4454 public static void main (String [] args ) {
4555 testJniMonitorOps ();
56+ testJniFieldAccess ();
57+ testJniArrayAccess ();
4658 }
4759
4860 final int x ;
@@ -71,4 +83,283 @@ public static void testJniMonitorOps() {
7183 }
7284 Asserts .assertTrue (sawImse , "Missing IllegalMonitorStateException" );
7385 }
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+ }
74365}
0 commit comments