blob: 56fae815126cff66aa3ced48c2eec2d834a1ebd4 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "class_linker.h"
#include <deque>
#include <iostream>
#include <memory>
#include <queue>
#include <string>
#include <unistd.h>
#include <utility>
#include <vector>
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/arena_allocator.h"
#include "base/casts.h"
#include "base/logging.h"
#include "base/scoped_arena_containers.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "base/value_object.h"
#include "class_linker-inl.h"
#include "compiler_callbacks.h"
#include "debugger.h"
#include "dex_file-inl.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "handle_scope.h"
#include "intern_table.h"
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "leb128.h"
#include "linear_alloc.h"
#include "oat.h"
#include "oat_file.h"
#include "oat_file-inl.h"
#include "oat_file_assistant.h"
#include "object_lock.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/field.h"
#include "mirror/iftable-inl.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
#include "mirror/reference-inl.h"
#include "mirror/stack_trace_element.h"
#include "mirror/string-inl.h"
#include "os.h"
#include "runtime.h"
#include "entrypoints/entrypoint_utils.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
#include "handle_scope-inl.h"
#include "thread-inl.h"
#include "utils.h"
#include "verifier/method_verifier.h"
#include "well_known_classes.h"
namespace art {
static constexpr bool kSanityCheckObjects = kIsDebugBuild;
// For b/21333911.
static constexpr bool kDuplicateClassesCheck = false;
static void ThrowNoClassDefFoundError(const char* fmt, ...)
__attribute__((__format__(__printf__, 1, 2)))
SHARED_REQUIRES(Locks::mutator_lock_);
static void ThrowNoClassDefFoundError(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
Thread* self = Thread::Current();
self->ThrowNewExceptionV("Ljava/lang/NoClassDefFoundError;", fmt, args);
va_end(args);
}
bool ClassLinker::HasInitWithString(Thread* self, const char* descriptor) {
ArtMethod* method = self->GetCurrentMethod(nullptr);
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(method != nullptr ?
method->GetDeclaringClass()->GetClassLoader() : nullptr));
mirror::Class* exception_class = FindClass(self, descriptor, class_loader);
if (exception_class == nullptr) {
// No exc class ~ no <init>-with-string.
CHECK(self->IsExceptionPending());
self->ClearException();
return false;
}
ArtMethod* exception_init_method = exception_class->FindDeclaredDirectMethod(
"<init>", "(Ljava/lang/String;)V", image_pointer_size_);
return exception_init_method != nullptr;
}
void ClassLinker::ThrowEarlierClassFailure(mirror::Class* c) {
// The class failed to initialize on a previous attempt, so we want to throw
// a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
// failed in verification, in which case v2 5.4.1 says we need to re-throw
// the previous error.
Runtime* const runtime = Runtime::Current();
if (!runtime->IsAotCompiler()) { // Give info if this occurs at runtime.
LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c);
}
CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus();
Thread* self = Thread::Current();
if (runtime->IsAotCompiler()) {
// At compile time, accurate errors and NCDFE are disabled to speed compilation.
mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
} else {
if (c->GetVerifyErrorClass() != nullptr) {
// TODO: change the verifier to store an _instance_, with a useful detail message?
// It's possible the exception doesn't have a <init>(String).
std::string temp;
const char* descriptor = c->GetVerifyErrorClass()->GetDescriptor(&temp);
if (HasInitWithString(self, descriptor)) {
self->ThrowNewException(descriptor, PrettyDescriptor(c).c_str());
} else {
self->ThrowNewException(descriptor, nullptr);
}
} else {
self->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
PrettyDescriptor(c).c_str());
}
}
}
static void VlogClassInitializationFailure(Handle<mirror::Class> klass)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (VLOG_IS_ON(class_linker)) {
std::string temp;
LOG(INFO) << "Failed to initialize class " << klass->GetDescriptor(&temp) << " from "
<< klass->GetLocation() << "\n" << Thread::Current()->GetException()->Dump();
}
}
static void WrapExceptionInInitializer(Handle<mirror::Class> klass)
SHARED_REQUIRES(Locks::mutator_lock_) {
Thread* self = Thread::Current();
JNIEnv* env = self->GetJniEnv();
ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
CHECK(cause.get() != nullptr);
env->ExceptionClear();
bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error);
env->Throw(cause.get());
// We only wrap non-Error exceptions; an Error can just be used as-is.
if (!is_error) {
self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", nullptr);
}
VlogClassInitializationFailure(klass);
}
// Gap between two fields in object layout.
struct FieldGap {
uint32_t start_offset; // The offset from the start of the object.
uint32_t size; // The gap size of 1, 2, or 4 bytes.
};
struct FieldGapsComparator {
explicit FieldGapsComparator() {
}
bool operator() (const FieldGap& lhs, const FieldGap& rhs)
NO_THREAD_SAFETY_ANALYSIS {
// Sort by gap size, largest first. Secondary sort by starting offset.
// Note that the priority queue returns the largest element, so operator()
// should return true if lhs is less than rhs.
return lhs.size < rhs.size || (lhs.size == rhs.size && lhs.start_offset > rhs.start_offset);
}
};
typedef std::priority_queue<FieldGap, std::vector<FieldGap>, FieldGapsComparator> FieldGaps;
// Adds largest aligned gaps to queue of gaps.
static void AddFieldGap(uint32_t gap_start, uint32_t gap_end, FieldGaps* gaps) {
DCHECK(gaps != nullptr);
uint32_t current_offset = gap_start;
while (current_offset != gap_end) {
size_t remaining = gap_end - current_offset;
if (remaining >= sizeof(uint32_t) && IsAligned<4>(current_offset)) {
gaps->push(FieldGap {current_offset, sizeof(uint32_t)});
current_offset += sizeof(uint32_t);
} else if (remaining >= sizeof(uint16_t) && IsAligned<2>(current_offset)) {
gaps->push(FieldGap {current_offset, sizeof(uint16_t)});
current_offset += sizeof(uint16_t);
} else {
gaps->push(FieldGap {current_offset, sizeof(uint8_t)});
current_offset += sizeof(uint8_t);
}
DCHECK_LE(current_offset, gap_end) << "Overran gap";
}
}
// Shuffle fields forward, making use of gaps whenever possible.
template<int n>
static void ShuffleForward(size_t* current_field_idx,
MemberOffset* field_offset,
std::deque<ArtField*>* grouped_and_sorted_fields,
FieldGaps* gaps)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(current_field_idx != nullptr);
DCHECK(grouped_and_sorted_fields != nullptr);
DCHECK(gaps != nullptr);
DCHECK(field_offset != nullptr);
DCHECK(IsPowerOfTwo(n));
while (!grouped_and_sorted_fields->empty()) {
ArtField* field = grouped_and_sorted_fields->front();
Primitive::Type type = field->GetTypeAsPrimitiveType();
if (Primitive::ComponentSize(type) < n) {
break;
}
if (!IsAligned<n>(field_offset->Uint32Value())) {
MemberOffset old_offset = *field_offset;
*field_offset = MemberOffset(RoundUp(field_offset->Uint32Value(), n));
AddFieldGap(old_offset.Uint32Value(), field_offset->Uint32Value(), gaps);
}
CHECK(type != Primitive::kPrimNot) << PrettyField(field); // should be primitive types
grouped_and_sorted_fields->pop_front();
if (!gaps->empty() && gaps->top().size >= n) {
FieldGap gap = gaps->top();
gaps->pop();
DCHECK_ALIGNED(gap.start_offset, n);
field->SetOffset(MemberOffset(gap.start_offset));
if (gap.size > n) {
AddFieldGap(gap.start_offset + n, gap.start_offset + gap.size, gaps);
}
} else {
DCHECK_ALIGNED(field_offset->Uint32Value(), n);
field->SetOffset(*field_offset);
*field_offset = MemberOffset(field_offset->Uint32Value() + n);
}
++(*current_field_idx);
}
}
ClassLinker::ClassLinker(InternTable* intern_table)
// dex_lock_ is recursive as it may be used in stack dumping.
: dex_lock_("ClassLinker dex lock", kDefaultMutexLevel),
dex_cache_image_class_lookup_required_(false),
failed_dex_cache_class_lookups_(0),
class_roots_(nullptr),
array_iftable_(nullptr),
find_array_class_cache_next_victim_(0),
init_done_(false),
log_new_dex_caches_roots_(false),
log_new_class_table_roots_(false),
intern_table_(intern_table),
quick_resolution_trampoline_(nullptr),
quick_imt_conflict_trampoline_(nullptr),
quick_generic_jni_trampoline_(nullptr),
quick_to_interpreter_bridge_trampoline_(nullptr),
image_pointer_size_(sizeof(void*)) {
CHECK(intern_table_ != nullptr);
static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_),
"Array cache size wrong.");
std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
}
void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path) {
VLOG(startup) << "ClassLinker::Init";
Thread* const self = Thread::Current();
Runtime* const runtime = Runtime::Current();
gc::Heap* const heap = runtime->GetHeap();
CHECK(!heap->HasImageSpace()) << "Runtime has image. We should use it.";
CHECK(!init_done_);
// Use the pointer size from the runtime since we are probably creating the image.
image_pointer_size_ = InstructionSetPointerSize(runtime->GetInstructionSet());
// java_lang_Class comes first, it's needed for AllocClass
// The GC can't handle an object with a null class since we can't get the size of this object.
heap->IncrementDisableMovingGC(self);
StackHandleScope<64> hs(self); // 64 is picked arbitrarily.
auto class_class_size = mirror::Class::ClassClassSize(image_pointer_size_);
Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>(
heap->AllocNonMovableObject<true>(self, nullptr, class_class_size, VoidFunctor()))));
CHECK(java_lang_Class.Get() != nullptr);
mirror::Class::SetClassClass(java_lang_Class.Get());
java_lang_Class->SetClass(java_lang_Class.Get());
if (kUseBakerOrBrooksReadBarrier) {
java_lang_Class->AssertReadBarrierPointer();
}
java_lang_Class->SetClassSize(class_class_size);
java_lang_Class->SetPrimitiveType(Primitive::kPrimNot);
heap->DecrementDisableMovingGC(self);
// AllocClass(mirror::Class*) can now be used
// Class[] is used for reflection support.
auto class_array_class_size = mirror::ObjectArray<mirror::Class>::ClassSize(image_pointer_size_);
Handle<mirror::Class> class_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), class_array_class_size)));
class_array_class->SetComponentType(java_lang_Class.Get());
// java_lang_Object comes next so that object_array_class can be created.
Handle<mirror::Class> java_lang_Object(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize(image_pointer_size_))));
CHECK(java_lang_Object.Get() != nullptr);
// backfill Object as the super class of Class.
java_lang_Class->SetSuperClass(java_lang_Object.Get());
mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusLoaded, self);
// Object[] next to hold class roots.
Handle<mirror::Class> object_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(),
mirror::ObjectArray<mirror::Object>::ClassSize(image_pointer_size_))));
object_array_class->SetComponentType(java_lang_Object.Get());
// Setup the char (primitive) class to be used for char[].
Handle<mirror::Class> char_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(),
mirror::Class::PrimitiveClassSize(image_pointer_size_))));
// The primitive char class won't be initialized by
// InitializePrimitiveClass until line 459, but strings (and
// internal char arrays) will be allocated before that and the
// component size, which is computed from the primitive type, needs
// to be set here.
char_class->SetPrimitiveType(Primitive::kPrimChar);
// Setup the char[] class to be used for String.
Handle<mirror::Class> char_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_))));
char_array_class->SetComponentType(char_class.Get());
mirror::CharArray::SetArrayClass(char_array_class.Get());
// Setup String.
Handle<mirror::Class> java_lang_String(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize(image_pointer_size_))));
mirror::String::SetClass(java_lang_String.Get());
mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusResolved, self);
java_lang_String->SetStringClass();
// Setup java.lang.ref.Reference.
Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize(image_pointer_size_))));
mirror::Reference::SetClass(java_lang_ref_Reference.Get());
java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize());
mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusResolved, self);
// Create storage for root classes, save away our work so far (requires descriptors).
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(
mirror::ObjectArray<mirror::Class>::Alloc(self, object_array_class.Get(),
kClassRootsMax));
CHECK(!class_roots_.IsNull());
SetClassRoot(kJavaLangClass, java_lang_Class.Get());
SetClassRoot(kJavaLangObject, java_lang_Object.Get());
SetClassRoot(kClassArrayClass, class_array_class.Get());
SetClassRoot(kObjectArrayClass, object_array_class.Get());
SetClassRoot(kCharArrayClass, char_array_class.Get());
SetClassRoot(kJavaLangString, java_lang_String.Get());
SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference.Get());
// Setup the primitive type classes.
SetClassRoot(kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean));
SetClassRoot(kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte));
SetClassRoot(kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort));
SetClassRoot(kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt));
SetClassRoot(kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong));
SetClassRoot(kPrimitiveFloat, CreatePrimitiveClass(self, Primitive::kPrimFloat));
SetClassRoot(kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble));
SetClassRoot(kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid));
// Create array interface entries to populate once we can load system classes.
array_iftable_ = GcRoot<mirror::IfTable>(AllocIfTable(self, 2));
// Create int array type for AllocDexCache (done in AppendToBootClassPath).
Handle<mirror::Class> int_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_))));
int_array_class->SetComponentType(GetClassRoot(kPrimitiveInt));
mirror::IntArray::SetArrayClass(int_array_class.Get());
SetClassRoot(kIntArrayClass, int_array_class.Get());
// Create long array type for AllocDexCache (done in AppendToBootClassPath).
Handle<mirror::Class> long_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_))));
long_array_class->SetComponentType(GetClassRoot(kPrimitiveLong));
mirror::LongArray::SetArrayClass(long_array_class.Get());
SetClassRoot(kLongArrayClass, long_array_class.Get());
// now that these are registered, we can use AllocClass() and AllocObjectArray
// Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache.
Handle<mirror::Class> java_lang_DexCache(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize(image_pointer_size_))));
SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get());
java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize());
mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusResolved, self);
// Set up array classes for string, field, method
Handle<mirror::Class> object_array_string(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(),
mirror::ObjectArray<mirror::String>::ClassSize(image_pointer_size_))));
object_array_string->SetComponentType(java_lang_String.Get());
SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get());
// Create runtime resolution and imt conflict methods.
runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod());
runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod());
// Setup boot_class_path_ and register class_path now that we can use AllocObjectArray to create
// DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
// these roots.
CHECK_NE(0U, boot_class_path.size());
for (auto& dex_file : boot_class_path) {
CHECK(dex_file.get() != nullptr);
AppendToBootClassPath(self, *dex_file);
opened_dex_files_.push_back(std::move(dex_file));
}
// now we can use FindSystemClass
// run char class through InitializePrimitiveClass to finish init
InitializePrimitiveClass(char_class.Get(), Primitive::kPrimChar);
SetClassRoot(kPrimitiveChar, char_class.Get()); // needs descriptor
// Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that
// we do not need friend classes or a publicly exposed setter.
quick_generic_jni_trampoline_ = GetQuickGenericJniStub();
if (!runtime->IsAotCompiler()) {
// We need to set up the generic trampolines since we don't have an image.
quick_resolution_trampoline_ = GetQuickResolutionStub();
quick_imt_conflict_trampoline_ = GetQuickImtConflictStub();
quick_to_interpreter_bridge_trampoline_ = GetQuickToInterpreterBridge();
}
// Object, String and DexCache need to be rerun through FindSystemClass to finish init
mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusNotReady, self);
CHECK_EQ(java_lang_Object.Get(), FindSystemClass(self, "Ljava/lang/Object;"));
CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize());
mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusNotReady, self);
mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;");
if (java_lang_String.Get() != String_class) {
std::ostringstream os1, os2;
java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail);
String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail);
LOG(FATAL) << os1.str() << "\n\n" << os2.str();
}
mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusNotReady, self);
CHECK_EQ(java_lang_DexCache.Get(), FindSystemClass(self, "Ljava/lang/DexCache;"));
CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize());
// Setup the primitive array type classes - can't be done until Object has a vtable.
SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z"));
mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B"));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
CHECK_EQ(char_array_class.Get(), FindSystemClass(self, "[C"));
SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S"));
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
CHECK_EQ(int_array_class.Get(), FindSystemClass(self, "[I"));
CHECK_EQ(long_array_class.Get(), FindSystemClass(self, "[J"));
SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F"));
mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D"));
mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
CHECK_EQ(class_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Class;"));
CHECK_EQ(object_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Object;"));
// Setup the single, global copy of "iftable".
auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;"));
CHECK(java_lang_Cloneable.Get() != nullptr);
auto java_io_Serializable = hs.NewHandle(FindSystemClass(self, "Ljava/io/Serializable;"));
CHECK(java_io_Serializable.Get() != nullptr);
// We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to
// crawl up and explicitly list all of the supers as well.
array_iftable_.Read()->SetInterface(0, java_lang_Cloneable.Get());
array_iftable_.Read()->SetInterface(1, java_io_Serializable.Get());
// Sanity check Class[] and Object[]'s interfaces. GetDirectInterface may cause thread
// suspension.
CHECK_EQ(java_lang_Cloneable.Get(),
mirror::Class::GetDirectInterface(self, class_array_class, 0));
CHECK_EQ(java_io_Serializable.Get(),
mirror::Class::GetDirectInterface(self, class_array_class, 1));
CHECK_EQ(java_lang_Cloneable.Get(),
mirror::Class::GetDirectInterface(self, object_array_class, 0));
CHECK_EQ(java_io_Serializable.Get(),
mirror::Class::GetDirectInterface(self, object_array_class, 1));
// Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their
// dex_cache_ fields and register them in class_table_.
CHECK_EQ(java_lang_Class.Get(), FindSystemClass(self, "Ljava/lang/Class;"));
CHECK_EQ(object_array_string.Get(),
FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass)));
// End of special init trickery, subsequent classes may be loaded via FindSystemClass.
// Create java.lang.reflect.Proxy root.
SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;"));
// Create java.lang.reflect.Field.class root.
auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectField, class_root);
mirror::Field::SetClass(class_root);
// Create java.lang.reflect.Field array root.
class_root = FindSystemClass(self, "[Ljava/lang/reflect/Field;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectFieldArrayClass, class_root);
mirror::Field::SetArrayClass(class_root);
// Create java.lang.reflect.Constructor.class root and array root.
class_root = FindSystemClass(self, "Ljava/lang/reflect/Constructor;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectConstructor, class_root);
mirror::Constructor::SetClass(class_root);
class_root = FindSystemClass(self, "[Ljava/lang/reflect/Constructor;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectConstructorArrayClass, class_root);
mirror::Constructor::SetArrayClass(class_root);
// Create java.lang.reflect.Method.class root and array root.
class_root = FindSystemClass(self, "Ljava/lang/reflect/Method;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectMethod, class_root);
mirror::Method::SetClass(class_root);
class_root = FindSystemClass(self, "[Ljava/lang/reflect/Method;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectMethodArrayClass, class_root);
mirror::Method::SetArrayClass(class_root);
// java.lang.ref classes need to be specially flagged, but otherwise are normal classes
// finish initializing Reference class
mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self);
CHECK_EQ(java_lang_ref_Reference.Get(), FindSystemClass(self, "Ljava/lang/ref/Reference;"));
CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize());
CHECK_EQ(java_lang_ref_Reference->GetClassSize(),
mirror::Reference::ClassSize(image_pointer_size_));
class_root = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
class_root->SetAccessFlags(class_root->GetAccessFlags() |
kAccClassIsReference | kAccClassIsFinalizerReference);
class_root = FindSystemClass(self, "Ljava/lang/ref/PhantomReference;");
class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference |
kAccClassIsPhantomReference);
class_root = FindSystemClass(self, "Ljava/lang/ref/SoftReference;");
class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference);
class_root = FindSystemClass(self, "Ljava/lang/ref/WeakReference;");
class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference |
kAccClassIsWeakReference);
// Setup the ClassLoader, verifying the object_size_.
class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;");
CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize());
SetClassRoot(kJavaLangClassLoader, class_root);
// Set up java.lang.Throwable, java.lang.ClassNotFoundException, and
// java.lang.StackTraceElement as a convenience.
SetClassRoot(kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;"));
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
SetClassRoot(kJavaLangClassNotFoundException,
FindSystemClass(self, "Ljava/lang/ClassNotFoundException;"));
SetClassRoot(kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;"));
SetClassRoot(kJavaLangStackTraceElementArrayClass,
FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
// Ensure void type is resolved in the core's dex cache so java.lang.Void is correctly
// initialized.
{
const DexFile& dex_file = java_lang_Object->GetDexFile();
const DexFile::StringId* void_string_id = dex_file.FindStringId("V");
CHECK(void_string_id != nullptr);
uint32_t void_string_index = dex_file.GetIndexForStringId(*void_string_id);
const DexFile::TypeId* void_type_id = dex_file.FindTypeId(void_string_index);
CHECK(void_type_id != nullptr);
uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id);
// Now we resolve void type so the dex cache contains it. We use java.lang.Object class
// as referrer so the used dex cache is core's one.
mirror::Class* resolved_type = ResolveType(dex_file, void_type_idx, java_lang_Object.Get());
CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid));
self->AssertNoPendingException();
}
FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
}
void ClassLinker::FinishInit(Thread* self) {
VLOG(startup) << "ClassLinker::FinishInit entering";
// Let the heap know some key offsets into java.lang.ref instances
// Note: we hard code the field indexes here rather than using FindInstanceField
// as the types of the field can't be resolved prior to the runtime being
// fully initialized
mirror::Class* java_lang_ref_Reference = GetClassRoot(kJavaLangRefReference);
mirror::Class* java_lang_ref_FinalizerReference =
FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
CHECK_STREQ(pendingNext->GetName(), "pendingNext");
CHECK_STREQ(pendingNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;");
ArtField* queue = java_lang_ref_Reference->GetInstanceField(1);
CHECK_STREQ(queue->GetName(), "queue");
CHECK_STREQ(queue->GetTypeDescriptor(), "Ljava/lang/ref/ReferenceQueue;");
ArtField* queueNext = java_lang_ref_Reference->GetInstanceField(2);
CHECK_STREQ(queueNext->GetName(), "queueNext");
CHECK_STREQ(queueNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;");
ArtField* referent = java_lang_ref_Reference->GetInstanceField(3);
CHECK_STREQ(referent->GetName(), "referent");
CHECK_STREQ(referent->GetTypeDescriptor(), "Ljava/lang/Object;");
ArtField* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2);
CHECK_STREQ(zombie->GetName(), "zombie");
CHECK_STREQ(zombie->GetTypeDescriptor(), "Ljava/lang/Object;");
// ensure all class_roots_ are initialized
for (size_t i = 0; i < kClassRootsMax; i++) {
ClassRoot class_root = static_cast<ClassRoot>(i);
mirror::Class* klass = GetClassRoot(class_root);
CHECK(klass != nullptr);
DCHECK(klass->IsArrayClass() || klass->IsPrimitive() || klass->GetDexCache() != nullptr);
// note SetClassRoot does additional validation.
// if possible add new checks there to catch errors early
}
CHECK(!array_iftable_.IsNull());
// disable the slow paths in FindClass and CreatePrimitiveClass now
// that Object, Class, and Object[] are setup
init_done_ = true;
VLOG(startup) << "ClassLinker::FinishInit exiting";
}
void ClassLinker::RunRootClinits() {
Thread* self = Thread::Current();
for (size_t i = 0; i < ClassLinker::kClassRootsMax; ++i) {
mirror::Class* c = GetClassRoot(ClassRoot(i));
if (!c->IsArrayClass() && !c->IsPrimitive()) {
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(GetClassRoot(ClassRoot(i))));
EnsureInitialized(self, h_class, true, true);
self->AssertNoPendingException();
}
}
}
const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
WriterMutexLock mu(Thread::Current(), dex_lock_);
if (kIsDebugBuild) {
for (size_t i = 0; i < oat_files_.size(); ++i) {
CHECK_NE(oat_file, oat_files_[i]) << oat_file->GetLocation();
}
}
VLOG(class_linker) << "Registering " << oat_file->GetLocation();
oat_files_.push_back(oat_file);
return oat_file;
}
OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) {
VLOG(startup) << "ClassLinker::GetImageOatFile entering";
OatFile* oat_file = space->ReleaseOatFile();
CHECK_EQ(RegisterOatFile(oat_file), oat_file);
VLOG(startup) << "ClassLinker::GetImageOatFile exiting";
return *oat_file;
}
class DexFileAndClassPair : ValueObject {
public:
DexFileAndClassPair(const DexFile* dex_file, size_t current_class_index, bool from_loaded_oat)
: cached_descriptor_(GetClassDescriptor(dex_file, current_class_index)),
dex_file_(dex_file),
current_class_index_(current_class_index),
from_loaded_oat_(from_loaded_oat) {}
DexFileAndClassPair(const DexFileAndClassPair&) = default;
DexFileAndClassPair& operator=(const DexFileAndClassPair& rhs) {
cached_descriptor_ = rhs.cached_descriptor_;
dex_file_ = rhs.dex_file_;
current_class_index_ = rhs.current_class_index_;
from_loaded_oat_ = rhs.from_loaded_oat_;
return *this;
}
const char* GetCachedDescriptor() const {
return cached_descriptor_;
}
bool operator<(const DexFileAndClassPair& rhs) const {
const char* lhsDescriptor = cached_descriptor_;
const char* rhsDescriptor = rhs.cached_descriptor_;
int cmp = strcmp(lhsDescriptor, rhsDescriptor);
if (cmp != 0) {
// Note that the order must be reversed. We want to iterate over the classes in dex files.
// They are sorted lexicographically. Thus, the priority-queue must be a min-queue.
return cmp > 0;
}
return dex_file_ < rhs.dex_file_;
}
bool DexFileHasMoreClasses() const {
return current_class_index_ + 1 < dex_file_->NumClassDefs();
}
DexFileAndClassPair GetNext() const {
return DexFileAndClassPair(dex_file_, current_class_index_ + 1, from_loaded_oat_);
}
size_t GetCurrentClassIndex() const {
return current_class_index_;
}
bool FromLoadedOat() const {
return from_loaded_oat_;
}
const DexFile* GetDexFile() const {
return dex_file_;
}
void DeleteDexFile() {
delete dex_file_;
dex_file_ = nullptr;
}
private:
static const char* GetClassDescriptor(const DexFile* dex_file, size_t index) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(static_cast<uint16_t>(index));
return dex_file->StringByTypeIdx(class_def.class_idx_);
}
const char* cached_descriptor_;
const DexFile* dex_file_;
size_t current_class_index_;
bool from_loaded_oat_; // We only need to compare mismatches between what we load now
// and what was loaded before. Any old duplicates must have been
// OK, and any new "internal" duplicates are as well (they must
// be from multidex, which resolves correctly).
};
static void AddDexFilesFromOat(const OatFile* oat_file, bool already_loaded,
std::priority_queue<DexFileAndClassPair>* heap) {
const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles();
for (const OatDexFile* oat_dex_file : oat_dex_files) {
std::string error;
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error);
if (dex_file.get() == nullptr) {
LOG(WARNING) << "Could not create dex file from oat file: " << error;
} else {
if (dex_file->NumClassDefs() > 0U) {
heap->emplace(dex_file.release(), 0U, already_loaded);
}
}
}
}
static void AddNext(DexFileAndClassPair* original,
std::priority_queue<DexFileAndClassPair>* heap) {
if (original->DexFileHasMoreClasses()) {
heap->push(original->GetNext());
} else {
// Need to delete the dex file.
original->DeleteDexFile();
}
}
static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap) {
while (!heap->empty()) {
delete heap->top().GetDexFile();
heap->pop();
}
}
const OatFile* ClassLinker::GetBootOatFile() {
gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
if (image_space == nullptr) {
return nullptr;
}
return image_space->GetOatFile();
}
const OatFile* ClassLinker::GetPrimaryOatFile() {
ReaderMutexLock mu(Thread::Current(), dex_lock_);
const OatFile* boot_oat_file = GetBootOatFile();
if (boot_oat_file != nullptr) {
for (const OatFile* oat_file : oat_files_) {
if (oat_file != boot_oat_file) {
return oat_file;
}
}
}
return nullptr;
}
// Check for class-def collisions in dex files.
//
// This works by maintaining a heap with one class from each dex file, sorted by the class
// descriptor. Then a dex-file/class pair is continually removed from the heap and compared
// against the following top element. If the descriptor is the same, it is now checked whether
// the two elements agree on whether their dex file was from an already-loaded oat-file or the
// new oat file. Any disagreement indicates a collision.
bool ClassLinker::HasCollisions(const OatFile* oat_file, std::string* error_msg) {
if (!kDuplicateClassesCheck) {
return false;
}
// Dex files are registered late - once a class is actually being loaded. We have to compare
// against the open oat files. Take the dex_lock_ that protects oat_files_ accesses.
ReaderMutexLock mu(Thread::Current(), dex_lock_);
std::priority_queue<DexFileAndClassPair> queue;
// Add dex files from already loaded oat files, but skip boot.
{
const OatFile* boot_oat = GetBootOatFile();
for (const OatFile* loaded_oat_file : oat_files_) {
if (loaded_oat_file == boot_oat) {
continue;
}
AddDexFilesFromOat(loaded_oat_file, true, &queue);
}
}
if (queue.empty()) {
// No other oat files, return early.
return false;
}
// Add dex files from the oat file to check.
AddDexFilesFromOat(oat_file, false, &queue);
// Now drain the queue.
while (!queue.empty()) {
DexFileAndClassPair compare_pop = queue.top();
queue.pop();
// Compare against the following elements.
while (!queue.empty()) {
DexFileAndClassPair top = queue.top();
if (strcmp(compare_pop.GetCachedDescriptor(), top.GetCachedDescriptor()) == 0) {
// Same descriptor. Check whether it's crossing old-oat-files to new-oat-files.
if (compare_pop.FromLoadedOat() != top.FromLoadedOat()) {
*error_msg =
StringPrintf("Found duplicated class when checking oat files: '%s' in %s and %s",
compare_pop.GetCachedDescriptor(),
compare_pop.GetDexFile()->GetLocation().c_str(),
top.GetDexFile()->GetLocation().c_str());
FreeDexFilesInHeap(&queue);
return true;
}
// Pop it.
queue.pop();
AddNext(&top, &queue);
} else {
// Something else. Done here.
break;
}
}
AddNext(&compare_pop, &queue);
}
return false;
}
std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat(
const char* dex_location, const char* oat_location,
std::vector<std::string>* error_msgs) {
CHECK(error_msgs != nullptr);
// Verify we aren't holding the mutator lock, which could starve GC if we
// have to generate or relocate an oat file.
Locks::mutator_lock_->AssertNotHeld(Thread::Current());
OatFileAssistant oat_file_assistant(dex_location, oat_location, kRuntimeISA,
!Runtime::Current()->IsAotCompiler());
// Lock the target oat location to avoid races generating and loading the
// oat file.
std::string error_msg;
if (!oat_file_assistant.Lock(&error_msg)) {
// Don't worry too much if this fails. If it does fail, it's unlikely we
// can generate an oat file anyway.
VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
}
// Check if we already have an up-to-date oat file open.
const OatFile* source_oat_file = nullptr;
{
ReaderMutexLock mu(Thread::Current(), dex_lock_);
for (const OatFile* oat_file : oat_files_) {
CHECK(oat_file != nullptr);
if (oat_file_assistant.GivenOatFileIsUpToDate(*oat_file)) {
source_oat_file = oat_file;
break;
}
}
}
// If we didn't have an up-to-date oat file open, try to load one from disk.
if (source_oat_file == nullptr) {
// Update the oat file on disk if we can. This may fail, but that's okay.
// Best effort is all that matters here.
if (!oat_file_assistant.MakeUpToDate(&error_msg)) {
LOG(WARNING) << error_msg;
}
// Get the oat file on disk.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
if (oat_file.get() != nullptr) {
// Take the file only if it has no collisions, or we must take it because of preopting.
bool accept_oat_file = !HasCollisions(oat_file.get(), &error_msg);
if (!accept_oat_file) {
// Failed the collision check. Print warning.
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
LOG(WARNING) << "Found duplicate classes, falling back to interpreter mode for "
<< dex_location;
} else {
LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to "
" load classes for " << dex_location;
}
LOG(WARNING) << error_msg;
// However, if the app was part of /system and preopted, there is no original dex file
// available. In that case grudgingly accept the oat file.
if (!DexFile::MaybeDex(dex_location)) {
accept_oat_file = true;
LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "
<< "Allow oat file use. This is potentially dangerous.";
}
}
if (accept_oat_file) {
source_oat_file = oat_file.release();
RegisterOatFile(source_oat_file);
}
}
}
std::vector<std::unique_ptr<const DexFile>> dex_files;
// Load the dex files from the oat file.
if (source_oat_file != nullptr) {
dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
if (dex_files.empty()) {
error_msgs->push_back("Failed to open dex files from "
+ source_oat_file->GetLocation());
}
}
// Fall back to running out of the original dex file if we couldn't load any
// dex_files from the oat file.
if (dex_files.empty()) {
if (oat_file_assistant.HasOriginalDexFiles()) {
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
LOG(WARNING) << error_msg;
error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
}
} else {
error_msgs->push_back("Fallback mode disabled, skipping dex files.");
}
} else {
error_msgs->push_back("No original dex files found for dex location "
+ std::string(dex_location));
}
}
return dex_files;
}
const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
ReaderMutexLock mu(Thread::Current(), dex_lock_);
for (size_t i = 0; i < oat_files_.size(); i++) {
const OatFile* oat_file = oat_files_[i];
DCHECK(oat_file != nullptr);
if (oat_file->GetLocation() == oat_location) {
return oat_file;
}
}
return nullptr;
}
static void SanityCheckArtMethod(ArtMethod* m, mirror::Class* expected_class,
gc::space::ImageSpace* space)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
} else if (m->IsMiranda()) {
CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m);
} else if (expected_class != nullptr) {
CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m);
}
if (space != nullptr) {
auto& header = space->GetImageHeader();
auto& methods = header.GetMethodsSection();
auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
CHECK(methods.Contains(offset)) << m << " not in " << methods;
}
}
static void SanityCheckArtMethodPointerArray(
mirror::PointerArray* arr, mirror::Class* expected_class, size_t pointer_size,
gc::space::ImageSpace* space) SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK(arr != nullptr);
for (int32_t j = 0; j < arr->GetLength(); ++j) {
auto* method = arr->GetElementPtrSize<ArtMethod*>(j, pointer_size);
// expected_class == null means we are a dex cache.
if (expected_class != nullptr) {
CHECK(method != nullptr);
}
if (method != nullptr) {
SanityCheckArtMethod(method, expected_class, space);
}
}
}
static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
CHECK(obj->GetClass() != nullptr) << "Null class in object " << obj;
CHECK(obj->GetClass()->GetClass() != nullptr) << "Null class class " << obj;
if (obj->IsClass()) {
auto klass = obj->AsClass();
ArtField* fields[2] = { klass->GetSFields(), klass->GetIFields() };
size_t num_fields[2] = { klass->NumStaticFields(), klass->NumInstanceFields() };
for (size_t i = 0; i < 2; ++i) {
for (size_t j = 0; j < num_fields[i]; ++j) {
CHECK_EQ(fields[i][j].GetDeclaringClass(), klass);
}
}
auto* runtime = Runtime::Current();
auto* image_space = runtime->GetHeap()->GetImageSpace();
auto pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
for (auto& m : klass->GetDirectMethods(pointer_size)) {
SanityCheckArtMethod(&m, klass, image_space);
}
for (auto& m : klass->GetVirtualMethods(pointer_size)) {
SanityCheckArtMethod(&m, klass, image_space);
}
auto* vtable = klass->GetVTable();
if (vtable != nullptr) {
SanityCheckArtMethodPointerArray(vtable, nullptr, pointer_size, image_space);
}
if (klass->ShouldHaveEmbeddedImtAndVTable()) {
for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
SanityCheckArtMethod(klass->GetEmbeddedImTableEntry(i, pointer_size), nullptr, image_space);
}
for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) {
SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_space);
}
}
auto* iftable = klass->GetIfTable();
if (iftable != nullptr) {
for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
if (iftable->GetMethodArrayCount(i) > 0) {
SanityCheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr, pointer_size,
image_space);
}
}
}
}
}
void ClassLinker::InitFromImage() {
VLOG(startup) << "ClassLinker::InitFromImage entering";
CHECK(!init_done_);
Runtime* const runtime = Runtime::Current();
Thread* const self = Thread::Current();
gc::Heap* const heap = runtime->GetHeap();
gc::space::ImageSpace* const space = heap->GetImageSpace();
CHECK(space != nullptr);
image_pointer_size_ = space->GetImageHeader().GetPointerSize();
dex_cache_image_class_lookup_required_ = true;
OatFile& oat_file = GetImageOatFile(space);
CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
const char* image_file_location = oat_file.GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
CHECK(image_file_location == nullptr || *image_file_location == 0);
quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge();
mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
mirror::ObjectArray<mirror::DexCache>* dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>();
StackHandleScope<1> hs(self);
Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->
AsObjectArray<mirror::Class>()));
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get());
// Special case of setting up the String class early so that we can test arbitrary objects
// as being Strings or not
mirror::String::SetClass(GetClassRoot(kJavaLangString));
CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(),
static_cast<uint32_t>(dex_caches->GetLength()));
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
StackHandleScope<1> hs2(self);
Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i)));
const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(),
nullptr);
CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location;
std::string error_msg;
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file.get() == nullptr) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
<< " from within oat file " << oat_file.GetLocation()
<< " error '" << error_msg << "'";
UNREACHABLE();
}
if (kSanityCheckObjects) {
SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(), nullptr,
image_pointer_size_, space);
}
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
AppendToBootClassPath(*dex_file.get(), dex_cache);
opened_dex_files_.push_back(std::move(dex_file));
}
CHECK(ValidPointerSize(image_pointer_size_)) << image_pointer_size_;
// Set classes on AbstractMethod early so that IsMethod tests can be performed during the live
// bitmap walk.
if (!runtime->IsAotCompiler()) {
// Only the Aot compiler supports having an image with a different pointer size than the
// runtime. This happens on the host for compile 32 bit tests since we use a 64 bit libart
// compiler. We may also use 32 bit dex2oat on a system with 64 bit apps.
CHECK_EQ(image_pointer_size_, sizeof(void*));
}
if (kSanityCheckObjects) {
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
auto* dex_cache = dex_caches->Get(i);
for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) {
auto* field = dex_cache->GetResolvedField(j, image_pointer_size_);
if (field != nullptr) {
CHECK(field->GetDeclaringClass()->GetClass() != nullptr);
}
}
}
heap->VisitObjects(SanityCheckObjectsCallback, nullptr);
}
// Set entry point to interpreter if in InterpretOnly mode.
if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
const auto& header = space->GetImageHeader();
const auto& methods = header.GetMethodsSection();
const auto art_method_size = ArtMethod::ObjectSize(image_pointer_size_);
for (uintptr_t pos = 0; pos < methods.Size(); pos += art_method_size) {
auto* method = reinterpret_cast<ArtMethod*>(space->Begin() + pos + methods.Offset());
if (kIsDebugBuild && !method->IsRuntimeMethod()) {
CHECK(method->GetDeclaringClass() != nullptr);
}
if (!method->IsNative() && !method->IsRuntimeMethod() && !method->IsResolutionMethod()) {
method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
image_pointer_size_);
}
}
}
// reinit class_roots_
mirror::Class::SetClassClass(class_roots->Get(kJavaLangClass));
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get());
// reinit array_iftable_ from any array class instance, they should be ==
array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable());
DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable());
// String class root was set above
mirror::Field::SetClass(GetClassRoot(kJavaLangReflectField));
mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass));
mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor));
mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass));
mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod));
mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass));
mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass));
mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
mirror::IntArray::SetArrayClass(GetClassRoot(kIntArrayClass));
mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass));
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromImage exiting";
}
bool ClassLinker::ClassInClassTable(mirror::Class* klass) {
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
ClassTable* const class_table = ClassTableForClassLoader(klass->GetClassLoader());
return class_table != nullptr && class_table->Contains(klass);
}
void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
visitor, RootInfo(kRootStickyClass));
if ((flags & kVisitRootFlagAllRoots) != 0) {
// Argument for how root visiting deals with ArtField and ArtMethod roots.
// There is 3 GC cases to handle:
// Non moving concurrent:
// This case is easy to handle since the reference members of ArtMethod and ArtFields are held
// live by the class and class roots.
//
// Moving non-concurrent:
// This case needs to call visit VisitNativeRoots in case the classes or dex cache arrays move.
// To prevent missing roots, this case needs to ensure that there is no
// suspend points between the point which we allocate ArtMethod arrays and place them in a
// class which is in the class table.
//
// Moving concurrent:
// Need to make sure to not copy ArtMethods without doing read barriers since the roots are
// marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy.
std::vector<std::pair<GcRoot<mirror::ClassLoader>, ClassTable*>> reinsert;
for (auto it = classes_.begin(); it != classes_.end(); ) {
it->second->VisitRoots(visitor, flags);
const GcRoot<mirror::ClassLoader>& root = it->first;
mirror::ClassLoader* old_ref = root.Read<kWithoutReadBarrier>();
root.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
mirror::ClassLoader* new_ref = root.Read<kWithoutReadBarrier>();
if (new_ref != old_ref) {
reinsert.push_back(*it);
it = classes_.erase(it);
} else {
++it;
}
}
for (auto& pair : reinsert) {
classes_.Put(pair.first, pair.second);
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
root.VisitRoot(visitor, RootInfo(kRootStickyClass));
mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
// Concurrent moving GC marked new roots through the to-space invariant.
CHECK_EQ(new_ref, old_ref);
}
}
buffered_visitor.Flush(); // Flush before clearing new_class_roots_.
if ((flags & kVisitRootFlagClearRootLog) != 0) {
new_class_roots_.clear();
}
if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
log_new_class_table_roots_ = true;
} else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
log_new_class_table_roots_ = false;
}
// We deliberately ignore the class roots in the image since we
// handle image roots by using the MS/CMS rescanning of dirty cards.
}
// Keep in sync with InitCallback. Anything we visit, we need to
// reinit references to when reinitializing a ClassLinker from a
// mapped image.
void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
class_roots_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
Thread* const self = Thread::Current();
{
ReaderMutexLock mu(self, dex_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
for (GcRoot<mirror::DexCache>& dex_cache : dex_caches_) {
dex_cache.VisitRoot(visitor, RootInfo(kRootVMInternal));
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (size_t index : new_dex_cache_roots_) {
dex_caches_[index].VisitRoot(visitor, RootInfo(kRootVMInternal));
}
}
if ((flags & kVisitRootFlagClearRootLog) != 0) {
new_dex_cache_roots_.clear();
}
if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
log_new_dex_caches_roots_ = true;
} else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
log_new_dex_caches_roots_ = false;
}
}
VisitClassRoots(visitor, flags);
array_iftable_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
for (GcRoot<mirror::Class>& root : find_array_class_cache_) {
root.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
}
}
void ClassLinker::VisitClassesInternal(ClassVisitor* visitor) {
for (auto& pair : classes_) {
ClassTable* const class_table = pair.second;
if (!class_table->Visit(visitor)) {
return;
}
}
}
void ClassLinker::VisitClasses(ClassVisitor* visitor) {
if (dex_cache_image_class_lookup_required_) {
MoveImageClassesToClassTable();
}
Thread* const self = Thread::Current();
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
// Not safe to have thread suspension when we are holding a lock.
if (self != nullptr) {
ScopedAssertNoThreadSuspension nts(self, __FUNCTION__);
VisitClassesInternal(visitor);
} else {
VisitClassesInternal(visitor);
}
}
class GetClassesInToVector : public ClassVisitor {
public:
bool Visit(mirror::Class* klass) OVERRIDE {
classes_.push_back(klass);
return true;
}
std::vector<mirror::Class*> classes_;
};
class GetClassInToObjectArray : public ClassVisitor {
public:
explicit GetClassInToObjectArray(mirror::ObjectArray<mirror::Class>* arr)
: arr_(arr), index_(0) {}
bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
++index_;
if (index_ <= arr_->GetLength()) {
arr_->Set(index_ - 1, klass);
return true;
}
return false;
}
bool Succeeded() const SHARED_REQUIRES(Locks::mutator_lock_) {
return index_ <= arr_->GetLength();
}
private:
mirror::ObjectArray<mirror::Class>* const arr_;
int32_t index_;
};
void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) {
// TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem
// is avoiding duplicates.
if (!kMovingClasses) {
GetClassesInToVector accumulator;
VisitClasses(&accumulator);
for (mirror::Class* klass : accumulator.classes_) {
if (!visitor->Visit(klass)) {
return;
}
}
} else {
Thread* const self = Thread::Current();
StackHandleScope<1> hs(self);
auto classes = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
// We size the array assuming classes won't be added to the class table during the visit.
// If this assumption fails we iterate again.
while (true) {
size_t class_table_size;
{
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
// Add 100 in case new classes get loaded when we are filling in the object array.
class_table_size = NumZygoteClasses() + NumNonZygoteClasses() + 100;
}
mirror::Class* class_type = mirror::Class::GetJavaLangClass();
mirror::Class* array_of_class = FindArrayClass(self, &class_type);
classes.Assign(
mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size));
CHECK(classes.Get() != nullptr); // OOME.
GetClassInToObjectArray accumulator(classes.Get());
VisitClasses(&accumulator);
if (accumulator.Succeeded()) {
break;
}
}
for (int32_t i = 0; i < classes->GetLength(); ++i) {
// If the class table shrank during creation of the clases array we expect null elements. If
// the class table grew then the loop repeats. If classes are created after the loop has
// finished then we don't visit.
mirror::Class* klass = classes->Get(i);
if (klass != nullptr && !visitor->Visit(klass)) {
return;
}
}
}
}
ClassLinker::~ClassLinker() {
mirror::Class::ResetClass();
mirror::Constructor::ResetClass();
mirror::Field::ResetClass();
mirror::Method::ResetClass();
mirror::Reference::ResetClass();
mirror::StackTraceElement::ResetClass();
mirror::String::ResetClass();
mirror::Throwable::ResetClass();
mirror::BooleanArray::ResetArrayClass();
mirror::ByteArray::ResetArrayClass();
mirror::CharArray::ResetArrayClass();
mirror::Constructor::ResetArrayClass();
mirror::DoubleArray::ResetArrayClass();
mirror::Field::ResetArrayClass();
mirror::FloatArray::ResetArrayClass();
mirror::Method::ResetArrayClass();
mirror::IntArray::ResetArrayClass();
mirror::LongArray::ResetArrayClass();
mirror::ShortArray::ResetArrayClass();
STLDeleteElements(&oat_files_);
STLDeleteValues(&classes_);
}
mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) {
return down_cast<mirror::PointerArray*>(image_pointer_size_ == 8u ?
static_cast<mirror::Array*>(mirror::LongArray::Alloc(self, length)) :
static_cast<mirror::Array*>(mirror::IntArray::Alloc(self, length)));
}
mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) {
StackHandleScope<6> hs(self);
auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>(
GetClassRoot(kJavaLangDexCache)->AllocObject(self))));
if (dex_cache.Get() == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
auto location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str())));
if (location.Get() == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
auto strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds())));
if (strings.Get() == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
auto types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds())));
if (types.Get() == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
auto methods(hs.NewHandle(AllocPointerArray(self, dex_file.NumMethodIds())));
if (methods.Get() == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
auto fields(hs.NewHandle(AllocPointerArray(self, dex_file.NumFieldIds())));
if (fields.Get() == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(),
fields.Get(), image_pointer_size_);
return dex_cache.Get();
}
mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class,
uint32_t class_size) {
DCHECK_GE(class_size, sizeof(mirror::Class));
gc::Heap* heap = Runtime::Current()->GetHeap();
mirror::Class::InitializeClassVisitor visitor(class_size);
mirror::Object* k = kMovingClasses ?
heap->AllocObject<true>(self, java_lang_Class, class_size, visitor) :
heap->AllocNonMovableObject<true>(self, java_lang_Class, class_size, visitor);
if (UNLIKELY(k == nullptr)) {
self->AssertPendingOOMException();
return nullptr;
}
return k->AsClass();
}
mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) {
return AllocClass(self, GetClassRoot(kJavaLangClass), class_size);
}
mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElementArray(
Thread* self, size_t length) {
return mirror::ObjectArray<mirror::StackTraceElement>::Alloc(
self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length);
}
mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor,
mirror::Class* klass) {
DCHECK(klass != nullptr);
// For temporary classes we must wait for them to be retired.
if (init_done_ && klass->IsTemp()) {
CHECK(!klass->IsResolved());
if (klass->IsErroneous()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(klass));
ObjectLock<mirror::Class> lock(self, h_class);
// Loop and wait for the resolving thread to retire this class.
while (!h_class->IsRetired() && !h_class->IsErroneous()) {
lock.WaitIgnoringInterrupts();
}
if (h_class->IsErroneous()) {
ThrowEarlierClassFailure(h_class.Get());
return nullptr;
}
CHECK(h_class->IsRetired());
// Get the updated class from class table.
klass = LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor),
h_class.Get()->GetClassLoader());
}
// Wait for the class if it has not already been linked.
if (!klass->IsResolved() && !klass->IsErroneous()) {
StackHandleScope<1> hs(self);
HandleWrapper<mirror::Class> h_class(hs.NewHandleWrapper(&klass));
ObjectLock<mirror::Class> lock(self, h_class);
// Check for circular dependencies between classes.
if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) {
ThrowClassCircularityError(h_class.Get());
mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self);
return nullptr;
}
// Wait for the pending initialization to complete.
while (!h_class->IsResolved() && !h_class->IsErroneous()) {
lock.WaitIgnoringInterrupts();
}
}
if (klass->IsErroneous()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
// Return the loaded class. No exceptions should be pending.
CHECK(klass->IsResolved()) << PrettyClass(klass);
self->AssertNoPendingException();
return klass;
}
typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry;
// Search a collection of DexFiles for a descriptor
ClassPathEntry FindInClassPath(const char* descriptor,
size_t hash, const std::vector<const DexFile*>& class_path) {
for (const DexFile* dex_file : class_path) {
const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash);
if (dex_class_def != nullptr) {
return ClassPathEntry(dex_file, dex_class_def);
}
}
return ClassPathEntry(nullptr, nullptr);
}
static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
mirror::ClassLoader* class_loader)
SHARED_REQUIRES(Locks::mutator_lock_) {
return class_loader == nullptr ||
class_loader->GetClass() ==
soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader);
}
bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self, const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
mirror::Class** result) {
// Termination case: boot class-loader.
if (IsBootClassLoader(soa, class_loader.Get())) {
// The boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr);
if (klass != nullptr) {
*result = EnsureResolved(self, descriptor, klass);
} else {
*result = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(),
*pair.first, *pair.second);
}
if (*result == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
}
} else {
*result = nullptr;
}
return true;
}
// Unsupported class-loader?
if (class_loader->GetClass() !=
soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) {
*result = nullptr;
return false;
}
// Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
StackHandleScope<4> hs(self);
Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
bool recursive_result = FindClassInPathClassLoader(soa, self, descriptor, hash, h_parent, result);
if (!recursive_result) {
// Something wrong up the chain.
return false;
}
if (*result != nullptr) {
// Found the class up the chain.
return true;
}
// Handle this step.
// Handle as if this is the child PathClassLoader.
// The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
// We need to get the DexPathList and loop through it.
ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie);
ArtField* const dex_file_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
mirror::Object* dex_path_list =
soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
GetObject(class_loader.Get());
if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
mirror::Object* dex_elements_obj =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj != nullptr) {
Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
mirror::Object* element = dex_elements->GetWithoutChecks(i);
if (element == nullptr) {
// Should never happen, fall back to java code to throw a NPE.
break;
}
mirror::Object* dex_file = dex_file_field->GetObject(element);
if (dex_file != nullptr) {
mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray();
if (long_array == nullptr) {
// This should never happen so log a warning.
LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
break;
}
int32_t long_array_size = long_array->GetLength();
for (int32_t j = 0; j < long_array_size; ++j) {
const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
long_array->GetWithoutChecks(j)));
const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash);
if (dex_class_def != nullptr) {
RegisterDexFile(*cp_dex_file);
mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader,
*cp_dex_file, *dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
// TODO: Is it really right to break here, and not check the other dex files?
return true;
}
*result = klass;
return true;
}
}
}
}
}
self->AssertNoPendingException();
}
// Result is still null from the parent call, no need to set it again...
return true;
}
mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
Handle<mirror::ClassLoader> class_loader) {
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
DCHECK(self != nullptr);
self->AssertNoPendingException();
if (descriptor[1] == '\0') {
// only the descriptors of primitive types should be 1 character long, also avoid class lookup
// for primitive classes that aren't backed by dex files.
return FindPrimitiveClass(descriptor[0]);
}
const size_t hash = ComputeModifiedUtf8Hash(descriptor);
// Find the class in the loaded classes table.
mirror::Class* klass = LookupClass(self, descriptor, hash, class_loader.Get());
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
if (descriptor[0] == '[') {
return CreateArrayClass(self, descriptor, hash, class_loader);
} else if (class_loader.Get() == nullptr) {
// The boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
*pair.second);
} else {
// The boot class loader is searched ahead of the application class loader, failures are
// expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
// trigger the chaining with a proper stack trace.
mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
return nullptr;
}
} else {
ScopedObjectAccessUnchecked soa(self);
mirror::Class* cp_klass;
if (FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) {
// The chain was understood. So the value in cp_klass is either the class we were looking
// for, or not found.
if (cp_klass != nullptr) {
return cp_klass;
}
// TODO: We handle the boot classpath loader in FindClassInPathClassLoader. Try to unify this
// and the branch above. TODO: throw the right exception here.
// We'll let the Java-side rediscover all this and throw the exception with the right stack
// trace.
}
if (Runtime::Current()->IsAotCompiler()) {
// Oops, compile-time, can't run actual class-loader code.
mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
return nullptr;
}
ScopedLocalRef<jobject> class_loader_object(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.Get()));
std::string class_name_string(DescriptorToDot(descriptor));
ScopedLocalRef<jobject> result(soa.Env(), nullptr);
{
ScopedThreadStateChange tsc(self, kNative);
ScopedLocalRef<jobject> class_name_object(soa.Env(),
soa.Env()->NewStringUTF(class_name_string.c_str()));
if (class_name_object.get() == nullptr) {
DCHECK(self->IsExceptionPending()); // OOME.
return nullptr;
}
CHECK(class_loader_object.get() != nullptr);
result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
WellKnownClasses::java_lang_ClassLoader_loadClass,
class_name_object.get()));
}
if (self->IsExceptionPending()) {
// If the ClassLoader threw, pass that exception up.
return nullptr;
} else if (result.get() == nullptr) {
// broken loader - throw NPE to be compatible with Dalvik
ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
class_name_string.c_str()).c_str());
return nullptr;
} else {
// success, return mirror::Class*
return soa.Decode<mirror::Class*>(result.get());
}
}
UNREACHABLE();
}
mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
StackHandleScope<3> hs(self);
auto klass = hs.NewHandle<mirror::Class>(nullptr);
// Load the class from the dex file.
if (UNLIKELY(!init_done_)) {
// finish up init of hand crafted class_roots_
if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
klass.Assign(GetClassRoot(kJavaLangObject));
} else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
klass.Assign(GetClassRoot(kJavaLangClass));
} else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
klass.Assign(GetClassRoot(kJavaLangString));
} else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) {
klass.Assign(GetClassRoot(kJavaLangRefReference));
} else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {
klass.Assign(GetClassRoot(kJavaLangDexCache));
}
}
if (klass.Get() == nullptr) {
// Allocate a class with the status of not ready.
// Interface object should get the right size here. Regular class will
// figure out the right size later and be replaced with one of the right
// size when the class becomes resolved.
klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
}
if (UNLIKELY(klass.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // Expect an OOME.
return nullptr;
}
klass->SetDexCache(FindDexCache(dex_file));
SetupClass(dex_file, dex_class_def, klass, class_loader.Get());
// Mark the string class by setting its access flag.
if (UNLIKELY(!init_done_)) {
if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
klass->SetStringClass();
}
}
ObjectLock<mirror::Class> lock(self, klass);
klass->SetClinitThreadId(self->GetTid());
// Add the newly loaded class to the loaded classes table.
mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash);
if (existing != nullptr) {
// We failed to insert because we raced with another thread. Calling EnsureResolved may cause
// this thread to block.
return EnsureResolved(self, descriptor, existing);
}
// Load the fields and other things after we are inserted in the table. This is so that we don't
// end up allocating unfree-able linear alloc resources and then lose the race condition. The
// other reason is that the field roots are only visited from the class table. So we need to be
// inserted before we allocate / fill in these fields.
LoadClass(self, dex_file, dex_class_def, klass);
if (self->IsExceptionPending()) {
// An exception occured during load, set status to erroneous while holding klass' lock in case
// notification is necessary.
if (!klass->IsErroneous()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
}
return nullptr;
}
// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
}
return nullptr;
}
CHECK(klass->IsLoaded());
// Link the class (if necessary)
CHECK(!klass->IsResolved());
// TODO: Use fast jobjects?
auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
MutableHandle<mirror::Class> h_new_class = hs.NewHandle<mirror::Class>(nullptr);
if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
// Linking failed.
if (!klass->IsErroneous()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
}
return nullptr;
}
self->AssertNoPendingException();
CHECK(h_new_class.Get() != nullptr) << descriptor;
CHECK(h_new_class->IsResolved()) << descriptor;
// Instrumentation may have updated entrypoints for all methods of all
// classes. However it could not update methods of this class while we
// were loading it. Now the class is resolved, we can update entrypoints
// as required by instrumentation.
if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
// We must be in the kRunnable state to prevent instrumentation from
// suspending all threads to update entrypoints while we are doing it
// for this class.
DCHECK_EQ(self->GetState(), kRunnable);
Runtime::Current()->GetInstrumentation()->InstallStubsForClass(h_new_class.Get());
}
/*
* We send CLASS_PREPARE events to the debugger from here. The
* definition of "preparation" is creating the static fields for a
* class and initializing them to the standard default values, but not
* executing any code (that comes later, during "initialization").
*
* We did the static preparation in LinkClass.
*
* The class has been prepared and resolved but possibly not yet verified
* at this point.
*/
Dbg::PostClassPrepare(h_new_class.Get());
return h_new_class.Get();
}
uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
size_t num_ref = 0;
size_t num_8 = 0;
size_t num_16 = 0;
size_t num_32 = 0;
size_t num_64 = 0;
if (class_data != nullptr) {
for (ClassDataItemIterator it(dex_file, class_data); it.HasNextStaticField(); it.Next()) {
const DexFile::FieldId& field_id = dex_file.GetFieldId(it.GetMemberIndex());
const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id);
char c = descriptor[0];
switch (c) {
case 'L':
case '[':
num_ref++;
break;
case 'J':
case 'D':
num_64++;
break;
case 'I':
case 'F':
num_32++;
break;
case 'S':
case 'C':
num_16++;
break;
case 'B':
case 'Z':
num_8++;
break;
default:
LOG(FATAL) << "Unknown descriptor: " << c;
UNREACHABLE();
}
}
}
return mirror::Class::ComputeClassSize(false, 0, num_8, num_16, num_32, num_64, num_ref,
image_pointer_size_);
}
OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx,
bool* found) {
DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
if (oat_dex_file == nullptr) {
*found = false;
return OatFile::OatClass::Invalid();
}
*found = true;
return oat_dex_file->GetOatClass(class_def_idx);
}
static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx,
uint32_t method_idx) {
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx);
const uint8_t* class_data = dex_file.GetClassData(class_def);
CHECK(class_data != nullptr);
ClassDataItemIterator it(dex_file, class_data);
// Skip fields
while (it.HasNextStaticField()) {
it.Next();
}
while (it.HasNextInstanceField()) {
it.Next();
}
// Process methods
size_t class_def_method_index = 0;
while (it.HasNextDirectMethod()) {
if (it.GetMemberIndex() == method_idx) {
return class_def_method_index;
}
class_def_method_index++;
it.Next();
}
while (it.HasNextVirtualMethod()) {
if (it.GetMemberIndex() == method_idx) {
return class_def_method_index;
}
class_def_method_index++;
it.Next();
}
DCHECK(!it.HasNext());
LOG(FATAL) << "Failed to find method index " << method_idx << " in " << dex_file.GetLocation();
UNREACHABLE();
}
const OatFile::OatMethod ClassLinker::FindOatMethodFor(ArtMethod* method, bool* found) {
// Although we overwrite the trampoline of non-static methods, we may get here via the resolution
// method for direct methods (or virtual methods made direct).
mirror::Class* declaring_class = method->GetDeclaringClass();
size_t oat_method_index;
if (method->IsStatic() || method->IsDirect()) {
// Simple case where the oat method index was stashed at load time.
oat_method_index = method->GetMethodIndex();
} else {
// We're invoking a virtual method directly (thanks to sharpening), compute the oat_method_index
// by search for its position in the declared virtual methods.
oat_method_index = declaring_class->NumDirectMethods();
size_t end = declaring_class->NumVirtualMethods();
bool found_virtual = false;
for (size_t i = 0; i < end; i++) {
// Check method index instead of identity in case of duplicate method definitions.
if (method->GetDexMethodIndex() ==
declaring_class->GetVirtualMethod(i, image_pointer_size_)->GetDexMethodIndex()) {
found_virtual = true;
break;
}
oat_method_index++;
}
CHECK(found_virtual) << "Didn't find oat method index for virtual method: "
<< PrettyMethod(method);
}
DCHECK_EQ(oat_method_index,
GetOatMethodIndexFromMethodIndex(*declaring_class->GetDexCache()->GetDexFile(),
method->GetDeclaringClass()->GetDexClassDefIndex(),
method->GetDexMethodIndex()));
OatFile::OatClass oat_class = FindOatClass(*declaring_class->GetDexCache()->GetDexFile(),
declaring_class->GetDexClassDefIndex(),
found);
if (!(*found)) {
return OatFile::OatMethod::Invalid();
}
return oat_class.GetOatMethod(oat_method_index);
}
// Special case to get oat code without overwriting a trampoline.
const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) {
CHECK(!method->IsAbstract()) << PrettyMethod(method);
if (method->IsProxyMethod()) {
return GetQuickProxyInvokeHandler();
}
bool found;
OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
if (found) {
auto* code = oat_method.GetQuickCode();
if (code != nullptr) {
return code;
}
}
jit::Jit* const jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
auto* code = jit->GetCodeCache()->GetCodeFor(method);
if (code != nullptr) {
return code;
}
}
if (method->IsNative()) {
// No code and native? Use generic trampoline.
return GetQuickGenericJniStub();
}
return GetQuickToInterpreterBridge();
}
const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) {
if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
return nullptr;
}
bool found;
OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
if (found) {
return oat_method.GetQuickCode();
}
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
auto* code = jit->GetCodeCache()->GetCodeFor(method);
if (code != nullptr) {
return code;
}
}
return nullptr;
}
const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
uint32_t method_idx) {
bool found;
OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
if (!found) {
return nullptr;
}
uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
return oat_class.GetOatMethod(oat_method_idx).GetQuickCode();
}
// Returns true if the method must run with interpreter, false otherwise.
static bool NeedsInterpreter(ArtMethod* method, const void* quick_code)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (quick_code == nullptr) {
// No code: need interpreter.
// May return true for native code, in the case of generic JNI
// DCHECK(!method->IsNative());
return true;
}
// If interpreter mode is enabled, every method (except native and proxy) must
// be run with interpreter.
return Runtime::Current()->GetInstrumentation()->InterpretOnly() &&
!method->IsNative() && !method->IsProxyMethod();
}
void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) {
DCHECK(klass->IsInitialized()) << PrettyDescriptor(klass);
if (klass->NumDirectMethods() == 0) {
return; // No direct methods => no static methods.
}
Runtime* runtime = Runtime::Current();
if (!runtime->IsStarted()) {
if (runtime->IsAotCompiler() || runtime->GetHeap()->HasImageSpace()) {
return; // OAT file unavailable.
}
}
const DexFile& dex_file = klass->GetDexFile();
const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
CHECK(dex_class_def != nullptr);
const uint8_t* class_data = dex_file.GetClassData(*dex_class_def);
// There should always be class data if there were direct methods.
CHECK(class_data != nullptr) << PrettyDescriptor(klass);
ClassDataItemIterator it(dex_file, class_data);
// Skip fields
while (it.HasNextStaticField()) {
it.Next();
}
while (it.HasNextInstanceField()) {
it.Next();
}
bool has_oat_class;
OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(),
&has_oat_class);
// Link the code of methods skipped by LinkCode.
for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) {
ArtMethod* method = klass->GetDirectMethod(method_index, image_pointer_size_);
if (!method->IsStatic()) {
// Only update static methods.
continue;
}
const void* quick_code = nullptr;
if (has_oat_class) {
OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
quick_code = oat_method.GetQuickCode();
}
const bool enter_interpreter = NeedsInterpreter(method, quick_code);
if (enter_interpreter) {
// Use interpreter entry point.
// Check whether the method is native, in which case it's generic JNI.
if (quick_code == nullptr && method->IsNative()) {
quick_code = GetQuickGenericJniStub();
} else {
quick_code = GetQuickToInterpreterBridge();
}
}
runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code);
}
// Ignore virtual methods on the iterator.
}
void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,
uint32_t class_def_method_index) {
Runtime* const runtime = Runtime::Current();
if (runtime->IsAotCompiler()) {
// The following code only applies to a non-compiler runtime.
return;
}
// Method shouldn't have already been linked.
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
if (oat_class != nullptr) {
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);
oat_method.LinkMethod(method);
}
// Install entry point from interpreter.
bool enter_interpreter = NeedsInterpreter(method, method->GetEntryPointFromQuickCompiledCode());
if (method->IsAbstract()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
return;
}
if (method->IsStatic() && !method->IsConstructor()) {
// For static methods excluding the class initializer, install the trampoline.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
} else if (enter_interpreter) {
if (!method->IsNative()) {
// Set entry point from compiled code if there's no code or in interpreter only mode.
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
} else {
method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
}
}
if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative();
if (enter_interpreter) {
// We have a native method here without code. Then it should have either the generic JNI
// trampoline as entrypoint (non-static), or the resolution trampoline (static).
// TODO: this doesn't handle all the cases where trampolines may be installed.
const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
DCHECK(IsQuickGenericJniStub(entry_point) || IsQuickResolutionStub(entry_point));
}
}
}
void ClassLinker::SetupClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass, mirror::ClassLoader* class_loader) {
CHECK(klass.Get() != nullptr);
CHECK(klass->GetDexCache() != nullptr);
CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());
const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
CHECK(descriptor != nullptr);
klass->SetClass(GetClassRoot(kJavaLangClass));
uint32_t access_flags = dex_class_def.GetJavaAccessFlags();
CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U);
klass->SetAccessFlags(access_flags);
klass->SetClassLoader(class_loader);
DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, nullptr);
klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
klass->SetDexTypeIndex(dex_class_def.class_idx_);
CHECK(klass->GetDexCacheStrings() != nullptr);
}
void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass) {
const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
if (class_data == nullptr) {
return; // no fields or methods - for example a marker interface
}
bool has_oat_class = false;
if (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) {
OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(),
&has_oat_class);
if (has_oat_class) {
LoadClassMembers(self, dex_file, class_data, klass, &oat_class);
}
}
if (!has_oat_class) {
LoadClassMembers(self, dex_file, class_data, klass, nullptr);
}
}
ArtField* ClassLinker::AllocArtFieldArray(Thread* self, size_t length) {
auto* const la = Runtime::Current()->GetLinearAlloc();
auto* ptr = reinterpret_cast<ArtField*>(la->AllocArray<ArtField>(self, length));
CHECK(ptr!= nullptr);
std::uninitialized_fill_n(ptr, length, ArtField());
return ptr;
}
ArtMethod* ClassLinker::AllocArtMethodArray(Thread* self, size_t length) {
const size_t method_size = ArtMethod::ObjectSize(image_pointer_size_);
uintptr_t ptr = reinterpret_cast<uintptr_t>(
Runtime::Current()->GetLinearAlloc()->Alloc(self, method_size * length));
CHECK_NE(ptr, 0u);
for (size_t i = 0; i < length; ++i) {
new(reinterpret_cast<void*>(ptr + i * method_size)) ArtMethod;
}
return reinterpret_cast<ArtMethod*>(ptr);
}
void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,
const uint8_t* class_data,
Handle<mirror::Class> klass,
const OatFile::OatClass* oat_class) {
{
// Note: We cannot have thread suspension until the field and method arrays are setup or else
// Class::VisitFieldRoots may miss some fields or methods.
ScopedAssertNoThreadSuspension nts(self, __FUNCTION__);
// Load static fields.
// We allow duplicate definitions of the same field in a class_data_item
// but ignore the repeated indexes here, b/21868015.
ClassDataItemIterator it(dex_file, class_data);
ArtField* sfields =
it.NumStaticFields() != 0 ? AllocArtFieldArray(self, it.NumStaticFields()) : nullptr;
size_t num_sfields = 0;
uint32_t last_field_idx = 0u;
for (; it.HasNextStaticField(); it.Next()) {
uint32_t field_idx = it.GetMemberIndex();
DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier.
if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) {
DCHECK_LT(num_sfields, it.NumStaticFields());
LoadField(it, klass, &sfields[num_sfields]);
++num_sfields;
last_field_idx = field_idx;
}
}
klass->SetSFields(sfields);
klass->SetNumStaticFields(num_sfields);
DCHECK_EQ(klass->NumStaticFields(), num_sfields);
// Load instance fields.
ArtField* ifields =
it.NumInstanceFields() != 0 ? AllocArtFieldArray(self, it.NumInstanceFields()) : nullptr;
size_t num_ifields = 0u;
last_field_idx = 0u;
for (; it.HasNextInstanceField(); it.Next()) {
uint32_t field_idx = it.GetMemberIndex();
DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier.
if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) {
DCHECK_LT(num_ifields, it.NumInstanceFields());
LoadField(it, klass, &ifields[num_ifields]);
++num_ifields;
last_field_idx = field_idx;
}
}
if (UNLIKELY(num_sfields != it.NumStaticFields()) ||
UNLIKELY(num_ifields != it.NumInstanceFields())) {
LOG(WARNING) << "Duplicate fields in class " << PrettyDescriptor(klass.Get())
<< " (unique static fields: " << num_sfields << "/" << it.NumStaticFields()
<< ", unique instance fields: " << num_ifields << "/" << it.NumInstanceFields() << ")";
// NOTE: Not shrinking the over-allocated sfields/ifields.
}
klass->SetIFields(ifields);
klass->SetNumInstanceFields(num_ifields);
DCHECK_EQ(klass->NumInstanceFields(), num_ifields);
// Load methods.
if (it.NumDirectMethods() != 0) {
klass->SetDirectMethodsPtr(AllocArtMethodArray(self, it.NumDirectMethods()));
}
klass->SetNumDirectMethods(it.NumDirectMethods());
if (it.NumVirtualMethods() != 0) {
klass->SetVirtualMethodsPtr(AllocArtMethodArray(self, it.NumVirtualMethods()));
}
klass->SetNumVirtualMethods(it.NumVirtualMethods());
size_t class_def_method_index = 0;
uint32_t last_dex_method_index = DexFile::kDexNoIndex;
size_t last_class_def_method_index = 0;
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);