< prev index next > src/hotspot/share/prims/jni.cpp
Print this page
/*
! * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 Red Hat, Inc.
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
/*
! * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 Red Hat, Inc.
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "nmt/memTracker.hpp"
#include "oops/access.inline.hpp"
#include "oops/arrayOop.hpp"
+ #include "oops/flatArrayOop.inline.hpp"
+ #include "oops/inlineKlass.inline.hpp"
#include "oops/instanceKlass.inline.hpp"
#include "oops/instanceOop.hpp"
#include "oops/klass.inline.hpp"
#include "oops/markWord.hpp"
#include "oops/method.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "oops/typeArrayKlass.hpp"
#include "oops/typeArrayOop.inline.hpp"
+ #include "oops/valuePayload.inline.hpp"
#include "prims/jniCheck.hpp"
#include "prims/jniExport.hpp"
#include "prims/jniFastGetField.hpp"
#include "prims/jvm_misc.hpp"
#include "prims/jvmtiExport.hpp"
// The slot is the index of the field description in the field-array
// The jfieldID is the offset of the field within the object
// It may also have hash bits for k, if VerifyJNIFields is turned on.
int offset = InstanceKlass::cast(k1)->field_offset( slot );
assert(InstanceKlass::cast(k1)->contains_field_offset(offset), "stay within object");
! ret = jfieldIDWorkaround::to_instance_jfieldID(k1, offset);
return ret;
JNI_END
DT_RETURN_MARK_DECL(ToReflectedMethod, jobject
// The slot is the index of the field description in the field-array
// The jfieldID is the offset of the field within the object
// It may also have hash bits for k, if VerifyJNIFields is turned on.
int offset = InstanceKlass::cast(k1)->field_offset( slot );
+ bool is_flat = InstanceKlass::cast(k1)->field_is_flat(slot);
assert(InstanceKlass::cast(k1)->contains_field_offset(offset), "stay within object");
! ret = jfieldIDWorkaround::to_instance_jfieldID(k1, offset, is_flat);
return ret;
JNI_END
DT_RETURN_MARK_DECL(ToReflectedMethod, jobject
DT_RETURN_MARK(ToReflectedMethod, jobject, (const jobject&)ret);
methodHandle m (THREAD, Method::resolve_jmethod_id(method_id));
assert(m->is_static() == (isStatic != 0), "jni_ToReflectedMethod access flags doesn't match");
oop reflection_method;
! if (m->is_object_initializer()) {
reflection_method = Reflection::new_constructor(m, CHECK_NULL);
} else {
// Note: Static initializers can theoretically be here, if JNI users manage
// to get their jmethodID. Record them as plain methods.
reflection_method = Reflection::new_method(m, false, CHECK_NULL);
DT_RETURN_MARK(ToReflectedMethod, jobject, (const jobject&)ret);
methodHandle m (THREAD, Method::resolve_jmethod_id(method_id));
assert(m->is_static() == (isStatic != 0), "jni_ToReflectedMethod access flags doesn't match");
oop reflection_method;
! if (m->is_object_constructor()) {
reflection_method = Reflection::new_constructor(m, CHECK_NULL);
} else {
// Note: Static initializers can theoretically be here, if JNI users manage
// to get their jmethodID. Record them as plain methods.
reflection_method = Reflection::new_method(m, false, CHECK_NULL);
THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig));
}
// A jfieldID for a non-static field is simply the offset of the field within the instanceOop
// It may also have hash bits for k, if VerifyJNIFields is turned on.
! ret = jfieldIDWorkaround::to_instance_jfieldID(k, fd.offset());
return ret;
JNI_END
JNI_ENTRY(jobject, jni_GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID))
HOTSPOT_JNI_GETOBJECTFIELD_ENTRY(env, obj, (uintptr_t) fieldID);
oop o = JNIHandles::resolve_non_null(obj);
Klass* k = o->klass();
int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID);
// Keep JVMTI addition small and only check enabled flag here.
// jni_GetField_probe() assumes that is okay to create handles.
if (JvmtiExport::should_post_field_access()) {
o = JvmtiExport::jni_GetField_probe(thread, obj, o, k, fieldID, false);
}
! oop loaded_obj = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_load_at(o, offset);
! jobject ret = JNIHandles::make_local(THREAD, loaded_obj);
HOTSPOT_JNI_GETOBJECTFIELD_RETURN(ret);
return ret;
JNI_END
-
-
#define DEFINE_GETFIELD(Return,Fieldname,Result \
, EntryProbe, ReturnProbe) \
\
DT_RETURN_MARK_DECL_FOR(Result, Get##Result##Field, Return \
, ReturnProbe); \
THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig));
}
// A jfieldID for a non-static field is simply the offset of the field within the instanceOop
// It may also have hash bits for k, if VerifyJNIFields is turned on.
! ret = jfieldIDWorkaround::to_instance_jfieldID(k, fd.offset(), fd.is_flat());
return ret;
JNI_END
JNI_ENTRY(jobject, jni_GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID))
HOTSPOT_JNI_GETOBJECTFIELD_ENTRY(env, obj, (uintptr_t) fieldID);
oop o = JNIHandles::resolve_non_null(obj);
Klass* k = o->klass();
int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID);
+ oop res = nullptr;
// Keep JVMTI addition small and only check enabled flag here.
// jni_GetField_probe() assumes that is okay to create handles.
if (JvmtiExport::should_post_field_access()) {
o = JvmtiExport::jni_GetField_probe(thread, obj, o, k, fieldID, false);
}
! if (!jfieldIDWorkaround::is_flat_jfieldID(fieldID)) {
! res = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_load_at(o, offset);
+ } else {
+ InstanceKlass* ik = InstanceKlass::cast(k);
+ fieldDescriptor fd;
+ bool found = ik->find_field_from_offset(offset, false, &fd); // performance bottleneck
+ assert(found, "Field not found");
+ FlatFieldPayload payload(instanceOop(o), &fd);
+ res = payload.read(CHECK_NULL);
+ }
+ jobject ret = JNIHandles::make_local(THREAD, res);
HOTSPOT_JNI_GETOBJECTFIELD_RETURN(ret);
return ret;
JNI_END
#define DEFINE_GETFIELD(Return,Fieldname,Result \
, EntryProbe, ReturnProbe) \
\
DT_RETURN_MARK_DECL_FOR(Result, Get##Result##Field, Return \
, ReturnProbe); \
if (JvmtiExport::should_post_field_modification()) {
jvalue field_value;
field_value.l = value;
o = JvmtiExport::jni_SetField_probe(thread, obj, o, k, fieldID, false, JVM_SIGNATURE_CLASS, (jvalue *)&field_value);
}
! HeapAccess<ON_UNKNOWN_OOP_REF>::oop_store_at(o, offset, JNIHandles::resolve(value));
log_debug_if_final_instance_field(thread, "SetObjectField", InstanceKlass::cast(k), offset);
HOTSPOT_JNI_SETOBJECTFIELD_RETURN();
JNI_END
// TODO: make this a template
if (JvmtiExport::should_post_field_modification()) {
jvalue field_value;
field_value.l = value;
o = JvmtiExport::jni_SetField_probe(thread, obj, o, k, fieldID, false, JVM_SIGNATURE_CLASS, (jvalue *)&field_value);
}
! if (!jfieldIDWorkaround::is_flat_jfieldID(fieldID)) {
+ oop v = JNIHandles::resolve(value);
+ if (v == nullptr) {
+ InstanceKlass *ik = InstanceKlass::cast(k);
+ fieldDescriptor fd;
+ ik->find_field_from_offset(offset, false, &fd);
+ if (fd.is_null_free_inline_type()) {
+ THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Cannot store null in a null-restricted field");
+ }
+ }
+ HeapAccess<ON_UNKNOWN_OOP_REF>::oop_store_at(o, offset, v);
+ } else {
+ assert(k->is_instance_klass(), "Only instances can have flat fields");
+ InstanceKlass* ik = InstanceKlass::cast(k);
+ fieldDescriptor fd;
+ bool found = ik->find_field_from_offset(offset, false, &fd);
+ assert(found, "Field not found");
+ FlatFieldPayload payload(instanceOop(o), &fd);
+ payload.write(inlineOop(JNIHandles::resolve(value)), CHECK);
+ }
log_debug_if_final_instance_field(thread, "SetObjectField", InstanceKlass::cast(k), offset);
HOTSPOT_JNI_SETOBJECTFIELD_RETURN();
JNI_END
// TODO: make this a template
HOTSPOT_JNI_GETOBJECTARRAYELEMENT_ENTRY(env, array, index);
jobject ret = nullptr;
DT_RETURN_MARK(GetObjectArrayElement, jobject, (const jobject&)ret);
objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
if (a->is_within_bounds(index)) {
! ret = JNIHandles::make_local(THREAD, a->obj_at(index));
return ret;
} else {
ResourceMark rm(THREAD);
stringStream ss;
ss.print("Index %d out of bounds for length %d", index, a->length());
HOTSPOT_JNI_GETOBJECTARRAYELEMENT_ENTRY(env, array, index);
jobject ret = nullptr;
DT_RETURN_MARK(GetObjectArrayElement, jobject, (const jobject&)ret);
objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
if (a->is_within_bounds(index)) {
! oop res = a->obj_at(index, CHECK_NULL);
+ assert(res != nullptr || !a->is_null_free_array(), "Invalid value");
+ ret = JNIHandles::make_local(THREAD, res);
return ret;
} else {
ResourceMark rm(THREAD);
stringStream ss;
ss.print("Index %d out of bounds for length %d", index, a->length());
DT_VOID_RETURN_MARK(SetObjectArrayElement);
objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
oop v = JNIHandles::resolve(value);
if (a->is_within_bounds(index)) {
! if (v == nullptr || v->is_a(ObjArrayKlass::cast(a->klass())->element_klass())) {
! a->obj_at_put(index, v);
} else {
ResourceMark rm(THREAD);
stringStream ss;
Klass *bottom_kl = ObjArrayKlass::cast(a->klass())->bottom_klass();
ss.print("type mismatch: can not store %s to %s[%d]",
DT_VOID_RETURN_MARK(SetObjectArrayElement);
objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
oop v = JNIHandles::resolve(value);
if (a->is_within_bounds(index)) {
! Klass* ek = a->is_flatArray() ? FlatArrayKlass::cast(a->klass())->element_klass() : RefArrayKlass::cast(a->klass())->element_klass();
! if (v == nullptr || v->is_a(ek)) {
+ a->obj_at_put(index, v, CHECK);
} else {
ResourceMark rm(THREAD);
stringStream ss;
Klass *bottom_kl = ObjArrayKlass::cast(a->klass())->bottom_klass();
ss.print("type mismatch: can not store %s to %s[%d]",
if (jobj == nullptr) {
THROW_(vmSymbols::java_lang_NullPointerException(), JNI_ERR);
}
Handle obj(thread, JNIHandles::resolve_non_null(jobj));
! ObjectSynchronizer::jni_enter(obj, thread);
return JNI_OK;
JNI_END
DT_RETURN_MARK_DECL(MonitorExit, jint
, HOTSPOT_JNI_MONITOREXIT_RETURN(_ret_ref));
if (jobj == nullptr) {
THROW_(vmSymbols::java_lang_NullPointerException(), JNI_ERR);
}
Handle obj(thread, JNIHandles::resolve_non_null(jobj));
! ObjectSynchronizer::jni_enter(obj, CHECK_(JNI_ERR));
return JNI_OK;
JNI_END
DT_RETURN_MARK_DECL(MonitorExit, jint
, HOTSPOT_JNI_MONITOREXIT_RETURN(_ret_ref));
JNI_END
JNI_ENTRY(jweak, jni_NewWeakGlobalRef(JNIEnv *env, jobject ref))
HOTSPOT_JNI_NEWWEAKGLOBALREF_ENTRY(env, ref);
+
Handle ref_handle(thread, JNIHandles::resolve(ref));
+
+ if (!ref_handle.is_null() && ref_handle->klass()->is_inline_klass()) {
+ ResourceMark rm(THREAD);
+ stringStream ss;
+ ss.print("%s is not an identity class", ref_handle->klass()->external_name());
+ THROW_MSG_(vmSymbols::java_lang_IdentityException(), ss.as_string(), nullptr);
+ }
+
jweak ret = JNIHandles::make_weak_global(ref_handle, AllocFailStrategy::RETURN_NULL);
if (ret == nullptr && ref_handle.not_null()) {
THROW_OOP_(Universe::out_of_memory_error_c_heap(), nullptr);
}
HOTSPOT_JNI_NEWWEAKGLOBALREF_RETURN(ret);
} else {
return JNI_FALSE;
}
JNI_END
+ JNI_ENTRY(jboolean, jni_IsValueObject(JNIEnv* env, jobject obj))
+ HOTSPOT_JNI_ISVALUEOBJECT_ENTRY(env, obj);
+ oop o = JNIHandles::resolve(obj);
+ if (o != nullptr && o->klass()->is_inline_klass()) {
+ HOTSPOT_JNI_ISVALUEOBJECT_RETURN(JNI_TRUE);
+ return JNI_TRUE;
+ } else {
+ HOTSPOT_JNI_ISVALUEOBJECT_RETURN(JNI_FALSE);
+ return JNI_FALSE;
+ }
+ JNI_END
+
// Structure containing all jni functions
struct JNINativeInterface_ jni_NativeInterface = {
nullptr,
nullptr,
jni_IsVirtualThread,
// Large UTF8 support
! jni_GetStringUTFLengthAsLong
};
// For jvmti use to modify jni function table.
// Java threads in native contiues to run until it is transitioned
jni_IsVirtualThread,
// Large UTF8 support
! jni_GetStringUTFLengthAsLong,
+
+ // Value classes
+
+ jni_IsValueObject
};
// For jvmti use to modify jni function table.
// Java threads in native contiues to run until it is transitioned
< prev index next >