Extensions to check JNI.
Ensure critical lock isn't held when returning from a down-call.
Log a warning if the critical lock is held for a significant period of
time.
Refactor JNIEnvExt to be a class rather than a struct.
Test: mma test-art-host
Change-Id: I4d149cb04d3a7308a22b92b196e51e2f1ae17ede
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 7324dff..e88ed68 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -959,7 +959,7 @@
}
if (self != nullptr) {
JNIEnvExt* const env = self->GetJniEnv();
- if (UNLIKELY(env != nullptr && env->runtime_deleted)) {
+ if (UNLIKELY(env != nullptr && env->IsRuntimeDeleted())) {
CHECK(self->IsDaemon());
// If the runtime has been deleted, then we cannot proceed. Just sleep forever. This may
// occur for user daemon threads that get a spurious wakeup. This occurs for test 132 with
diff --git a/runtime/base/time_utils.h b/runtime/base/time_utils.h
index 919937f..7648a10 100644
--- a/runtime/base/time_utils.h
+++ b/runtime/base/time_utils.h
@@ -76,6 +76,15 @@
return ms * 1000 * 1000;
}
+// Converts the given number of milliseconds to microseconds
+static constexpr inline uint64_t MsToUs(uint64_t ms) {
+ return ms * 1000;
+}
+
+static constexpr inline uint64_t UsToNs(uint64_t us) {
+ return us * 1000;
+}
+
#if defined(__APPLE__)
#ifndef CLOCK_REALTIME
// No clocks to specify on OS/X < 10.12, fake value to pass to routines that require a clock.
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 90f478f..ce4cee8 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -28,6 +28,7 @@
#include "art_method-inl.h"
#include "base/macros.h"
#include "base/to_str.h"
+#include "base/time_utils.h"
#include "class_linker-inl.h"
#include "class_linker.h"
#include "dex_file-inl.h"
@@ -55,24 +56,37 @@
* ===========================================================================
*/
+// Warn if a JNI critical is held for longer than 16ms.
+static constexpr uint64_t kCriticalWarnTimeUs = MsToUs(16);
+static_assert(kCriticalWarnTimeUs > 0, "No JNI critical warn time set");
+
// Flags passed into ScopedCheck.
-#define kFlag_Default 0x0000
+static constexpr uint16_t kFlag_Default = 0x0000;
-#define kFlag_CritBad 0x0000 // Calling while in critical is not allowed.
-#define kFlag_CritOkay 0x0001 // Calling while in critical is allowed.
-#define kFlag_CritGet 0x0002 // This is a critical "get".
-#define kFlag_CritRelease 0x0003 // This is a critical "release".
-#define kFlag_CritMask 0x0003 // Bit mask to get "crit" value.
+// Calling while in critical is not allowed.
+static constexpr uint16_t kFlag_CritBad = 0x0000;
+// Calling while in critical is allowed.
+static constexpr uint16_t kFlag_CritOkay = 0x0001;
+// This is a critical "get".
+static constexpr uint16_t kFlag_CritGet = 0x0002;
+// This is a critical "release".
+static constexpr uint16_t kFlag_CritRelease = 0x0003;
+// Bit mask to get "crit" value.
+static constexpr uint16_t kFlag_CritMask = 0x0003;
-#define kFlag_ExcepBad 0x0000 // Raised exceptions are not allowed.
-#define kFlag_ExcepOkay 0x0004 // Raised exceptions are allowed.
+// Raised exceptions are allowed.
+static constexpr uint16_t kFlag_ExcepOkay = 0x0004;
-#define kFlag_Release 0x0010 // Are we in a non-critical release function?
-#define kFlag_NullableUtf 0x0020 // Are our UTF parameters nullable?
+// Are we in a non-critical release function?
+static constexpr uint16_t kFlag_Release = 0x0010;
+// Are our UTF parameters nullable?
+static constexpr uint16_t kFlag_NullableUtf = 0x0020;
-#define kFlag_Invocation 0x8000 // Part of the invocation interface (JavaVM*).
+// Part of the invocation interface (JavaVM*).
+static constexpr uint16_t kFlag_Invocation = 0x0100;
-#define kFlag_ForceTrace 0x80000000 // Add this to a JNI function's flags if you want to trace every call.
+// Add this to a JNI function's flags if you want to trace every call.
+static constexpr uint16_t kFlag_ForceTrace = 0x8000;
class VarArgs;
/*
@@ -249,8 +263,8 @@
class ScopedCheck {
public:
- ScopedCheck(int flags, const char* functionName, bool has_method = true)
- : function_name_(functionName), flags_(flags), indent_(0), has_method_(has_method) {
+ ScopedCheck(uint16_t flags, const char* functionName, bool has_method = true)
+ : function_name_(functionName), indent_(0), flags_(flags), has_method_(has_method) {
}
~ScopedCheck() {}
@@ -1197,7 +1211,7 @@
// this particular instance of JNIEnv.
if (env != threadEnv) {
// Get the thread owning the JNIEnv that's being used.
- Thread* envThread = reinterpret_cast<JNIEnvExt*>(env)->self;
+ Thread* envThread = reinterpret_cast<JNIEnvExt*>(env)->self_;
AbortF("thread %s using JNIEnv* from thread %s",
ToStr<Thread>(*self).c_str(), ToStr<Thread>(*envThread).c_str());
return false;
@@ -1209,7 +1223,7 @@
case kFlag_CritOkay: // okay to call this method
break;
case kFlag_CritBad: // not okay to call
- if (threadEnv->critical) {
+ if (threadEnv->critical_ > 0) {
AbortF("thread %s using JNI after critical get",
ToStr<Thread>(*self).c_str());
return false;
@@ -1217,15 +1231,25 @@
break;
case kFlag_CritGet: // this is a "get" call
// Don't check here; we allow nested gets.
- threadEnv->critical++;
+ if (threadEnv->critical_ == 0) {
+ threadEnv->critical_start_us_ = self->GetCpuMicroTime();
+ }
+ threadEnv->critical_++;
break;
case kFlag_CritRelease: // this is a "release" call
- threadEnv->critical--;
- if (threadEnv->critical < 0) {
+ if (threadEnv->critical_ == 0) {
AbortF("thread %s called too many critical releases",
ToStr<Thread>(*self).c_str());
return false;
+ } else if (threadEnv->critical_ == 1) {
+ // Leaving the critical region, possibly warn about long critical regions.
+ uint64_t critical_duration_us = self->GetCpuMicroTime() - threadEnv->critical_start_us_;
+ if (critical_duration_us > kCriticalWarnTimeUs) {
+ LOG(WARNING) << "JNI critical lock held for "
+ << PrettyDuration(UsToNs(critical_duration_us)) << " on " << *self;
+ }
}
+ threadEnv->critical_--;
break;
default:
LOG(FATAL) << "Bad flags (internal error): " << flags_;
@@ -1357,9 +1381,10 @@
// The name of the JNI function being checked.
const char* const function_name_;
- const int flags_;
int indent_;
+ const uint16_t flags_;
+
const bool has_method_;
DISALLOW_COPY_AND_ASSIGN(ScopedCheck);
@@ -2592,11 +2617,11 @@
private:
static JavaVMExt* GetJavaVMExt(JNIEnv* env) {
- return reinterpret_cast<JNIEnvExt*>(env)->vm;
+ return reinterpret_cast<JNIEnvExt*>(env)->GetVm();
}
static const JNINativeInterface* baseEnv(JNIEnv* env) {
- return reinterpret_cast<JNIEnvExt*>(env)->unchecked_functions;
+ return reinterpret_cast<JNIEnvExt*>(env)->unchecked_functions_;
}
static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 55fa632..ae285c7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3391,7 +3391,7 @@
// Clean up pass to remove null dex caches. Also check if we need to initialize OatFile .bss.
// Null dex caches can occur due to class unloading and we are lazily removing null entries.
bool initialize_oat_file_bss = (oat_file != nullptr);
- JavaVMExt* const vm = self->GetJniEnv()->vm;
+ JavaVMExt* const vm = self->GetJniEnv()->GetVm();
for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) {
DexCacheData data = *it;
if (self->IsJWeakCleared(data.weak_root)) {
@@ -5269,7 +5269,7 @@
CHECK(class_loader->GetClassTable() == nullptr);
Thread* const self = Thread::Current();
ClassLoaderData data;
- data.weak_root = self->GetJniEnv()->vm->AddWeakGlobalRef(self, class_loader);
+ data.weak_root = self->GetJniEnv()->GetVm()->AddWeakGlobalRef(self, class_loader);
// Create and set the class table.
data.class_table = new ClassTable;
class_loader->SetClassTable(data.class_table);
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index b13b6fb..3c41a8c 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -51,8 +51,8 @@
extern uint32_t JniMethodFastStart(Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
DCHECK(env != nullptr);
- uint32_t saved_local_ref_cookie = bit_cast<uint32_t>(env->local_ref_cookie);
- env->local_ref_cookie = env->locals.GetSegmentState();
+ uint32_t saved_local_ref_cookie = bit_cast<uint32_t>(env->GetLocalRefCookie());
+ env->SetLocalRefCookie(env->GetLocalsSegmentState());
if (kIsDebugBuild) {
ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
@@ -66,8 +66,8 @@
extern uint32_t JniMethodStart(Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
DCHECK(env != nullptr);
- uint32_t saved_local_ref_cookie = bit_cast<uint32_t>(env->local_ref_cookie);
- env->local_ref_cookie = env->locals.GetSegmentState();
+ uint32_t saved_local_ref_cookie = bit_cast<uint32_t>(env->GetLocalRefCookie());
+ env->SetLocalRefCookie(env->GetLocalsSegmentState());
ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
// TODO: Introduce special entrypoint for synchronized @FastNative methods?
// Or ban synchronized @FastNative outright to avoid the extra check here?
@@ -115,11 +115,11 @@
static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
JNIEnvExt* env = self->GetJniEnv();
- if (UNLIKELY(env->check_jni)) {
+ if (UNLIKELY(env->IsCheckJniEnabled())) {
env->CheckNoHeldMonitors();
}
- env->locals.SetSegmentState(env->local_ref_cookie);
- env->local_ref_cookie = bit_cast<IRTSegmentState>(saved_local_ref_cookie);
+ env->SetLocalSegmentState(env->GetLocalRefCookie());
+ env->SetLocalRefCookie(bit_cast<IRTSegmentState>(saved_local_ref_cookie));
self->PopHandleScope();
}
@@ -156,7 +156,7 @@
}
PopLocalReferences(saved_local_ref_cookie, self);
// Process result.
- if (UNLIKELY(self->GetJniEnv()->check_jni)) {
+ if (UNLIKELY(self->GetJniEnv()->IsCheckJniEnabled())) {
// CheckReferenceResult can resolve types.
StackHandleScope<1> hs(self);
HandleWrapperObjPtr<mirror::Object> h_obj(hs.NewHandleWrapper(&o));
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index f7be4c8..3ba12ca 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1299,7 +1299,7 @@
explicit TrimIndirectReferenceTableClosure(Barrier* barrier) : barrier_(barrier) {
}
virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
- thread->GetJniEnv()->locals.Trim();
+ thread->GetJniEnv()->TrimLocals();
// If thread is a running mutator, then act on behalf of the trim thread.
// See the code in ThreadList::RunCheckpoint.
barrier_->Pass(Thread::Current());
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index d58d09c..c59642f 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -273,7 +273,7 @@
jobject cleared_references;
{
ReaderMutexLock mu(self, *Locks::mutator_lock_);
- cleared_references = self->GetJniEnv()->vm->AddGlobalRef(
+ cleared_references = self->GetJniEnv()->GetVm()->AddGlobalRef(
self, cleared_references_.GetList());
}
if (kAsyncReferenceQueueAdd) {
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 2c8ec47..5187831 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -357,7 +357,7 @@
if (self->HandleScopeContains(reinterpret_cast<jobject>(iref))) {
auto* env = self->GetJniEnv();
DCHECK(env != nullptr);
- if (env->check_jni) {
+ if (env->IsCheckJniEnabled()) {
ScopedObjectAccess soa(self);
LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread";
if (kDumpStackOnNonLocalReference) {
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index f8b82ed..104fb66 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -337,7 +337,7 @@
} else {
VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]: Calling...";
JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym);
- jni_on_unload(self->GetJniEnv()->vm, nullptr);
+ jni_on_unload(self->GetJniEnv()->GetVm(), nullptr);
}
delete library;
}
diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni_env_ext-inl.h
index d66df08..14f708b 100644
--- a/runtime/jni_env_ext-inl.h
+++ b/runtime/jni_env_ext-inl.h
@@ -26,7 +26,7 @@
template<typename T>
inline T JNIEnvExt::AddLocalReference(ObjPtr<mirror::Object> obj) {
std::string error_msg;
- IndirectRef ref = locals.Add(local_ref_cookie, obj, &error_msg);
+ IndirectRef ref = locals_.Add(local_ref_cookie_, obj, &error_msg);
if (UNLIKELY(ref == nullptr)) {
// This is really unexpected if we allow resizing local IRTs...
LOG(FATAL) << error_msg;
@@ -35,10 +35,10 @@
// TODO: fix this to understand PushLocalFrame, so we can turn it on.
if (false) {
- if (check_jni) {
- size_t entry_count = locals.Capacity();
+ if (check_jni_) {
+ size_t entry_count = locals_.Capacity();
if (entry_count > 16) {
- locals.Dump(LOG_STREAM(WARNING) << "Warning: more than 16 JNI local references: "
+ locals_.Dump(LOG_STREAM(WARNING) << "Warning: more than 16 JNI local references: "
<< entry_count << " (most recent was a "
<< mirror::Object::PrettyTypeOf(obj) << ")\n");
// TODO: LOG(FATAL) in a later release?
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 8352657..efe43ee 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -21,6 +21,7 @@
#include "android-base/stringprintf.h"
+#include "base/to_str.h"
#include "check_jni.h"
#include "indirect_reference_table.h"
#include "java_vm_ext.h"
@@ -28,6 +29,7 @@
#include "lock_word.h"
#include "mirror/object-inl.h"
#include "nth_caller_visitor.h"
+#include "scoped_thread_state_change.h"
#include "thread-current-inl.h"
#include "thread_list.h"
@@ -40,14 +42,11 @@
const JNINativeInterface* JNIEnvExt::table_override_ = nullptr;
-// Checking "locals" requires the mutator lock, but at creation time we're really only interested
-// in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged
-// with NO_THREAD_SAFETY_ANALYSIS.
-static bool CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS {
+bool JNIEnvExt::CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS {
if (in == nullptr) {
return false;
}
- return in->locals.IsValid();
+ return in->locals_.IsValid();
}
jint JNIEnvExt::GetEnvHandler(JavaVMExt* vm, /*out*/void** env, jint version) {
@@ -73,23 +72,23 @@
}
JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg)
- : self(self_in),
- vm(vm_in),
- local_ref_cookie(kIRTFirstSegment),
- locals(kLocalsInitial, kLocal, IndirectReferenceTable::ResizableCapacity::kYes, error_msg),
- check_jni(false),
- runtime_deleted(false),
- critical(0),
- monitors("monitors", kMonitorsInitial, kMonitorsMax) {
+ : self_(self_in),
+ vm_(vm_in),
+ local_ref_cookie_(kIRTFirstSegment),
+ locals_(kLocalsInitial, kLocal, IndirectReferenceTable::ResizableCapacity::kYes, error_msg),
+ monitors_("monitors", kMonitorsInitial, kMonitorsMax),
+ critical_(0),
+ check_jni_(false),
+ runtime_deleted_(false) {
MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
- check_jni = vm->IsCheckJniEnabled();
- functions = GetFunctionTable(check_jni);
- unchecked_functions = GetJniNativeInterface();
+ check_jni_ = vm_in->IsCheckJniEnabled();
+ functions = GetFunctionTable(check_jni_);
+ unchecked_functions_ = GetJniNativeInterface();
}
void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() {
functions = GetRuntimeShutdownNativeInterface();
- runtime_deleted = true;
+ runtime_deleted_ = true;
}
JNIEnvExt::~JNIEnvExt() {
@@ -100,7 +99,7 @@
return nullptr;
}
std::string error_msg;
- jobject ref = reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj, &error_msg));
+ jobject ref = reinterpret_cast<jobject>(locals_.Add(local_ref_cookie_, obj, &error_msg));
if (UNLIKELY(ref == nullptr)) {
// This is really unexpected if we allow resizing local IRTs...
LOG(FATAL) << error_msg;
@@ -111,12 +110,12 @@
void JNIEnvExt::DeleteLocalRef(jobject obj) {
if (obj != nullptr) {
- locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
+ locals_.Remove(local_ref_cookie_, reinterpret_cast<IndirectRef>(obj));
}
}
void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
- check_jni = enabled;
+ check_jni_ = enabled;
MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
functions = GetFunctionTable(enabled);
// Check whether this is a no-op because of override.
@@ -126,20 +125,20 @@
}
void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
- locals.Dump(os);
- monitors.Dump(os);
+ locals_.Dump(os);
+ monitors_.Dump(os);
}
void JNIEnvExt::PushFrame(int capacity) {
- DCHECK_GE(locals.FreeCapacity(), static_cast<size_t>(capacity));
- stacked_local_ref_cookies.push_back(local_ref_cookie);
- local_ref_cookie = locals.GetSegmentState();
+ DCHECK_GE(locals_.FreeCapacity(), static_cast<size_t>(capacity));
+ stacked_local_ref_cookies_.push_back(local_ref_cookie_);
+ local_ref_cookie_ = locals_.GetSegmentState();
}
void JNIEnvExt::PopFrame() {
- locals.SetSegmentState(local_ref_cookie);
- local_ref_cookie = stacked_local_ref_cookies.back();
- stacked_local_ref_cookies.pop_back();
+ locals_.SetSegmentState(local_ref_cookie_);
+ local_ref_cookie_ = stacked_local_ref_cookies_.back();
+ stacked_local_ref_cookies_.pop_back();
}
// Note: the offset code is brittle, as we can't use OFFSETOF_MEMBER or offsetof easily. Thus, there
@@ -188,7 +187,7 @@
}
void JNIEnvExt::RecordMonitorEnter(jobject obj) {
- locked_objects_.push_back(std::make_pair(GetJavaCallFrame(self), obj));
+ locked_objects_.push_back(std::make_pair(GetJavaCallFrame(self_), obj));
}
static std::string ComputeMonitorDescription(Thread* self,
@@ -230,7 +229,7 @@
}
void JNIEnvExt::CheckMonitorRelease(jobject obj) {
- uintptr_t current_frame = GetJavaCallFrame(self);
+ uintptr_t current_frame = GetJavaCallFrame(self_);
std::pair<uintptr_t, jobject> exact_pair = std::make_pair(current_frame, obj);
auto it = std::find(locked_objects_.begin(), locked_objects_.end(), exact_pair);
bool will_abort = false;
@@ -238,11 +237,11 @@
locked_objects_.erase(it);
} else {
// Check whether this monitor was locked in another JNI "session."
- ObjPtr<mirror::Object> mirror_obj = self->DecodeJObject(obj);
+ ObjPtr<mirror::Object> mirror_obj = self_->DecodeJObject(obj);
for (std::pair<uintptr_t, jobject>& pair : locked_objects_) {
- if (self->DecodeJObject(pair.second) == mirror_obj) {
- std::string monitor_descr = ComputeMonitorDescription(self, pair.second);
- vm->JniAbortF("<JNI MonitorExit>",
+ if (self_->DecodeJObject(pair.second) == mirror_obj) {
+ std::string monitor_descr = ComputeMonitorDescription(self_, pair.second);
+ vm_->JniAbortF("<JNI MonitorExit>",
"Unlocking monitor that wasn't locked here: %s",
monitor_descr.c_str());
will_abort = true;
@@ -255,26 +254,26 @@
// the monitors table, otherwise we may visit local objects in GC during abort (which won't be
// valid anymore).
if (will_abort) {
- RemoveMonitors(self, current_frame, &monitors, &locked_objects_);
+ RemoveMonitors(self_, current_frame, &monitors_, &locked_objects_);
}
}
void JNIEnvExt::CheckNoHeldMonitors() {
- uintptr_t current_frame = GetJavaCallFrame(self);
// The locked_objects_ are grouped by their stack frame component, as this enforces structured
// locking, and the groups form a stack. So the current frame entries are at the end. Check
// whether the vector is empty, and when there are elements, whether the last element belongs
// to this call - this signals that there are unlocked monitors.
if (!locked_objects_.empty()) {
+ uintptr_t current_frame = GetJavaCallFrame(self_);
std::pair<uintptr_t, jobject>& pair = locked_objects_[locked_objects_.size() - 1];
if (pair.first == current_frame) {
- std::string monitor_descr = ComputeMonitorDescription(self, pair.second);
- vm->JniAbortF("<JNI End>",
+ std::string monitor_descr = ComputeMonitorDescription(self_, pair.second);
+ vm_->JniAbortF("<JNI End>",
"Still holding a locked object on JNI end: %s",
monitor_descr.c_str());
// When we abort, also make sure that any locks from the current "session" are removed from
// the monitors table, otherwise we may visit local objects in GC during abort.
- RemoveMonitors(self, current_frame, &monitors, &locked_objects_);
+ RemoveMonitors(self_, current_frame, &monitors_, &locked_objects_);
} else if (kIsDebugBuild) {
// Make sure there are really no other entries and our checking worked as expected.
for (std::pair<uintptr_t, jobject>& check_pair : locked_objects_) {
@@ -282,12 +281,18 @@
}
}
}
+ // Ensure critical locks aren't held when returning to Java.
+ if (critical_ > 0) {
+ vm_->JniAbortF("<JNI End>",
+ "Critical lock held when returning to Java on thread %s",
+ ToStr<Thread>(*self_).c_str());
+ }
}
static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED)
REQUIRES(Locks::jni_function_table_lock_) {
JNIEnvExt* env = thread->GetJniEnv();
- bool check_jni = env->check_jni;
+ bool check_jni = env->IsCheckJniEnabled();
env->functions = JNIEnvExt::GetFunctionTable(check_jni);
}
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 2f6c5dc..0e8fd03 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -37,10 +37,15 @@
// low enough that it forces sanity checks.
static constexpr size_t kLocalsInitial = 512;
-struct JNIEnvExt : public JNIEnv {
+class JNIEnvExt : public JNIEnv {
+ public:
// Creates a new JNIEnvExt. Returns null on error, in which case error_msg
// will contain a description of the error.
static JNIEnvExt* Create(Thread* self, JavaVMExt* vm, std::string* error_msg);
+ static Offset SegmentStateOffset(size_t pointer_size);
+ static Offset LocalRefCookieOffset(size_t pointer_size);
+ static Offset SelfOffset(size_t pointer_size);
+ static jint GetEnvHandler(JavaVMExt* vm, /*out*/void** out, jint version);
~JNIEnvExt();
@@ -58,43 +63,44 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::alloc_tracker_lock_);
- static Offset SegmentStateOffset(size_t pointer_size);
- static Offset LocalRefCookieOffset(size_t pointer_size);
- static Offset SelfOffset(size_t pointer_size);
-
- static jint GetEnvHandler(JavaVMExt* vm, /*out*/void** out, jint version);
+ void UpdateLocal(IndirectRef iref, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ locals_.Update(iref, obj);
+ }
jobject NewLocalRef(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
void DeleteLocalRef(jobject obj) REQUIRES_SHARED(Locks::mutator_lock_);
- Thread* const self;
- JavaVMExt* const vm;
+ void TrimLocals() REQUIRES_SHARED(Locks::mutator_lock_) {
+ locals_.Trim();
+ }
+ void AssertLocalsEmpty() REQUIRES_SHARED(Locks::mutator_lock_) {
+ locals_.AssertEmpty();
+ }
+ size_t GetLocalsCapacity() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return locals_.Capacity();
+ }
- // Cookie used when using the local indirect reference table.
- IRTSegmentState local_ref_cookie;
+ IRTSegmentState GetLocalRefCookie() const { return local_ref_cookie_; }
+ void SetLocalRefCookie(IRTSegmentState new_cookie) { local_ref_cookie_ = new_cookie; }
- // JNI local references.
- IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_);
+ IRTSegmentState GetLocalsSegmentState() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return locals_.GetSegmentState();
+ }
+ void SetLocalSegmentState(IRTSegmentState new_state) REQUIRES_SHARED(Locks::mutator_lock_) {
+ locals_.SetSegmentState(new_state);
+ }
- // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
- // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
- // to a native method.
- std::vector<IRTSegmentState> stacked_local_ref_cookies;
+ void VisitJniLocalRoots(RootVisitor* visitor, const RootInfo& root_info)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ locals_.VisitRoots(visitor, root_info);
+ }
- // Frequently-accessed fields cached from JavaVM.
- bool check_jni;
+ Thread* GetSelf() const { return self_; }
+ JavaVMExt* GetVm() const { return vm_; }
- // If we are a JNI env for a daemon thread with a deleted runtime.
- bool runtime_deleted;
+ bool IsRuntimeDeleted() const { return runtime_deleted_; }
+ bool IsCheckJniEnabled() const { return check_jni_; }
- // How many nested "critical" JNI calls are we in?
- int critical;
-
- // Entered JNI monitors, for bulk exit on thread detach.
- ReferenceTable monitors;
-
- // Used by -Xcheck:jni.
- const JNINativeInterface* unchecked_functions;
// Functions to keep track of monitor lock and unlock operations. Used to ensure proper locking
// rules in CheckJNI mode.
@@ -108,6 +114,11 @@
// Check that no monitors are held that have been acquired in this JNI "segment."
void CheckNoHeldMonitors() REQUIRES_SHARED(Locks::mutator_lock_);
+ void VisitMonitorRoots(RootVisitor* visitor, const RootInfo& root_info)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ monitors_.VisitRoots(visitor, root_info);
+ }
+
// Set the functions to the runtime shutdown functions.
void SetFunctionsToRuntimeShutdownFunctions();
@@ -124,6 +135,11 @@
REQUIRES(Locks::jni_function_table_lock_);
private:
+ // Checking "locals" requires the mutator lock, but at creation time we're
+ // really only interested in validity, which isn't changing. To avoid grabbing
+ // the mutator lock, factored out and tagged with NO_THREAD_SAFETY_ANALYSIS.
+ static bool CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS;
+
// Override of function tables. This applies to both default as well as instrumented (CheckJNI)
// function tables.
static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_);
@@ -133,29 +149,73 @@
JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg)
REQUIRES(!Locks::jni_function_table_lock_);
+ // Link to Thread::Current().
+ Thread* const self_;
+
+ // The invocation interface JavaVM.
+ JavaVMExt* const vm_;
+
+ // Cookie used when using the local indirect reference table.
+ IRTSegmentState local_ref_cookie_;
+
+ // JNI local references.
+ IndirectReferenceTable locals_ GUARDED_BY(Locks::mutator_lock_);
+
+ // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
+ // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
+ // to a native method.
+ std::vector<IRTSegmentState> stacked_local_ref_cookies_;
+
+ // Entered JNI monitors, for bulk exit on thread detach.
+ ReferenceTable monitors_;
+
+ // Used by -Xcheck:jni.
+ const JNINativeInterface* unchecked_functions_;
+
// All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI
// to ensure that only monitors locked in this native frame are being unlocked, and that at
// the end all are unlocked.
std::vector<std::pair<uintptr_t, jobject>> locked_objects_;
+
+ // Start time of "critical" JNI calls to ensure that their use doesn't
+ // excessively block the VM with CheckJNI.
+ uint64_t critical_start_us_;
+
+ // How many nested "critical" JNI calls are we in? Used by CheckJNI to ensure that criticals are
+ uint32_t critical_;
+
+ // Frequently-accessed fields cached from JavaVM.
+ bool check_jni_;
+
+ // If we are a JNI env for a daemon thread with a deleted runtime.
+ bool runtime_deleted_;
+
+ friend class CheckJNI;
+ friend class JNI;
+ friend class ScopedCheck;
+ friend class ScopedJniEnvLocalRefState;
+ friend class Thread;
+ ART_FRIEND_TEST(JniInternalTest, JNIEnvExtOffsets);
};
// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
// compiler.
class ScopedJniEnvLocalRefState {
public:
- explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) {
- saved_local_ref_cookie_ = env->local_ref_cookie;
- env->local_ref_cookie = env->locals.GetSegmentState();
+ explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) :
+ env_(env),
+ saved_local_ref_cookie_(env->local_ref_cookie_) {
+ env->local_ref_cookie_ = env->locals_.GetSegmentState();
}
~ScopedJniEnvLocalRefState() {
- env_->locals.SetSegmentState(env_->local_ref_cookie);
- env_->local_ref_cookie = saved_local_ref_cookie_;
+ env_->locals_.SetSegmentState(env_->local_ref_cookie_);
+ env_->local_ref_cookie_ = saved_local_ref_cookie_;
}
private:
JNIEnvExt* const env_;
- IRTSegmentState saved_local_ref_cookie_;
+ const IRTSegmentState saved_local_ref_cookie_;
DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
};
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 48fc5f7..53ae628 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -383,7 +383,7 @@
}
static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) {
- return reinterpret_cast<JNIEnvExt*>(env)->vm;
+ return reinterpret_cast<JNIEnvExt*>(env)->GetVm();
}
#define CHECK_NON_NULL_ARGUMENT(value) \
@@ -545,7 +545,7 @@
}
static jboolean ExceptionCheck(JNIEnv* env) {
- return static_cast<JNIEnvExt*>(env)->self->IsExceptionPending() ? JNI_TRUE : JNI_FALSE;
+ return static_cast<JNIEnvExt*>(env)->self_->IsExceptionPending() ? JNI_TRUE : JNI_FALSE;
}
static void ExceptionClear(JNIEnv* env) {
@@ -623,8 +623,8 @@
}
static void DeleteGlobalRef(JNIEnv* env, jobject obj) {
- JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->vm;
- Thread* self = down_cast<JNIEnvExt*>(env)->self;
+ JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->GetVm();
+ Thread* self = down_cast<JNIEnvExt*>(env)->self_;
vm->DeleteGlobalRef(self, obj);
}
@@ -635,8 +635,8 @@
}
static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
- JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->vm;
- Thread* self = down_cast<JNIEnvExt*>(env)->self;
+ JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->GetVm();
+ Thread* self = down_cast<JNIEnvExt*>(env)->self_;
vm->DeleteWeakGlobalRef(self, obj);
}
@@ -659,7 +659,7 @@
// it. b/22119403
ScopedObjectAccess soa(env);
auto* ext_env = down_cast<JNIEnvExt*>(env);
- if (!ext_env->locals.Remove(ext_env->local_ref_cookie, obj)) {
+ if (!ext_env->locals_.Remove(ext_env->local_ref_cookie_, obj)) {
// Attempting to delete a local reference that is not in the
// topmost local reference frame is a no-op. DeleteLocalRef returns
// void and doesn't throw any exceptions, but we should probably
@@ -2310,7 +2310,7 @@
// first, either as a direct or a virtual method. Then move to
// the parent.
ArtMethod* m = nullptr;
- bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled();
+ bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled();
for (ObjPtr<mirror::Class> current_class = c.Get();
current_class != nullptr;
current_class = current_class->GetSuperClass()) {
@@ -2399,7 +2399,7 @@
ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
o = o->MonitorEnter(soa.Self());
if (soa.Self()->HoldsLock(o)) {
- soa.Env()->monitors.Add(o);
+ soa.Env()->monitors_.Add(o);
}
if (soa.Self()->IsExceptionPending()) {
return JNI_ERR;
@@ -2414,7 +2414,7 @@
bool remove_mon = soa.Self()->HoldsLock(o);
o->MonitorExit(soa.Self());
if (remove_mon) {
- soa.Env()->monitors.Remove(o);
+ soa.Env()->monitors_.Remove(o);
}
if (soa.Self()->IsExceptionPending()) {
return JNI_ERR;
@@ -2458,7 +2458,7 @@
jobject result = env->NewObject(WellKnownClasses::java_nio_DirectByteBuffer,
WellKnownClasses::java_nio_DirectByteBuffer_init,
address_arg, capacity_arg);
- return static_cast<JNIEnvExt*>(env)->self->IsExceptionPending() ? nullptr : result;
+ return static_cast<JNIEnvExt*>(env)->self_->IsExceptionPending() ? nullptr : result;
}
static void* GetDirectBufferAddress(JNIEnv* env, jobject java_buffer) {
@@ -2504,7 +2504,7 @@
}
std::string error_msg;
- if (!soa.Env()->locals.EnsureFreeCapacity(static_cast<size_t>(desired_capacity), &error_msg)) {
+ if (!soa.Env()->locals_.EnsureFreeCapacity(static_cast<size_t>(desired_capacity), &error_msg)) {
std::string caller_error = android::base::StringPrintf("%s: %s", caller, error_msg.c_str());
soa.Self()->ThrowOutOfMemoryError(caller_error.c_str());
return JNI_ERR;
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index efeff0a..ad24c94 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -867,7 +867,7 @@
static size_t GetLocalsCapacity(JNIEnv* env) {
ScopedObjectAccess soa(Thread::Current());
- return reinterpret_cast<JNIEnvExt*>(env)->locals.Capacity();
+ return reinterpret_cast<JNIEnvExt*>(env)->GetLocalsCapacity();
}
TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) {
@@ -2400,15 +2400,15 @@
// Test the offset computation of JNIEnvExt offsets. b/26071368.
TEST_F(JniInternalTest, JNIEnvExtOffsets) {
- EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie),
+ EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie_),
JNIEnvExt::LocalRefCookieOffset(sizeof(void*)).Uint32Value());
- EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self), JNIEnvExt::SelfOffset(sizeof(void*)).Uint32Value());
+ EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self_), JNIEnvExt::SelfOffset(sizeof(void*)).Uint32Value());
// segment_state_ is private in the IndirectReferenceTable. So this test isn't as good as we'd
// hope it to be.
uint32_t segment_state_now =
- OFFSETOF_MEMBER(JNIEnvExt, locals) +
+ OFFSETOF_MEMBER(JNIEnvExt, locals_) +
IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Uint32Value();
uint32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Uint32Value();
EXPECT_EQ(segment_state_now, segment_state_computed);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 1b5c535..6446e1b 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -223,7 +223,7 @@
}
static jboolean VMRuntime_isCheckJniEnabled(JNIEnv* env, jobject) {
- return down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE;
+ return down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE;
}
static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) {
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index a717264..9a52f70 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -37,7 +37,7 @@
}
static jboolean Thread_interrupted(JNIEnv* env, jclass) {
- return static_cast<JNIEnvExt*>(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE;
+ return static_cast<JNIEnvExt*>(env)->GetSelf()->Interrupted() ? JNI_TRUE : JNI_FALSE;
}
static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 7b73382..836637f 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -32,6 +32,10 @@
namespace art {
+static Thread* GetSelf(JNIEnv* env) {
+ return static_cast<JNIEnvExt*>(env)->GetSelf();
+}
+
static void DdmVmInternal_enableRecentAllocations(JNIEnv*, jclass, jboolean enable) {
Dbg::SetAllocTrackingEnabled(enable);
}
@@ -51,7 +55,7 @@
*/
static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) {
jobjectArray trace = nullptr;
- Thread* const self = Thread::Current();
+ Thread* const self = GetSelf(env);
if (static_cast<uint32_t>(thin_lock_id) == self->GetThreadId()) {
// No need to suspend ourself to build stacktrace.
ScopedObjectAccess soa(env);
@@ -136,7 +140,7 @@
static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) {
std::vector<uint8_t> bytes;
- Thread* self = static_cast<JNIEnvExt*>(env)->self;
+ Thread* self = GetSelf(env);
{
MutexLock mu(self, *Locks::thread_list_lock_);
ThreadList* thread_list = Runtime::Current()->GetThreadList();
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 9683ced..cacbe87 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -447,7 +447,7 @@
const char* shorty)
REQUIRES_SHARED(Locks::mutator_lock_) {
uint32_t* args = arg_array->GetArray();
- if (UNLIKELY(soa.Env()->check_jni)) {
+ if (UNLIKELY(soa.Env()->IsCheckJniEnabled())) {
CheckMethodArguments(soa.Vm(), method->GetInterfaceMethodIfProxy(kRuntimePointerSize), args);
}
method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
@@ -927,14 +927,14 @@
IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref);
if (kind == kLocal) {
- self->GetJniEnv()->locals.Update(obj, result);
+ self->GetJniEnv()->UpdateLocal(obj, result);
} else if (kind == kHandleScopeOrInvalid) {
LOG(FATAL) << "Unsupported UpdateReference for kind kHandleScopeOrInvalid";
} else if (kind == kGlobal) {
- self->GetJniEnv()->vm->UpdateGlobal(self, ref, result);
+ self->GetJniEnv()->GetVm()->UpdateGlobal(self, ref, result);
} else {
DCHECK_EQ(kind, kWeakGlobal);
- self->GetJniEnv()->vm->UpdateWeakGlobal(self, ref, result);
+ self->GetJniEnv()->GetVm()->UpdateWeakGlobal(self, ref, result);
}
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a172392..54aa9e5 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -807,7 +807,7 @@
{
ScopedObjectAccess soa(self);
- self->GetJniEnv()->locals.AssertEmpty();
+ self->GetJniEnv()->AssertLocalsEmpty();
}
VLOG(startup) << "Runtime::Start exiting";
diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h
index a9702a7..f955932 100644
--- a/runtime/scoped_thread_state_change-inl.h
+++ b/runtime/scoped_thread_state_change-inl.h
@@ -97,12 +97,12 @@
}
inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
- : self_(ThreadForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {}
+ : self_(ThreadForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->GetVm()) {}
inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(Thread* self)
: self_(self),
env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
- vm_(env_ != nullptr ? env_->vm : nullptr) {}
+ vm_(env_ != nullptr ? env_->GetVm() : nullptr) {}
inline ScopedObjectAccessUnchecked::ScopedObjectAccessUnchecked(JNIEnv* env)
: ScopedObjectAccessAlreadyRunnable(env), tsc_(Self(), kRunnable) {
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 02b6124..0c42c5a 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -27,7 +27,7 @@
namespace art {
class JavaVMExt;
-struct JNIEnvExt;
+class JNIEnvExt;
template<class MirrorType> class ObjPtr;
class Thread;
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 62b0789..7392dcf 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -34,7 +34,7 @@
// Quickly access the current thread from a JNIEnv.
static inline Thread* ThreadForEnv(JNIEnv* env) {
JNIEnvExt* full_env(down_cast<JNIEnvExt*>(env));
- return full_env->self;
+ return full_env->GetSelf();
}
inline void Thread::AllowThreadSuspension() {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index b86e56b..5777d14 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -621,7 +621,7 @@
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != nullptr);
- Thread* self = static_cast<JNIEnvExt*>(env)->self;
+ Thread* self = static_cast<JNIEnvExt*>(env)->GetSelf();
if (VLOG_IS_ON(threads)) {
ScopedObjectAccess soa(env);
@@ -754,8 +754,8 @@
tls32_.thin_lock_thread_id = thread_list->AllocThreadId(this);
if (jni_env_ext != nullptr) {
- DCHECK_EQ(jni_env_ext->vm, java_vm);
- DCHECK_EQ(jni_env_ext->self, this);
+ DCHECK_EQ(jni_env_ext->GetVm(), java_vm);
+ DCHECK_EQ(jni_env_ext->GetSelf(), this);
tlsPtr_.jni_env = jni_env_ext;
} else {
std::string error_msg;
@@ -853,7 +853,7 @@
if (thread_name != nullptr) {
self->tlsPtr_.name->assign(thread_name);
::art::SetThreadName(thread_name);
- } else if (self->GetJniEnv()->check_jni) {
+ } else if (self->GetJniEnv()->IsCheckJniEnabled()) {
LOG(WARNING) << *Thread::Current() << " attached without supplying a name";
}
}
@@ -2170,7 +2170,7 @@
ScopedObjectAccess soa(self);
MonitorExitVisitor visitor(self);
// On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
- tlsPtr_.jni_env->monitors.VisitRoots(&visitor, RootInfo(kRootVMInternal));
+ tlsPtr_.jni_env->monitors_.VisitRoots(&visitor, RootInfo(kRootVMInternal));
}
// Release locally held global references which releasing may require the mutator lock.
if (tlsPtr_.jpeer != nullptr) {
@@ -2339,7 +2339,7 @@
bool expect_null = false;
// The "kinds" below are sorted by the frequency we expect to encounter them.
if (kind == kLocal) {
- IndirectReferenceTable& locals = tlsPtr_.jni_env->locals;
+ IndirectReferenceTable& locals = tlsPtr_.jni_env->locals_;
// Local references do not need a read barrier.
result = locals.Get<kWithoutReadBarrier>(ref);
} else if (kind == kHandleScopeOrInvalid) {
@@ -2350,15 +2350,15 @@
result = reinterpret_cast<StackReference<mirror::Object>*>(obj)->AsMirrorPtr();
VerifyObject(result);
} else {
- tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj);
+ tlsPtr_.jni_env->vm_->JniAbortF(nullptr, "use of invalid jobject %p", obj);
expect_null = true;
result = nullptr;
}
} else if (kind == kGlobal) {
- result = tlsPtr_.jni_env->vm->DecodeGlobal(ref);
+ result = tlsPtr_.jni_env->vm_->DecodeGlobal(ref);
} else {
DCHECK_EQ(kind, kWeakGlobal);
- result = tlsPtr_.jni_env->vm->DecodeWeakGlobal(const_cast<Thread*>(this), ref);
+ result = tlsPtr_.jni_env->vm_->DecodeWeakGlobal(const_cast<Thread*>(this), ref);
if (Runtime::Current()->IsClearedJniWeakGlobal(result)) {
// This is a special case where it's okay to return null.
expect_null = true;
@@ -2367,7 +2367,7 @@
}
if (UNLIKELY(!expect_null && result == nullptr)) {
- tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of deleted %s %p",
+ tlsPtr_.jni_env->vm_->JniAbortF(nullptr, "use of deleted %s %p",
ToStr<IndirectRefKind>(kind).c_str(), obj);
}
return result;
@@ -2378,7 +2378,7 @@
IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref);
CHECK_EQ(kind, kWeakGlobal);
- return tlsPtr_.jni_env->vm->IsWeakGlobalCleared(const_cast<Thread*>(this), ref);
+ return tlsPtr_.jni_env->vm_->IsWeakGlobalCleared(const_cast<Thread*>(this), ref);
}
// Implements java.lang.Thread.interrupted.
@@ -3516,8 +3516,8 @@
RootInfo(kRootNativeStack, thread_id));
}
visitor->VisitRootIfNonNull(&tlsPtr_.monitor_enter_object, RootInfo(kRootNativeStack, thread_id));
- tlsPtr_.jni_env->locals.VisitRoots(visitor, RootInfo(kRootJNILocal, thread_id));
- tlsPtr_.jni_env->monitors.VisitRoots(visitor, RootInfo(kRootJNIMonitor, thread_id));
+ tlsPtr_.jni_env->VisitJniLocalRoots(visitor, RootInfo(kRootJNILocal, thread_id));
+ tlsPtr_.jni_env->VisitMonitorRoots(visitor, RootInfo(kRootJNIMonitor, thread_id));
HandleScopeVisitRoots(visitor, thread_id);
if (tlsPtr_.debug_invoke_req != nullptr) {
tlsPtr_.debug_invoke_req->VisitRoots(visitor, RootInfo(kRootDebugger, thread_id));
diff --git a/runtime/thread.h b/runtime/thread.h
index 0803975..0f1d7a4 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -86,7 +86,7 @@
class DexFile;
class FrameIdToShadowFrame;
class JavaVMExt;
-struct JNIEnvExt;
+class JNIEnvExt;
class Monitor;
class RootVisitor;
class ScopedObjectAccessAlreadyRunnable;