Working ClassLoader
Change-Id: Ia1122165e47f846a1d4506111849f830d9f14c1b
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 8ca633b..3f30b9e 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -436,10 +436,9 @@
java_lang_ref_WeakReference->GetAccessFlags() |
kAccClassIsReference | kAccClassIsWeakReference);
- // Setup the ClassLoaders, adjusting the object_size_ as necessary
+ // Setup the ClassLoaders, verifying the object_size_
Class* java_lang_ClassLoader = FindSystemClass("Ljava/lang/ClassLoader;");
- CHECK_LT(java_lang_ClassLoader->GetObjectSize(), sizeof(ClassLoader));
- java_lang_ClassLoader->SetObjectSize(sizeof(ClassLoader));
+ CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), sizeof(ClassLoader));
SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader);
Class* dalvik_system_BaseDexClassLoader = FindSystemClass("Ldalvik/system/BaseDexClassLoader;");
@@ -539,6 +538,7 @@
}
OatFile* ClassLinker::OpenOat(const Space* space) {
+ MutexLock mu(lock_);
const Runtime* runtime = Runtime::Current();
if (runtime->IsVerboseStartup()) {
LOG(INFO) << "ClassLinker::OpenOat entering";
@@ -568,6 +568,37 @@
return oat_file;
}
+const OatFile* ClassLinker::FindOatFile(const DexFile& dex_file) {
+ MutexLock mu(lock_);
+ std::string dex_file_location = dex_file.GetLocation();
+ std::string location(dex_file_location);
+ CHECK(StringPiece(location).ends_with(".dex")
+ || StringPiece(location).ends_with(".zip")
+ || StringPiece(location).ends_with(".jar")
+ || StringPiece(location).ends_with(".apk"));
+ location.erase(location.size()-3);
+ location += "oat";
+ // TODO: check if dex_file matches an OatDexFile location and checksum
+ return FindOatFile(location);
+}
+
+const OatFile* ClassLinker::FindOatFile(const std::string& location) {
+ for (size_t i = 0; i < oat_files_.size(); i++) {
+ const OatFile* oat_file = oat_files_[i];
+ DCHECK(oat_file != NULL);
+ if (oat_file->GetLocation() == location) {
+ return oat_file;
+ }
+ }
+
+ const OatFile* oat_file = OatFile::Open(location, "", NULL);
+ if (oat_file == NULL) {
+ LOG(ERROR) << "Failed to open oat file " << location;
+ return NULL;
+ }
+ return oat_file;
+}
+
void ClassLinker::InitFromImage() {
const Runtime* runtime = Runtime::Current();
if (runtime->IsVerboseStartup()) {
@@ -599,8 +630,8 @@
<< " referenced from oat file as " << dex_file_location;
}
- const OatFile::OatDexFile& oat_dex_file = oat_file->GetOatDexFile(dex_file_location);
- CHECK_EQ(dex_file->GetHeader().checksum_, oat_dex_file.GetDexFileChecksum());
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location);
+ CHECK_EQ(dex_file->GetHeader().checksum_, oat_dex_file->GetDexFileChecksum());
RegisterDexFile(*dex_file, dex_cache);
}
@@ -653,21 +684,13 @@
class_linker->intern_table_->RegisterStrong(obj->AsString());
return;
}
- if (!obj->IsClass()) {
+ if (obj->IsClass()) {
+ // restore class to ClassLinker::classes_ table
+ Class* klass = obj->AsClass();
+ std::string descriptor = klass->GetDescriptor()->ToModifiedUtf8();
+ class_linker->InsertClass(descriptor, klass);
return;
}
- Class* klass = obj->AsClass();
- // TODO: restore ClassLoader's list of DexFiles after image load
- // CHECK(klass->GetClassLoader() == NULL);
- const ClassLoader* class_loader = klass->GetClassLoader();
- if (class_loader != NULL) {
- // TODO: replace this hack with something based on command line arguments
- Thread::Current()->SetClassLoaderOverride(class_loader);
- }
-
- std::string descriptor = klass->GetDescriptor()->ToModifiedUtf8();
- // restore class to ClassLinker::classes_ table
- class_linker->InsertClass(descriptor, klass);
}
// Keep in sync with InitCallback. Anything we visit, we need to
@@ -759,100 +782,10 @@
length);
}
-Class* ClassLinker::FindClass(const StringPiece& descriptor,
- const ClassLoader* class_loader) {
- // TODO: remove this contrived parent class loader check when we have a real ClassLoader.
- if (class_loader != NULL) {
- Class* klass = FindClass(descriptor, NULL);
- if (klass != NULL) {
- return klass;
- }
- Thread::Current()->ClearException();
- }
-
+Class* EnsureResolved(Class* klass) {
+ DCHECK(klass != NULL);
+ // Wait for the class if it has not already been linked.
Thread* self = Thread::Current();
- DCHECK(self != NULL);
- CHECK(!self->IsExceptionPending()) << PrettyTypeOf(self->GetException());
- // Find the class in the loaded classes table.
- Class* klass = LookupClass(descriptor, class_loader);
- if (klass == NULL) {
- // Class is not yet loaded.
- if (descriptor[0] == '[' && descriptor[1] != '\0') {
- return CreateArrayClass(descriptor, class_loader);
- }
- const DexFile::ClassPath& class_path = ((class_loader != NULL)
- ? ClassLoader::GetClassPath(class_loader)
- : boot_class_path_);
- DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, class_path);
- if (pair.second == NULL) {
- std::string name(PrintableString(descriptor));
- ThrowNoClassDefFoundError("Class %s not found in class loader %p", name.c_str(), class_loader);
- return NULL;
- }
- const DexFile& dex_file = *pair.first;
- const DexFile::ClassDef& dex_class_def = *pair.second;
- DexCache* dex_cache = FindDexCache(dex_file);
- // Load the class from the dex file.
- if (!init_done_) {
- // finish up init of hand crafted class_roots_
- if (descriptor == "Ljava/lang/Object;") {
- klass = GetClassRoot(kJavaLangObject);
- } else if (descriptor == "Ljava/lang/Class;") {
- klass = GetClassRoot(kJavaLangClass);
- } else if (descriptor == "Ljava/lang/String;") {
- klass = GetClassRoot(kJavaLangString);
- } else if (descriptor == "Ljava/lang/reflect/Constructor;") {
- klass = GetClassRoot(kJavaLangReflectConstructor);
- } else if (descriptor == "Ljava/lang/reflect/Field;") {
- klass = GetClassRoot(kJavaLangReflectField);
- } else if (descriptor == "Ljava/lang/reflect/Method;") {
- klass = GetClassRoot(kJavaLangReflectMethod);
- } else {
- klass = AllocClass(SizeOfClass(dex_file, dex_class_def));
- }
- } else {
- klass = AllocClass(SizeOfClass(dex_file, dex_class_def));
- }
- if (!klass->IsResolved()) {
- klass->SetDexCache(dex_cache);
- LoadClass(dex_file, dex_class_def, klass, class_loader);
- // Check for a pending exception during load
- if (self->IsExceptionPending()) {
- return NULL;
- }
- ObjectLock lock(klass);
- klass->SetClinitThreadId(self->GetTid());
- // Add the newly loaded class to the loaded classes table.
- bool success = InsertClass(descriptor, klass); // TODO: just return collision
- if (!success) {
- // We may fail to insert if we raced with another thread.
- klass->SetClinitThreadId(0);
- klass = LookupClass(descriptor, class_loader);
- CHECK(klass != NULL);
- return klass;
- } else {
- // Finish loading (if necessary) by finding parents
- CHECK(!klass->IsLoaded());
- if (!LoadSuperAndInterfaces(klass, dex_file)) {
- // Loading failed.
- CHECK(self->IsExceptionPending());
- lock.NotifyAll();
- return NULL;
- }
- CHECK(klass->IsLoaded());
- // Link the class (if necessary)
- CHECK(!klass->IsResolved());
- if (!LinkClass(klass)) {
- // Linking failed.
- CHECK(self->IsExceptionPending());
- lock.NotifyAll();
- return NULL;
- }
- CHECK(klass->IsResolved());
- }
- }
- }
- // Link the class if it has not already been linked.
if (!klass->IsResolved() && !klass->IsErroneous()) {
ObjectLock lock(klass);
// Check for circular dependencies between classes.
@@ -871,8 +804,134 @@
return NULL;
}
// Return the loaded class. No exceptions should be pending.
- CHECK(klass->IsResolved()) << descriptor;
- CHECK(!self->IsExceptionPending()) << descriptor << " " << PrettyTypeOf(self->GetException());
+ CHECK(klass->IsResolved()) << PrettyClass(klass);
+ CHECK(!self->IsExceptionPending())
+ << PrettyClass(klass) << " " << PrettyTypeOf(self->GetException());
+ return klass;
+}
+
+Class* ClassLinker::FindClass(const std::string& descriptor,
+ const ClassLoader* class_loader) {
+ CHECK_NE(descriptor.size(), 0U);
+ Thread* self = Thread::Current();
+ DCHECK(self != NULL);
+ CHECK(!self->IsExceptionPending()) << PrettyTypeOf(self->GetException());
+ // Find the class in the loaded classes table.
+ Class* klass = LookupClass(descriptor, class_loader);
+ if (klass != NULL) {
+ return EnsureResolved(klass);
+ }
+ if (descriptor.size() == 1) {
+ // only the descriptors of primitive types should be 1 character long
+ return FindPrimitiveClass(descriptor[0]);
+ }
+ // Class is not yet loaded.
+ if (descriptor[0] == '[') {
+ return CreateArrayClass(descriptor, class_loader);
+ }
+ if (class_loader == NULL) {
+ DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
+ if (pair.second == NULL) {
+ std::string name(PrintableString(descriptor));
+ ThrowNoClassDefFoundError("Class %s not found in boot class loader", name.c_str());
+ return NULL;
+ }
+ return DefineClass(descriptor, NULL, *pair.first, *pair.second);
+ }
+
+ if (ClassLoader::UseCompileTimeClassPath()) {
+ const std::vector<const DexFile*>& class_path
+ = ClassLoader::GetCompileTimeClassPath(class_loader);
+ DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, class_path);
+ if (pair.second == NULL) {
+ return FindSystemClass(descriptor);
+ }
+ return DefineClass(descriptor, class_loader, *pair.first, *pair.second);
+ }
+
+ std::string class_name_string = DescriptorToDot(descriptor);
+ ScopedThreadStateChange(self, Thread::kNative);
+ JNIEnv* env = self->GetJniEnv();
+ jclass c = AddLocalReference<jclass>(env, GetClassRoot(kJavaLangClassLoader));
+ CHECK(c != NULL);
+ // TODO: cache method?
+ jmethodID mid = env->GetMethodID(c, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+ CHECK(mid != NULL);
+ jobject class_name_object = env->NewStringUTF(class_name_string.c_str());
+ if (class_name_string == NULL) {
+ return NULL;
+ }
+ jobject class_loader_object = AddLocalReference<jobject>(env, class_loader);
+ jobject result = env->CallObjectMethod(class_loader_object, mid, class_name_object);
+ Class* klass_result = Decode<Class*>(env, result);
+ env->DeleteLocalRef(result);
+ env->DeleteLocalRef(class_name_object);
+ env->DeleteLocalRef(c);
+ return klass_result;
+}
+
+Class* ClassLinker::DefineClass(const std::string& descriptor,
+ const ClassLoader* class_loader,
+ const DexFile& dex_file,
+ const DexFile::ClassDef& dex_class_def) {
+ Class* klass;
+ // Load the class from the dex file.
+ if (!init_done_) {
+ // finish up init of hand crafted class_roots_
+ if (descriptor == "Ljava/lang/Object;") {
+ klass = GetClassRoot(kJavaLangObject);
+ } else if (descriptor == "Ljava/lang/Class;") {
+ klass = GetClassRoot(kJavaLangClass);
+ } else if (descriptor == "Ljava/lang/String;") {
+ klass = GetClassRoot(kJavaLangString);
+ } else if (descriptor == "Ljava/lang/reflect/Constructor;") {
+ klass = GetClassRoot(kJavaLangReflectConstructor);
+ } else if (descriptor == "Ljava/lang/reflect/Field;") {
+ klass = GetClassRoot(kJavaLangReflectField);
+ } else if (descriptor == "Ljava/lang/reflect/Method;") {
+ klass = GetClassRoot(kJavaLangReflectMethod);
+ } else {
+ klass = AllocClass(SizeOfClass(dex_file, dex_class_def));
+ }
+ } else {
+ klass = AllocClass(SizeOfClass(dex_file, dex_class_def));
+ }
+ klass->SetDexCache(FindDexCache(dex_file));
+ LoadClass(dex_file, dex_class_def, klass, class_loader);
+ // Check for a pending exception during load
+ Thread* self = Thread::Current();
+ if (self->IsExceptionPending()) {
+ return NULL;
+ }
+ ObjectLock lock(klass);
+ klass->SetClinitThreadId(self->GetTid());
+ // Add the newly loaded class to the loaded classes table.
+ bool success = InsertClass(descriptor, klass); // TODO: just return collision
+ if (!success) {
+ // We may fail to insert if we raced with another thread.
+ klass->SetClinitThreadId(0);
+ klass = LookupClass(descriptor, class_loader);
+ CHECK(klass != NULL);
+ return klass;
+ }
+ // Finish loading (if necessary) by finding parents
+ CHECK(!klass->IsLoaded());
+ if (!LoadSuperAndInterfaces(klass, dex_file)) {
+ // Loading failed.
+ CHECK(self->IsExceptionPending());
+ lock.NotifyAll();
+ return NULL;
+ }
+ CHECK(klass->IsLoaded());
+ // Link the class (if necessary)
+ CHECK(!klass->IsResolved());
+ if (!LinkClass(klass)) {
+ // Linking failed.
+ CHECK(self->IsExceptionPending());
+ lock.NotifyAll();
+ return NULL;
+ }
+ CHECK(klass->IsResolved());
return klass;
}
@@ -991,18 +1050,36 @@
}
}
+ UniquePtr<const OatFile::OatClass> oat_class;
+ if (Runtime::Current()->IsStarted() && !ClassLoader::UseCompileTimeClassPath()) {
+ const OatFile* oat_file = FindOatFile(dex_file);
+ if (oat_file != NULL) {
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation());
+ if (oat_dex_file != NULL) {
+ uint32_t class_def_index;
+ bool found = dex_file.FindClassDefIndex(descriptor, class_def_index);
+ CHECK(found) << descriptor;
+ oat_class.reset(oat_dex_file->GetOatClass(class_def_index));
+ }
+ }
+ }
+ size_t method_index = 0;
+
// Load direct methods.
if (num_direct_methods != 0) {
// TODO: append direct methods to class object
klass->SetDirectMethods(AllocObjectArray<Method>(num_direct_methods));
uint32_t last_idx = 0;
- for (size_t i = 0; i < num_direct_methods; ++i) {
+ for (size_t i = 0; i < num_direct_methods; ++i, ++method_index) {
DexFile::Method dex_method;
dex_file.dexReadClassDataMethod(&class_data, &dex_method, &last_idx);
Method* meth = AllocMethod();
klass->SetDirectMethod(i, meth);
LoadMethod(dex_file, dex_method, klass, meth);
- // TODO: register maps
+ if (oat_class.get() != NULL) {
+ const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
+ oat_method.LinkMethod(meth);
+ }
}
}
@@ -1011,13 +1088,16 @@
// TODO: append virtual methods to class object
klass->SetVirtualMethods(AllocObjectArray<Method>(num_virtual_methods));
uint32_t last_idx = 0;
- for (size_t i = 0; i < num_virtual_methods; ++i) {
+ for (size_t i = 0; i < num_virtual_methods; ++i, ++method_index) {
DexFile::Method dex_method;
dex_file.dexReadClassDataMethod(&class_data, &dex_method, &last_idx);
Method* meth = AllocMethod();
klass->SetVirtualMethod(i, meth);
LoadMethod(dex_file, dex_method, klass, meth);
- // TODO: register maps
+ if (oat_class.get() != NULL) {
+ const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
+ oat_method.LinkMethod(meth);
+ }
}
}
}
@@ -1130,18 +1210,42 @@
RegisterDexFile(dex_file, dex_cache);
}
-void ClassLinker::RegisterDexFile(const DexFile& dex_file) {
- RegisterDexFile(dex_file, AllocDexCache(dex_file));
+bool ClassLinker::IsDexFileRegisteredLocked(const DexFile& dex_file) const {
+ lock_.AssertHeld();
+ for (size_t i = 0; i != dex_files_.size(); ++i) {
+ if (dex_files_[i] == &dex_file) {
+ return true;
+ }
+ }
+ return false;
}
-void ClassLinker::RegisterDexFile(const DexFile& dex_file, DexCache* dex_cache) {
+bool ClassLinker::IsDexFileRegistered(const DexFile& dex_file) const {
MutexLock mu(lock_);
+ return IsDexFileRegistered(dex_file);
+}
+
+void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, DexCache* dex_cache) {
+ lock_.AssertHeld();
CHECK(dex_cache != NULL) << dex_file.GetLocation();
CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation()));
dex_files_.push_back(&dex_file);
dex_caches_.push_back(dex_cache);
}
+void ClassLinker::RegisterDexFile(const DexFile& dex_file) {
+ MutexLock mu(lock_);
+ if (IsDexFileRegisteredLocked(dex_file)) {
+ return;
+ }
+ RegisterDexFileLocked(dex_file, AllocDexCache(dex_file));
+}
+
+void ClassLinker::RegisterDexFile(const DexFile& dex_file, DexCache* dex_cache) {
+ MutexLock mu(lock_);
+ RegisterDexFileLocked(dex_file, dex_cache);
+}
+
const DexFile& ClassLinker::FindDexFile(const DexCache* dex_cache) const {
MutexLock mu(lock_);
for (size_t i = 0; i != dex_caches_.size(); ++i) {
@@ -1191,7 +1295,7 @@
// array class; that always comes from the base element class.
//
// Returns NULL with an exception raised on failure.
-Class* ClassLinker::CreateArrayClass(const StringPiece& descriptor,
+Class* ClassLinker::CreateArrayClass(const std::string& descriptor,
const ClassLoader* class_loader) {
CHECK_EQ('[', descriptor[0]);
@@ -1259,7 +1363,7 @@
if (new_class->GetDescriptor() != NULL) {
DCHECK(new_class->GetDescriptor()->Equals(descriptor));
} else {
- new_class->SetDescriptor(intern_table_->InternStrong(descriptor.ToString().c_str()));
+ new_class->SetDescriptor(intern_table_->InternStrong(descriptor.c_str()));
}
Class* java_lang_Object = GetClassRoot(kJavaLangObject);
new_class->SetSuperClass(java_lang_Object);
@@ -1339,14 +1443,14 @@
return NULL;
}
-bool ClassLinker::InsertClass(const StringPiece& descriptor, Class* klass) {
+bool ClassLinker::InsertClass(const std::string& descriptor, Class* klass) {
size_t hash = StringPieceHash()(descriptor);
MutexLock mu(lock_);
Table::iterator it = classes_.insert(std::make_pair(hash, klass));
return ((*it).second == klass);
}
-Class* ClassLinker::LookupClass(const StringPiece& descriptor, const ClassLoader* class_loader) {
+Class* ClassLinker::LookupClass(const std::string& descriptor, const ClassLoader* class_loader) {
size_t hash = StringPieceHash()(descriptor);
MutexLock mu(lock_);
typedef Table::const_iterator It; // TODO: C++0x auto
@@ -2290,12 +2394,7 @@
Class* resolved = dex_cache->GetResolvedType(type_idx);
if (resolved == NULL) {
const char* descriptor = dex_file.dexStringByTypeIdx(type_idx);
- if (descriptor[1] == '\0') {
- // only the descriptors of primitive types should be 1 character long
- resolved = FindPrimitiveClass(descriptor[0]);
- } else {
- resolved = FindClass(descriptor, class_loader);
- }
+ resolved = FindClass(descriptor, class_loader);
if (resolved != NULL) {
Class* check = resolved;
while (check->IsArrayClass()) {