< prev index next >

src/hotspot/share/prims/jni.cpp

Print this page
@@ -1,7 +1,7 @@
  /*
-  * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+  * 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

@@ -49,10 +49,12 @@
  #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"

@@ -60,10 +62,11 @@
  #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"

@@ -416,12 +419,13 @@
  
    // 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);
+   ret = jfieldIDWorkaround::to_instance_jfieldID(k1, offset, is_flat);
    return ret;
  JNI_END
  
  
  DT_RETURN_MARK_DECL(ToReflectedMethod, jobject

@@ -434,11 +438,11 @@
    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()) {
+   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);

@@ -1768,33 +1772,41 @@
      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());
+   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);
    }
-   oop loaded_obj = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_load_at(o, offset);
-   jobject ret = JNIHandles::make_local(THREAD, loaded_obj);
+   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); \

@@ -1902,11 +1914,30 @@
    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));
+   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

@@ -2335,11 +2366,13 @@
   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));
+     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());

@@ -2355,12 +2388,13 @@
    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);
+     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]",

@@ -2751,11 +2785,11 @@
    if (jobj == nullptr) {
      THROW_(vmSymbols::java_lang_NullPointerException(), JNI_ERR);
    }
  
    Handle obj(thread, JNIHandles::resolve_non_null(jobj));
-   ObjectSynchronizer::jni_enter(obj, thread);
+   ObjectSynchronizer::jni_enter(obj, CHECK_(JNI_ERR));
    return JNI_OK;
  JNI_END
  
  DT_RETURN_MARK_DECL(MonitorExit, jint
                      , HOTSPOT_JNI_MONITOREXIT_RETURN(_ret_ref));

@@ -2909,11 +2943,20 @@
  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);

@@ -3139,10 +3182,22 @@
    } 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,

@@ -3431,11 +3486,15 @@
  
      jni_IsVirtualThread,
  
      // Large UTF8 support
  
-     jni_GetStringUTFLengthAsLong
+     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 >