diff options
author | 2019-12-23 06:59:06 -0800 | |
---|---|---|
committer | 2020-02-08 00:01:36 +0000 | |
commit | b682ea4d1f9f88ef41589007f385398033d61b65 (patch) | |
tree | b843077f05e2fe37ce5f566dfc823552f289f562 /runtime/class_loader_context_test.cc | |
parent | 77aa6807053a830fe5c951c7cb700813d7e2e27b (diff) |
Introduce BaseDexClassLoader.computeClassLoaderContextsNative
This will be used to compute the contexts that should be sent over to
the dex load reporter. See associated changes in libcore &
frameworks/base.
Motivation: At the moment of committing there are two classloader
context encoders- one in ART and one in the package manager. The
duplicate logic is susceptible to divergences. For example at the moment
if a package uses shared libraries and has secondary dex files then the
context encoded for secondary dex files will be incorrect[1]. In order to
eliminate this bug and future possible bugs lets centralize where all
classloader context computation is done.
[1]: The context will be incorrect because it doesn't take into account
the shared libraries that are loaded at runtime.
Test: m test-art-host-gtest-class_loader_context_test
Test: m test-art-host-gtest
Test: ./test/testrunner/testrunner.py --host -b
Test: Introduced a set of tests for the new API(s)
Test: See tests in associated libcore & framework/base commits
Bug: 148494302
Change-Id: Id39293a2e1d3d05194f2864f4febb3e652bce075
Diffstat (limited to 'runtime/class_loader_context_test.cc')
-rw-r--r-- | runtime/class_loader_context_test.cc | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 008327834b..e082c8cbcf 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -1136,6 +1136,186 @@ TEST_F(ClassLoaderContextTest, EncodeForDex2oatIMC) { ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat("")); } +TEST_F(ClassLoaderContextTest, EncodeContextsSinglePath) { + jobject class_loader = LoadDexInPathClassLoader("Main", nullptr); + std::unique_ptr<ClassLoaderContext> context = + CreateContextForClassLoader(class_loader); + + std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); + ASSERT_EQ(1u, encodings.size()); + ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main"))); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsMultiDex) { + jobject class_loader = LoadDexInPathClassLoader("MultiDex", nullptr); + std::unique_ptr<ClassLoaderContext> context = + CreateContextForClassLoader(class_loader); + + std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); + ASSERT_EQ(1u, encodings.size()); + ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("MultiDex"))); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsRepeatedMultiDex) { + jobject top_class_loader = LoadDexInPathClassLoader("MultiDex", nullptr); + jobject middle_class_loader = + LoadDexInPathClassLoader("Main", top_class_loader); + jobject bottom_class_loader = + LoadDexInPathClassLoader("MultiDex", middle_class_loader); + std::unique_ptr<ClassLoaderContext> context = + CreateContextForClassLoader(bottom_class_loader); + + std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); + ASSERT_EQ(1u, encodings.size()); + + std::string main_dex_name = GetTestDexFileName("Main"); + std::string multidex_dex_name = GetTestDexFileName("MultiDex"); + ASSERT_EQ( + "PCL[];PCL[" + main_dex_name + "];PCL[" + multidex_dex_name + "]", + encodings.at(multidex_dex_name)); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsSinglePathWithShared) { + jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr); + + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ObjectArray<mirror::ClassLoader>> libraries = hs.NewHandle( + mirror::ObjectArray<mirror::ClassLoader>::Alloc( + soa.Self(), + GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(), + 1)); + libraries->Set(0, soa.Decode<mirror::ClassLoader>(class_loader_a)); + + jobject class_loader_b = LoadDexInPathClassLoader( + "Main", nullptr, soa.AddLocalReference<jobject>(libraries.Get())); + + std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_b); + + std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); + ASSERT_EQ(1u, encodings.size()); + ASSERT_EQ( + "PCL[]{PCL[" + GetTestDexFileName("MyClass") + "]}", + encodings.at(GetTestDexFileName("Main"))); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsMultiplePaths) { + jobject class_loader = LoadDexInPathClassLoader( + std::vector<std::string>{ "Main", "MultiDex"}, nullptr); + + std::unique_ptr<ClassLoaderContext> context = + CreateContextForClassLoader(class_loader); + + std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); + ASSERT_EQ(2u, encodings.size()); + ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main"))); + ASSERT_EQ( + "PCL[" + GetTestDexFileName("Main") + "]", encodings.at(GetTestDexFileName("MultiDex"))); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsMultiplePathsWithShared) { + jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr); + + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ObjectArray<mirror::ClassLoader>> libraries = hs.NewHandle( + mirror::ObjectArray<mirror::ClassLoader>::Alloc( + soa.Self(), + GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(), + 1)); + libraries->Set(0, soa.Decode<mirror::ClassLoader>(class_loader_a)); + + jobject class_loader_b = LoadDexInPathClassLoader( + std::vector<std::string> { "Main", "MultiDex" }, + nullptr, soa.AddLocalReference<jobject>(libraries.Get())); + + std::unique_ptr<ClassLoaderContext> context = + CreateContextForClassLoader(class_loader_b); + + std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); + ASSERT_EQ(2u, encodings.size()); + const std::string context_suffix = + "{PCL[" + GetTestDexFileName("MyClass") + "]}"; + ASSERT_EQ("PCL[]" + context_suffix, encodings.at(GetTestDexFileName("Main"))); + ASSERT_EQ( + "PCL[" + GetTestDexFileName("Main") + "]" + context_suffix, + encodings.at(GetTestDexFileName("MultiDex"))); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsIMC) { + jobject class_loader_a = LoadDexInPathClassLoader("Main", nullptr); + jobject class_loader_b = + LoadDexInInMemoryDexClassLoader("MyClass", class_loader_a); + + std::unique_ptr<ClassLoaderContext> context = + CreateContextForClassLoader(class_loader_b); + + std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); + ASSERT_EQ(1u, encodings.size()); + ASSERT_EQ( + "IMC[];PCL[" + GetTestDexFileName("Main") + "]", + encodings.at("<unknown>")); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsForSingleDex) { + jobject class_loader = LoadDexInPathClassLoader("Main", nullptr); + std::map<std::string, std::string> encodings = + ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader); + ASSERT_EQ(1u, encodings.size()); + ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main"))); +} + +static jobject CreateForeignClassLoader() { + ScopedObjectAccess soa(Thread::Current()); + JNIEnv* env = soa.Env(); + + // We cannot instantiate a ClassLoader directly, so instead we allocate an Object to represent + // our foreign ClassLoader (this works because the runtime does proper instanceof checks before + // operating on this object. + jmethodID ctor = env->GetMethodID(WellKnownClasses::java_lang_Object, "<init>", "()V"); + return env->NewObject(WellKnownClasses::java_lang_Object, ctor); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedBase) { + std::map<std::string, std::string> empty; + ASSERT_EQ( + empty, ClassLoaderContext::EncodeClassPathContextsForClassLoader(CreateForeignClassLoader())); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChain) { + jobject class_loader = LoadDexInPathClassLoader("Main", CreateForeignClassLoader()); + std::map<std::string, std::string> encodings = + ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader); + ASSERT_EQ(1u, encodings.size()); + ASSERT_EQ( + ClassLoaderContext::kUnsupportedClassLoaderContextEncoding, + encodings.at(GetTestDexFileName("Main"))); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChainMultiPath) { + jobject class_loader = LoadDexInPathClassLoader(std::vector<std::string> { "Main", "MyClass" }, + CreateForeignClassLoader()); + std::map<std::string, std::string> encodings = + ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader); + ASSERT_EQ(2u, encodings.size()); + ASSERT_EQ( + ClassLoaderContext::kUnsupportedClassLoaderContextEncoding, + encodings.at(GetTestDexFileName("Main"))); + ASSERT_EQ( + ClassLoaderContext::kUnsupportedClassLoaderContextEncoding, + encodings.at(GetTestDexFileName("MyClass"))); +} + +TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChainMultiDex) { + jobject class_loader = LoadDexInPathClassLoader("MultiDex", CreateForeignClassLoader()); + std::map<std::string, std::string> encodings = + ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader); + ASSERT_EQ(1u, encodings.size()); + ASSERT_EQ( + ClassLoaderContext::kUnsupportedClassLoaderContextEncoding, + encodings.at(GetTestDexFileName("MultiDex"))); +} + // TODO(calin) add a test which creates the context for a class loader together with dex_elements. TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { // The chain is |