blob: 03eb0e42a082e32418d868cac5e869db45af9ea8 [file] [log] [blame]
/*
* 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.
*/
#include <gtest/gtest.h>
#include "class_loader_context.h"
#include "common_runtime_test.h"
#include "base/dchecked_vector.h"
#include "base/stl_util.h"
#include "class_linker.h"
#include "dex_file.h"
#include "handle_scope-inl.h"
#include "mirror/class.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
#include "oat_file_assistant.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "well_known_classes.h"
namespace art {
class ClassLoaderContextTest : public CommonRuntimeTest {
public:
void VerifyContextSize(ClassLoaderContext* context, size_t expected_size) {
ASSERT_TRUE(context != nullptr);
ASSERT_EQ(expected_size, context->class_loader_chain_.size());
}
void VerifyClassLoaderPCL(ClassLoaderContext* context,
size_t index,
std::string classpath) {
VerifyClassLoaderInfo(
context, index, ClassLoaderContext::kPathClassLoader, classpath);
}
void VerifyClassLoaderDLC(ClassLoaderContext* context,
size_t index,
std::string classpath) {
VerifyClassLoaderInfo(
context, index, ClassLoaderContext::kDelegateLastClassLoader, classpath);
}
void VerifyOpenDexFiles(
ClassLoaderContext* context,
size_t index,
std::vector<std::vector<std::unique_ptr<const DexFile>>*>& all_dex_files) {
ASSERT_TRUE(context != nullptr);
ASSERT_TRUE(context->dex_files_open_attempted_);
ASSERT_TRUE(context->dex_files_open_result_);
ClassLoaderContext::ClassLoaderInfo& info = context->class_loader_chain_[index];
ASSERT_EQ(all_dex_files.size(), info.classpath.size());
size_t cur_open_dex_index = 0;
for (size_t k = 0; k < all_dex_files.size(); k++) {
std::vector<std::unique_ptr<const DexFile>>& dex_files_for_cp_elem = *(all_dex_files[k]);
for (size_t i = 0; i < dex_files_for_cp_elem.size(); i++) {
ASSERT_LT(cur_open_dex_index, info.opened_dex_files.size());
std::unique_ptr<const DexFile>& opened_dex_file =
info.opened_dex_files[cur_open_dex_index++];
std::unique_ptr<const DexFile>& expected_dex_file = dex_files_for_cp_elem[i];
ASSERT_EQ(expected_dex_file->GetLocation(), opened_dex_file->GetLocation());
ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
ASSERT_EQ(info.classpath[k], opened_dex_file->GetBaseLocation());
}
}
}
private:
void VerifyClassLoaderInfo(ClassLoaderContext* context,
size_t index,
ClassLoaderContext::ClassLoaderType type,
std::string classpath) {
ASSERT_TRUE(context != nullptr);
ASSERT_GT(context->class_loader_chain_.size(), index);
ClassLoaderContext::ClassLoaderInfo& info = context->class_loader_chain_[index];
ASSERT_EQ(type, info.type);
std::vector<std::string> expected_classpath;
Split(classpath, ':', &expected_classpath);
ASSERT_EQ(expected_classpath, info.classpath);
}
};
TEST_F(ClassLoaderContextTest, ParseValidContextPCL) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[a.dex]");
VerifyContextSize(context.get(), 1);
VerifyClassLoaderPCL(context.get(), 0, "a.dex");
}
TEST_F(ClassLoaderContextTest, ParseValidContextDLC) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("DLC[a.dex]");
VerifyContextSize(context.get(), 1);
VerifyClassLoaderDLC(context.get(), 0, "a.dex");
}
TEST_F(ClassLoaderContextTest, ParseValidContextChain) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[a.dex:b.dex];DLC[c.dex:d.dex];PCL[e.dex]");
VerifyContextSize(context.get(), 3);
VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
VerifyClassLoaderDLC(context.get(), 1, "c.dex:d.dex");
VerifyClassLoaderPCL(context.get(), 2, "e.dex");
}
TEST_F(ClassLoaderContextTest, ParseValidEmptyContextDLC) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("DLC[]");
VerifyContextSize(context.get(), 1);
VerifyClassLoaderDLC(context.get(), 0, "");
}
TEST_F(ClassLoaderContextTest, ParseValidContextSpecialSymbol) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create(OatFile::kSpecialSharedLibrary);
VerifyContextSize(context.get(), 0);
}
TEST_F(ClassLoaderContextTest, ParseInvalidValidContexts) {
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("ABC[a.dex]"));
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL"));
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex"));
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCLa.dex]"));
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{a.dex}"));
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex];DLC[b.dex"));
}
TEST_F(ClassLoaderContextTest, OpenInvalidDexFiles) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[does_not_exist.dex]");
VerifyContextSize(context.get(), 1);
ASSERT_FALSE(context->OpenDexFiles(InstructionSet::kArm, "."));
}
TEST_F(ClassLoaderContextTest, OpenValidDexFiles) {
std::string multidex_name = GetTestDexFileName("MultiDex");
std::vector<std::unique_ptr<const DexFile>> multidex_files = OpenTestDexFiles("MultiDex");
std::string myclass_dex_name = GetTestDexFileName("MyClass");
std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
std::string dex_name = GetTestDexFileName("Main");
std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main");
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create(
"PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
"DLC[" + dex_name + "]");
ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ ""));
VerifyContextSize(context.get(), 2);
std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
all_dex_files0.push_back(&multidex_files);
all_dex_files0.push_back(&myclass_dex_files);
std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files1;
all_dex_files1.push_back(&dex_files);
VerifyOpenDexFiles(context.get(), 0, all_dex_files0);
VerifyOpenDexFiles(context.get(), 1, all_dex_files1);
}
TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) {
std::string dex_name = GetTestDexFileName("Main");
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[does_not_exist.dex];DLC[" + dex_name + "]");
ASSERT_FALSE(context->OpenDexFiles(InstructionSet::kArm, ""));
}
TEST_F(ClassLoaderContextTest, CreateClassLoader) {
std::string dex_name = GetTestDexFileName("Main");
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[" + dex_name + "]");
ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
std::vector<std::unique_ptr<const DexFile>> classpath_dex = OpenTestDexFiles("Main");
std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
std::vector<const DexFile*> compilation_sources_raw =
MakeNonOwningPointerVector(compilation_sources);
jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
ASSERT_TRUE(jclass_loader != nullptr);
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
soa.Decode<mirror::ClassLoader>(jclass_loader));
ASSERT_TRUE(class_loader->GetClass() ==
soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(jclass_loader);
ASSERT_EQ(classpath_dex.size() + compilation_sources.size(), class_loader_dex_files.size());
// The classpath dex files must come first.
for (size_t i = 0; i < classpath_dex.size(); i++) {
ASSERT_EQ(classpath_dex[i]->GetLocation(),
class_loader_dex_files[i]->GetLocation());
ASSERT_EQ(classpath_dex[i]->GetLocationChecksum(),
class_loader_dex_files[i]->GetLocationChecksum());
}
// The compilation dex files must come second.
for (size_t i = 0, k = classpath_dex.size(); i < compilation_sources.size(); i++, k++) {
ASSERT_EQ(compilation_sources[i]->GetLocation(),
class_loader_dex_files[k]->GetLocation());
ASSERT_EQ(compilation_sources[i]->GetLocationChecksum(),
class_loader_dex_files[k]->GetLocationChecksum());
}
}
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("");
ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
std::vector<const DexFile*> compilation_sources_raw =
MakeNonOwningPointerVector(compilation_sources);
jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
ASSERT_TRUE(jclass_loader != nullptr);
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
soa.Decode<mirror::ClassLoader>(jclass_loader));
ASSERT_TRUE(class_loader->GetClass() ==
soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(jclass_loader);
// The compilation sources should be the only files present in the class loader
ASSERT_EQ(compilation_sources.size(), class_loader_dex_files.size());
for (size_t i = 0; i < compilation_sources.size(); i++) {
ASSERT_EQ(compilation_sources[i]->GetLocation(),
class_loader_dex_files[i]->GetLocation());
ASSERT_EQ(compilation_sources[i]->GetLocationChecksum(),
class_loader_dex_files[i]->GetLocationChecksum());
}
}
TEST_F(ClassLoaderContextTest, RemoveSourceLocations) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[a.dex]");
dchecked_vector<std::string> classpath_dex;
classpath_dex.push_back("a.dex");
dchecked_vector<std::string> compilation_sources;
compilation_sources.push_back("src.dex");
// Nothing should be removed.
ASSERT_FALSE(context->RemoveLocationsFromClassPaths(compilation_sources));
VerifyClassLoaderPCL(context.get(), 0, "a.dex");
// Classes should be removed.
ASSERT_TRUE(context->RemoveLocationsFromClassPaths(classpath_dex));
VerifyClassLoaderPCL(context.get(), 0, "");
}
TEST_F(ClassLoaderContextTest, EncodeInOatFile) {
std::string dex1_name = GetTestDexFileName("Main");
std::string dex2_name = GetTestDexFileName("MyClass");
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]");
ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main");
std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MyClass");
std::string encoding = context->EncodeContextForOatFile("");
std::string expected_encoding = "PCL[" +
dex1[0]->GetLocation() + "*" + std::to_string(dex1[0]->GetLocationChecksum()) + ":" +
dex2[0]->GetLocation() + "*" + std::to_string(dex2[0]->GetLocationChecksum()) + "]";
ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
}
TEST_F(ClassLoaderContextTest, DecodeOatFileKey) {
std::string oat_file_encoding = "PCL[a.dex*123:b.dex*456]";
std::vector<std::string> classpath;
std::vector<uint32_t> checksums;
bool is_special_shared_library;
bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
oat_file_encoding,
&classpath,
&checksums,
&is_special_shared_library);
ASSERT_TRUE(result);
ASSERT_FALSE(is_special_shared_library);
ASSERT_EQ(2u, classpath.size());
ASSERT_EQ(2u, checksums.size());
ASSERT_EQ("a.dex", classpath[0]);
ASSERT_EQ(123u, checksums[0]);
ASSERT_EQ("b.dex", classpath[1]);
ASSERT_EQ(456u, checksums[1]);
}
TEST_F(ClassLoaderContextTest, DecodeOatFileKeySpecialLibrary) {
std::string oat_file_encoding = "&";
std::vector<std::string> classpath;
std::vector<uint32_t> checksums;
bool is_special_shared_library;
bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
oat_file_encoding,
&classpath,
&checksums,
&is_special_shared_library);
ASSERT_TRUE(result);
ASSERT_TRUE(is_special_shared_library);
ASSERT_TRUE(classpath.empty());
ASSERT_TRUE(checksums.empty());
}
} // namespace art