summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2022-11-18 10:55:05 +0000
committer VladimĂ­r Marko <vmarko@google.com> 2022-11-21 14:25:03 +0000
commitf7bd1fa21250302ef885ffef3b1e096d53b0845c (patch)
treecf2deeb39a4f19550c9190f958171dadd932b950
parentf356dcd48faaab0bb78fe085b9d429ff4cdd6d59 (diff)
Change well known thread methods to `ArtMethod*`.
Also rename `java_lang_ThreadGroup_removeThread` to `java_lang_ThreadGroup_threadTerminated` in line with the actual method it references. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: Iaf2644d006f44759a47587c82db2dcdba0bfa67e
-rw-r--r--adbconnection/adbconnection.cc40
-rw-r--r--openjdkjvmti/ti_redefine.cc2
-rw-r--r--openjdkjvmti/ti_stack.cc5
-rw-r--r--openjdkjvmti/ti_thread.cc72
-rw-r--r--openjdkjvmti/ti_threadgroup.cc30
-rw-r--r--runtime/art_method-inl.h42
-rw-r--r--runtime/art_method.h21
-rw-r--r--runtime/class_linker.cc3
-rw-r--r--runtime/hidden_api.cc4
-rw-r--r--runtime/interpreter/unstarted_runtime.cc10
-rw-r--r--runtime/jni/jni_internal.cc5
-rw-r--r--runtime/native/jdk_internal_misc_Unsafe.cc8
-rw-r--r--runtime/native/sun_misc_Unsafe.cc8
-rw-r--r--runtime/runtime.cc22
-rw-r--r--runtime/runtime_callbacks_test.cc56
-rw-r--r--runtime/thread.cc187
-rw-r--r--runtime/thread.h20
-rw-r--r--runtime/well_known_classes.cc85
-rw-r--r--runtime/well_known_classes.h12
-rw-r--r--test/913-heaps/expected-stdout.txt8
20 files changed, 361 insertions, 279 deletions
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index de6f6fcff7..e1980bd604 100644
--- a/adbconnection/adbconnection.cc
+++ b/adbconnection/adbconnection.cc
@@ -33,6 +33,7 @@
#include "debugger.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_env_ext.h"
+#include "mirror/class-alloc-inl.h"
#include "mirror/throwable.h"
#include "nativehelper/scoped_local_ref.h"
#include "runtime-inl.h"
@@ -181,24 +182,33 @@ AdbConnectionState::~AdbConnectionState() {
}
}
-static jobject CreateAdbConnectionThread(art::ScopedObjectAccess& soa)
+static art::ObjPtr<art::mirror::Object> CreateAdbConnectionThread(art::Thread* self)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
- JNIEnv* env = soa.Self()->GetJniEnv();
- ScopedLocalRef<jstring> thr_name(env, env->NewStringUTF(kAdbConnectionThreadName));
+ art::StackHandleScope<2u> hs(self);
+ art::Handle<art::mirror::String> thr_name =
+ hs.NewHandle(art::mirror::String::AllocFromModifiedUtf8(self, kAdbConnectionThreadName));
+ if (thr_name == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ art::ObjPtr<art::mirror::Class> thread_class =
+ art::WellKnownClasses::java_lang_Thread_init->GetDeclaringClass();
+ art::Handle<art::mirror::Object> thread = hs.NewHandle(thread_class->AllocObject(self));
+ if (thread == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
art::ArtField* system_thread_group_field =
art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+ DCHECK(system_thread_group_field->GetDeclaringClass()->IsInitialized());
// Avoid using `ArtField::GetObject` as it requires linking against `libdexfile` for
// `operator<<(std::ostream&, Primitive::Type)`.
art::ObjPtr<art::mirror::Object> system_thread_group =
system_thread_group_field->GetDeclaringClass()->GetFieldObject<art::mirror::Object>(
system_thread_group_field->GetOffset());
- ScopedLocalRef<jobject> thr_group(env, soa.AddLocalReference<jobject>(system_thread_group));
- return env->NewObject(art::WellKnownClasses::java_lang_Thread,
- art::WellKnownClasses::java_lang_Thread_init,
- thr_group.get(),
- thr_name.get(),
- /*Priority=*/ 0,
- /*Daemon=*/ true);
+ art::WellKnownClasses::java_lang_Thread_init->InvokeInstance<'V', 'L', 'L', 'I', 'Z'>(
+ self, thread.Get(), system_thread_group, thr_name.Get(), /*priority=*/ 0, /*daemon=*/ 1u);
+ return self->IsExceptionPending() ? nullptr : thread.Get();
}
struct CallbackData {
@@ -278,10 +288,14 @@ void AdbConnectionState::StartDebuggerThreads() {
}
runtime->StartThreadBirth();
}
- ScopedLocalRef<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa));
+ jobject thr = soa.Env()->GetVm()->AddGlobalRef(self, CreateAdbConnectionThread(soa.Self()));
+ if (thr == nullptr) {
+ LOG(ERROR) << "Failed to create debugger thread!";
+ return;
+ }
// Note: Using pthreads instead of std::thread to not abort when the thread cannot be
// created (exception support required).
- std::unique_ptr<CallbackData> data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) });
+ std::unique_ptr<CallbackData> data(new CallbackData { this, thr });
started_debugger_threads_ = true;
gPthread.emplace();
int pthread_create_result = pthread_create(&gPthread.value(),
@@ -293,7 +307,7 @@ void AdbConnectionState::StartDebuggerThreads() {
started_debugger_threads_ = false;
// If the create succeeded the other thread will call EndThreadBirth.
art::Runtime* runtime = art::Runtime::Current();
- soa.Env()->DeleteGlobalRef(data->thr_);
+ soa.Env()->DeleteGlobalRef(thr);
LOG(ERROR) << "Failed to create thread for adb-jdwp connection manager!";
art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
runtime->EndThreadBirth();
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index c1f6d6319e..a8c4495689 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -457,7 +457,7 @@ jvmtiError Redefiner::GetClassRedefinitionError(art::Handle<art::mirror::Class>
// Check Thread specifically since it's not a root but too many things reach into it with Unsafe
// too allow structural redefinition.
if (klass->IsAssignableFrom(
- self->DecodeJObject(art::WellKnownClasses::java_lang_Thread)->AsClass())) {
+ art::WellKnownClasses::java_lang_Thread_init->GetDeclaringClass())) {
*error_msg =
"java.lang.Thread has fields accessed using sun.misc.unsafe directly. It is not "
"safe to structurally redefine it.";
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 38257f1d6a..74dae8e9f7 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -578,10 +578,11 @@ jvmtiError StackUtil::GetThreadListStackTraces(jvmtiEnv* env,
if (thread_list[i] == nullptr) {
return ERR(INVALID_THREAD);
}
- if (!soa.Env()->IsInstanceOf(thread_list[i], art::WellKnownClasses::java_lang_Thread)) {
+ art::ObjPtr<art::mirror::Object> thread = soa.Decode<art::mirror::Object>(thread_list[i]);
+ if (!thread->InstanceOf(art::WellKnownClasses::java_lang_Thread_init->GetDeclaringClass())) {
return ERR(INVALID_THREAD);
}
- data.handles.push_back(hs.NewHandle(soa.Decode<art::mirror::Object>(thread_list[i])));
+ data.handles.push_back(hs.NewHandle(thread));
}
RunCheckpointAndWait(&data, static_cast<size_t>(max_frame_count));
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index a138c69945..5c0c3854a4 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -193,7 +193,7 @@ void ThreadUtil::CacheData() {
art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
art::ObjPtr<art::mirror::Class> thread_class =
- soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread);
+ art::WellKnownClasses::java_lang_Thread_init->GetDeclaringClass();
CHECK(thread_class != nullptr);
context_class_loader_ = thread_class->FindDeclaredInstanceField("contextClassLoader",
"Ljava/lang/ClassLoader;");
@@ -236,7 +236,9 @@ bool ThreadUtil::GetNativeThread(jthread thread,
if (thread == nullptr) {
*thr = art::Thread::Current();
return true;
- } else if (!soa.Env()->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) {
+ }
+ art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread);
+ if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread_init->GetDeclaringClass())) {
*err = ERR(INVALID_THREAD);
return false;
} else {
@@ -618,7 +620,7 @@ jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED,
// Need to read the Java "started" field to know whether this is starting or terminated.
art::Handle<art::mirror::Object> peer(hs.NewHandle(soa.Decode<art::mirror::Object>(thread)));
art::ObjPtr<art::mirror::Class> thread_klass =
- soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread);
+ art::WellKnownClasses::java_lang_Thread_init->GetDeclaringClass();
if (!thread_klass->IsAssignableFrom(peer->GetClass())) {
return ERR(INVALID_THREAD);
}
@@ -815,39 +817,44 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env,
if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) {
return ERR(INVALID_PRIORITY);
}
- JNIEnv* env = art::Thread::Current()->GetJniEnv();
- if (thread == nullptr || !env->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) {
+ if (thread == nullptr) {
return ERR(INVALID_THREAD);
}
- if (proc == nullptr) {
- return ERR(NULL_POINTER);
- }
-
+ art::Runtime* runtime = art::Runtime::Current();
+ art::Thread* self = art::Thread::Current();
+ std::unique_ptr<AgentData> data;
{
- art::Runtime* runtime = art::Runtime::Current();
- art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
- if (runtime->IsShuttingDownLocked()) {
- // The runtime is shutting down so we cannot create new threads.
- // TODO It's not fully clear from the spec what we should do here. We aren't yet in
- // JVMTI_PHASE_DEAD so we cannot return ERR(WRONG_PHASE) but creating new threads is now
- // impossible. Existing agents don't seem to generally do anything with this return value so
- // it doesn't matter too much. We could do something like sending a fake ThreadStart event
- // even though code is never actually run.
- return ERR(INTERNAL);
+ art::ScopedObjectAccess soa(self);
+ art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread);
+ if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread_init->GetDeclaringClass())) {
+ return ERR(INVALID_THREAD);
+ }
+ if (proc == nullptr) {
+ return ERR(NULL_POINTER);
}
- runtime->StartThreadBirth();
- }
- std::unique_ptr<AgentData> data(new AgentData);
- data->arg = arg;
- data->proc = proc;
- // We need a global ref for Java objects, as local refs will be invalid.
- data->thread = env->NewGlobalRef(thread);
- data->java_vm = art::Runtime::Current()->GetJavaVM();
- data->jvmti_env = jvmti_env;
- data->priority = priority;
- {
- art::ScopedObjectAccess soa(env);
+ {
+ art::MutexLock mu(soa.Self(), *art::Locks::runtime_shutdown_lock_);
+ if (runtime->IsShuttingDownLocked()) {
+ // The runtime is shutting down so we cannot create new threads.
+ // TODO It's not fully clear from the spec what we should do here. We aren't yet in
+ // JVMTI_PHASE_DEAD so we cannot return ERR(WRONG_PHASE) but creating new threads is now
+ // impossible. Existing agents don't seem to generally do anything with this return value so
+ // it doesn't matter too much. We could do something like sending a fake ThreadStart event
+ // even though code is never actually run.
+ return ERR(INTERNAL);
+ }
+ runtime->StartThreadBirth();
+ }
+
+ data.reset(new AgentData);
+ data->arg = arg;
+ data->proc = proc;
+ // We need a global ref for Java objects, as local refs will be invalid.
+ data->thread = runtime->GetJavaVM()->AddGlobalRef(soa.Self(), othread);
+ data->java_vm = runtime->GetJavaVM();
+ data->jvmti_env = jvmti_env;
+ data->priority = priority;
art::ObjPtr<art::mirror::Object> name =
art::WellKnownClasses::java_lang_Thread_name->GetObject(
soa.Decode<art::mirror::Object>(thread));
@@ -865,8 +872,7 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env,
reinterpret_cast<void*>(data.get()));
if (pthread_create_result != 0) {
// If the create succeeded the other thread will call EndThreadBirth.
- art::Runtime* runtime = art::Runtime::Current();
- art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
+ art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_);
runtime->EndThreadBirth();
return ERR(INTERNAL);
}
diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc
index e7d91effa7..979e3d1653 100644
--- a/openjdkjvmti/ti_threadgroup.cc
+++ b/openjdkjvmti/ti_threadgroup.cc
@@ -95,21 +95,21 @@ jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env,
}
art::ScopedObjectAccess soa(art::Thread::Current());
- if (soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup) == JNI_FALSE) {
+ art::StackHandleScope<2> hs(soa.Self());
+ art::Handle<art::mirror::Class> tg_class =
+ hs.NewHandle(art::WellKnownClasses::java_lang_ThreadGroup_add->GetDeclaringClass());
+ art::Handle<art::mirror::Object> thread_group =
+ hs.NewHandle(soa.Decode<art::mirror::Object>(group));
+ if (!thread_group->InstanceOf(tg_class.Get())) {
return ERR(INVALID_THREAD_GROUP);
}
- art::StackHandleScope<2> hs(soa.Self());
- art::Handle<art::mirror::Class> tg_class(
- hs.NewHandle(soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_ThreadGroup)));
- art::Handle<art::mirror::Object> obj(hs.NewHandle(soa.Decode<art::mirror::Object>(group)));
-
// Do the name first. It's the only thing that can fail.
{
art::ArtField* name_field = art::WellKnownClasses::java_lang_ThreadGroup_name;
CHECK(name_field != nullptr);
art::ObjPtr<art::mirror::String> name_obj =
- art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(obj.Get()));
+ art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(thread_group.Get()));
std::string tmp_str;
const char* tmp_cstr;
if (name_obj == nullptr) {
@@ -130,7 +130,7 @@ jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env,
{
art::ArtField* parent_field = art::WellKnownClasses::java_lang_ThreadGroup_parent;
CHECK(parent_field != nullptr);
- art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(obj.Get());
+ art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(thread_group.Get());
info_ptr->parent = parent_group == nullptr
? nullptr
: soa.AddLocalReference<jthreadGroup>(parent_group);
@@ -140,14 +140,14 @@ jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env,
{
art::ArtField* prio_field = tg_class->FindDeclaredInstanceField("maxPriority", "I");
CHECK(prio_field != nullptr);
- info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(obj.Get()));
+ info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(thread_group.Get()));
}
// Daemon.
{
art::ArtField* daemon_field = tg_class->FindDeclaredInstanceField("daemon", "Z");
CHECK(daemon_field != nullptr);
- info_ptr->is_daemon = daemon_field->GetBoolean(obj.Get()) == 0 ? JNI_FALSE : JNI_TRUE;
+ info_ptr->is_daemon = daemon_field->GetBoolean(thread_group.Get()) == 0 ? JNI_FALSE : JNI_TRUE;
}
return ERR(NONE);
@@ -221,14 +221,14 @@ jvmtiError ThreadGroupUtil::GetThreadGroupChildren(jvmtiEnv* env,
}
art::ScopedObjectAccess soa(art::Thread::Current());
-
- if (!soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup)) {
+ art::StackHandleScope<1> hs(soa.Self());
+ art::Handle<art::mirror::Object> thread_group =
+ hs.NewHandle(soa.Decode<art::mirror::Object>(group));
+ if (!thread_group->InstanceOf(
+ art::WellKnownClasses::java_lang_ThreadGroup_add->GetDeclaringClass())) {
return ERR(INVALID_THREAD_GROUP);
}
- art::StackHandleScope<1> hs(soa.Self());
- art::Handle<art::mirror::Object> thread_group = hs.NewHandle(
- soa.Decode<art::mirror::Object>(group));
art::ObjectLock<art::mirror::Object> thread_group_lock(soa.Self(), thread_group);
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index d105d9e41d..c3aa5c914a 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -192,6 +192,7 @@ typename detail::ShortyTraits<ReturnType>::Type
ArtMethod::InvokeInstance(Thread* self,
ObjPtr<mirror::Object> receiver,
typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
DCHECK(!IsStatic());
JValue result;
constexpr auto shorty = detail::MaterializeShorty<ReturnType, ArgType...>();
@@ -200,6 +201,47 @@ ArtMethod::InvokeInstance(Thread* self,
return detail::ShortyTraits<ReturnType>::Get(result);
}
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeFinal(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ DCHECK(IsFinal());
+ DCHECK(receiver != nullptr);
+ return InvokeInstance<ReturnType, ArgType...>(self, receiver, args...);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeVirtual(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ DCHECK(!IsFinal());
+ DCHECK(receiver != nullptr);
+ ArtMethod* target_method =
+ receiver->GetClass()->FindVirtualMethodForVirtual(this, kRuntimePointerSize);
+ DCHECK(target_method != nullptr);
+ return target_method->InvokeInstance<ReturnType, ArgType...>(self, receiver, args...);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeInterface(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ DCHECK(receiver != nullptr);
+ ArtMethod* target_method =
+ receiver->GetClass()->FindVirtualMethodForInterface(this, kRuntimePointerSize);
+ DCHECK(target_method != nullptr);
+ return target_method->InvokeInstance<ReturnType, ArgType...>(self, receiver, args...);
+}
+
template <ReadBarrierOption kReadBarrierOption>
inline ObjPtr<mirror::Class> ArtMethod::GetDeclaringClassUnchecked() {
GcRootSource gc_root_source(this);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 16350e746d..1065429b17 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -677,6 +677,27 @@ class ArtMethod final {
typename detail::ShortyTraits<ArgType>::Type... args)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeFinal(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeVirtual(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeInterface(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
const void* GetEntryPointFromQuickCompiledCode() const {
return GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize);
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f76fe2a86b..919383f1ca 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1141,6 +1141,9 @@ void ClassLinker::RunRootClinits(Thread* self) {
WellKnownClasses::java_lang_Integer_valueOf,
WellKnownClasses::java_lang_Long_valueOf,
WellKnownClasses::java_lang_Short_valueOf,
+ // Ensure `Thread` and `ThreadGroup` classes are initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_Thread_init,
+ WellKnownClasses::java_lang_ThreadGroup_add,
// Ensure `DirectByteBuffer` class is initialized (avoid check at runtime).
WellKnownClasses::java_nio_DirectByteBuffer_init,
// Ensure reflection annotation classes are initialized (avoid check at runtime).
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index ba317c872d..315eb898cb 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -432,9 +432,7 @@ void MemberSignature::NotifyHiddenApiListener(AccessMethod access_method) {
CHECK(signature_str != nullptr);
// Call through to Consumer.accept(String memberSignature);
- ArtMethod* accept_method = consumer_object->GetClass()->FindVirtualMethodForInterface(
- WellKnownClasses::java_util_function_Consumer_accept, kRuntimePointerSize);
- accept_method->InvokeInstance<'V', 'L'>(
+ WellKnownClasses::java_util_function_Consumer_accept->InvokeInterface<'V', 'L'>(
soa.Self(), consumer_object.Get(), signature_str.Get());
}
}
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 5678732c9b..c1cae6f124 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1112,18 +1112,14 @@ void UnstartedRuntime::UnstartedThreadCurrentThread(
// thread as unstarted to the ThreadGroup. A faked-up main thread peer is good enough for
// these purposes.
Runtime::Current()->InitThreadGroups(self);
- jobject main_peer =
- self->CreateCompileTimePeer(self->GetJniEnv(),
- "main",
- false,
- Runtime::Current()->GetMainThreadGroup());
+ ObjPtr<mirror::Object> main_peer = self->CreateCompileTimePeer(
+ "main", /*as_daemon=*/ false, Runtime::Current()->GetMainThreadGroup());
if (main_peer == nullptr) {
AbortTransactionOrFail(self, "Failed allocating peer");
return;
}
- result->SetL(self->DecodeJObject(main_peer));
- self->GetJniEnv()->DeleteLocalRef(main_peer);
+ result->SetL(main_peer);
} else {
AbortTransactionOrFail(self,
"Thread.currentThread() does not support %s",
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 4d0b7aa0cb..6f1ef24ed4 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -2827,9 +2827,8 @@ class JNI {
//
// NB GetDirectBufferAddress() does not need to call Buffer.isDirect() since it is only
// able return a valid address if the Buffer address field is not-null.
- ArtMethod* is_direct_method = buffer->GetClass()->FindVirtualMethodForVirtual(
- WellKnownClasses::java_nio_Buffer_isDirect, kRuntimePointerSize);
- uint8_t direct = is_direct_method->InvokeInstance<'Z'>(soa.Self(), buffer.Get());
+ uint8_t direct = WellKnownClasses::java_nio_Buffer_isDirect->InvokeVirtual<'Z'>(
+ soa.Self(), buffer.Get());
if (direct == 0u) {
return -1;
}
diff --git a/runtime/native/jdk_internal_misc_Unsafe.cc b/runtime/native/jdk_internal_misc_Unsafe.cc
index 7ab915209b..fa84f0e687 100644
--- a/runtime/native/jdk_internal_misc_Unsafe.cc
+++ b/runtime/native/jdk_internal_misc_Unsafe.cc
@@ -474,12 +474,14 @@ static void Unsafe_park(JNIEnv* env, jobject, jboolean isAbsolute, jlong time) {
static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
art::ScopedFastNativeObjectAccess soa(env);
- if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) {
+ ObjPtr<mirror::Object> mirror_thread = soa.Decode<mirror::Object>(jthread);
+ if (mirror_thread == nullptr ||
+ !mirror_thread->InstanceOf(WellKnownClasses::java_lang_Thread_init->GetDeclaringClass())) {
ThrowIllegalArgumentException("Argument to unpark() was not a Thread");
return;
}
art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
- art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, mirror_thread);
if (thread != nullptr) {
thread->Unpark();
} else {
@@ -489,7 +491,7 @@ static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
// already terminated.
ArtField* unparked = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
// JNI must use non transactional mode.
- unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
+ unparked->SetBoolean<false>(mirror_thread, JNI_TRUE);
}
}
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index da28489c49..52407db292 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -521,12 +521,14 @@ static void Unsafe_park(JNIEnv* env, jobject, jboolean isAbsolute, jlong time) {
static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
art::ScopedFastNativeObjectAccess soa(env);
- if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) {
+ ObjPtr<mirror::Object> mirror_thread = soa.Decode<mirror::Object>(jthread);
+ if (mirror_thread == nullptr ||
+ !mirror_thread->InstanceOf(WellKnownClasses::java_lang_Thread_init->GetDeclaringClass())) {
ThrowIllegalArgumentException("Argument to unpark() was not a Thread");
return;
}
art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
- art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, mirror_thread);
if (thread != nullptr) {
thread->Unpark();
} else {
@@ -536,7 +538,7 @@ static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
// already terminated.
ArtField* unparked = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
// JNI must use non transactional mode.
- unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
+ unparked->SetBoolean<false>(mirror_thread, JNI_TRUE);
}
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index eaeda04256..51a2c977cd 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -891,7 +891,7 @@ static jobject CreateSystemClassLoader(Runtime* runtime) {
soa.Self()->SetClassLoaderOverride(g_system_class_loader);
ObjPtr<mirror::Class> thread_class =
- WellKnownClasses::ToClass(WellKnownClasses::java_lang_Thread);
+ WellKnownClasses::java_lang_Thread_init->GetDeclaringClass();
ArtField* contextClassLoader =
thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;");
CHECK(contextClassLoader != nullptr);
@@ -2226,12 +2226,26 @@ void Runtime::InitThreadGroups(Thread* self) {
ScopedObjectAccess soa(self);
ArtField* main_thread_group_field = WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
ArtField* system_thread_group_field = WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
- ObjPtr<mirror::Class> thread_group_class = main_thread_group_field->GetDeclaringClass();
+ // Note: This is running before `ClassLinker::RunRootClinits()`, so we cannot rely on
+ // `ThreadGroup` and `Thread` being initialized.
+ // TODO: Clean up initialization order after all well-known methods are converted to `ArtMethod*`
+ // (and therefore the `WellKnownClasses::Init()` shall not initialize any classes).
+ StackHandleScope<2u> hs(self);
+ Handle<mirror::Class> thread_group_class =
+ hs.NewHandle(main_thread_group_field->GetDeclaringClass());
+ bool initialized = GetClassLinker()->EnsureInitialized(
+ self, thread_group_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ CHECK(initialized);
+ Handle<mirror::Class> thread_class =
+ hs.NewHandle(WellKnownClasses::java_lang_Thread_init->GetDeclaringClass());
+ initialized = GetClassLinker()->EnsureInitialized(
+ self, thread_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ CHECK(initialized);
main_thread_group_ =
- soa.Vm()->AddGlobalRef(self, main_thread_group_field->GetObject(thread_group_class));
+ soa.Vm()->AddGlobalRef(self, main_thread_group_field->GetObject(thread_group_class.Get()));
CHECK_IMPLIES(main_thread_group_ == nullptr, IsAotCompiler());
system_thread_group_ =
- soa.Vm()->AddGlobalRef(self, system_thread_group_field->GetObject(thread_group_class));
+ soa.Vm()->AddGlobalRef(self, system_thread_group_field->GetObject(thread_group_class.Get()));
CHECK_IMPLIES(system_thread_group_ == nullptr, IsAotCompiler());
}
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 302d57e74b..c38bd6156e 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -35,7 +35,7 @@
#include "dex/class_reference.h"
#include "handle.h"
#include "handle_scope-inl.h"
-#include "mirror/class-inl.h"
+#include "mirror/class-alloc-inl.h"
#include "mirror/class_loader.h"
#include "monitor-inl.h"
#include "nativehelper/scoped_local_ref.h"
@@ -164,40 +164,40 @@ TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackJava)
cb_.state = CallbackState::kBase; // Ignore main thread attach.
- {
- ScopedObjectAccess soa(self);
- MakeExecutable(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Thread));
- }
-
- JNIEnv* env = self->GetJniEnv();
+ ScopedObjectAccess soa(self);
+ MakeExecutable(WellKnownClasses::java_lang_Thread_init->GetDeclaringClass());
- ScopedLocalRef<jobject> thread_name(env,
- env->NewStringUTF("ThreadLifecycleCallback test thread"));
- ASSERT_TRUE(thread_name.get() != nullptr);
+ StackHandleScope<2u> hs(self);
+ Handle<mirror::String> thread_name = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(self, "ThreadLifecycleCallback test thread"));
+ ASSERT_TRUE(thread_name != nullptr);
- ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
- ASSERT_TRUE(thread.get() != nullptr);
+ DCHECK(WellKnownClasses::java_lang_Thread_init->GetDeclaringClass()->IsInitialized());
+ Handle<mirror::Object> thread = hs.NewHandle(
+ WellKnownClasses::java_lang_Thread_init->GetDeclaringClass()->AllocObject(self));
+ ASSERT_TRUE(thread != nullptr);
- env->CallNonvirtualVoidMethod(thread.get(),
- WellKnownClasses::java_lang_Thread,
- WellKnownClasses::java_lang_Thread_init,
- runtime_->GetMainThreadGroup(),
- thread_name.get(),
- kMinThreadPriority,
- JNI_FALSE);
- ASSERT_FALSE(env->ExceptionCheck());
+ WellKnownClasses::java_lang_Thread_init->InvokeInstance<'V', 'L', 'L', 'I', 'Z'>(
+ self,
+ thread.Get(),
+ soa.Decode<mirror::Object>(runtime_->GetMainThreadGroup()),
+ thread_name.Get(),
+ kMinThreadPriority,
+ /*daemon=*/ 0u);
+ ASSERT_FALSE(self->IsExceptionPending());
- jmethodID start_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "start", "()V");
- ASSERT_TRUE(start_id != nullptr);
+ ArtMethod* start_method =
+ thread->GetClass()->FindClassMethod("start", "()V", kRuntimePointerSize);
+ ASSERT_TRUE(start_method != nullptr);
- env->CallVoidMethod(thread.get(), start_id);
- ASSERT_FALSE(env->ExceptionCheck());
+ start_method->InvokeVirtual<'V'>(self, thread.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
- jmethodID join_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "join", "()V");
- ASSERT_TRUE(join_id != nullptr);
+ ArtMethod* join_method = thread->GetClass()->FindClassMethod("join", "()V", kRuntimePointerSize);
+ ASSERT_TRUE(join_method != nullptr);
- env->CallVoidMethod(thread.get(), join_id);
- ASSERT_FALSE(env->ExceptionCheck());
+ join_method->InvokeFinal<'V'>(self, thread.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
EXPECT_EQ(cb_.state, CallbackState::kDied);
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 80f1ff5331..19c2ce8d17 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -667,9 +667,7 @@ void* Thread::CreateCallback(void* arg) {
}
// Invoke the 'run' method of our java.lang.Thread.
ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
- jmethodID mid = WellKnownClasses::java_lang_Thread_run;
- ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
- InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
+ WellKnownClasses::java_lang_Thread_run->InvokeVirtual<'V'>(self, receiver);
}
// Detach and delete self.
Runtime::Current()->GetThreadList()->Unregister(self, /* should_run_callbacks= */ true);
@@ -1125,43 +1123,40 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_p
void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) {
Runtime* runtime = Runtime::Current();
CHECK(runtime->IsStarted());
- JNIEnv* env = tlsPtr_.jni_env;
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
- if (thread_group == nullptr) {
- thread_group = runtime->GetMainThreadGroup();
- }
- ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
+ ScopedObjectAccess soa(self);
+ StackHandleScope<4u> hs(self);
+ DCHECK(WellKnownClasses::java_lang_ThreadGroup_add->GetDeclaringClass()->IsInitialized());
+ Handle<mirror::Object> thr_group = hs.NewHandle(soa.Decode<mirror::Object>(
+ thread_group != nullptr ? thread_group : runtime->GetMainThreadGroup()));
+ Handle<mirror::String> thread_name = hs.NewHandle(
+ name != nullptr ? mirror::String::AllocFromModifiedUtf8(self, name) : nullptr);
// Add missing null check in case of OOM b/18297817
- if (name != nullptr && thread_name.get() == nullptr) {
- CHECK(IsExceptionPending());
+ if (name != nullptr && UNLIKELY(thread_name == nullptr)) {
+ CHECK(self->IsExceptionPending());
return;
}
jint thread_priority = GetNativePriority();
jboolean thread_is_daemon = as_daemon;
- ScopedLocalRef<jobject> peer(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
- if (peer.get() == nullptr) {
+ DCHECK(WellKnownClasses::java_lang_Thread_init->GetDeclaringClass()->IsInitialized());
+ Handle<mirror::Object> peer = hs.NewHandle(
+ WellKnownClasses::java_lang_Thread_init->GetDeclaringClass()->AllocObject(self));
+ if (UNLIKELY(peer == nullptr)) {
CHECK(IsExceptionPending());
return;
}
- {
- ScopedObjectAccess soa(this);
- tlsPtr_.opeer = soa.Decode<mirror::Object>(peer.get()).Ptr();
- }
- env->CallNonvirtualVoidMethod(peer.get(),
- WellKnownClasses::java_lang_Thread,
- WellKnownClasses::java_lang_Thread_init,
- thread_group, thread_name.get(), thread_priority, thread_is_daemon);
- if (IsExceptionPending()) {
+ tlsPtr_.opeer = peer.Get();
+ WellKnownClasses::java_lang_Thread_init->InvokeInstance<'V', 'L', 'L', 'I', 'Z'>(
+ self, peer.Get(), thr_group.Get(), thread_name.Get(), thread_priority, thread_is_daemon);
+ if (self->IsExceptionPending()) {
return;
}
- Thread* self = this;
- DCHECK_EQ(self, Thread::Current());
- ScopedObjectAccess soa(self);
- SetNativePeer</*kSupportTransaction=*/ false>(soa.Decode<mirror::Object>(peer.get()), self);
+ SetNativePeer</*kSupportTransaction=*/ false>(peer.Get(), self);
- StackHandleScope<1> hs(self);
MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName()));
if (peer_thread_name == nullptr) {
// The Thread constructor should have set the Thread.name to a
@@ -1169,18 +1164,16 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group)
// available (in the compiler, in tests), we manually assign the
// fields the constructor should have set.
if (runtime->IsActiveTransaction()) {
- InitPeer<true>(soa,
- tlsPtr_.opeer,
+ InitPeer<true>(tlsPtr_.opeer,
thread_is_daemon,
- thread_group,
- thread_name.get(),
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
} else {
- InitPeer<false>(soa,
- tlsPtr_.opeer,
+ InitPeer<false>(tlsPtr_.opeer,
thread_is_daemon,
- thread_group,
- thread_name.get(),
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
}
peer_thread_name.Assign(GetThreadName());
@@ -1191,27 +1184,33 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group)
}
}
-jobject Thread::CreateCompileTimePeer(JNIEnv* env,
- const char* name,
- bool as_daemon,
- jobject thread_group) {
+ObjPtr<mirror::Object> Thread::CreateCompileTimePeer(const char* name,
+ bool as_daemon,
+ jobject thread_group) {
Runtime* runtime = Runtime::Current();
CHECK(!runtime->IsStarted());
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
- if (thread_group == nullptr) {
- thread_group = runtime->GetMainThreadGroup();
- }
- ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
+ ScopedObjectAccessUnchecked soa(self);
+ StackHandleScope<3u> hs(self);
+ DCHECK(WellKnownClasses::java_lang_ThreadGroup_add->GetDeclaringClass()->IsInitialized());
+ Handle<mirror::Object> thr_group = hs.NewHandle(soa.Decode<mirror::Object>(
+ thread_group != nullptr ? thread_group : runtime->GetMainThreadGroup()));
+ Handle<mirror::String> thread_name = hs.NewHandle(
+ name != nullptr ? mirror::String::AllocFromModifiedUtf8(self, name) : nullptr);
// Add missing null check in case of OOM b/18297817
- if (name != nullptr && thread_name.get() == nullptr) {
- CHECK(Thread::Current()->IsExceptionPending());
+ if (name != nullptr && UNLIKELY(thread_name == nullptr)) {
+ CHECK(self->IsExceptionPending());
return nullptr;
}
jint thread_priority = kNormThreadPriority; // Always normalize to NORM priority.
jboolean thread_is_daemon = as_daemon;
- ScopedLocalRef<jobject> peer(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
- if (peer.get() == nullptr) {
+ DCHECK(WellKnownClasses::java_lang_Thread_init->GetDeclaringClass()->IsInitialized());
+ Handle<mirror::Object> peer = hs.NewHandle(
+ WellKnownClasses::java_lang_Thread_init->GetDeclaringClass()->AllocObject(self));
+ if (peer == nullptr) {
CHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
@@ -1222,39 +1221,32 @@ jobject Thread::CreateCompileTimePeer(JNIEnv* env,
// non-null value. However, because we can run without code
// available (in the compiler, in tests), we manually assign the
// fields the constructor should have set.
- ScopedObjectAccessUnchecked soa(Thread::Current());
if (runtime->IsActiveTransaction()) {
- InitPeer<true>(soa,
- soa.Decode<mirror::Object>(peer.get()),
+ InitPeer<true>(peer.Get(),
thread_is_daemon,
- thread_group,
- thread_name.get(),
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
} else {
- InitPeer<false>(soa,
- soa.Decode<mirror::Object>(peer.get()),
+ InitPeer<false>(peer.Get(),
thread_is_daemon,
- thread_group,
- thread_name.get(),
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
}
- return peer.release();
+ return peer.Get();
}
template<bool kTransactionActive>
-void Thread::InitPeer(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::Object> peer,
+void Thread::InitPeer(ObjPtr<mirror::Object> peer,
jboolean thread_is_daemon,
- jobject thread_group,
- jobject thread_name,
+ ObjPtr<mirror::Object> thread_group,
+ ObjPtr<mirror::String> thread_name,
jint thread_priority) {
- WellKnownClasses::java_lang_Thread_daemon->
- SetBoolean<kTransactionActive>(peer, thread_is_daemon);
- WellKnownClasses::java_lang_Thread_group->
- SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_group));
- WellKnownClasses::java_lang_Thread_name->
- SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_name));
+ WellKnownClasses::java_lang_Thread_daemon->SetBoolean<kTransactionActive>(peer, thread_is_daemon);
+ WellKnownClasses::java_lang_Thread_group->SetObject<kTransactionActive>(peer, thread_group);
+ WellKnownClasses::java_lang_Thread_name->SetObject<kTransactionActive>(peer, thread_name);
WellKnownClasses::java_lang_Thread_priority->SetInt<kTransactionActive>(peer, thread_priority);
}
@@ -2434,25 +2426,19 @@ void Thread::Shutdown() {
}
void Thread::NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject thread_group) {
- ScopedLocalRef<jobject> thread_jobject(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
- ScopedLocalRef<jobject> thread_group_jobject_scoped(
- soa.Env(), nullptr);
- jobject thread_group_jobject = thread_group;
+ ObjPtr<mirror::Object> thread_object = soa.Self()->GetPeer();
+ ObjPtr<mirror::Object> thread_group_object = soa.Decode<mirror::Object>(thread_group);
if (thread_group == nullptr || kIsDebugBuild) {
// There is always a group set. Retrieve it.
- thread_group_jobject_scoped.reset(soa.AddLocalReference<jobject>(
- WellKnownClasses::java_lang_Thread_group->GetObject(
- soa.Decode<mirror::Object>(thread_jobject.get()))));
- thread_group_jobject = thread_group_jobject_scoped.get();
+ thread_group_object = WellKnownClasses::java_lang_Thread_group->GetObject(thread_object);
if (kIsDebugBuild && thread_group != nullptr) {
- CHECK(soa.Env()->IsSameObject(thread_group, thread_group_jobject));
+ CHECK(thread_group_object == soa.Decode<mirror::Object>(thread_group));
}
}
- soa.Env()->CallNonvirtualVoidMethod(thread_group_jobject,
- WellKnownClasses::java_lang_ThreadGroup,
- WellKnownClasses::java_lang_ThreadGroup_add,
- thread_jobject.get());
+ // TODO: Why are we calling the non-final method `ThreadGroup.add(Thread)` directly
+ // instead of using the virtual dispatch? (Preserved from old code.)
+ WellKnownClasses::java_lang_ThreadGroup_add->InvokeInstance<'V', 'L'>(
+ soa.Self(), thread_group_object, thread_object);
}
Thread::Thread(bool daemon)
@@ -2577,8 +2563,8 @@ void Thread::Destroy(bool should_run_callbacks) {
if (tlsPtr_.opeer != nullptr) {
ScopedObjectAccess soa(self);
// We may need to call user-supplied managed code, do this before final clean-up.
- HandleUncaughtExceptions(soa);
- RemoveFromThreadGroup(soa);
+ HandleUncaughtExceptions();
+ RemoveFromThreadGroup();
Runtime* runtime = Runtime::Current();
if (runtime != nullptr && should_run_callbacks) {
runtime->GetRuntimeCallbacks()->ThreadDeath(self);
@@ -2657,38 +2643,35 @@ Thread::~Thread() {
TearDownAlternateSignalStack();
}
-void Thread::HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa) {
- if (!IsExceptionPending()) {
+void Thread::HandleUncaughtExceptions() {
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
+ if (!self->IsExceptionPending()) {
return;
}
- ScopedLocalRef<jobject> peer(tlsPtr_.jni_env, soa.AddLocalReference<jobject>(tlsPtr_.opeer));
- ScopedThreadStateChange tsc(this, ThreadState::kNative);
// Get and clear the exception.
- ScopedLocalRef<jthrowable> exception(tlsPtr_.jni_env, tlsPtr_.jni_env->ExceptionOccurred());
- tlsPtr_.jni_env->ExceptionClear();
+ ObjPtr<mirror::Object> exception = self->GetException();
+ self->ClearException();
// Call the Thread instance's dispatchUncaughtException(Throwable)
- tlsPtr_.jni_env->CallVoidMethod(peer.get(),
- WellKnownClasses::java_lang_Thread_dispatchUncaughtException,
- exception.get());
+ WellKnownClasses::java_lang_Thread_dispatchUncaughtException->InvokeFinal<'V', 'L'>(
+ self, tlsPtr_.opeer, exception);
// If the dispatchUncaughtException threw, clear that exception too.
- tlsPtr_.jni_env->ExceptionClear();
+ self->ClearException();
}
-void Thread::RemoveFromThreadGroup(ScopedObjectAccessAlreadyRunnable& soa) {
- // this.group.removeThread(this);
+void Thread::RemoveFromThreadGroup() {
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
+ // this.group.threadTerminated(this);
// group can be null if we're in the compiler or a test.
- ObjPtr<mirror::Object> ogroup =
+ ObjPtr<mirror::Object> group =
WellKnownClasses::java_lang_Thread_group->GetObject(tlsPtr_.opeer);
- if (ogroup != nullptr) {
- ScopedLocalRef<jobject> group(soa.Env(), soa.AddLocalReference<jobject>(ogroup));
- ScopedLocalRef<jobject> peer(soa.Env(), soa.AddLocalReference<jobject>(tlsPtr_.opeer));
- ScopedThreadStateChange tsc(soa.Self(), ThreadState::kNative);
- tlsPtr_.jni_env->CallVoidMethod(group.get(),
- WellKnownClasses::java_lang_ThreadGroup_removeThread,
- peer.get());
+ if (group != nullptr) {
+ WellKnownClasses::java_lang_ThreadGroup_threadTerminated->InvokeVirtual<'V', 'L'>(
+ self, group, tlsPtr_.opeer);
}
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 2a6e148fa0..0b108ad771 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1403,10 +1403,9 @@ class Thread {
// Set to the read barrier marking entrypoints to be non-null.
void SetReadBarrierEntrypoints();
- static jobject CreateCompileTimePeer(JNIEnv* env,
- const char* name,
- bool as_daemon,
- jobject thread_group)
+ ObjPtr<mirror::Object> CreateCompileTimePeer(const char* name,
+ bool as_daemon,
+ jobject thread_group)
REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE InterpreterCache* GetInterpreterCache() {
@@ -1486,11 +1485,10 @@ class Thread {
void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
template<bool kTransactionActive>
- static void InitPeer(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::Object> peer,
+ static void InitPeer(ObjPtr<mirror::Object> peer,
jboolean thread_is_daemon,
- jobject thread_group,
- jobject thread_name,
+ ObjPtr<mirror::Object> thread_group,
+ ObjPtr<mirror::String> thread_name,
jint thread_priority)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1556,10 +1554,8 @@ class Thread {
static NO_INLINE void* CreateCallbackWithUffdGc(void* arg);
static void* CreateCallback(void* arg);
- void HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(Locks::mutator_lock_);
- void RemoveFromThreadGroup(ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ void HandleUncaughtExceptions() REQUIRES_SHARED(Locks::mutator_lock_);
+ void RemoveFromThreadGroup() REQUIRES_SHARED(Locks::mutator_lock_);
// Initialize a thread.
//
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index f7d4c8ab2c..9f1b8e70c8 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -76,8 +76,6 @@ jclass WellKnownClasses::java_lang_StackOverflowError;
jclass WellKnownClasses::java_lang_String;
jclass WellKnownClasses::java_lang_StringFactory;
jclass WellKnownClasses::java_lang_System;
-jclass WellKnownClasses::java_lang_Thread;
-jclass WellKnownClasses::java_lang_ThreadGroup;
jclass WellKnownClasses::java_lang_Throwable;
jclass WellKnownClasses::java_lang_Void;
jclass WellKnownClasses::libcore_reflect_AnnotationMember__array;
@@ -111,11 +109,11 @@ jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
ArtMethod* WellKnownClasses::java_lang_Short_valueOf;
jmethodID WellKnownClasses::java_lang_String_charAt;
-jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
-jmethodID WellKnownClasses::java_lang_Thread_init;
-jmethodID WellKnownClasses::java_lang_Thread_run;
-jmethodID WellKnownClasses::java_lang_ThreadGroup_add;
-jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread;
+ArtMethod* WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
+ArtMethod* WellKnownClasses::java_lang_Thread_init;
+ArtMethod* WellKnownClasses::java_lang_Thread_run;
+ArtMethod* WellKnownClasses::java_lang_ThreadGroup_add;
+ArtMethod* WellKnownClasses::java_lang_ThreadGroup_threadTerminated;
ArtMethod* WellKnownClasses::java_nio_Buffer_isDirect;
ArtMethod* WellKnownClasses::java_nio_DirectByteBuffer_init;
ArtMethod* WellKnownClasses::java_util_function_Consumer_accept;
@@ -392,8 +390,6 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_lang_String = CacheClass(env, "java/lang/String");
java_lang_StringFactory = CacheClass(env, "java/lang/StringFactory");
java_lang_System = CacheClass(env, "java/lang/System");
- java_lang_Thread = CacheClass(env, "java/lang/Thread");
- java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
java_lang_Void = CacheClass(env, "java/lang/Void");
libcore_reflect_AnnotationMember__array = CacheClass(env, "[Llibcore/reflect/AnnotationMember;");
@@ -445,17 +441,16 @@ void WellKnownClasses::InitFieldsAndMethodsOnly(JNIEnv* env) {
java_lang_reflect_InvocationTargetException_init = CacheMethod(env, java_lang_reflect_InvocationTargetException, false, "<init>", "(Ljava/lang/Throwable;)V");
java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
java_lang_String_charAt = CacheMethod(env, java_lang_String, false, "charAt", "(I)C");
- java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
- java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
- java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
- java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V");
- java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V");
- StackHandleScope<12u> hs(self);
+ StackHandleScope<14u> hs(self);
Handle<mirror::Class> d_s_vmr =
hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/VMRuntime;"));
Handle<mirror::Class> j_i_fd =
hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/io/FileDescriptor;"));
+ Handle<mirror::Class> j_l_Thread =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Thread;"));
+ Handle<mirror::Class> j_l_tg =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ThreadGroup;"));
Handle<mirror::Class> j_n_b =
hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/nio/Buffer;"));
Handle<mirror::Class> j_n_bb =
@@ -494,6 +489,29 @@ void WellKnownClasses::InitFieldsAndMethodsOnly(JNIEnv* env) {
java_lang_Float_floatToRawIntBits =
CacheMethod(j_l_Float, /*is_static=*/ true, "floatToRawIntBits", "(F)I", pointer_size);
+ java_lang_Thread_dispatchUncaughtException = CacheMethod(
+ j_l_Thread.Get(),
+ /*is_static=*/ false,
+ "dispatchUncaughtException",
+ "(Ljava/lang/Throwable;)V",
+ pointer_size);
+ java_lang_Thread_init = CacheMethod(
+ j_l_Thread.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V",
+ pointer_size);
+ java_lang_Thread_run = CacheMethod(
+ j_l_Thread.Get(), /*is_static=*/ false, "run", "()V", pointer_size);
+ java_lang_ThreadGroup_add = CacheMethod(
+ j_l_tg.Get(), /*is_static=*/ false, "add", "(Ljava/lang/Thread;)V", pointer_size);
+ java_lang_ThreadGroup_threadTerminated = CacheMethod(
+ j_l_tg.Get(),
+ /*is_static=*/ false,
+ "threadTerminated",
+ "(Ljava/lang/Thread;)V",
+ pointer_size);
+
java_nio_Buffer_isDirect =
CacheMethod(j_n_b.Get(), /*is_static=*/ false, "isDirect", "()Z", pointer_size);
java_nio_DirectByteBuffer_init =
@@ -556,35 +574,34 @@ void WellKnownClasses::InitFieldsAndMethodsOnly(JNIEnv* env) {
java_lang_ClassLoader_parent = CacheField(
j_l_cl, /*is_static=*/ false, "parent", "Ljava/lang/ClassLoader;");
- ObjPtr<mirror::Class> j_l_Thread = soa.Decode<mirror::Class>(java_lang_Thread);
java_lang_Thread_parkBlocker =
- CacheField(j_l_Thread, /*is_static=*/ false, "parkBlocker", "Ljava/lang/Object;");
- java_lang_Thread_daemon = CacheField(j_l_Thread, /*is_static=*/ false, "daemon", "Z");
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "parkBlocker", "Ljava/lang/Object;");
+ java_lang_Thread_daemon = CacheField(j_l_Thread.Get(), /*is_static=*/ false, "daemon", "Z");
java_lang_Thread_group =
- CacheField(j_l_Thread, /*is_static=*/ false, "group", "Ljava/lang/ThreadGroup;");
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "group", "Ljava/lang/ThreadGroup;");
java_lang_Thread_lock =
- CacheField(j_l_Thread, /*is_static=*/ false, "lock", "Ljava/lang/Object;");
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "lock", "Ljava/lang/Object;");
java_lang_Thread_name =
- CacheField(j_l_Thread, /*is_static=*/ false, "name", "Ljava/lang/String;");
- java_lang_Thread_priority = CacheField(j_l_Thread, /*is_static=*/ false, "priority", "I");
- java_lang_Thread_nativePeer = CacheField(j_l_Thread, /*is_static=*/ false, "nativePeer", "J");
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "name", "Ljava/lang/String;");
+ java_lang_Thread_priority = CacheField(j_l_Thread.Get(), /*is_static=*/ false, "priority", "I");
+ java_lang_Thread_nativePeer =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "nativePeer", "J");
java_lang_Thread_systemDaemon =
- CacheField(j_l_Thread, /*is_static=*/ false, "systemDaemon", "Z");
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "systemDaemon", "Z");
java_lang_Thread_unparkedBeforeStart =
- CacheField(j_l_Thread, /*is_static=*/ false, "unparkedBeforeStart", "Z");
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "unparkedBeforeStart", "Z");
- ObjPtr<mirror::Class> j_l_tg = soa.Decode<mirror::Class>(java_lang_ThreadGroup);
java_lang_ThreadGroup_groups =
- CacheField(j_l_tg, /*is_static=*/ false, "groups", "[Ljava/lang/ThreadGroup;");
- java_lang_ThreadGroup_ngroups = CacheField(j_l_tg, /*is_static=*/ false, "ngroups", "I");
+ CacheField(j_l_tg.Get(), /*is_static=*/ false, "groups", "[Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_ngroups = CacheField(j_l_tg.Get(), /*is_static=*/ false, "ngroups", "I");
java_lang_ThreadGroup_mainThreadGroup =
- CacheField(j_l_tg, /*is_static=*/ true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
+ CacheField(j_l_tg.Get(), /*is_static=*/ true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
java_lang_ThreadGroup_name =
- CacheField(j_l_tg, /*is_static=*/ false, "name", "Ljava/lang/String;");
+ CacheField(j_l_tg.Get(), /*is_static=*/ false, "name", "Ljava/lang/String;");
java_lang_ThreadGroup_parent =
- CacheField(j_l_tg, /*is_static=*/ false, "parent", "Ljava/lang/ThreadGroup;");
+ CacheField(j_l_tg.Get(), /*is_static=*/ false, "parent", "Ljava/lang/ThreadGroup;");
java_lang_ThreadGroup_systemThreadGroup =
- CacheField(j_l_tg, /*is_static=*/ true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
+ CacheField(j_l_tg.Get(), /*is_static=*/ true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
ObjPtr<mirror::Class> j_l_Throwable = soa.Decode<mirror::Class>(java_lang_Throwable);
java_lang_Throwable_cause = CacheField(
@@ -686,8 +703,6 @@ void WellKnownClasses::Clear() {
java_lang_String = nullptr;
java_lang_StringFactory = nullptr;
java_lang_System = nullptr;
- java_lang_Thread = nullptr;
- java_lang_ThreadGroup = nullptr;
java_lang_Throwable = nullptr;
java_lang_Void = nullptr;
libcore_reflect_AnnotationMember__array = nullptr;
@@ -725,7 +740,7 @@ void WellKnownClasses::Clear() {
java_lang_Thread_init = nullptr;
java_lang_Thread_run = nullptr;
java_lang_ThreadGroup_add = nullptr;
- java_lang_ThreadGroup_removeThread = nullptr;
+ java_lang_ThreadGroup_threadTerminated = nullptr;
java_nio_Buffer_isDirect = nullptr;
java_nio_DirectByteBuffer_init = nullptr;
libcore_reflect_AnnotationFactory_createAnnotation = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 14962a85c7..0a58766708 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -87,8 +87,6 @@ struct WellKnownClasses {
static jclass java_lang_String;
static jclass java_lang_StringFactory;
static jclass java_lang_System;
- static jclass java_lang_Thread;
- static jclass java_lang_ThreadGroup;
static jclass java_lang_Throwable;
static jclass java_lang_Void;
static jclass libcore_reflect_AnnotationMember__array;
@@ -122,11 +120,11 @@ struct WellKnownClasses {
static jmethodID java_lang_Runtime_nativeLoad;
static ArtMethod* java_lang_Short_valueOf;
static jmethodID java_lang_String_charAt;
- static jmethodID java_lang_Thread_dispatchUncaughtException;
- static jmethodID java_lang_Thread_init;
- static jmethodID java_lang_Thread_run;
- static jmethodID java_lang_ThreadGroup_add;
- static jmethodID java_lang_ThreadGroup_removeThread;
+ static ArtMethod* java_lang_Thread_dispatchUncaughtException;
+ static ArtMethod* java_lang_Thread_init;
+ static ArtMethod* java_lang_Thread_run;
+ static ArtMethod* java_lang_ThreadGroup_add;
+ static ArtMethod* java_lang_ThreadGroup_threadTerminated;
static ArtMethod* java_nio_Buffer_isDirect;
static ArtMethod* java_nio_DirectByteBuffer_init;
static ArtMethod* java_util_function_Consumer_accept;
diff --git a/test/913-heaps/expected-stdout.txt b/test/913-heaps/expected-stdout.txt
index 5f882b1e24..e7a372aa10 100644
--- a/test/913-heaps/expected-stdout.txt
+++ b/test/913-heaps/expected-stdout.txt
@@ -1,6 +1,5 @@
---
true true
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
@@ -44,7 +43,6 @@ root@root --(thread)--> 3000@0 [size=120, length=-1]
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
@@ -97,7 +95,6 @@ root@root --(thread)--> 3000@0 [size=120, length=-1]
---
3@1001 --(class)--> 1001@0 [size=123456780016, length=-1]
---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
@@ -109,7 +106,6 @@ root@root --(thread)--> 3000@0 [size=120, length=-1]
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
@@ -195,7 +191,6 @@ root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot
---
---
---- untagged objects
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
@@ -239,7 +234,6 @@ root@root --(thread)--> 3000@0 [size=120, length=-1]
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
@@ -285,7 +279,6 @@ root@root --(thread)--> 3000@0 [size=120, length=-1]
6@1000 --(class)--> 1000@0 [size=123456780055, length=-1]
---
---- tagged classes
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780060, length=-1]
@@ -312,7 +305,6 @@ root@root --(thread)--> 3000@0 [size=120, length=-1]
5@1002 --(field@8)--> 500@0 [size=20, length=2]
6@1000 --(class)--> 1000@0 [size=123456780060, length=-1]
---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780065, length=-1]