Initialize array classes in pre-fence visitor.
Rewrite array class initialization to make it easier to
reason about memory visibility. Initialize all members in
the pre-fence visitor for the normal use case. Refactor
initialization of core array classes without boot image to
avoid special-casing in ClassLinker::CreateArrayClass().
Note that the Class::object_size_alloc_fast_path_ field
of primitive classes (instances of which cannot be
allocated) will be kept at numeric_limits<uint32_t>::max().
The boot image before and after is otherwise identical.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: aosp_taimen-userdebug boots.
Test: run-gtests.sh
Test: testrunner.py --target --optimizing
Change-Id: I570e3af011c8d3383ce46c81eb6f2fa60c5a4b0f
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2bbcbb7..b0d2da6 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -559,27 +559,27 @@
DCHECK_EQ(GetArrayIfTable(), object_array_class->GetIfTable());
// Setup the primitive type classes.
- SetClassRoot(ClassRoot::kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean));
- SetClassRoot(ClassRoot::kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte));
- SetClassRoot(ClassRoot::kPrimitiveChar, CreatePrimitiveClass(self, Primitive::kPrimChar));
- SetClassRoot(ClassRoot::kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort));
- SetClassRoot(ClassRoot::kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt));
- SetClassRoot(ClassRoot::kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong));
- SetClassRoot(ClassRoot::kPrimitiveFloat, CreatePrimitiveClass(self, Primitive::kPrimFloat));
- SetClassRoot(ClassRoot::kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble));
- SetClassRoot(ClassRoot::kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid));
+ CreatePrimitiveClass(self, Primitive::kPrimBoolean, ClassRoot::kPrimitiveBoolean);
+ CreatePrimitiveClass(self, Primitive::kPrimByte, ClassRoot::kPrimitiveByte);
+ CreatePrimitiveClass(self, Primitive::kPrimChar, ClassRoot::kPrimitiveChar);
+ CreatePrimitiveClass(self, Primitive::kPrimShort, ClassRoot::kPrimitiveShort);
+ CreatePrimitiveClass(self, Primitive::kPrimInt, ClassRoot::kPrimitiveInt);
+ CreatePrimitiveClass(self, Primitive::kPrimLong, ClassRoot::kPrimitiveLong);
+ CreatePrimitiveClass(self, Primitive::kPrimFloat, ClassRoot::kPrimitiveFloat);
+ CreatePrimitiveClass(self, Primitive::kPrimDouble, ClassRoot::kPrimitiveDouble);
+ CreatePrimitiveClass(self, Primitive::kPrimVoid, ClassRoot::kPrimitiveVoid);
- // Create int array type for native pointer arrays (for example vtables) on 32-bit archs.
- Handle<mirror::Class> int_array_class(hs.NewHandle(
- AllocPrimitiveArrayClass(self, java_lang_Class.Get())));
- int_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveInt, this));
- SetClassRoot(ClassRoot::kIntArrayClass, int_array_class.Get());
-
- // Create long array type for native pointer arrays (for example vtables) on 64-bit archs.
- Handle<mirror::Class> long_array_class(hs.NewHandle(
- AllocPrimitiveArrayClass(self, java_lang_Class.Get())));
- long_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveLong, this));
- SetClassRoot(ClassRoot::kLongArrayClass, long_array_class.Get());
+ // Allocate the primitive array classes. We need only the native pointer
+ // array at this point (int[] or long[], depending on architecture) but
+ // we shall perform the same setup steps for all primitive array classes.
+ AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveBoolean, ClassRoot::kBooleanArrayClass);
+ AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveByte, ClassRoot::kByteArrayClass);
+ AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveChar, ClassRoot::kCharArrayClass);
+ AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveShort, ClassRoot::kShortArrayClass);
+ AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveInt, ClassRoot::kIntArrayClass);
+ AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveLong, ClassRoot::kLongArrayClass);
+ AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveFloat, ClassRoot::kFloatArrayClass);
+ AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveDouble, ClassRoot::kDoubleArrayClass);
// now that these are registered, we can use AllocClass() and AllocObjectArray
@@ -652,37 +652,23 @@
CheckSystemClass(self, dalvik_system_ClassExt, "Ldalvik/system/ClassExt;");
CHECK_EQ(dalvik_system_ClassExt->GetObjectSize(), mirror::ClassExt::InstanceSize());
- // Setup the primitive array type classes - can't be done until Object has a vtable.
- AllocAndSetPrimitiveArrayClassRoot(self,
- java_lang_Class.Get(),
- ClassRoot::kBooleanArrayClass,
- ClassRoot::kPrimitiveBoolean,
- "[Z");
-
- AllocAndSetPrimitiveArrayClassRoot(
- self, java_lang_Class.Get(), ClassRoot::kByteArrayClass, ClassRoot::kPrimitiveByte, "[B");
-
- AllocAndSetPrimitiveArrayClassRoot(
- self, java_lang_Class.Get(), ClassRoot::kCharArrayClass, ClassRoot::kPrimitiveChar, "[C");
-
- AllocAndSetPrimitiveArrayClassRoot(
- self, java_lang_Class.Get(), ClassRoot::kShortArrayClass, ClassRoot::kPrimitiveShort, "[S");
-
- CheckSystemClass(self, int_array_class, "[I");
- CheckSystemClass(self, long_array_class, "[J");
-
- AllocAndSetPrimitiveArrayClassRoot(
- self, java_lang_Class.Get(), ClassRoot::kFloatArrayClass, ClassRoot::kPrimitiveFloat, "[F");
-
- AllocAndSetPrimitiveArrayClassRoot(
- self, java_lang_Class.Get(), ClassRoot::kDoubleArrayClass, ClassRoot::kPrimitiveDouble, "[D");
-
// Run Class through FindSystemClass. This initializes the dex_cache_ fields and register it
// in class_table_.
CheckSystemClass(self, java_lang_Class, "Ljava/lang/Class;");
- CheckSystemClass(self, class_array_class, "[Ljava/lang/Class;");
- CheckSystemClass(self, object_array_class, "[Ljava/lang/Object;");
+ // Setup core array classes, i.e. Object[], String[] and Class[] and primitive
+ // arrays - can't be done until Object has a vtable and component classes are loaded.
+ FinishCoreArrayClassSetup(ClassRoot::kObjectArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kClassArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kJavaLangStringArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kBooleanArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kByteArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kCharArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kShortArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kIntArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kLongArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kFloatArrayClass);
+ FinishCoreArrayClassSetup(ClassRoot::kDoubleArrayClass);
// Setup the single, global copy of "iftable".
auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;"));
@@ -2567,16 +2553,16 @@
return dex_cache;
}
-template <bool kMovable>
+template <bool kMovable, typename PreFenceVisitor>
ObjPtr<mirror::Class> ClassLinker::AllocClass(Thread* self,
ObjPtr<mirror::Class> java_lang_Class,
- uint32_t class_size) {
+ uint32_t class_size,
+ const PreFenceVisitor& pre_fence_visitor) {
DCHECK_GE(class_size, sizeof(mirror::Class));
gc::Heap* heap = Runtime::Current()->GetHeap();
- mirror::Class::InitializeClassVisitor visitor(class_size);
ObjPtr<mirror::Object> k = (kMovingClasses && kMovable) ?
- heap->AllocObject(self, java_lang_Class, class_size, visitor) :
- heap->AllocNonMovableObject(self, java_lang_Class, class_size, visitor);
+ heap->AllocObject(self, java_lang_Class, class_size, pre_fence_visitor) :
+ heap->AllocNonMovableObject(self, java_lang_Class, class_size, pre_fence_visitor);
if (UNLIKELY(k == nullptr)) {
self->AssertPendingOOMException();
return nullptr;
@@ -2584,20 +2570,96 @@
return k->AsClass();
}
+template <bool kMovable>
+ObjPtr<mirror::Class> ClassLinker::AllocClass(Thread* self,
+ ObjPtr<mirror::Class> java_lang_Class,
+ uint32_t class_size) {
+ mirror::Class::InitializeClassVisitor visitor(class_size);
+ return AllocClass<kMovable>(self, java_lang_Class, class_size, visitor);
+}
+
ObjPtr<mirror::Class> ClassLinker::AllocClass(Thread* self, uint32_t class_size) {
return AllocClass(self, GetClassRoot<mirror::Class>(this), class_size);
}
-ObjPtr<mirror::Class> ClassLinker::AllocPrimitiveArrayClass(Thread* self,
- ObjPtr<mirror::Class> java_lang_Class) {
+void ClassLinker::AllocPrimitiveArrayClass(Thread* self,
+ ClassRoot primitive_root,
+ ClassRoot array_root) {
// We make this class non-movable for the unlikely case where it were to be
// moved by a sticky-bit (minor) collection when using the Generational
// Concurrent Copying (CC) collector, potentially creating a stale reference
// in the `klass_` field of one of its instances allocated in the Large-Object
// Space (LOS) -- see the comment about the dirty card scanning logic in
// art::gc::collector::ConcurrentCopying::MarkingPhase.
- return AllocClass</* kMovable= */ false>(
- self, java_lang_Class, mirror::Array::ClassSize(image_pointer_size_));
+ ObjPtr<mirror::Class> array_class = AllocClass</* kMovable= */ false>(
+ self, GetClassRoot<mirror::Class>(this), mirror::Array::ClassSize(image_pointer_size_));
+ ObjPtr<mirror::Class> component_type = GetClassRoot(primitive_root, this);
+ DCHECK(component_type->IsPrimitive());
+ array_class->SetComponentType(component_type);
+ SetClassRoot(array_root, array_class);
+}
+
+void ClassLinker::FinishArrayClassSetup(ObjPtr<mirror::Class> array_class) {
+ ObjPtr<mirror::Class> java_lang_Object = GetClassRoot<mirror::Object>(this);
+ array_class->SetSuperClass(java_lang_Object);
+ array_class->SetVTable(java_lang_Object->GetVTable());
+ array_class->SetPrimitiveType(Primitive::kPrimNot);
+ ObjPtr<mirror::Class> component_type = array_class->GetComponentType();
+ array_class->SetClassFlags(component_type->IsPrimitive()
+ ? mirror::kClassFlagNoReferenceFields
+ : mirror::kClassFlagObjectArray);
+ array_class->SetClassLoader(component_type->GetClassLoader());
+ array_class->SetStatusForPrimitiveOrArray(ClassStatus::kLoaded);
+ array_class->PopulateEmbeddedVTable(image_pointer_size_);
+ ImTable* object_imt = java_lang_Object->GetImt(image_pointer_size_);
+ array_class->SetImt(object_imt, image_pointer_size_);
+ // Skip EnsureSkipAccessChecksMethods(). We can skip the verified status,
+ // the kAccVerificationAttempted flag is added below, and there are no
+ // methods that need the kAccSkipAccessChecks flag.
+ DCHECK_EQ(array_class->NumMethods(), 0u);
+
+ // don't need to set new_class->SetObjectSize(..)
+ // because Object::SizeOf delegates to Array::SizeOf
+
+ // All arrays have java/lang/Cloneable and java/io/Serializable as
+ // interfaces. We need to set that up here, so that stuff like
+ // "instanceof" works right.
+
+ // Use the single, global copies of "interfaces" and "iftable"
+ // (remember not to free them for arrays).
+ {
+ ObjPtr<mirror::IfTable> array_iftable = GetArrayIfTable();
+ CHECK(array_iftable != nullptr);
+ array_class->SetIfTable(array_iftable);
+ }
+
+ // Inherit access flags from the component type.
+ int access_flags = component_type->GetAccessFlags();
+ // Lose any implementation detail flags; in particular, arrays aren't finalizable.
+ access_flags &= kAccJavaFlagsMask;
+ // Arrays can't be used as a superclass or interface, so we want to add "abstract final"
+ // and remove "interface".
+ access_flags |= kAccAbstract | kAccFinal;
+ access_flags &= ~kAccInterface;
+ // Arrays are access-checks-clean and preverified.
+ access_flags |= kAccVerificationAttempted;
+
+ array_class->SetAccessFlags(access_flags);
+
+ array_class->SetStatusForPrimitiveOrArray(ClassStatus::kInitialized);
+}
+
+void ClassLinker::FinishCoreArrayClassSetup(ClassRoot array_root) {
+ // Do not hold lock on the array class object, the initialization of
+ // core array classes is done while the process is still single threaded.
+ ObjPtr<mirror::Class> array_class = GetClassRoot(array_root, this);
+ FinishArrayClassSetup(array_class);
+
+ std::string temp;
+ const char* descriptor = array_class->GetDescriptor(&temp);
+ size_t hash = ComputeModifiedUtf8Hash(descriptor);
+ ObjPtr<mirror::Class> existing = InsertClass(descriptor, array_class, hash);
+ CHECK(existing == nullptr);
}
ObjPtr<mirror::ObjectArray<mirror::StackTraceElement>> ClassLinker::AllocStackTraceElementArray(
@@ -4081,28 +4143,29 @@
return DexCacheData();
}
-ObjPtr<mirror::Class> ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) {
+void ClassLinker::CreatePrimitiveClass(Thread* self,
+ Primitive::Type type,
+ ClassRoot primitive_root) {
ObjPtr<mirror::Class> primitive_class =
AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_));
- if (UNLIKELY(primitive_class == nullptr)) {
- self->AssertPendingOOMException();
- return nullptr;
- }
- // Must hold lock on object when initializing.
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(primitive_class));
- ObjectLock<mirror::Class> lock(self, h_class);
- h_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract);
- h_class->SetPrimitiveType(type);
- h_class->SetIfTable(GetClassRoot<mirror::Object>(this)->GetIfTable());
- EnsureSkipAccessChecksMethods</* kNeedsVerified= */ true>(h_class, image_pointer_size_);
- mirror::Class::SetStatus(h_class, ClassStatus::kInitialized, self);
+ CHECK(primitive_class != nullptr) << "OOM for primitive class " << type;
+ // Do not hold lock on the primitive class object, the initialization of
+ // primitive classes is done while the process is still single threaded.
+ primitive_class->SetAccessFlags(
+ kAccPublic | kAccFinal | kAccAbstract | kAccVerificationAttempted);
+ primitive_class->SetPrimitiveType(type);
+ primitive_class->SetIfTable(GetClassRoot<mirror::Object>(this)->GetIfTable());
+ // Skip EnsureSkipAccessChecksMethods(). We can skip the verified status,
+ // the kAccVerificationAttempted flag was added above, and there are no
+ // methods that need the kAccSkipAccessChecks flag.
+ DCHECK_EQ(primitive_class->NumMethods(), 0u);
+ primitive_class->SetStatusForPrimitiveOrArray(ClassStatus::kInitialized);
const char* descriptor = Primitive::Descriptor(type);
ObjPtr<mirror::Class> existing = InsertClass(descriptor,
- h_class.Get(),
+ primitive_class,
ComputeModifiedUtf8Hash(descriptor));
CHECK(existing == nullptr) << "InitPrimitiveClass(" << type << ") failed";
- return h_class.Get();
+ SetClassRoot(primitive_root, primitive_class);
}
inline ObjPtr<mirror::IfTable> ClassLinker::GetArrayIfTable() {
@@ -4183,6 +4246,14 @@
return new_class;
}
}
+ // Core array classes, i.e. Object[], Class[], String[] and primitive
+ // arrays, have special initialization and they should be found above.
+ DCHECK(!component_type->IsObjectClass() ||
+ // Guard from false positives for errors before setting superclass.
+ component_type->IsErroneousUnresolved());
+ DCHECK(!component_type->IsStringClass());
+ DCHECK(!component_type->IsClassClass());
+ DCHECK(!component_type->IsPrimitive());
// Fill out the fields in the Class.
//
@@ -4192,92 +4263,26 @@
//
// Array classes are simple enough that we don't need to do a full
// link step.
- auto new_class = hs.NewHandle<mirror::Class>(nullptr);
- if (UNLIKELY(!init_done_)) {
- // Classes that were hand created, ie not by FindSystemClass
- if (strcmp(descriptor, "[Ljava/lang/Class;") == 0) {
- new_class.Assign(GetClassRoot<mirror::ObjectArray<mirror::Class>>(this));
- } else if (strcmp(descriptor, "[Ljava/lang/Object;") == 0) {
- new_class.Assign(GetClassRoot<mirror::ObjectArray<mirror::Object>>(this));
- } else if (strcmp(descriptor, "[Ljava/lang/String;") == 0) {
- new_class.Assign(GetClassRoot<mirror::ObjectArray<mirror::String>>(this));
- } else if (strcmp(descriptor, "[Z") == 0) {
- new_class.Assign(GetClassRoot<mirror::BooleanArray>(this));
- } else if (strcmp(descriptor, "[B") == 0) {
- new_class.Assign(GetClassRoot<mirror::ByteArray>(this));
- } else if (strcmp(descriptor, "[C") == 0) {
- new_class.Assign(GetClassRoot<mirror::CharArray>(this));
- } else if (strcmp(descriptor, "[S") == 0) {
- new_class.Assign(GetClassRoot<mirror::ShortArray>(this));
- } else if (strcmp(descriptor, "[I") == 0) {
- new_class.Assign(GetClassRoot<mirror::IntArray>(this));
- } else if (strcmp(descriptor, "[J") == 0) {
- new_class.Assign(GetClassRoot<mirror::LongArray>(this));
- } else if (strcmp(descriptor, "[F") == 0) {
- new_class.Assign(GetClassRoot<mirror::FloatArray>(this));
- } else if (strcmp(descriptor, "[D") == 0) {
- new_class.Assign(GetClassRoot<mirror::DoubleArray>(this));
- }
- }
+ size_t array_class_size = mirror::Array::ClassSize(image_pointer_size_);
+ auto visitor = [this, array_class_size, component_type](ObjPtr<mirror::Object> obj,
+ size_t usable_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class::InitializeClassVisitor init_class(array_class_size);
+ init_class(obj, usable_size);
+ ObjPtr<mirror::Class> klass = ObjPtr<mirror::Class>::DownCast(obj);
+ klass->SetComponentType(component_type.Get());
+ // Do not hold lock for initialization, the fence issued after the visitor
+ // returns ensures memory visibility together with the implicit consume
+ // semantics (for all supported architectures) for any thread that loads
+ // the array class reference from any memory locations afterwards.
+ FinishArrayClassSetup(klass);
+ };
+ auto new_class = hs.NewHandle<mirror::Class>(
+ AllocClass(self, GetClassRoot<mirror::Class>(this), array_class_size, visitor));
if (new_class == nullptr) {
- new_class.Assign(AllocClass(self, mirror::Array::ClassSize(image_pointer_size_)));
- if (new_class == nullptr) {
- self->AssertPendingOOMException();
- return nullptr;
- }
- new_class->SetComponentType(component_type.Get());
+ self->AssertPendingOOMException();
+ return nullptr;
}
- ObjectLock<mirror::Class> lock(self, new_class); // Must hold lock on object when initializing.
- DCHECK(new_class->GetComponentType() != nullptr);
- ObjPtr<mirror::Class> java_lang_Object = GetClassRoot<mirror::Object>(this);
- new_class->SetSuperClass(java_lang_Object);
- new_class->SetVTable(java_lang_Object->GetVTable());
- new_class->SetPrimitiveType(Primitive::kPrimNot);
- new_class->SetClassLoader(component_type->GetClassLoader());
- if (component_type->IsPrimitive()) {
- new_class->SetClassFlags(mirror::kClassFlagNoReferenceFields);
- } else {
- new_class->SetClassFlags(mirror::kClassFlagObjectArray);
- }
- mirror::Class::SetStatus(new_class, ClassStatus::kLoaded, self);
- new_class->PopulateEmbeddedVTable(image_pointer_size_);
- ImTable* object_imt = java_lang_Object->GetImt(image_pointer_size_);
- new_class->SetImt(object_imt, image_pointer_size_);
- EnsureSkipAccessChecksMethods</* kNeedsVerified= */ true>(new_class, image_pointer_size_);
- mirror::Class::SetStatus(new_class, ClassStatus::kInitialized, self);
- // don't need to set new_class->SetObjectSize(..)
- // because Object::SizeOf delegates to Array::SizeOf
-
- // All arrays have java/lang/Cloneable and java/io/Serializable as
- // interfaces. We need to set that up here, so that stuff like
- // "instanceof" works right.
- //
- // Note: The GC could run during the call to FindSystemClass,
- // so we need to make sure the class object is GC-valid while we're in
- // there. Do this by clearing the interface list so the GC will just
- // think that the entries are null.
-
-
- // Use the single, global copies of "interfaces" and "iftable"
- // (remember not to free them for arrays).
- {
- ObjPtr<mirror::IfTable> array_iftable = GetArrayIfTable();
- CHECK(array_iftable != nullptr);
- new_class->SetIfTable(array_iftable);
- }
-
- // Inherit access flags from the component type.
- int access_flags = new_class->GetComponentType()->GetAccessFlags();
- // Lose any implementation detail flags; in particular, arrays aren't finalizable.
- access_flags &= kAccJavaFlagsMask;
- // Arrays can't be used as a superclass or interface, so we want to add "abstract final"
- // and remove "interface".
- access_flags |= kAccAbstract | kAccFinal;
- access_flags &= ~kAccInterface;
- // Arrays are access-checks-clean and preverified.
- access_flags |= kAccVerificationAttempted;
-
- new_class->SetAccessFlags(access_flags);
ObjPtr<mirror::Class> existing = InsertClass(descriptor, new_class.Get(), hash);
if (existing == nullptr) {
@@ -9398,19 +9403,6 @@
class_roots->Set<false>(index, klass);
}
-void ClassLinker::AllocAndSetPrimitiveArrayClassRoot(Thread* self,
- ObjPtr<mirror::Class> java_lang_Class,
- ClassRoot primitive_array_class_root,
- ClassRoot primitive_class_root,
- const char* descriptor) {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> primitive_array_class(hs.NewHandle(
- AllocPrimitiveArrayClass(self, java_lang_Class)));
- primitive_array_class->SetComponentType(GetClassRoot(primitive_class_root, this));
- SetClassRoot(primitive_array_class_root, primitive_array_class.Get());
- CheckSystemClass(self, primitive_array_class, descriptor);
-}
-
ObjPtr<mirror::ClassLoader> ClassLinker::CreateWellKnownClassLoader(
Thread* self,
const std::vector<const DexFile*>& dex_files,