/* * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 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 * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
// The DT_RETURN_MARK macros create a scoped object to fire the dtrace // '-return' probe regardless of the return path is taken out of the function. // Methods that have multiple return paths use this to avoid having to // instrument each return path. Methods that use CHECK or THROW must use this // since those macros can cause an immediate uninstrumented return. // // In order to get the return value, a reference to the variable containing // the return value must be passed to the constructor of the object, and // the return value must be set before return (since the mark object has // a reference to it). // // Example: // DT_RETURN_MARK_DECL(SomeFunc, int); // JNI_ENTRY(int, SomeFunc, ...) // int return_value = 0; // DT_RETURN_MARK(SomeFunc, int, (const int&)return_value); // foo(CHECK_0) // return_value = 5; // return return_value; // JNI_END #define DT_RETURN_MARK_DECL(name, type, probe) \
DTRACE_ONLY( \ class DTraceReturnProbeMark_##name { \ public: \ const type& _ret_ref; \
DTraceReturnProbeMark_##name(const type& v) : _ret_ref(v) {} \
~DTraceReturnProbeMark_##name() { \
probe; \
} \
} \
) // Void functions are simpler since there's no return value #define DT_VOID_RETURN_MARK_DECL(name, probe) \
DTRACE_ONLY( \ class DTraceReturnProbeMark_##name { \ public: \
~DTraceReturnProbeMark_##name() { \
probe; \
} \
} \
)
// Place these macros in the function to mark the return. Non-void // functions need the type and address of the return value. #define DT_RETURN_MARK(name, type, ref) \
DTRACE_ONLY( DTraceReturnProbeMark_##name dtrace_return_mark(ref) ) #define DT_VOID_RETURN_MARK(name) \
DTRACE_ONLY( DTraceReturnProbeMark_##name dtrace_return_mark )
// Use these to select distinct code for floating-point vs. non-floating point // situations. Used from within common macros where we need slightly // different behavior for Float/Double #define FP_SELECT_Boolean(intcode, fpcode) intcode #define FP_SELECT_Byte(intcode, fpcode) intcode #define FP_SELECT_Char(intcode, fpcode) intcode #define FP_SELECT_Short(intcode, fpcode) intcode #define FP_SELECT_Object(intcode, fpcode) intcode #define FP_SELECT_Int(intcode, fpcode) intcode #define FP_SELECT_Long(intcode, fpcode) intcode #define FP_SELECT_Float(intcode, fpcode) fpcode #define FP_SELECT_Double(intcode, fpcode) fpcode #define FP_SELECT(TypeName, intcode, fpcode) \
FP_SELECT_##TypeName(intcode, fpcode)
// out-of-line helpers for class jfieldIDWorkaround:
bool jfieldIDWorkaround::is_valid_jfieldID(Klass* k, jfieldID id) { if (jfieldIDWorkaround::is_instance_jfieldID(k, id)) {
uintptr_t as_uint = (uintptr_t) id;
intptr_t offset = raw_instance_offset(id); if (is_checked_jfieldID(id)) { if (!klass_hash_ok(k, id)) { returnfalse;
}
} return InstanceKlass::cast(k)->contains_field_offset(offset);
} else {
JNIid* result = (JNIid*) id; #ifdef ASSERT return result != NULL && result->is_static_field_id(); #else return result != NULL; #endif
}
}
intptr_t jfieldIDWorkaround::encode_klass_hash(Klass* k, intptr_t offset) { if (offset <= small_offset_mask) {
Klass* field_klass = k;
Klass* super_klass = field_klass->super(); // With compressed oops the most super class with nonstatic fields would // be the owner of fields embedded in the header. while (InstanceKlass::cast(super_klass)->has_nonstatic_fields() &&
InstanceKlass::cast(super_klass)->contains_field_offset(offset)) {
field_klass = super_klass; // super contains the field also
super_klass = field_klass->super();
}
debug_only(NoSafepointVerifier nosafepoint;)
uintptr_t klass_hash = field_klass->identity_hash(); return ((klass_hash & klass_mask) << klass_shift) | checked_mask_in_place;
} else { #if 0 #ifndef PRODUCT
{
ResourceMark rm;
warning("VerifyJNIFields: long offset %d in %s", offset, k->external_name());
} #endif #endif return 0;
}
}
bool jfieldIDWorkaround::klass_hash_ok(Klass* k, jfieldID id) {
uintptr_t as_uint = (uintptr_t) id;
intptr_t klass_hash = (as_uint >> klass_shift) & klass_mask; do {
debug_only(NoSafepointVerifier nosafepoint;) // Could use a non-blocking query for identity_hash here... if ((k->identity_hash() & klass_mask) == klass_hash) returntrue;
k = k->super();
} while (k != NULL); returnfalse;
}
void jfieldIDWorkaround::verify_instance_jfieldID(Klass* k, jfieldID id) {
guarantee(jfieldIDWorkaround::is_instance_jfieldID(k, id), "must be an instance field");
uintptr_t as_uint = (uintptr_t) id;
intptr_t offset = raw_instance_offset(id); if (VerifyJNIFields) { if (is_checked_jfieldID(id)) {
guarantee(klass_hash_ok(k, id), "Bug in native code: jfieldID class must match object");
} else { #if 0 #ifndef PRODUCT if (Verbose) {
ResourceMark rm;
warning("VerifyJNIFields: unverified offset %d for %s", offset, k->external_name());
} #endif #endif
}
}
guarantee(InstanceKlass::cast(k)->contains_field_offset(offset), "Bug in native code: jfieldID offset must address interior of object");
}
// Class resolution will get the class name from the .class stream if the name is null.
TempNewSymbol class_name = name == NULL ? NULL :
SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_NoClassDefFoundError(),
CHECK_NULL);
jclass result = NULL;
DT_RETURN_MARK(FindClass, jclass, (const jclass&)result);
// This should be ClassNotFoundException imo.
TempNewSymbol class_name =
SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_NoClassDefFoundError(),
CHECK_NULL);
//%note jni_3
Handle protection_domain; // Find calling class
Klass* k = thread->security_get_caller_class(0); // default to the system loader when no context
Handle loader(THREAD, SystemDictionary::java_system_loader()); if (k != NULL) { // Special handling to make sure JNI_OnLoad and JNI_OnUnload are executed // in the correct class context. if (k->class_loader() == NULL &&
k->name() == vmSymbols::jdk_internal_loader_NativeLibraries()) {
JavaValue result(T_OBJECT);
JavaCalls::call_static(&result, k,
vmSymbols::getFromClass_name(),
vmSymbols::void_class_signature(),
CHECK_NULL); // When invoked from JNI_OnLoad, NativeLibraries::getFromClass returns // a non-NULL Class object. When invoked from JNI_OnUnload, // it will return NULL to indicate no context.
oop mirror = result.get_oop(); if (mirror != NULL) {
Klass* fromClass = java_lang_Class::as_Klass(mirror);
loader = Handle(THREAD, fromClass->class_loader());
protection_domain = Handle(THREAD, fromClass->protection_domain());
}
} else {
loader = Handle(THREAD, k->class_loader());
}
}
result = find_class_from_class_loader(env, class_name, true, loader,
protection_domain, true, thread);
if (log_is_enabled(Debug, class, resolve) && result != NULL) {
trace_class_resolution(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(result)));
}
// Make sure class is initialized before handing id's out to methods
k1->initialize(CHECK_NULL);
Method* m = InstanceKlass::cast(k1)->method_with_idnum(slot);
ret = m==NULL? NULL : m->jmethod_id(); // return NULL if reflected method deleted return ret;
JNI_END
jfieldID ret = NULL;
DT_RETURN_MARK(FromReflectedField, jfieldID, (const jfieldID&)ret);
// field is a handle to a java.lang.reflect.Field object
oop reflected = JNIHandles::resolve_non_null(field);
oop mirror = java_lang_reflect_Field::clazz(reflected);
Klass* k1 = java_lang_Class::as_Klass(mirror); int slot = java_lang_reflect_Field::slot(reflected); int modifiers = java_lang_reflect_Field::modifiers(reflected);
// Make sure class is initialized before handing id's out to fields
k1->initialize(CHECK_NULL);
// First check if this is a static field if (modifiers & JVM_ACC_STATIC) {
intptr_t offset = InstanceKlass::cast(k1)->field_offset( slot );
JNIid* id = InstanceKlass::cast(k1)->jni_id_for(offset);
assert(id != NULL, "corrupt Field object");
debug_only(id->set_is_static_field_id();) // A jfieldID for a static field is a JNIid specifying the field holder and the offset within the Klass*
ret = jfieldIDWorkaround::to_static_jfieldID(id); return ret;
}
// 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.
intptr_t 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
// JNI functions only transform a pending async exception to a synchronous // exception in ExceptionOccurred and ExceptionCheck calls, since // delivering an async exception in other places won't change the native // code's control flow and would be harmful when native code further calls // JNI functions with a pending exception. Async exception is also checked // during the call, so ExceptionOccurred/ExceptionCheck won't return // false but deliver the async exception at the very end during // state transition.
staticvoid jni_check_async_exceptions(JavaThread *thread) {
assert(thread == Thread::current(), "must be itself"); if (thread->has_async_exception_condition()) {
SafepointMechanism::process_if_requested_with_exit_check(thread, true/* check asyncs */);
}
}
if (thread->has_pending_exception()) {
Handle ex(thread, thread->pending_exception());
thread->clear_pending_exception();
jio_fprintf(defaultStream::error_stream(), "Exception "); if (thread != NULL && thread->threadObj() != NULL) {
ResourceMark rm(THREAD);
jio_fprintf(defaultStream::error_stream(), "in thread \"%s\" ", thread->name());
} if (ex->is_a(vmClasses::Throwable_klass())) {
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,
ex,
vmClasses::Throwable_klass(),
vmSymbols::printStackTrace_name(),
vmSymbols::void_method_signature(),
THREAD); // If an exception is thrown in the call it gets thrown away. Not much // we can do with it. The native code that calls this, does not check // for the exception - hence, it might still be in the thread when DestroyVM gets // called, potentially causing a few asserts to trigger - since no pending exception // is expected.
CLEAR_PENDING_EXCEPTION;
} else {
ResourceMark rm(THREAD);
jio_fprintf(defaultStream::error_stream(), ". Uncaught exception of type %s.",
ex->klass()->external_name());
}
}
// The jni code might be using this API to clear java thrown exception. // So just mark jvmti thread exception state as exception caught.
JvmtiThreadState *state = JavaThread::current()->jvmti_thread_state(); if (state != NULL && state->is_exception_detected()) {
state->set_exception_caught();
}
thread->clear_pending_exception();
//%note jni_11
Handle result_handle(thread, JNIHandles::resolve(result));
JNIHandleBlock* old_handles = thread->active_handles();
JNIHandleBlock* new_handles = old_handles->pop_frame_link(); if (new_handles != NULL) { // As a sanity check we only release the handle blocks if the pop_frame_link is not NULL. // This way code will still work if PopLocalFrame is called without a corresponding // PushLocalFrame call. Note that we set the pop_frame_link to NULL explicitly, otherwise // the release_block call will release the blocks.
thread->set_active_handles(new_handles);
old_handles->set_pop_frame_link(NULL); // clear link we won't release new_handles below
JNIHandleBlock::release_block(old_handles, thread); // may block
result = JNIHandles::make_local(thread, result_handle());
}
HOTSPOT_JNI_POPLOCALFRAME_RETURN(result); return result;
JNI_END
void push_boolean(jboolean b) { // Normalize boolean arguments from native code by converting 1-255 to JNI_TRUE and // 0 to JNI_FALSE. Boolean return values from native are normalized the same in // TemplateInterpreterGenerator::generate_result_handler_for and // SharedRuntime::generate_native_wrapper.
push_int(b == 0 ? JNI_FALSE : JNI_TRUE);
}
class JNI_ArgumentPusherVaArg : public JNI_ArgumentPusher {
va_list _ap;
void set_ap(va_list rap) {
va_copy(_ap, rap);
}
friendclass SignatureIterator; // so do_parameters_on can call do_type void do_type(BasicType type) { switch (type) { // these are coerced to int when using va_arg case T_BYTE: case T_CHAR: case T_SHORT: case T_INT: push_int(va_arg(_ap, jint)); break; case T_BOOLEAN: push_boolean((jboolean) va_arg(_ap, jint)); break;
// each of these paths is exercised by the various jck Call[Static,Nonvirtual,][Void,Int,..]Method[A,V,] tests
case T_LONG: push_long(va_arg(_ap, jlong)); break; // float is coerced to double w/ va_arg case T_FLOAT: push_float((jfloat) va_arg(_ap, jdouble)); break; case T_DOUBLE: push_double(va_arg(_ap, jdouble)); break;
case T_ARRAY: case T_OBJECT: push_object(va_arg(_ap, jobject)); break; default: ShouldNotReachHere();
}
}
friendclass SignatureIterator; // so do_parameters_on can call do_type void do_type(BasicType type) { switch (type) { case T_CHAR: push_int((_ap++)->c); break; case T_SHORT: push_int((_ap++)->s); break; case T_BYTE: push_int((_ap++)->b); break; case T_INT: push_int((_ap++)->i); break; case T_BOOLEAN: push_boolean((_ap++)->z); break; case T_LONG: push_long((_ap++)->j); break; case T_FLOAT: push_float((_ap++)->f); break; case T_DOUBLE: push_double((_ap++)->d); break; case T_ARRAY: case T_OBJECT: push_object((_ap++)->l); break; default: ShouldNotReachHere();
}
}
// Create object to hold arguments for the JavaCall, and associate it with // the jni parser
ResourceMark rm(THREAD); int number_of_parameters = method->size_of_parameters();
JavaCallArguments java_args(number_of_parameters);
assert(method->is_static(), "method should be static");
// Fill out JavaCallArguments object
args->push_arguments_on(&java_args); // Initialize result type
result->set_type(args->return_type());
// Invoke the method. Result is returned as oop.
JavaCalls::call(result, method, &java_args, CHECK);
// Convert result if (is_reference_type(result->get_type())) {
result->set_jobject(JNIHandles::make_local(THREAD, result->get_oop()));
}
}
int number_of_parameters;
Method* selected_method;
{
Method* m = Method::resolve_jmethod_id(method_id);
number_of_parameters = m->size_of_parameters();
InstanceKlass* holder = m->method_holder(); if (call_type != JNI_VIRTUAL) {
selected_method = m;
} elseif (!m->has_itable_index()) { // non-interface call -- for that little speed boost, don't handlize
debug_only(NoSafepointVerifier nosafepoint;) // jni_GetMethodID makes sure class is linked and initialized // so m should have a valid vtable index.
assert(m->valid_vtable_index(), "no valid vtable index"); int vtbl_index = m->vtable_index(); if (vtbl_index != Method::nonvirtual_vtable_index) {
selected_method = h_recv->klass()->method_at_vtable(vtbl_index);
} else { // final method
selected_method = m;
}
} else { // interface call int itbl_index = m->itable_index();
Klass* k = h_recv->klass();
selected_method = InstanceKlass::cast(k)->method_at_itable(holder, itbl_index, CHECK);
}
}
methodHandle method(THREAD, selected_method);
// Create object to hold arguments for the JavaCall, and associate it with // the jni parser
ResourceMark rm(THREAD);
JavaCallArguments java_args(number_of_parameters);
// handle arguments
assert(!method->is_static(), "method %s should not be static", method->name_and_sig_as_C_string());
java_args.push_oop(h_recv); // Push jobject handle
// Fill out JavaCallArguments object
args->push_arguments_on(&java_args); // Initialize result type
result->set_type(args->return_type());
// Invoke the method. Result is returned as oop.
JavaCalls::call(result, method, &java_args, CHECK);
// Convert result if (is_reference_type(result->get_type())) {
result->set_jobject(JNIHandles::make_local(THREAD, result->get_oop()));
}
}
static jmethodID get_method_id(JNIEnv *env, jclass clazz, constchar *name_str, constchar *sig, bool is_static, TRAPS) { // %%%% This code should probably just call into a method in the LinkResolver // // The class should have been loaded (we have an instance of the class // passed in) so the method and signature should already be in the symbol // table. If they're not there, the method doesn't exist. constchar *name_to_probe = (name_str == NULL)
? vmSymbols::object_initializer_name()->as_C_string()
: name_str;
TempNewSymbol name = SymbolTable::probe(name_to_probe, (int)strlen(name_to_probe));
TempNewSymbol signature = SymbolTable::probe(sig, (int)strlen(sig));
oop mirror = JNIHandles::resolve_non_null(clazz);
Klass* klass = java_lang_Class::as_Klass(mirror);
// Throw a NoSuchMethodError exception if we have an instance of a // primitive java.lang.Class if (java_lang_Class::is_primitive(mirror)) {
ResourceMark rm(THREAD);
THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), err_msg("%s%s.%s%s", is_static ? "static " : "", klass->signature_name(), name_str, sig));
}
// Make sure class is linked and initialized before handing id's out to // Method*s.
klass->initialize(CHECK_NULL);
Method* m; if (name == vmSymbols::object_initializer_name() ||
name == vmSymbols::class_initializer_name()) { // Never search superclasses for constructors if (klass->is_instance_klass()) {
m = InstanceKlass::cast(klass)->find_method(name, signature);
} else {
m = NULL;
}
} else {
m = klass->lookup_method(name, signature); if (m == NULL && klass->is_instance_klass()) {
m = InstanceKlass::cast(klass)->lookup_method_in_ordered_interfaces(name, signature);
}
} if (m == NULL || (m->is_static() != is_static)) {
ResourceMark rm(THREAD);
THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), err_msg("%s%s.%s%s", is_static ? "static " : "", klass->signature_name(), name_str, sig));
} return m->jmethod_id();
}
Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz));
// The class should have been loaded (we have an instance of the class // passed in) so the field and signature should already be in the symbol // table. If they're not there, the field doesn't exist.
TempNewSymbol fieldname = SymbolTable::probe(name, (int)strlen(name));
TempNewSymbol signame = SymbolTable::probe(sig, (int)strlen(sig)); if (fieldname == NULL || signame == NULL) {
ResourceMark rm;
THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig));
}
// Make sure class is initialized before handing id's out to fields
k->initialize(CHECK_NULL);
// 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); \
\
JNI_ENTRY_NO_PRESERVE(Return, jni_Get##Result##Field(JNIEnv *env, jobject obj, jfieldID fieldID)) \
\
EntryProbe; \ Return ret = 0;\
DT_RETURN_MARK_FOR(Result, Get##Result##Field, Return, (constReturn&)ret);\
\
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. */ \ if (JvmtiExport::should_post_field_access()) { \
o = JvmtiExport::jni_GetField_probe(thread, obj, o, k, fieldID, false); \
} \
ret = o->Fieldname##_field(offset); \ return ret; \
JNI_END
DEFINE_GETFIELD(jboolean, bool, Boolean
, HOTSPOT_JNI_GETBOOLEANFIELD_ENTRY(env, obj, (uintptr_t)fieldID),
HOTSPOT_JNI_GETBOOLEANFIELD_RETURN(_ret_ref))
DEFINE_GETFIELD(jbyte, byte, Byte
, HOTSPOT_JNI_GETBYTEFIELD_ENTRY(env, obj, (uintptr_t)fieldID),
HOTSPOT_JNI_GETBYTEFIELD_RETURN(_ret_ref))
DEFINE_GETFIELD(jchar, char, Char
, HOTSPOT_JNI_GETCHARFIELD_ENTRY(env, obj, (uintptr_t)fieldID),
HOTSPOT_JNI_GETCHARFIELD_RETURN(_ret_ref))
DEFINE_GETFIELD(jshort, short, Short
, HOTSPOT_JNI_GETSHORTFIELD_ENTRY(env, obj, (uintptr_t)fieldID),
HOTSPOT_JNI_GETSHORTFIELD_RETURN(_ret_ref))
DEFINE_GETFIELD(jint, int, Int
, HOTSPOT_JNI_GETINTFIELD_ENTRY(env, obj, (uintptr_t)fieldID),
HOTSPOT_JNI_GETINTFIELD_RETURN(_ret_ref))
DEFINE_GETFIELD(jlong, long, Long
, HOTSPOT_JNI_GETLONGFIELD_ENTRY(env, obj, (uintptr_t)fieldID),
HOTSPOT_JNI_GETLONGFIELD_RETURN(_ret_ref)) // Float and double probes don't return value because dtrace doesn't currently support it
DEFINE_GETFIELD(jfloat, float, Float
, HOTSPOT_JNI_GETFLOATFIELD_ENTRY(env, obj, (uintptr_t)fieldID),
HOTSPOT_JNI_GETFLOATFIELD_RETURN())
DEFINE_GETFIELD(jdouble, double, Double
, HOTSPOT_JNI_GETDOUBLEFIELD_ENTRY(env, obj, (uintptr_t)fieldID),
HOTSPOT_JNI_GETDOUBLEFIELD_RETURN())
if (isStatic) { // Static field. The fieldID a JNIid specifying the field holder and the offset within the Klass*.
JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fieldID);
assert(id->is_static_field_id(), "invalid static field id");
found = id->find_local_field(&fd);
} else { // Non-static field. The fieldID is really the offset of the field within the instanceOop. int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID);
found = InstanceKlass::cast(k)->find_field_from_offset(offset, false, &fd);
}
assert(found, "bad fieldID passed into jni_ToReflectedField");
oop reflected = Reflection::new_field(&fd, CHECK_NULL);
ret = JNIHandles::make_local(THREAD, reflected); return ret;
JNI_END
// The class should have been loaded (we have an instance of the class // passed in) so the field and signature should already be in the symbol // table. If they're not there, the field doesn't exist.
TempNewSymbol fieldname = SymbolTable::probe(name, (int)strlen(name));
TempNewSymbol signame = SymbolTable::probe(sig, (int)strlen(sig)); if (fieldname == NULL || signame == NULL) {
THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), (char*) name);
}
Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); // Make sure class is initialized before handing id's out to static fields
k->initialize(CHECK_NULL);
// A jfieldID for a static field is a JNIid specifying the field holder and the offset within the Klass*
JNIid* id = fd.field_holder()->jni_id_for(fd.offset());
debug_only(id->set_is_static_field_id();)
debug_only(id->verify(fd.field_holder()));
ret = jfieldIDWorkaround::to_static_jfieldID(id); return ret;
JNI_END
JNI_ENTRY(jobject, jni_GetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID))
HOTSPOT_JNI_GETSTATICOBJECTFIELD_ENTRY(env, clazz, (uintptr_t) fieldID); #if INCLUDE_JNI_CHECK
DEBUG_ONLY(Klass* param_k = jniCheck::validate_class(thread, clazz);) #endif// INCLUDE_JNI_CHECK
JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fieldID);
assert(id->is_static_field_id(), "invalid static field id"); // 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()) {
JvmtiExport::jni_GetField_probe(thread, NULL, NULL, id->holder(), fieldID, true);
}
jobject ret = JNIHandles::make_local(THREAD, id->holder()->java_mirror()->obj_field(id->offset()));
HOTSPOT_JNI_GETSTATICOBJECTFIELD_RETURN(ret); return ret;
JNI_END
#define DEFINE_GETSTATICFIELD(Return,Fieldname,Result \
, EntryProbe, ReturnProbe) \
\
DT_RETURN_MARK_DECL_FOR(Result, GetStatic##Result##Field, Return \
, ReturnProbe); \
\
JNI_ENTRY(Return, jni_GetStatic##Result##Field(JNIEnv *env, jclass clazz, jfieldID fieldID)) \
EntryProbe; \ Return ret = 0;\
DT_RETURN_MARK_FOR(Result, GetStatic##Result##Field, Return, \
(constReturn&)ret);\
JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fieldID); \
assert(id->is_static_field_id(), "invalid static field id"); \ /* 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()) { \
JvmtiExport::jni_GetField_probe(thread, NULL, NULL, id->holder(), fieldID, true); \
} \
ret = id->holder()->java_mirror()-> Fieldname##_field (id->offset()); \ return ret;\
JNI_END
JNI_ENTRY_NO_PRESERVE(jsize, jni_GetStringLength(JNIEnv *env, jstring string))
HOTSPOT_JNI_GETSTRINGLENGTH_ENTRY(env, string);
jsize ret = 0;
oop s = JNIHandles::resolve_non_null(string);
ret = java_lang_String::length(s);
HOTSPOT_JNI_GETSTRINGLENGTH_RETURN(ret); return ret;
JNI_END
JNI_ENTRY_NO_PRESERVE(const jchar*, jni_GetStringChars(
JNIEnv *env, jstring string, jboolean *isCopy))
HOTSPOT_JNI_GETSTRINGCHARS_ENTRY(env, string, (uintptr_t *) isCopy);
jchar* buf = NULL;
oop s = JNIHandles::resolve_non_null(string);
typeArrayOop s_value = java_lang_String::value(s); if (s_value != NULL) { int s_len = java_lang_String::length(s, s_value); bool is_latin1 = java_lang_String::is_latin1(s);
buf = NEW_C_HEAP_ARRAY_RETURN_NULL(jchar, s_len + 1, mtInternal); // add one for zero termination /* JNI Specification states return NULL on OOM */ if (buf != NULL) { if (s_len > 0) { if (!is_latin1) {
ArrayAccess<>::arraycopy_to_native(s_value, (size_t) typeArrayOopDesc::element_offset<jchar>(0),
buf, s_len);
} else { for (int i = 0; i < s_len; i++) {
buf[i] = ((jchar) s_value->byte_at(i)) & 0xff;
}
}
}
buf[s_len] = 0; //%note jni_5 if (isCopy != NULL) {
*isCopy = JNI_TRUE;
}
}
}
HOTSPOT_JNI_GETSTRINGCHARS_RETURN(buf); return buf;
JNI_END
JNI_ENTRY_NO_PRESERVE(void, jni_ReleaseStringChars(JNIEnv *env, jstring str, const jchar *chars))
HOTSPOT_JNI_RELEASESTRINGCHARS_ENTRY(env, str, (uint16_t *) chars); //%note jni_6 if (chars != NULL) { // Since String objects are supposed to be immutable, don't copy any // new data back. A bad user will have to go after the char array.
FreeHeap((void*) chars);
}
HOTSPOT_JNI_RELEASESTRINGCHARS_RETURN();
JNI_END
Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz));
// There are no restrictions on native code registering native methods, // which allows agents to redefine the bindings to native methods, however // we issue a warning if any code running outside of the boot/platform // loader is rebinding any native methods in classes loaded by the // boot/platform loader that are in named modules. That will catch changes // to platform classes while excluding classes added to the bootclasspath. bool do_warning = false;
// Only instanceKlasses can have native methods if (k->is_instance_klass()) {
oop cl = k->class_loader();
InstanceKlass* ik = InstanceKlass::cast(k); // Check for a platform class if ((cl == NULL || SystemDictionary::is_platform_class_loader(cl)) &&
ik->module()->is_named()) {
Klass* caller = thread->security_get_caller_class(1); // If no caller class, or caller class has a different loader, then // issue a warning below.
do_warning = (caller == NULL) || caller->class_loader() != cl;
}
}
for (int index = 0; index < nMethods; index++) { constchar* meth_name = methods[index].name; constchar* meth_sig = methods[index].signature; int meth_name_len = (int)strlen(meth_name);
// The class should have been loaded (we have an instance of the class // passed in) so the method and signature should already be in the symbol // table. If they're not there, the method doesn't exist.
TempNewSymbol name = SymbolTable::probe(meth_name, meth_name_len);
TempNewSymbol signature = SymbolTable::probe(meth_sig, (int)strlen(meth_sig));
if (name == NULL || signature == NULL) {
ResourceMark rm(THREAD);
stringStream st;
st.print("Method %s.%s%s not found", k->external_name(), meth_name, meth_sig); // Must return negative value on failure
THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), -1);
}
if (do_warning) {
ResourceMark rm(THREAD);
log_warning(jni, resolve)("Re-registering of platform native method: %s.%s%s " "from code in a different classloader", k->external_name(), meth_name, meth_sig);
}
bool res = Method::register_native(k, name, signature,
(address) methods[index].fnPtr, THREAD); if (!res) {
ret = -1; break;
}
} return ret;
JNI_END
JNI_ENTRY(jint, jni_UnregisterNatives(JNIEnv *env, jclass clazz))
HOTSPOT_JNI_UNREGISTERNATIVES_ENTRY(env, clazz);
Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); //%note jni_2 if (k->is_instance_klass()) { for (int index = 0; index < InstanceKlass::cast(k)->methods()->length(); index++) {
Method* m = InstanceKlass::cast(k)->methods()->at(index); if (m->is_native()) {
m->clear_native_function();
m->set_signature_handler(NULL);
}
}
}
HOTSPOT_JNI_UNREGISTERNATIVES_RETURN(0); return 0;
JNI_END
JNI_ENTRY(void, jni_GetStringUTFRegion(JNIEnv *env, jstring string, jsize start, jsize len, char *buf))
HOTSPOT_JNI_GETSTRINGUTFREGION_ENTRY(env, string, start, len, buf);
DT_VOID_RETURN_MARK(GetStringUTFRegion);
oop s = JNIHandles::resolve_non_null(string);
typeArrayOop s_value = java_lang_String::value(s); int s_len = java_lang_String::length(s, s_value); if (start < 0 || len < 0 || start > s_len - len) { THROW(vmSymbols::java_lang_StringIndexOutOfBoundsException());
} else { //%note jni_7 if (len > 0) { // Assume the buffer is large enough as the JNI spec. does not require user error checking
java_lang_String::as_utf8_string(s, s_value, start, len, buf, INT_MAX); // as_utf8_string null-terminates the result string
} else { // JDK null-terminates the buffer even in len is zero if (buf != NULL) {
buf[0] = 0;
}
}
}
JNI_END
static typeArrayOop lock_gc_or_pin_string_value(JavaThread* thread, oop str) { if (Universe::heap()->supports_object_pinning()) { // Forbid deduplication before obtaining the value array, to prevent // deduplication from replacing the value array while setting up or in // the critical section. That would lead to the release operation // unpinning the wrong object. if (StringDedup::is_enabled()) {
NoSafepointVerifier nsv;
StringDedup::forbid_deduplication(str);
}
typeArrayOop s_value = java_lang_String::value(str); return (typeArrayOop) Universe::heap()->pin_object(thread, s_value);
} else {
Handle h(thread, str); // Handlize across potential safepoint.
GCLocker::lock_critical(thread); return java_lang_String::value(h());
}
}
if (log_is_enabled(Debug, class, resolve) && result != NULL) {
trace_class_resolution(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(result)));
} return result;
}
// These lookups are done with the NULL (bootstrap) ClassLoader to // circumvent any security checks that would be done by jni_FindClass.
JNI_ENTRY(bool, lookupDirectBufferClasses(JNIEnv* env))
{ if ((bufferClass = lookupOne(env, "java/nio/Buffer", thread)) == NULL) { returnfalse; } if ((directBufferClass = lookupOne(env, "sun/nio/ch/DirectBuffer", thread)) == NULL) { returnfalse; } if ((directByteBufferClass = lookupOne(env, "java/nio/DirectByteBuffer", thread)) == NULL) { returnfalse; } returntrue;
}
JNI_END
if (Atomic::cmpxchg(&directBufferSupportInitializeStarted, 0, 1) == 0) { if (!lookupDirectBufferClasses(env)) {
directBufferSupportInitializeFailed = 1; returnfalse;
}
// Make global references for these
bufferClass = (jclass) env->NewGlobalRef(bufferClass);
directBufferClass = (jclass) env->NewGlobalRef(directBufferClass);
directByteBufferClass = (jclass) env->NewGlobalRef(directByteBufferClass);
// Global refs will be NULL if out-of-memory (no exception is pending) if (bufferClass == NULL || directBufferClass == NULL || directByteBufferClass == NULL) {
directBufferSupportInitializeFailed = 1; returnfalse;
}
// Get needed field and method IDs
directByteBufferConstructor = env->GetMethodID(directByteBufferClass, "<init>", "(JI)V"); if (env->ExceptionCheck()) {
env->ExceptionClear();
directBufferSupportInitializeFailed = 1; returnfalse;
}
directBufferAddressField = env->GetFieldID(bufferClass, "address", "J"); if (env->ExceptionCheck()) {
env->ExceptionClear();
directBufferSupportInitializeFailed = 1; returnfalse;
}
bufferCapacityField = env->GetFieldID(bufferClass, "capacity", "I"); if (env->ExceptionCheck()) {
env->ExceptionClear();
directBufferSupportInitializeFailed = 1; returnfalse;
}
extern"C"void* JNICALL jni_GetDirectBufferAddress(JNIEnv *env, jobject buf)
{ // thread_from_jni_environment() will block if VM is gone.
JavaThread* thread = JavaThread::thread_from_jni_environment(env);
HOTSPOT_JNI_GETDIRECTBUFFERADDRESS_ENTRY(env, buf); void* ret = NULL;
DT_RETURN_MARK(GetDirectBufferAddress, void*, (constvoid*&)ret);
if (!directBufferSupportInitializeEnded) { if (!initializeDirectBufferSupport(env, thread)) { return 0;
}
}
extern"C" jlong JNICALL jni_GetDirectBufferCapacity(JNIEnv *env, jobject buf)
{ // thread_from_jni_environment() will block if VM is gone.
JavaThread* thread = JavaThread::thread_from_jni_environment(env);
HOTSPOT_JNI_GETDIRECTBUFFERCAPACITY_ENTRY(env, buf);
jlong ret = -1;
DT_RETURN_MARK(GetDirectBufferCapacity, jlong, (const jlong&)ret);
if (!directBufferSupportInitializeEnded) { if (!initializeDirectBufferSupport(env, thread)) {
ret = 0; return ret;
}
}
if (buf == NULL) { return -1;
}
if (!env->IsInstanceOf(buf, directBufferClass)) { return -1;
}
// NOTE that capacity is currently an int in the implementation
ret = env->GetIntField(buf, bufferCapacityField); return ret;
}
// For jvmti use to modify jni function table. // Java threads in native contiues to run until it is transitioned // to VM at safepoint. Before the transition or before it is blocked // for safepoint it may access jni function table. VM could crash if // any java thread access the jni function table in the middle of memcpy. // To avoid this each function pointers are copied automically. void copy_jni_function_table(conststruct JNINativeInterface_ *new_jni_NativeInterface) {
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
intptr_t *a = (intptr_t *) jni_functions();
intptr_t *b = (intptr_t *) new_jni_NativeInterface; for (uint i=0; i < sizeof(struct JNINativeInterface_)/sizeof(void *); i++) {
Atomic::store(a++, *b++);
}
}
// Global invocation API vars volatileint vm_created = 0; // Indicate whether it is safe to recreate VM. Recreation is only // possible after a failed initial creation attempt in some cases. volatileint safe_to_recreate_vm = 1; struct JavaVM_ main_vm = {&jni_InvokeInterface};
#define JAVASTACKSIZE (400 * 1024) /* Default size of a thread java stack */ enum { VERIFY_NONE, VERIFY_REMOTE, VERIFY_ALL };
if (Threads::is_supported_jni_version(args->version)) {
ret = JNI_OK;
} // 1.1 style no longer supported in hotspot. // According the JNI spec, we should update args->version on return. // We also use the structure to communicate with launcher about default // stack size. if (args->version == JNI_VERSION_1_1) {
args->version = JNI_VERSION_1_2; // javaStackSize is int in arguments structure
assert(jlong(ThreadStackSize) * K < INT_MAX, "integer overflow");
args->javaStackSize = (jint)(ThreadStackSize * K);
} return ret;
}
jint result = JNI_ERR;
DT_RETURN_MARK(CreateJavaVM, jint, (const jint&)result);
// We're about to use Atomic::xchg for synchronization. Some Zero // platforms use the GCC builtin __sync_lock_test_and_set for this, // but __sync_lock_test_and_set is not guaranteed to do what we want // on all architectures. So we check it works before relying on it. #ifdefined(ZERO) && defined(ASSERT)
{
jint a = 0xcafebabe;
jint b = Atomic::xchg(&a, (jint) 0xdeadbeef); void *c = &a; void *d = Atomic::xchg(&c, &b);
assert(a == (jint) 0xdeadbeef && b == (jint) 0xcafebabe, "Atomic::xchg() works");
assert(c == &b && d == &a, "Atomic::xchg() works");
} #endif// ZERO && ASSERT
// At the moment it's only possible to have one Java VM, // since some of the runtime state is in global variables.
// We cannot use our mutex locks here, since they only work on // Threads. We do an atomic compare and exchange to ensure only // one thread can call this method at a time
// We use Atomic::xchg rather than Atomic::add/dec since on some platforms // the add/dec implementations are dependent on whether we are running // on a multiprocessor Atomic::xchg does not have this problem. if (Atomic::xchg(&vm_created, 1) == 1) { return JNI_EEXIST; // already created, or create attempt in progress
}
// If a previous creation attempt failed but can be retried safely, // then safe_to_recreate_vm will have been reset to 1 after being // cleared here. If a previous creation attempt succeeded and we then // destroyed that VM, we will be prevented from trying to recreate // the VM in the same process, as the value will still be 0. if (Atomic::xchg(&safe_to_recreate_vm, 0) == 0) { return JNI_ERR;
}
assert(vm_created == 1, "vm_created is true during the creation");
/** * Certain errors during initialization are recoverable and do not * prevent this method from being called again at a later time * (perhaps with different arguments). However, at a certain * point during initialization if an error occurs we cannot allow * this function to be called again (or it will crash). In those * situations, the 'canTryAgain' flag is set to false, which atomically * sets safe_to_recreate_vm to 1, such that any new call to * JNI_CreateJavaVM will immediately fail using the above logic.
*/ bool can_try_again = true;
result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again); if (result == JNI_OK) {
JavaThread *thread = JavaThread::current();
assert(!thread->has_pending_exception(), "should have returned not OK"); /* thread is thread_in_vm here */
*vm = (JavaVM *)(&main_vm);
*(JNIEnv**)penv = thread->jni_environment();
#if INCLUDE_JVMCI if (EnableJVMCI) { if (UseJVMCICompiler) { // JVMCI is initialized on a CompilerThread if (BootstrapJVMCI) {
JavaThread* THREAD = thread; // For exception macros.
JVMCICompiler* compiler = JVMCICompiler::instance(true, CATCH);
compiler->bootstrap(THREAD); if (HAS_PENDING_EXCEPTION) {
HandleMark hm(THREAD);
vm_exit_during_initialization(Handle(THREAD, PENDING_EXCEPTION));
}
}
}
} #endif
// Notify JVMTI if (JvmtiExport::should_post_thread_life()) {
JvmtiExport::post_thread_start(thread);
}
post_thread_start_event(thread);
if (ReplayCompiles) ciReplay::replay(thread);
#ifdef ASSERT // Some platforms (like Win*) need a wrapper around these test // functions in order to properly handle error conditions. if (ErrorHandlerTest != 0) {
VMError::controlled_crash(ErrorHandlerTest);
} #endif
// Since this is not a JVM_ENTRY we have to set the thread state manually before leaving.
ThreadStateTransition::transition_from_vm(thread, _thread_in_native);
MACOS_AARCH64_ONLY(thread->enable_wx(WXExec));
} else { // If create_vm exits because of a pending exception, exit with that // exception. In the future when we figure out how to reclaim memory, // we may be able to exit with JNI_ERR and allow the calling application // to continue. if (Universe::is_fully_initialized()) { // otherwise no pending exception possible - VM will already have aborted
JavaThread* THREAD = JavaThread::current(); // For exception macros. if (HAS_PENDING_EXCEPTION) {
HandleMark hm(THREAD);
vm_exit_during_initialization(Handle(THREAD, PENDING_EXCEPTION));
}
}
if (can_try_again) { // reset safe_to_recreate_vm to 1 so that retrial would be possible
safe_to_recreate_vm = 1;
}
// Creation failed. We must reset vm_created
*vm = 0;
*(JNIEnv**)penv = 0; // reset vm_created last to avoid race condition. Use OrderAccess to // control both compiler and architectural-based reordering.
Atomic::release_store(&vm_created, 0);
}
// Flush stdout and stderr before exit.
fflush(stdout);
fflush(stderr);
return result;
}
_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {
jint result = JNI_ERR; // On Windows, let CreateJavaVM run with SEH protection #ifdefined(_WIN32) && !defined(USE_VECTORED_EXCEPTION_HANDLING)
__try { #endif
result = JNI_CreateJavaVM_inner(vm, penv, args); #ifdefined(_WIN32) && !defined(USE_VECTORED_EXCEPTION_HANDLING)
} __except(topLevelExceptionFilter((_EXCEPTION_POINTERS*)_exception_info())) { // Nothing to do.
} #endif return result;
}
// Check below commented out from JDK1.2fcs as well /* if (args && (args->version != JNI_VERSION_1_1 || args->version != JNI_VERSION_1_2)) { return JNI_EVERSION; }
*/
Thread* t = Thread::current_or_null(); if (t != NULL) { // If executing from an atexit hook we may be in the VMThread. if (t->is_Java_thread()) { // If the thread has been attached this operation is a no-op
*(JNIEnv**)penv = JavaThread::cast(t)->jni_environment(); return JNI_OK;
} else { return JNI_ERR;
}
}
// Create a thread and mark it as attaching so it will be skipped by the // ThreadsListEnumerator - see CR 6404306
JavaThread* thread = new JavaThread(true);
// Set correct safepoint info. The thread is going to call into Java when // initializing the Java level thread object. Hence, the correct state must // be set in order for the Safepoint code to deal with it correctly.
thread->set_thread_state(_thread_in_vm);
thread->record_stack_base_and_size();
thread->register_thread_stack_with_NMT();
thread->initialize_thread_current();
MACOS_AARCH64_ONLY(thread->init_wx());
// This thread will not do a safepoint check, since it has // not been added to the Thread list yet.
{ MutexLocker ml(Threads_lock); // This must be inside this lock in order to get FullGCALot to work properly, i.e., to // avoid this thread trying to do a GC before it is added to the thread-list
thread->set_active_handles(JNIHandleBlock::allocate_block());
Threads::add(thread, daemon);
} // Create thread group and name info from attach arguments
oop group = NULL; char* thread_name = NULL; if (args != NULL && Threads::is_supported_jni_version(args->version)) {
group = JNIHandles::resolve(args->group);
thread_name = args->name; // may be NULL
} if (group == NULL) group = Universe::main_thread_group();
// Create Java level thread object and attach it to this thread bool attach_failed = false;
{
EXCEPTION_MARK;
HandleMark hm(THREAD);
Handle thread_group(THREAD, group);
thread->allocate_threadObj(thread_group, thread_name, daemon, THREAD); if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION; // cleanup outside the handle mark.
attach_failed = true;
}
}
// mark the thread as no longer attaching // this uses a fence to push the change through so we don't have // to regrab the threads_lock
thread->set_done_attaching_via_jni();
// Set java thread status.
java_lang_Thread::set_thread_status(thread->threadObj(),
JavaThreadStatus::RUNNABLE);
// Notify the debugger if (JvmtiExport::should_post_thread_life()) {
JvmtiExport::post_thread_start(thread);
}
post_thread_start_event(thread);
*(JNIEnv**)penv = thread->jni_environment();
// Now leaving the VM, so change thread_state. This is normally automatically taken care // of in the JVM_ENTRY. But in this situation we have to do it manually.
ThreadStateTransition::transition_from_vm(thread, _thread_in_native);
MACOS_AARCH64_ONLY(thread->enable_wx(WXExec));
// Perform any platform dependent FPU setup
os::setup_fpu();
// If the thread has already been detached the operation is a no-op if (current == NULL) {
HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN(JNI_OK); return JNI_OK;
}
// If executing from an atexit hook we may be in the VMThread. if (!current->is_Java_thread()) {
HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN((uint32_t) JNI_ERR); return JNI_ERR;
}
VM_Exit::block_if_vm_exited();
JavaThread* thread = JavaThread::cast(current); if (thread->has_last_Java_frame()) {
HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN((uint32_t) JNI_ERR); // Can't detach a thread that's running java, that can't work. return JNI_ERR;
}
// We are going to VM, change W^X state to the expected one.
MACOS_AARCH64_ONLY(thread->enable_wx(WXWrite));
// Safepoint support. Have to do call-back to safepoint code, if in the // middle of a safepoint operation
ThreadStateTransition::transition_from_native(thread, _thread_in_vm);
// XXX: Note that JavaThread::exit() call below removes the guards on the // stack pages set up via enable_stack_{red,yellow}_zone() calls // above in jni_AttachCurrentThread. Unfortunately, while the setting // of the guards is visible in jni_AttachCurrentThread above, // the removal of the guards is buried below in JavaThread::exit() // here. The abstraction should be more symmetrically either exposed // or hidden (e.g. it could probably be hidden in the same // (platform-dependent) methods where we do alternate stack // maintenance work?)
thread->exit(false, JavaThread::jni_detach);
thread->smr_delete();
// Go to the execute mode, the initial state of the thread on creation. // Use os interface as the thread is not a JavaThread anymore.
MACOS_AARCH64_ONLY(os::current_thread_enable_wx(WXExec));
if (vm_created == 0) {
*penv = NULL;
ret = JNI_EDETACHED; return ret;
}
// No JVM TI with --enable-preview and no continuations support. if (!VMContinuations && Arguments::enable_preview() && JvmtiExport::is_jvmti_version(version)) {
*penv = NULL;
ret = JNI_EVERSION; return ret;
}
if (JniExportedInterface::GetExportedInterface(vm, penv, version, &ret)) { return ret;
}
#ifndef JVMPI_VERSION_1 // need these in order to be polite about older agents #define JVMPI_VERSION_1 ((jint)0x10000001) #define JVMPI_VERSION_1_1 ((jint)0x10000002) #define JVMPI_VERSION_1_2 ((jint)0x10000003) #endif// !JVMPI_VERSION_1
Thread* thread = Thread::current_or_null(); if (thread != NULL && thread->is_Java_thread()) { if (Threads::is_supported_jni_version_including_1_1(version)) {
*(JNIEnv**)penv = JavaThread::cast(thread)->jni_environment();
ret = JNI_OK; return ret;
} elseif (version == JVMPI_VERSION_1 ||
version == JVMPI_VERSION_1_1 ||
version == JVMPI_VERSION_1_2) {
tty->print_cr("ERROR: JVMPI, an experimental interface, is no longer supported.");
tty->print_cr("Please use the supported interface: the JVM Tool Interface (JVM TI).");
ret = JNI_EVERSION; return ret;
} elseif (JvmtiExport::is_jvmdi_version(version)) {
tty->print_cr("FATAL ERROR: JVMDI is no longer supported.");
tty->print_cr("Please use the supported interface: the JVM Tool Interface (JVM TI).");
ret = JNI_EVERSION; return ret;
} else {
*penv = NULL;
ret = JNI_EVERSION; return ret;
}
} else {
*penv = NULL;
ret = JNI_EDETACHED; return ret;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.