Faster Class::FindField().
Change `Class::FindField()` to take dex cache and type index
arguments instead of strings and optimize it similarly to
`Class::FindClassMethod()`, namely search by indexes when
possible (except when searching interfaces where we only add
a TODO comment for now) and avoid `strlen()` calls for ASCII
names and type descriptors.
Update `ClassLinker::FindResolvedFieldJLS()` to pass the new
arguments and replace all other `Class::FindField()` calls
with more direct calls as we know the classes where those
fields are defined.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 181943478
Change-Id: Iba798819043eb1b53b0dbc41ef8d7fd1c5d2164d
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index c85751a..8b9e342 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -103,15 +103,28 @@
return StringDataByIdx(type_id.descriptor_idx_);
}
+inline std::string_view DexFile::GetTypeDescriptorView(const dex::TypeId& type_id) const {
+ return StringViewByIdx(type_id.descriptor_idx_);
+}
+
inline const char* DexFile::GetFieldTypeDescriptor(const dex::FieldId& field_id) const {
const dex::TypeId& type_id = GetTypeId(field_id.type_idx_);
return GetTypeDescriptor(type_id);
}
+inline std::string_view DexFile::GetFieldTypeDescriptorView(const dex::FieldId& field_id) const {
+ const dex::TypeId& type_id = GetTypeId(field_id.type_idx_);
+ return GetTypeDescriptorView(type_id);
+}
+
inline const char* DexFile::GetFieldName(const dex::FieldId& field_id) const {
return StringDataByIdx(field_id.name_idx_);
}
+inline std::string_view DexFile::GetFieldNameView(const dex::FieldId& field_id) const {
+ return StringViewByIdx(field_id.name_idx_);
+}
+
inline const char* DexFile::GetMethodDeclaringClassDescriptor(const dex::MethodId& method_id)
const {
const dex::TypeId& type_id = GetTypeId(method_id.class_idx_);
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index ca5e73b..c2a2d64 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -307,6 +307,7 @@
// Returns the type descriptor string of a type id.
const char* GetTypeDescriptor(const dex::TypeId& type_id) const;
+ std::string_view GetTypeDescriptorView(const dex::TypeId& type_id) const;
// Looks up a type for the given string index
const dex::TypeId* FindTypeId(dex::StringIndex string_idx) const;
@@ -354,9 +355,11 @@
// Returns the class descriptor string of a field id.
const char* GetFieldTypeDescriptor(const dex::FieldId& field_id) const;
+ std::string_view GetFieldTypeDescriptorView(const dex::FieldId& field_id) const;
// Returns the name of a field id.
const char* GetFieldName(const dex::FieldId& field_id) const;
+ std::string_view GetFieldNameView(const dex::FieldId& field_id) const;
// Returns the number of method identifiers in the .dex file.
size_t NumMethodIds() const {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5f4f0ab..50b5357 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -9346,14 +9346,8 @@
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader,
uint32_t field_idx) {
- ArtField* resolved = nullptr;
Thread* self = Thread::Current();
- const DexFile& dex_file = *dex_cache->GetDexFile();
- const dex::FieldId& field_id = dex_file.GetFieldId(field_idx);
-
- const char* name = dex_file.GetFieldName(field_id);
- const char* type = dex_file.GetFieldTypeDescriptor(field_id);
- resolved = mirror::Class::FindField(self, klass, name, type);
+ ArtField* resolved = mirror::Class::FindField(self, klass, dex_cache, field_idx);
if (resolved != nullptr &&
hiddenapi::ShouldDenyAccessToMember(resolved,
@@ -10030,10 +10024,7 @@
// Make a pretend boot-classpath.
// TODO: Should we scan the image?
ArtField* const parent_field =
- mirror::Class::FindField(self,
- h_class_loader->GetClass(),
- "parent",
- "Ljava/lang/ClassLoader;");
+ jni::DecodeArtField(WellKnownClasses::java_lang_ClassLoader_parent);
DCHECK(parent_field != nullptr);
if (parent_loader.Get() == nullptr) {
ScopedObjectAccessUnchecked soa(self);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 8064bcc..e6397cb 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -908,27 +908,28 @@
}
// Custom binary search to avoid double comparisons from std::binary_search.
-static ArtField* FindFieldByNameAndType(LengthPrefixedArray<ArtField>* fields,
+static ArtField* FindFieldByNameAndType(const DexFile& dex_file,
+ LengthPrefixedArray<ArtField>* fields,
std::string_view name,
std::string_view type)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (fields == nullptr) {
- return nullptr;
- }
+ DCHECK(fields != nullptr);
size_t low = 0;
size_t high = fields->size();
ArtField* ret = nullptr;
while (low < high) {
size_t mid = (low + high) / 2;
ArtField& field = fields->At(mid);
+ DCHECK(field.GetDexFile() == &dex_file);
+ const dex::FieldId& field_id = dex_file.GetFieldId(field.GetDexFieldIndex());
// Fields are sorted by class, then name, then type descriptor. This is verified in dex file
// verifier. There can be multiple fields with the same in the same class name due to proguard.
// Note: std::string_view::compare() uses lexicographical comparison and treats the `char` as
// unsigned; for modified-UTF-8 without embedded nulls this is consistent with the
// CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues() ordering.
- int result = std::string_view(field.GetName()).compare(name);
+ int result = dex_file.GetFieldNameView(field_id).compare(name);
if (result == 0) {
- result = std::string_view(field.GetTypeDescriptor()).compare(type);
+ result = dex_file.GetFieldTypeDescriptorView(field_id).compare(type);
}
if (result < 0) {
low = mid + 1;
@@ -954,7 +955,12 @@
ArtField* Class::FindDeclaredInstanceField(std::string_view name, std::string_view type) {
// Binary search by name. Interfaces are not relevant because they can't contain instance fields.
- return FindFieldByNameAndType(GetIFieldsPtr(), name, type);
+ LengthPrefixedArray<ArtField>* ifields = GetIFieldsPtr();
+ if (ifields == nullptr) {
+ return nullptr;
+ }
+ DCHECK(!IsProxyClass());
+ return FindFieldByNameAndType(GetDexFile(), ifields, name, type);
}
ArtField* Class::FindDeclaredInstanceField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) {
@@ -994,7 +1000,28 @@
ArtField* Class::FindDeclaredStaticField(std::string_view name, std::string_view type) {
DCHECK(!type.empty());
- return FindFieldByNameAndType(GetSFieldsPtr(), name, type);
+ LengthPrefixedArray<ArtField>* sfields = GetSFieldsPtr();
+ if (sfields == nullptr) {
+ return nullptr;
+ }
+ if (UNLIKELY(IsProxyClass())) {
+ // Proxy fields do not have appropriate dex field indexes required by
+ // `FindFieldByNameAndType()`. However, each proxy class has exactly
+ // the same artificial fields created by the `ClassLinker`.
+ DCHECK_EQ(sfields->size(), 2u);
+ DCHECK_EQ(strcmp(sfields->At(0).GetName(), "interfaces"), 0);
+ DCHECK_EQ(strcmp(sfields->At(0).GetTypeDescriptor(), "[Ljava/lang/Class;"), 0);
+ DCHECK_EQ(strcmp(sfields->At(1).GetName(), "throws"), 0);
+ DCHECK_EQ(strcmp(sfields->At(1).GetTypeDescriptor(), "[[Ljava/lang/Class;"), 0);
+ if (name == "interfaces") {
+ return (type == "[Ljava/lang/Class;") ? &sfields->At(0) : nullptr;
+ } else if (name == "throws") {
+ return (type == "[[Ljava/lang/Class;") ? &sfields->At(1) : nullptr;
+ } else {
+ return nullptr;
+ }
+ }
+ return FindFieldByNameAndType(GetDexFile(), sfields, name, type);
}
ArtField* Class::FindDeclaredStaticField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) {
@@ -1059,30 +1086,124 @@
return nullptr;
}
+// Find a field using the JLS field resolution order
+FLATTEN
ArtField* Class::FindField(Thread* self,
ObjPtr<Class> klass,
- std::string_view name,
- std::string_view type) {
- // Find a field using the JLS field resolution order
- for (ObjPtr<Class> k = klass; k != nullptr; k = k->GetSuperClass()) {
+ ObjPtr<mirror::DexCache> dex_cache,
+ uint32_t field_idx) {
+ // FIXME: Hijacking a proxy class by a custom class loader can break this assumption.
+ DCHECK(!klass->IsProxyClass());
+
+ // First try to find a declared field by `field_idx` if we have a `dex_cache` match.
+ ObjPtr<DexCache> klass_dex_cache = klass->GetDexCache();
+ if (klass_dex_cache == dex_cache) {
+ // Lookup is always performed in the class referenced by the FieldId.
+ DCHECK_EQ(klass->dex_type_idx_,
+ klass_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_.index_);
+ ArtField* f = klass->FindDeclaredInstanceField(klass_dex_cache, field_idx);
+ if (f == nullptr) {
+ f = klass->FindDeclaredStaticField(klass_dex_cache, field_idx);
+ }
+ if (f != nullptr) {
+ return f;
+ }
+ }
+
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ const dex::FieldId& field_id = dex_file.GetFieldId(field_idx);
+
+ std::string_view name; // Do not touch the dex file string data until actually needed.
+ std::string_view type;
+ auto ensure_name_and_type_initialized = [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (name.empty()) {
+ name = dex_file.GetFieldNameView(field_id);
+ type = dex_file.GetFieldTypeDescriptorView(field_id);
+ }
+ };
+
+ auto search_direct_interfaces = [&](ObjPtr<mirror::Class> k)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // TODO: The `FindStaticField()` performs a recursive search and it's possible to
+ // construct interface hierarchies that make the time complexity exponential in depth.
+ // Rewrite this with a `HashSet<mirror::Class*>` to mark classes we have already
+ // searched for the field, so that we call `FindDeclaredStaticField()` only once
+ // on each interface. And use a work queue to avoid unlimited recursion depth.
+ // TODO: Once we call `FindDeclaredStaticField()` directly, use search by indexes
+ // instead of strings if the interface's dex cache matches `dex_cache`. This shall
+ // allow delaying the `ensure_name_and_type_initialized()` call further.
+ uint32_t num_interfaces = k->NumDirectInterfaces();
+ if (num_interfaces != 0u) {
+ ensure_name_and_type_initialized();
+ for (uint32_t i = 0; i != num_interfaces; ++i) {
+ ObjPtr<Class> interface = GetDirectInterface(self, k, i);
+ DCHECK(interface != nullptr);
+ ArtField* f = FindStaticField(self, interface, name, type);
+ if (f != nullptr) {
+ return f;
+ }
+ }
+ }
+ return static_cast<ArtField*>(nullptr);
+ };
+
+ // If we had a dex cache mismatch, search declared fields by name and type.
+ if (klass_dex_cache != dex_cache &&
+ (klass->GetIFieldsPtr() != nullptr || klass->GetSFieldsPtr() != nullptr)) {
+ ensure_name_and_type_initialized();
+ ArtField* f = klass->FindDeclaredInstanceField(name, type);
+ if (f == nullptr) {
+ f = klass->FindDeclaredStaticField(name, type);
+ }
+ if (f != nullptr) {
+ return f;
+ }
+ }
+
+ // Search direct interfaces.
+ {
+ ArtField* f = search_direct_interfaces(klass);
+ if (f != nullptr) {
+ return f;
+ }
+ }
+
+ // Continue searching in superclasses.
+ for (ObjPtr<Class> k = klass->GetSuperClass(); k != nullptr; k = k->GetSuperClass()) {
// Is the field in this class?
- ArtField* f = k->FindDeclaredInstanceField(name, type);
- if (f != nullptr) {
- return f;
- }
- f = k->FindDeclaredStaticField(name, type);
- if (f != nullptr) {
- return f;
- }
- // Is this field in any of this class' interfaces?
- for (uint32_t i = 0, num_interfaces = k->NumDirectInterfaces(); i != num_interfaces; ++i) {
- ObjPtr<Class> interface = GetDirectInterface(self, k, i);
- DCHECK(interface != nullptr);
- f = FindStaticField(self, interface, name, type);
+ ObjPtr<DexCache> k_dex_cache = k->GetDexCache();
+ if (k_dex_cache == dex_cache) {
+ // Matching dex_cache. We cannot compare the `field_idx` anymore because
+ // the type index differs, so compare the name index and type index.
+ for (ArtField& field : k->GetIFields()) {
+ const dex::FieldId& other_field_id = dex_file.GetFieldId(field.GetDexFieldIndex());
+ if (other_field_id.name_idx_ == field_id.name_idx_ &&
+ other_field_id.type_idx_ == field_id.type_idx_) {
+ return &field;
+ }
+ }
+ for (ArtField& field : k->GetSFields()) {
+ const dex::FieldId& other_field_id = dex_file.GetFieldId(field.GetDexFieldIndex());
+ if (other_field_id.name_idx_ == field_id.name_idx_ &&
+ other_field_id.type_idx_ == field_id.type_idx_) {
+ return &field;
+ }
+ }
+ } else if (k->GetIFieldsPtr() != nullptr || k->GetSFieldsPtr() != nullptr) {
+ ensure_name_and_type_initialized();
+ ArtField* f = k->FindDeclaredInstanceField(name, type);
+ if (f == nullptr) {
+ f = k->FindDeclaredStaticField(name, type);
+ }
if (f != nullptr) {
return f;
}
}
+ // Is this field in any of this class' interfaces?
+ ArtField* f = search_direct_interfaces(k);
+ if (f != nullptr) {
+ return f;
+ }
}
return nullptr;
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index cc2924e..aff98ca 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1094,8 +1094,8 @@
// Find a static or instance field using the JLS resolution order
static ArtField* FindField(Thread* self,
ObjPtr<Class> klass,
- std::string_view name,
- std::string_view type)
+ ObjPtr<mirror::DexCache> dex_cache,
+ uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
// Finds the given instance field in this class or a superclass.
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index 5e763dd..c32c702 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -278,7 +278,7 @@
ScopedObjectAccess soa(self);
ObjPtr<Object> i = BoxPrimitive(Primitive::kPrimInt, JValue::FromPrimitive<int32_t>(37));
- ArtField* value = mirror::Class::FindField(self, i->GetClass(), "value", "I");
+ ArtField* value = i->GetClass()->FindDeclaredInstanceField("value", "I");
int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
VarHandle::AccessMode::kGetAndSet,
VarHandle::AccessMode::kGetAndBitwiseXor);
@@ -481,7 +481,7 @@
ScopedObjectAccess soa(self);
ObjPtr<Object> i = BoxPrimitive(Primitive::kPrimInt, JValue::FromPrimitive<int32_t>(37));
- ArtField* value = mirror::Class::FindField(self, i->GetClass(), "MIN_VALUE", "I");
+ ArtField* value = i->GetClass()->FindDeclaredStaticField("MIN_VALUE", "I");
int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kSet,
VarHandle::AccessMode::kGetOpaque,
VarHandle::AccessMode::kGetAndBitwiseAndRelease);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 16a5f93..61a751c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3158,14 +3158,14 @@
return nullptr;
}
- ArtField* stack_trace_element_field = h_aste_class->FindField(
- soa.Self(), h_aste_class.Get(), "stackTraceElement", "Ljava/lang/StackTraceElement;");
+ ArtField* stack_trace_element_field =
+ h_aste_class->FindDeclaredInstanceField("stackTraceElement", "Ljava/lang/StackTraceElement;");
DCHECK(stack_trace_element_field != nullptr);
- ArtField* held_locks_field = h_aste_class->FindField(
- soa.Self(), h_aste_class.Get(), "heldLocks", "[Ljava/lang/Object;");
+ ArtField* held_locks_field =
+ h_aste_class->FindDeclaredInstanceField("heldLocks", "[Ljava/lang/Object;");
DCHECK(held_locks_field != nullptr);
- ArtField* blocked_on_field = h_aste_class->FindField(
- soa.Self(), h_aste_class.Get(), "blockedOn", "Ljava/lang/Object;");
+ ArtField* blocked_on_field =
+ h_aste_class->FindDeclaredInstanceField("blockedOn", "Ljava/lang/Object;");
DCHECK(blocked_on_field != nullptr);
int32_t length = static_cast<int32_t>(dumper.stack_trace_elements_.size());
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index ee7c591..f606d02 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -627,15 +627,13 @@
class_linker_->FindClass(soa.Self(), "Ljava/lang/Boolean;", class_loader));
ASSERT_TRUE(boolean_class != nullptr);
ASSERT_TRUE(heap->ObjectIsInBootImageSpace(boolean_class.Get()));
- ArtField* true_field =
- mirror::Class::FindField(soa.Self(), boolean_class.Get(), "TRUE", "Ljava/lang/Boolean;");
+ ArtField* true_field = boolean_class->FindDeclaredStaticField("TRUE", "Ljava/lang/Boolean;");
ASSERT_TRUE(true_field != nullptr);
ASSERT_TRUE(true_field->IsStatic());
Handle<mirror::Object> true_value = hs.NewHandle(true_field->GetObject(boolean_class.Get()));
ASSERT_TRUE(true_value != nullptr);
ASSERT_TRUE(heap->ObjectIsInBootImageSpace(true_value.Get()));
- ArtField* value_field =
- mirror::Class::FindField(soa.Self(), boolean_class.Get(), "value", "Z");
+ ArtField* value_field = boolean_class->FindDeclaredInstanceField("value", "Z");
ASSERT_TRUE(value_field != nullptr);
ASSERT_FALSE(value_field->IsStatic());
@@ -643,8 +641,7 @@
class_linker_->FindClass(soa.Self(), "LTransaction$StaticFieldClass;", class_loader)));
ASSERT_TRUE(static_field_class != nullptr);
ASSERT_FALSE(heap->ObjectIsInBootImageSpace(static_field_class.Get()));
- ArtField* int_field =
- mirror::Class::FindField(soa.Self(), static_field_class.Get(), "intField", "I");
+ ArtField* int_field = static_field_class->FindDeclaredStaticField("intField", "I");
ASSERT_TRUE(int_field != nullptr);
Handle<mirror::Class> static_fields_test_class(hs.NewHandle(
@@ -652,7 +649,7 @@
ASSERT_TRUE(static_fields_test_class != nullptr);
ASSERT_FALSE(heap->ObjectIsInBootImageSpace(static_fields_test_class.Get()));
ArtField* static_fields_test_int_field =
- mirror::Class::FindField(soa.Self(), static_fields_test_class.Get(), "intField", "I");
+ static_fields_test_class->FindDeclaredStaticField("intField", "I");
ASSERT_TRUE(static_fields_test_int_field != nullptr);
Handle<mirror::Class> instance_fields_test_class(hs.NewHandle(
@@ -660,7 +657,7 @@
ASSERT_TRUE(instance_fields_test_class != nullptr);
ASSERT_FALSE(heap->ObjectIsInBootImageSpace(instance_fields_test_class.Get()));
ArtField* instance_fields_test_int_field =
- mirror::Class::FindField(soa.Self(), instance_fields_test_class.Get(), "intField", "I");
+ instance_fields_test_class->FindDeclaredInstanceField("intField", "I");
ASSERT_TRUE(instance_fields_test_int_field != nullptr);
Handle<mirror::Object> instance_fields_test_object = hs.NewHandle(
instance_fields_test_class->Alloc(soa.Self(), heap->GetCurrentAllocator()));
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 3b3a1a3..8414aa4 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -136,6 +136,7 @@
jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
jfieldID WellKnownClasses::java_io_FileDescriptor_descriptor;
+jfieldID WellKnownClasses::java_lang_ClassLoader_parent;
jfieldID WellKnownClasses::java_lang_Thread_parkBlocker;
jfieldID WellKnownClasses::java_lang_Thread_daemon;
jfieldID WellKnownClasses::java_lang_Thread_group;
@@ -429,30 +430,46 @@
ScopedLocalRef<jclass> java_io_FileDescriptor(env, env->FindClass("java/io/FileDescriptor"));
java_io_FileDescriptor_descriptor = CacheField(env, java_io_FileDescriptor.get(), false, "descriptor", "I");
- java_lang_Thread_parkBlocker = CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
+ java_lang_ClassLoader_parent =
+ CacheField(env, java_lang_ClassLoader, false, "parent", "Ljava/lang/ClassLoader;");
+ java_lang_Thread_parkBlocker =
+ CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
- java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
+ java_lang_Thread_group =
+ CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;");
java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
java_lang_Thread_systemDaemon = CacheField(env, java_lang_Thread, false, "systemDaemon", "Z");
- java_lang_Thread_unparkedBeforeStart = CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
- java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
+ java_lang_Thread_unparkedBeforeStart =
+ CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
+ java_lang_ThreadGroup_groups =
+ CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I");
- java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
- java_lang_ThreadGroup_name = CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;");
- java_lang_ThreadGroup_parent = CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;");
- java_lang_ThreadGroup_systemThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
- java_lang_Throwable_cause = CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;");
- java_lang_Throwable_detailMessage = CacheField(env, java_lang_Throwable, false, "detailMessage", "Ljava/lang/String;");
- java_lang_Throwable_stackTrace = CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;");
- java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
- java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
+ java_lang_ThreadGroup_mainThreadGroup =
+ CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_name =
+ CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;");
+ java_lang_ThreadGroup_parent =
+ CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_systemThreadGroup =
+ CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
+ java_lang_Throwable_cause =
+ CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;");
+ java_lang_Throwable_detailMessage =
+ CacheField(env, java_lang_Throwable, false, "detailMessage", "Ljava/lang/String;");
+ java_lang_Throwable_stackTrace =
+ CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;");
+ java_lang_Throwable_stackState =
+ CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
+ java_lang_Throwable_suppressedExceptions =
+ CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
java_nio_Buffer_address = CacheField(env, java_nio_Buffer, false, "address", "J");
java_nio_Buffer_capacity = CacheField(env, java_nio_Buffer, false, "capacity", "I");
- java_nio_Buffer_elementSizeShift = CacheField(env, java_nio_Buffer, false, "_elementSizeShift", "I");
+ java_nio_Buffer_elementSizeShift =
+ CacheField(env, java_nio_Buffer, false, "_elementSizeShift", "I");
java_nio_Buffer_limit = CacheField(env, java_nio_Buffer, false, "limit", "I");
java_nio_Buffer_position = CacheField(env, java_nio_Buffer, false, "position", "I");
@@ -461,12 +478,18 @@
java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z");
java_nio_ByteBuffer_limit = CacheField(env, java_nio_ByteBuffer, false, "limit", "I");
java_nio_ByteBuffer_offset = CacheField(env, java_nio_ByteBuffer, false, "offset", "I");
- java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
- libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
- org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B");
- org_apache_harmony_dalvik_ddmc_Chunk_length = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I");
- org_apache_harmony_dalvik_ddmc_Chunk_offset = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I");
- org_apache_harmony_dalvik_ddmc_Chunk_type = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "type", "I");
+ java_util_Collections_EMPTY_LIST =
+ CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
+ libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(
+ env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
+ org_apache_harmony_dalvik_ddmc_Chunk_data =
+ CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B");
+ org_apache_harmony_dalvik_ddmc_Chunk_length =
+ CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I");
+ org_apache_harmony_dalvik_ddmc_Chunk_offset =
+ CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I");
+ org_apache_harmony_dalvik_ddmc_Chunk_type =
+ CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "type", "I");
java_lang_Boolean_valueOf = CachePrimitiveBoxingMethod(env, 'Z', "java/lang/Boolean");
java_lang_Byte_valueOf = CachePrimitiveBoxingMethod(env, 'B', "java/lang/Byte");
@@ -602,6 +625,7 @@
dalvik_system_DexPathList_dexElements = nullptr;
dalvik_system_DexPathList__Element_dexFile = nullptr;
dalvik_system_VMRuntime_nonSdkApiUsageConsumer = nullptr;
+ java_lang_ClassLoader_parent = nullptr;
java_lang_Thread_parkBlocker = nullptr;
java_lang_Thread_daemon = nullptr;
java_lang_Thread_group = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index b2e01ae..434d041 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -149,6 +149,7 @@
static jfieldID dalvik_system_DexPathList__Element_dexFile;
static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
static jfieldID java_io_FileDescriptor_descriptor;
+ static jfieldID java_lang_ClassLoader_parent;
static jfieldID java_lang_Thread_parkBlocker;
static jfieldID java_lang_Thread_daemon;
static jfieldID java_lang_Thread_group;