Revert "Do ClassTable::Lookup when walking the class loader chain."
This reverts commit f07e4aac9583cb95d7b34a121aa8146be7c040ed.
Reason for revert: b/259266411
Change-Id: I6afb135edc0c8ed872dad08ee5a9e06d7853e136
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b939924..f76fe2a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2694,35 +2694,6 @@
return klass;
}
-namespace {
-
-// Matches exceptions caught in DexFile.defineClass.
-ALWAYS_INLINE bool MatchesDexFileCaughtExceptions(ObjPtr<mirror::Throwable> throwable,
- ClassLinker* class_linker)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return
- // ClassNotFoundException.
- throwable->InstanceOf(GetClassRoot(ClassRoot::kJavaLangClassNotFoundException,
- class_linker))
- ||
- // NoClassDefFoundError. TODO: Reconsider this. b/130746382.
- throwable->InstanceOf(Runtime::Current()->GetPreAllocatedNoClassDefFoundError()->GetClass());
-}
-
-// Clear exceptions caught in DexFile.defineClass.
-ALWAYS_INLINE void PotentiallyFilterDexFileCaughtExceptions(ObjPtr<mirror::Class> found,
- Thread* self,
- ClassLinker* class_linker)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(found != nullptr || self->IsExceptionPending());
- if (UNLIKELY(found == nullptr) &&
- MatchesDexFileCaughtExceptions(self->GetException(), class_linker)) {
- self->ClearException();
- }
-}
-
-} // namespace
-
using ClassPathEntry = std::pair<const DexFile*, const dex::ClassDef*>;
// Search a collection of DexFiles for a descriptor
@@ -2754,7 +2725,7 @@
if (local_thread->IsExceptionPending()) { \
/* Pending exception means there was an error other than */ \
/* ClassNotFound that must be returned to the caller. */ \
- return true; \
+ return false; \
} \
} while (0)
@@ -2806,30 +2777,11 @@
size_t hash,
Handle<mirror::ClassLoader> class_loader,
/*out*/ ObjPtr<mirror::Class>* result) {
- ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, class_loader.Get());
- if (klass != nullptr) {
- *result = EnsureResolved(self, descriptor, klass);
- PotentiallyFilterDexFileCaughtExceptions(*result, self, this);
- return true;
- }
- return FindClassInBaseDexClassLoaderHelper(self, descriptor, hash, class_loader, result);
-}
-
-bool ClassLinker::FindClassInBaseDexClassLoaderHelper(Thread* self,
- const char* descriptor,
- size_t hash,
- Handle<mirror::ClassLoader> class_loader,
- /*out*/ ObjPtr<mirror::Class>* result) {
// Termination case: boot class loader.
- if (class_loader == nullptr) {
+ if (IsBootClassLoader(class_loader.Get())) {
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
FindClassInBootClassLoaderClassPath(self, descriptor, hash, result), *result, self);
return true;
- } else if (IsBootClassLoader(class_loader.Get())) {
- // Call back into `FindClassInBaseDexClassLoader` to do the lookup in the
- // class table.
- return FindClassInBaseDexClassLoader(
- self, descriptor, hash, ScopedNullHandle<mirror::ClassLoader>(), result);
}
if (IsPathOrDexClassLoader(class_loader) || IsInMemoryDexClassLoader(class_loader)) {
@@ -2869,10 +2821,7 @@
// - class loader dex files
// - parent
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoader(
- self, descriptor, hash, ScopedNullHandle<mirror::ClassLoader>(), result),
- *result,
- self);
+ FindClassInBootClassLoaderClassPath(self, descriptor, hash, result), *result, self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
FindClassInSharedLibraries(self, descriptor, hash, class_loader, result),
*result,
@@ -2905,6 +2854,31 @@
#undef RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION
+namespace {
+
+// Matches exceptions caught in DexFile.defineClass.
+ALWAYS_INLINE bool MatchesDexFileCaughtExceptions(ObjPtr<mirror::Throwable> throwable,
+ ClassLinker* class_linker)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return
+ // ClassNotFoundException.
+ throwable->InstanceOf(GetClassRoot(ClassRoot::kJavaLangClassNotFoundException,
+ class_linker))
+ ||
+ // NoClassDefFoundError. TODO: Reconsider this. b/130746382.
+ throwable->InstanceOf(Runtime::Current()->GetPreAllocatedNoClassDefFoundError()->GetClass());
+}
+
+// Clear exceptions caught in DexFile.defineClass.
+ALWAYS_INLINE void FilterDexFileCaughtExceptions(Thread* self, ClassLinker* class_linker)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (MatchesDexFileCaughtExceptions(self->GetException(), class_linker)) {
+ self->ClearException();
+ }
+}
+
+} // namespace
+
// Finds the class in the boot class loader.
// If the class is found the method returns the resolved class. Otherwise it returns null.
bool ClassLinker::FindClassInBootClassLoaderClassPath(Thread* self,
@@ -2913,13 +2887,21 @@
/*out*/ ObjPtr<mirror::Class>* result) {
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
- *result = DefineClass(self,
- descriptor,
- hash,
- ScopedNullHandle<mirror::ClassLoader>(),
- *pair.first,
- *pair.second);
- PotentiallyFilterDexFileCaughtExceptions(*result, self, this);
+ ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
+ if (klass != nullptr) {
+ *result = EnsureResolved(self, descriptor, klass);
+ } else {
+ *result = DefineClass(self,
+ descriptor,
+ hash,
+ ScopedNullHandle<mirror::ClassLoader>(),
+ *pair.first,
+ *pair.second);
+ }
+ if (*result == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ FilterDexFileCaughtExceptions(self, this);
+ }
}
// The boot classloader is always a known lookup.
return true;
@@ -2952,120 +2934,17 @@
if (class_def != nullptr) {
*result = DefineClass(self, descriptor, hash, class_loader, *dex_file, *class_def);
- PotentiallyFilterDexFileCaughtExceptions(*result, self, this);
+ if (UNLIKELY(*result == nullptr)) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ FilterDexFileCaughtExceptions(self, this);
+ } else {
+ DCHECK(!self->IsExceptionPending());
+ }
}
// A BaseDexClassLoader is always a known lookup.
return true;
}
-ObjPtr<mirror::Class> ClassLinker::FindClassInAppClassLoader(Thread* self,
- const char* descriptor,
- size_t hash,
- Handle<mirror::ClassLoader> loader,
- bool* descriptor_equals) {
- ObjPtr<mirror::Class> result_ptr;
- ScopedObjectAccessUnchecked soa(self);
- // Call the helper directly, as the lookup has already been done in
- // `ClassLinker::FindClass`.
- bool known_hierarchy =
- FindClassInBaseDexClassLoaderHelper(self, descriptor, hash, loader, &result_ptr);
-
- if (result_ptr != nullptr) {
- DCHECK(!self->IsExceptionPending());
- // The chain was understood and we found the class. We still need to add the class to
- // the class table to protect from racy programs that can try and redefine the path list
- // which would change the Class<?> returned for subsequent evaluation of const-class.
- DCHECK(known_hierarchy);
- DCHECK(result_ptr->DescriptorEquals(descriptor));
- *descriptor_equals = true;
- return result_ptr;
- }
-
- if (self->IsExceptionPending()) {
- DCHECK(known_hierarchy);
- DCHECK(!MatchesDexFileCaughtExceptions(self->GetException(), this));
- // Not an exception due to class loading, propagate it up.
- return nullptr;
- }
-
- // Either the chain wasn't understood or the class wasn't found.
- // If there is a pending exception we didn't clear, it is a not a ClassNotFoundException and
- // we should return it instead of silently clearing and retrying.
- //
- // If the chain was understood but we did not find the class, let the Java-side
- // rediscover all this and throw the exception with the right stack trace. Note that
- // the Java-side could still succeed for racy programs if another thread is actively
- // modifying the class loader's path list.
-
- // The runtime is not allowed to call into java from a runtime-thread so just abort.
- if (self->IsRuntimeThread()) {
- // Oops, we can't call into java so we can't run actual class-loader code.
- // This is true for e.g. for the compiler (jit or aot).
- ObjPtr<mirror::Throwable> pre_allocated =
- Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
- self->SetException(pre_allocated);
- return nullptr;
- }
-
- // Inlined DescriptorToDot(descriptor) with extra validation.
- //
- // Throw NoClassDefFoundError early rather than potentially load a class only to fail
- // the DescriptorEquals() check below and give a confusing error message. For example,
- // when native code erroneously calls JNI GetFieldId() with signature "java/lang/String"
- // instead of "Ljava/lang/String;", the message below using the "dot" names would be
- // "class loader [...] returned class java.lang.String instead of java.lang.String".
- size_t descriptor_length = strlen(descriptor);
- if (UNLIKELY(descriptor[0] != 'L') ||
- UNLIKELY(descriptor[descriptor_length - 1] != ';') ||
- UNLIKELY(memchr(descriptor + 1, '.', descriptor_length - 2) != nullptr)) {
- ThrowNoClassDefFoundError("Invalid descriptor: %s.", descriptor);
- return nullptr;
- }
-
- std::string class_name_string(descriptor + 1, descriptor_length - 2);
- std::replace(class_name_string.begin(), class_name_string.end(), '/', '.');
- if (known_hierarchy &&
- fast_class_not_found_exceptions_ &&
- !Runtime::Current()->IsJavaDebuggable()) {
- // For known hierarchy, we know that the class is going to throw an exception. If we aren't
- // debuggable, optimize this path by throwing directly here without going back to Java
- // language. This reduces how many ClassNotFoundExceptions happen.
- self->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
- "%s",
- class_name_string.c_str());
- return nullptr;
- }
-
- ScopedLocalRef<jobject> result(soa.Env(), nullptr);
- {
- ScopedLocalRef<jobject> class_loader_object(
- soa.Env(), soa.AddLocalReference<jobject>(loader.Get()));
- ScopedThreadStateChange tsc(self, ThreadState::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()));
- }
- result_ptr = soa.Decode<mirror::Class>(result.get());
-
- if (result_ptr == nullptr && !self->IsExceptionPending()) {
- // 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;
- }
-
- // Check the name of the returned class.
- *descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor);
- return result_ptr;
-}
-
ObjPtr<mirror::Class> ClassLinker::FindClass(Thread* self,
const char* descriptor,
Handle<mirror::ClassLoader> class_loader) {
@@ -3084,14 +2963,9 @@
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
-
- // Fast path for boot class loader.
+ // Class is not yet loaded.
if (descriptor[0] != '[' && class_loader == nullptr) {
// Non-array class and the boot class loader, search the boot class path.
- // Note we do not check if class_loader is an instance of BootClassLoader as
- // the Java-level Class.forName will pass the BootClassLoader instance
- // instead of null and expects a ClassNotFoundException, not a
- // NoClassDefFoundError if the class is not found.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self,
@@ -3110,18 +2984,101 @@
return nullptr;
}
}
-
ObjPtr<mirror::Class> result_ptr;
- bool descriptor_equals = false;
+ bool descriptor_equals;
if (descriptor[0] == '[') {
result_ptr = CreateArrayClass(self, descriptor, hash, class_loader);
+ DCHECK_EQ(result_ptr == nullptr, self->IsExceptionPending());
DCHECK(result_ptr == nullptr || result_ptr->DescriptorEquals(descriptor));
descriptor_equals = true;
} else {
- result_ptr =
- FindClassInAppClassLoader(self, descriptor, hash, class_loader, &descriptor_equals);
+ ScopedObjectAccessUnchecked soa(self);
+ bool known_hierarchy =
+ FindClassInBaseDexClassLoader(self, descriptor, hash, class_loader, &result_ptr);
+ if (result_ptr != nullptr) {
+ // The chain was understood and we found the class. We still need to add the class to
+ // the class table to protect from racy programs that can try and redefine the path list
+ // which would change the Class<?> returned for subsequent evaluation of const-class.
+ DCHECK(known_hierarchy);
+ DCHECK(result_ptr->DescriptorEquals(descriptor));
+ descriptor_equals = true;
+ } else if (!self->IsExceptionPending()) {
+ // Either the chain wasn't understood or the class wasn't found.
+ // If there is a pending exception we didn't clear, it is a not a ClassNotFoundException and
+ // we should return it instead of silently clearing and retrying.
+ //
+ // If the chain was understood but we did not find the class, let the Java-side
+ // rediscover all this and throw the exception with the right stack trace. Note that
+ // the Java-side could still succeed for racy programs if another thread is actively
+ // modifying the class loader's path list.
+
+ // The runtime is not allowed to call into java from a runtime-thread so just abort.
+ if (self->IsRuntimeThread()) {
+ // Oops, we can't call into java so we can't run actual class-loader code.
+ // This is true for e.g. for the compiler (jit or aot).
+ ObjPtr<mirror::Throwable> pre_allocated =
+ Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+ self->SetException(pre_allocated);
+ return nullptr;
+ }
+
+ // Inlined DescriptorToDot(descriptor) with extra validation.
+ //
+ // Throw NoClassDefFoundError early rather than potentially load a class only to fail
+ // the DescriptorEquals() check below and give a confusing error message. For example,
+ // when native code erroneously calls JNI GetFieldId() with signature "java/lang/String"
+ // instead of "Ljava/lang/String;", the message below using the "dot" names would be
+ // "class loader [...] returned class java.lang.String instead of java.lang.String".
+ size_t descriptor_length = strlen(descriptor);
+ if (UNLIKELY(descriptor[0] != 'L') ||
+ UNLIKELY(descriptor[descriptor_length - 1] != ';') ||
+ UNLIKELY(memchr(descriptor + 1, '.', descriptor_length - 2) != nullptr)) {
+ ThrowNoClassDefFoundError("Invalid descriptor: %s.", descriptor);
+ return nullptr;
+ }
+
+ std::string class_name_string(descriptor + 1, descriptor_length - 2);
+ std::replace(class_name_string.begin(), class_name_string.end(), '/', '.');
+ if (known_hierarchy &&
+ fast_class_not_found_exceptions_ &&
+ !Runtime::Current()->IsJavaDebuggable()) {
+ // For known hierarchy, we know that the class is going to throw an exception. If we aren't
+ // debuggable, optimize this path by throwing directly here without going back to Java
+ // language. This reduces how many ClassNotFoundExceptions happen.
+ self->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
+ "%s",
+ class_name_string.c_str());
+ } else {
+ ScopedLocalRef<jobject> class_loader_object(
+ soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
+ ScopedLocalRef<jobject> result(soa.Env(), nullptr);
+ {
+ ScopedThreadStateChange tsc(self, ThreadState::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 (result.get() == nullptr && !self->IsExceptionPending()) {
+ // 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;
+ }
+ result_ptr = soa.Decode<mirror::Class>(result.get());
+ // Check the name of the returned class.
+ descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor);
+ }
+ } else {
+ DCHECK(!MatchesDexFileCaughtExceptions(self->GetException(), this));
+ }
}
- DCHECK_EQ(result_ptr == nullptr, self->IsExceptionPending());
if (self->IsExceptionPending()) {
// If the ClassLoader threw or array class allocation failed, pass that exception up.
@@ -3134,12 +3091,6 @@
return nullptr;
}
- // If the class loader of the class is `class_loader` return early as the
- // class has already been inserted.
- if (descriptor_equals && result_ptr->GetClassLoader() == class_loader.Get()) {
- return result_ptr;
- }
-
// Try to insert the class to the class table, checking for mismatch.
ObjPtr<mirror::Class> old;
{
@@ -3154,7 +3105,6 @@
} // else throw below, after releasing the lock.
}
}
-
if (UNLIKELY(old != result_ptr)) {
// Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel
// capable class loaders. (All class loaders are considered parallel capable on Android.)
@@ -3166,7 +3116,6 @@
<< DescriptorToDot(descriptor) << "\").";
return EnsureResolved(self, descriptor, old);
}
-
if (UNLIKELY(!descriptor_equals)) {
std::string result_storage;
const char* result_name = result_ptr->GetDescriptor(&result_storage);
@@ -4926,26 +4875,19 @@
return nullptr;
}
- StackHandleScope<13> hs(self);
+ StackHandleScope<12> hs(self);
MutableHandle<mirror::Class> temp_klass(hs.NewHandle(
AllocClass(self, GetClassRoot<mirror::Class>(this), sizeof(mirror::Class))));
if (temp_klass == nullptr) {
CHECK(self->IsExceptionPending()); // OOME.
return nullptr;
}
- // For consistency in the runtime, replace an instance of `BootClassLoader`
- // with the null loader.
- MutableHandle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader)));
- if (IsBootClassLoader(class_loader.Get())) {
- class_loader.Assign(nullptr);
- }
DCHECK(temp_klass->GetClass() != nullptr);
temp_klass->SetObjectSize(sizeof(mirror::Proxy));
// Set the class access flags incl. VerificationAttempted, so we do not try to set the flag on
// the methods.
temp_klass->SetAccessFlagsDuringLinking(kAccClassIsProxy | kAccPublic | kAccFinal);
- temp_klass->SetClassLoader(class_loader.Get());
+ temp_klass->SetClassLoader(soa.Decode<mirror::ClassLoader>(loader));
DCHECK_EQ(temp_klass->GetPrimitiveType(), Primitive::kPrimNot);
temp_klass->SetName(soa.Decode<mirror::String>(name));
temp_klass->SetDexCache(GetClassRoot<mirror::Proxy>(this)->GetDexCache());
@@ -5951,11 +5893,6 @@
CHECK(class_loader->GetAllocator() == nullptr);
CHECK(class_loader->GetClassTable() == nullptr);
Thread* const self = Thread::Current();
- // Instances of `BootClassLoader` get created at the Java level, but for the
- // runtime, `null` is the true loader for boot classpath. We don't register
- // a `BootClassLoader` as it shall not have an allocator or class table, but
- // instead use `boot_class_table_` and runtime allocator.
- DCHECK(!IsBootClassLoader(class_loader));
ClassLoaderData data;
data.weak_root = self->GetJniEnv()->GetVm()->AddWeakGlobalRef(self, class_loader);
// Create and set the class table.
@@ -5974,9 +5911,6 @@
}
ClassTable* class_table = class_loader->GetClassTable();
if (class_table == nullptr) {
- if (IsBootClassLoader(class_loader)) {
- return boot_class_table_.get();
- }
RegisterClassLoader(class_loader);
class_table = class_loader->GetClassTable();
DCHECK(class_table != nullptr);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 13a40ae..5f674b8 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -1025,16 +1025,6 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- // Same as above, but doesn't lookup the class in the class loader's class
- // table.
- bool FindClassInBaseDexClassLoaderHelper(Thread* self,
- const char* descriptor,
- size_t hash,
- Handle<mirror::ClassLoader> class_loader,
- /*out*/ ObjPtr<mirror::Class>* result)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_);
-
bool FindClassInSharedLibraries(Thread* self,
const char* descriptor,
size_t hash,
@@ -1087,15 +1077,6 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- // Finds the class in a non null class loader.
- ObjPtr<mirror::Class> FindClassInAppClassLoader(Thread* self,
- const char* descriptor,
- size_t hash,
- Handle<mirror::ClassLoader> class_loader,
- /*out*/ bool* descriptor_equals)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_);
-
// Implementation of LookupResolvedType() called when the type was not found in the dex cache.
ObjPtr<mirror::Class> DoLookupResolvedType(dex::TypeIndex type_idx,
ObjPtr<mirror::Class> referrer)