diff options
author | 2017-07-05 20:09:53 -0700 | |
---|---|---|
committer | 2017-07-06 12:32:16 -0700 | |
commit | cdd4912eef02cce1ae4ec95e025794dced089466 (patch) | |
tree | d212b7d061f5dad7861945b9d86cea728e68d133 | |
parent | 7865ac7e233b8a8dcfd6ca8466d30b39a8089c3e (diff) |
Add fast path to FindClass for DelegateLastClassLoader
Test: m test-art-host
Bug: 38138251
Change-Id: I8d0f534195c31a5d291e38b0eea47534a5d471fb
-rw-r--r-- | build/Android.gtest.mk | 6 | ||||
-rw-r--r-- | runtime/class_linker.cc | 75 | ||||
-rw-r--r-- | runtime/class_linker_test.cc | 100 | ||||
-rw-r--r-- | test/ForClassLoaderA/Classes.java | 31 | ||||
-rw-r--r-- | test/ForClassLoaderB/Classes.java | 30 | ||||
-rw-r--r-- | test/ForClassLoaderC/Classes.java | 30 | ||||
-rw-r--r-- | test/ForClassLoaderD/Classes.java | 27 | ||||
-rw-r--r-- | test/ForClassLoaderD/java/lang/JavaLangFromD.java | 21 | ||||
-rw-r--r-- | test/ForClassLoaderD/java/lang/String.java | 19 |
9 files changed, 312 insertions, 27 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index bcf48fd891..b53e2229f3 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -30,6 +30,10 @@ GTEST_DEX_DIRECTORIES := \ ErroneousA \ ErroneousB \ ErroneousInit \ + ForClassLoaderA \ + ForClassLoaderB \ + ForClassLoaderC \ + ForClassLoaderD \ ExceptionHandle \ GetMethodSignature \ ImageLayoutA \ @@ -99,7 +103,7 @@ $(ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMul ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces -ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode +ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e1817c0ac3..097fc4ebf5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2423,47 +2423,70 @@ static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader)); } +static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* class_loader_class = class_loader->GetClass(); + return class_loader_class == + soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader); +} + bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Thread* self, const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader, ObjPtr<mirror::Class>* result) { - // Termination case: boot class-loader. + // Termination case: boot class loader. if (IsBootClassLoader(soa, class_loader.Get())) { *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash); return true; } - // Check that we support the class loader. - if (!IsPathOrDexClassLoader(soa, class_loader)) { - *result = nullptr; - return false; - } + if (IsPathOrDexClassLoader(soa, class_loader)) { + // For regular path or dex class loader the search order is: + // - parent + // - class loader dex files - // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). - StackHandleScope<1> hs(self); - Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent())); - bool recursive_result = FindClassInBaseDexClassLoader(soa, - self, - descriptor, - hash, - h_parent, - result); - if (!recursive_result) { - // One of the parents is not supported. - *result = nullptr; - return false; - } + // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent())); + if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result)) { + return false; // One of the parents is not supported. + } + if (*result != nullptr) { + return true; // Found the class up the chain. + } - if (*result != nullptr) { - // Found the class up the chain. + // Search the current class loader classpath. + *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader); return true; } - // Search the current class loader classpath. - *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader); - return true; + if (IsDelegateLastClassLoader(soa, class_loader)) { + // For delegate last, the search order is: + // - boot class path + // - class loader dex files + // - parent + *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash); + if (*result != nullptr) { + return true; // The class is part of the boot class path. + } + + *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader); + if (*result != nullptr) { + return true; // Found the class in the current class loader + } + + // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent())); + return FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result); + } + + // Unsupported class loader. + *result = nullptr; + return false; } // Finds the class in the boot class loader. @@ -2498,7 +2521,7 @@ ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath( const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader) { - CHECK(IsPathOrDexClassLoader(soa, class_loader)) + CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader)) << "Unexpected class loader for descriptor " << descriptor; Thread* self = soa.Self(); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 6f0691795f..a156229b55 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -1544,4 +1544,104 @@ TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) { LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c); } +class ClassLinkerClassLoaderTest : public ClassLinkerTest { + protected: + // Verifies that the class identified by the given descriptor is loaded with + // the expected_class_loader_obj when search from class_loader_to_search_obj. + // When expected_class_loader_obj is null the check will be done against BootClassLoader. + void VerifyClassResolution(const std::string& descriptor, + jobject class_loader_to_search_obj, + jobject expected_class_loader_obj, + bool should_find = true) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + StackHandleScope<3> hs(self); + Handle<mirror::ClassLoader> class_loader_to_search( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_to_search_obj))); + + Handle<mirror::Class> klass = hs.NewHandle( + class_linker_->FindClass(soa.Self(), descriptor.c_str(), class_loader_to_search)); + + if (!should_find) { + if (self->IsExceptionPending()) { + self->ClearException(); + } + ASSERT_TRUE(klass == nullptr); + } else if (expected_class_loader_obj == nullptr) { + ASSERT_TRUE(ClassLinker::IsBootClassLoader(soa, klass->GetClassLoader())); + } else { + ASSERT_TRUE(klass != nullptr) << descriptor; + Handle<mirror::ClassLoader> expected_class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(expected_class_loader_obj))); + ASSERT_EQ(klass->GetClassLoader(), expected_class_loader.Get()); + } + } +}; + +TEST_F(ClassLinkerClassLoaderTest, CreatePathClassLoader) { + jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); + VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a); + VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr); + VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false); +} + +TEST_F(ClassLinkerClassLoaderTest, CreateDelegateLastClassLoader) { + jobject class_loader_a = LoadDexInDelegateLastClassLoader("ForClassLoaderA", nullptr); + VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a); + VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr); + VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false); +} + +TEST_F(ClassLinkerClassLoaderTest, CreateClassLoaderChain) { + // The chain is + // ClassLoaderA (PathClassLoader, defines: A, AB, AC, AD) + // ^ + // | + // ClassLoaderB (DelegateLastClassLoader, defines: B, AB, BC, BD) + // ^ + // | + // ClassLoaderC (PathClassLoader, defines: C, AC, BC, CD) + // ^ + // | + // ClassLoaderD (DelegateLastClassLoader, defines: D, AD, BD, CD, Ljava/lang/String;) + + jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); + jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a); + jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b); + jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c); + + // Verify exclusive classes (present in only one class loader). + VerifyClassResolution("LDefinedInD;", class_loader_d, class_loader_d); + VerifyClassResolution("LDefinedInC;", class_loader_d, class_loader_c); + VerifyClassResolution("LDefinedInB;", class_loader_d, class_loader_b); + VerifyClassResolution("LDefinedInA;", class_loader_d, class_loader_a); + + // Verify classes that are defined in multiple classloader. + + // Classes defined in B should be found in B even if they are defined in A or C because + // B is a DelegateLastClassLoader. + VerifyClassResolution("LDefinedInAB;", class_loader_d, class_loader_b); + VerifyClassResolution("LDefinedInABC;", class_loader_d, class_loader_b); + VerifyClassResolution("LDefinedInBC;", class_loader_d, class_loader_b); + + // Classes defined in D should be found in D even if they are defined in parent class loaders + // as well because D is a DelegateLastClassLoader. + VerifyClassResolution("LDefinedInAD;", class_loader_d, class_loader_d); + VerifyClassResolution("LDefinedInBD;", class_loader_d, class_loader_d); + VerifyClassResolution("LDefinedInCD;", class_loader_d, class_loader_d); + + + // Classes not defined in the DelegateLastClassLoaders (i.e. D or B) should be found + // in the top parent. + VerifyClassResolution("LDefinedInAC;", class_loader_d, class_loader_a); + + // Boot classes should be found in the boot class loader even if they are redefined locally. + VerifyClassResolution("Ljava/lang/String;", class_loader_d, nullptr); + // Sanity check that what seems like a boot class is actually loaded from D. + VerifyClassResolution("Ljava/lang/JavaLangFromD;", class_loader_d, class_loader_d); + + // Sanity check that we don't find an undefined class. + VerifyClassResolution("LNotDefined;", class_loader_d, nullptr, /*should_find*/ false); +} + } // namespace art diff --git a/test/ForClassLoaderA/Classes.java b/test/ForClassLoaderA/Classes.java new file mode 100644 index 0000000000..a65ef64161 --- /dev/null +++ b/test/ForClassLoaderA/Classes.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 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. + */ + +class DefinedInA { +} + +class DefinedInAB { +} + +class DefinedInABC { +} + +class DefinedInAC { +} + +class DefinedInAD { +} + diff --git a/test/ForClassLoaderB/Classes.java b/test/ForClassLoaderB/Classes.java new file mode 100644 index 0000000000..8c85ed5fc0 --- /dev/null +++ b/test/ForClassLoaderB/Classes.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 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. + */ + +class DefinedInB { +} + +class DefinedInAB { +} + +class DefinedInABC { +} + +class DefinedInBC { +} + +class DefinedInBD { +} diff --git a/test/ForClassLoaderC/Classes.java b/test/ForClassLoaderC/Classes.java new file mode 100644 index 0000000000..7b9e83ffff --- /dev/null +++ b/test/ForClassLoaderC/Classes.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 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. + */ + +class DefinedInC { +} + +class DefinedInAC { +} + +class DefinedInABC { +} + +class DefinedInBC { +} + +class DefinedInCD { +} diff --git a/test/ForClassLoaderD/Classes.java b/test/ForClassLoaderD/Classes.java new file mode 100644 index 0000000000..b34177f05f --- /dev/null +++ b/test/ForClassLoaderD/Classes.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 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. + */ + +class DefinedInD { +} + +class DefinedInAD { +} + +class DefinedInBD { +} + +class DefinedInCD { +} diff --git a/test/ForClassLoaderD/java/lang/JavaLangFromD.java b/test/ForClassLoaderD/java/lang/JavaLangFromD.java new file mode 100644 index 0000000000..9abae7053d --- /dev/null +++ b/test/ForClassLoaderD/java/lang/JavaLangFromD.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 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. + */ + +package java.lang; + +public class JavaLangFromD { + +} diff --git a/test/ForClassLoaderD/java/lang/String.java b/test/ForClassLoaderD/java/lang/String.java new file mode 100644 index 0000000000..11afb3d133 --- /dev/null +++ b/test/ForClassLoaderD/java/lang/String.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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. + */ + +public final class String { + +} |