Skip to content

Commit 0e899b2

Browse files
committed
8341761: Update JNI methods to support new field and array layouts
1 parent 603ac0e commit 0e899b2

File tree

3 files changed

+348
-22
lines changed

3 files changed

+348
-22
lines changed

src/hotspot/share/prims/jni.cpp

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,7 +1927,16 @@ JNI_ENTRY_NO_PRESERVE(void, jni_SetObjectField(JNIEnv *env, jobject obj, jfieldI
19271927
o = JvmtiExport::jni_SetField_probe(thread, obj, o, k, fieldID, false, JVM_SIGNATURE_CLASS, (jvalue *)&field_value);
19281928
}
19291929
if (!jfieldIDWorkaround::is_flat_jfieldID(fieldID)) {
1930-
HeapAccess<ON_UNKNOWN_OOP_REF>::oop_store_at(o, offset, JNIHandles::resolve(value));
1930+
oop v = JNIHandles::resolve(value);
1931+
if (v == nullptr) {
1932+
InstanceKlass *ik = InstanceKlass::cast(k);
1933+
fieldDescriptor fd;
1934+
ik->find_field_from_offset(offset, false, &fd);
1935+
if (fd.is_null_free_inline_type()) {
1936+
THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Cannot store null in a null-restricted field");
1937+
}
1938+
}
1939+
HeapAccess<ON_UNKNOWN_OOP_REF>::oop_store_at(o, offset, v);
19311940
} else {
19321941
assert(k->is_instance_klass(), "Only instances can have flat fields");
19331942
InstanceKlass* ik = InstanceKlass::cast(k);
@@ -1936,8 +1945,8 @@ JNI_ENTRY_NO_PRESERVE(void, jni_SetObjectField(JNIEnv *env, jobject obj, jfieldI
19361945
InstanceKlass* holder = fd.field_holder();
19371946
InlineLayoutInfo* li = holder->inline_layout_info_adr(fd.index());
19381947
InlineKlass* vklass = li->klass();
1939-
oop v = JNIHandles::resolve_non_null(value);
1940-
vklass->write_value_to_addr(v, ((char*)(oopDesc*)obj) + offset, li->kind(), true, CHECK);
1948+
oop v = JNIHandles::resolve(value);
1949+
vklass->write_value_to_addr(v, ((char*)(oopDesc*)o) + offset, li->kind(), true, CHECK);
19411950
}
19421951
HOTSPOT_JNI_SETOBJECTFIELD_RETURN();
19431952
JNI_END
@@ -2369,7 +2378,7 @@ JNI_ENTRY(jobject, jni_GetObjectArrayElement(JNIEnv *env, jobjectArray array, js
23692378
if (arr->is_flatArray()) {
23702379
flatArrayOop a = flatArrayOop(JNIHandles::resolve_non_null(array));
23712380
res = a->read_value_from_flat_array(index, CHECK_NULL);
2372-
assert(res != nullptr, "Must be set in one of two paths above");
2381+
assert(res != nullptr || !arr->is_null_free_array(), "Invalid value");
23732382
} else {
23742383
assert(arr->is_objArray(), "If not a valueArray. must be an objArray");
23752384
objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
@@ -2402,26 +2411,15 @@ JNI_ENTRY(void, jni_SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize
24022411
oop v = JNIHandles::resolve(value);
24032412
FlatArrayKlass* vaklass = FlatArrayKlass::cast(a->klass());
24042413
InlineKlass* element_vklass = vaklass->element_klass();
2405-
if (v != nullptr && v->is_a(element_vklass)) {
2406-
a->write_value_to_flat_array(v, index, CHECK);
2407-
} else {
2408-
ResourceMark rm(THREAD);
2409-
stringStream ss;
2410-
Klass *kl = FlatArrayKlass::cast(a->klass());
2411-
ss.print("type mismatch: can not store %s to %s[%d]",
2412-
v->klass()->external_name(),
2413-
kl->external_name(),
2414-
index);
2415-
for (int dims = ArrayKlass::cast(a->klass())->dimension(); dims > 1; --dims) {
2416-
ss.print("[]");
2417-
}
2418-
THROW_MSG(vmSymbols::java_lang_ArrayStoreException(), ss.as_string());
2419-
}
2414+
a->write_value_to_flat_array(v, index, CHECK);
24202415
} else {
24212416
assert(arr->is_objArray(), "If not a valueArray. must be an objArray");
24222417
objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
24232418
oop v = JNIHandles::resolve(value);
24242419
if (v == nullptr || v->is_a(ObjArrayKlass::cast(a->klass())->element_klass())) {
2420+
if (v == nullptr && ObjArrayKlass::cast(a->klass())->is_null_free_array_klass()) {
2421+
THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Cannot store null in a null-restricted array");
2422+
}
24252423
a->obj_at_put(index, v);
24262424
} else {
24272425
ResourceMark rm(THREAD);

test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineWithJni.java

Lines changed: 294 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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
@@ -26,11 +26,21 @@
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

3545
public 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

Comments
 (0)