OSDN Git Service

ART: Make the PathClassLoader fast-path recursive
authorAndreas Gampe <agampe@google.com>
Tue, 14 Apr 2015 05:14:19 +0000 (22:14 -0700)
committerAndreas Gampe <agampe@google.com>
Tue, 14 Apr 2015 20:45:04 +0000 (13:45 -0700)
Allow an arbitrary nesting of path class-loaders. This will also
handle the fake java.lang.BootClassLoader as a class-loader object
correctly (which is of some importance to compile-time initialization).

Bug: 19781184
Bug: 19542228
Change-Id: I61f0249cf4ec8df08a83ccbd29bcf067619c28c0

runtime/class_linker.cc
runtime/class_linker.h
runtime/native/java_lang_VMClassLoader.cc

index 935c401..b380fed 100644 (file)
@@ -1259,94 +1259,124 @@ ClassPathEntry FindInClassPath(const char* descriptor,
   return ClassPathEntry(nullptr, nullptr);
 }
 
-mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
-                                                       Thread* self, const char* descriptor,
-                                                       size_t hash,
-                                                       Handle<mirror::ClassLoader> class_loader) {
-  // Can we special case for a well understood PathClassLoader with the BootClassLoader as parent?
-  if (class_loader->GetClass() !=
-      soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) ||
-      class_loader->GetParent()->GetClass() !=
-          soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
-    return nullptr;
-  }
-  ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
-  // Check if this would be found in the parent boot class loader.
-  if (pair.second != nullptr) {
-    mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr);
-    if (klass != nullptr) {
-      // May return null if resolution on another thread fails.
-      klass = EnsureResolved(self, descriptor, klass);
+static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                              mirror::ClassLoader* class_loader)
+    SHARED_LOCKS_REQUIRED(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 {
-      // May OOME.
-      klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
-                          *pair.second);
+      *result = nullptr;
     }
-    if (klass == nullptr) {
-      CHECK(self->IsExceptionPending()) << descriptor;
-      self->ClearException();
-    }
-    return klass;
-  } else {
-    // Handle as if this is the child PathClassLoader.
-    // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
-    StackHandleScope<3> hs(self);
-    // 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.
+    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;
           }
-          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();
-                  return nullptr;
-                }
-                return klass;
+          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();
-    return nullptr;
   }
+
+  // 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,
@@ -1384,10 +1414,18 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
     }
   } else {
     ScopedObjectAccessUnchecked soa(self);
-    mirror::Class* cp_klass = FindClassInPathClassLoader(soa, self, descriptor, hash,
-                                                         class_loader);
-    if (cp_klass != nullptr) {
-      return cp_klass;
+    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()) {
index 2427462..68624b0 100644 (file)
@@ -117,11 +117,15 @@ class ClassLinker {
                            Handle<mirror::ClassLoader> class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Find a class in the path class loader, loading it if necessary without using JNI. Hash
-  // function is supposed to be ComputeModifiedUtf8Hash(descriptor).
-  mirror::Class* FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
-                                            Thread* self, const char* descriptor, size_t hash,
-                                            Handle<mirror::ClassLoader> class_loader)
+  // Finds a class in the path class loader, loading it if necessary without using JNI. Hash
+  // function is supposed to be ComputeModifiedUtf8Hash(descriptor). Returns true if the
+  // class-loader chain could be handled, false otherwise, i.e., a non-supported class-loader
+  // was encountered while walking the parent chain (currently only BootClassLoader and
+  // PathClassLoader are supported).
+  bool FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                  Thread* self, const char* descriptor, size_t hash,
+                                  Handle<mirror::ClassLoader> class_loader,
+                                  mirror::Class** result)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Finds a class by its descriptor using the "system" class loader, ie by searching the
index 35932e0..0c39f2b 100644 (file)
@@ -44,8 +44,8 @@ static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoa
   if (loader != nullptr) {
     // Try the common case.
     StackHandleScope<1> hs(soa.Self());
-    c = cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), descriptor_hash,
-                                       hs.NewHandle(loader));
+    cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), descriptor_hash,
+                                   hs.NewHandle(loader), &c);
     if (c != nullptr) {
       return soa.AddLocalReference<jclass>(c);
     }