You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1706 lines
75 KiB
1706 lines
75 KiB
/*
|
|
* 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 "class_loader_context.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "android-base/strings.h"
|
|
#include "art_field-inl.h"
|
|
#include "base/dchecked_vector.h"
|
|
#include "base/stl_util.h"
|
|
#include "class_linker.h"
|
|
#include "class_root-inl.h"
|
|
#include "common_runtime_test.h"
|
|
#include "dex/dex_file.h"
|
|
#include "handle_scope-inl.h"
|
|
#include "jni/jni_internal.h"
|
|
#include "mirror/class.h"
|
|
#include "mirror/class_loader-inl.h"
|
|
#include "mirror/object-inl.h"
|
|
#include "mirror/object_array-alloc-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->GetParentChainSize());
|
|
}
|
|
|
|
void VerifyClassLoaderPCL(ClassLoaderContext* context,
|
|
size_t index,
|
|
const std::string& classpath) {
|
|
VerifyClassLoaderInfo(
|
|
context, index, ClassLoaderContext::kPathClassLoader, classpath);
|
|
}
|
|
|
|
void VerifyClassLoaderDLC(ClassLoaderContext* context,
|
|
size_t index,
|
|
const std::string& classpath) {
|
|
VerifyClassLoaderInfo(
|
|
context, index, ClassLoaderContext::kDelegateLastClassLoader, classpath);
|
|
}
|
|
|
|
void VerifyClassLoaderIMC(ClassLoaderContext* context,
|
|
size_t index,
|
|
const std::string& classpath) {
|
|
VerifyClassLoaderInfo(
|
|
context, index, ClassLoaderContext::kInMemoryDexClassLoader, classpath);
|
|
}
|
|
|
|
void VerifyClassLoaderSharedLibraryPCL(ClassLoaderContext* context,
|
|
size_t loader_index,
|
|
size_t shared_library_index,
|
|
const std::string& classpath) {
|
|
VerifyClassLoaderInfoSL(
|
|
context, loader_index, shared_library_index, ClassLoaderContext::kPathClassLoader,
|
|
classpath);
|
|
}
|
|
|
|
void VerifyClassLoaderSharedLibraryIMC(ClassLoaderContext* context,
|
|
size_t loader_index,
|
|
size_t shared_library_index,
|
|
const std::string& classpath) {
|
|
VerifyClassLoaderInfoSL(
|
|
context, loader_index, shared_library_index, ClassLoaderContext::kInMemoryDexClassLoader,
|
|
classpath);
|
|
}
|
|
|
|
void VerifySharedLibrariesSize(ClassLoaderContext* context,
|
|
size_t loader_index,
|
|
size_t expected_size) {
|
|
ASSERT_TRUE(context != nullptr);
|
|
ASSERT_GT(context->GetParentChainSize(), loader_index);
|
|
const ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(loader_index);
|
|
ASSERT_EQ(info.shared_libraries.size(), expected_size);
|
|
}
|
|
|
|
void VerifyClassLoaderSharedLibraryDLC(ClassLoaderContext* context,
|
|
size_t loader_index,
|
|
size_t shared_library_index,
|
|
const std::string& classpath) {
|
|
VerifyClassLoaderInfoSL(
|
|
context, loader_index, shared_library_index, ClassLoaderContext::kDelegateLastClassLoader,
|
|
classpath);
|
|
}
|
|
|
|
void VerifyClassLoaderPCLFromTestDex(ClassLoaderContext* context,
|
|
size_t index,
|
|
const std::string& test_name) {
|
|
VerifyClassLoaderFromTestDex(
|
|
context, index, ClassLoaderContext::kPathClassLoader, test_name);
|
|
}
|
|
|
|
void VerifyClassLoaderDLCFromTestDex(ClassLoaderContext* context,
|
|
size_t index,
|
|
const std::string& test_name) {
|
|
VerifyClassLoaderFromTestDex(
|
|
context, index, ClassLoaderContext::kDelegateLastClassLoader, test_name);
|
|
}
|
|
|
|
void VerifyClassLoaderIMCFromTestDex(ClassLoaderContext* context,
|
|
size_t index,
|
|
const std::string& test_name) {
|
|
VerifyClassLoaderFromTestDex(
|
|
context, index, ClassLoaderContext::kInMemoryDexClassLoader, test_name, "<unknown>");
|
|
}
|
|
|
|
enum class LocationCheck {
|
|
kEquals,
|
|
kEndsWith
|
|
};
|
|
enum class BaseLocationCheck {
|
|
kEquals,
|
|
kEndsWith
|
|
};
|
|
|
|
static bool IsAbsoluteLocation(const std::string& location) {
|
|
return !location.empty() && location[0] == '/';
|
|
}
|
|
|
|
void VerifyOpenDexFiles(
|
|
ClassLoaderContext* context,
|
|
size_t index,
|
|
std::vector<std::unique_ptr<const DexFile>>* all_dex_files,
|
|
bool classpath_matches_dex_location = true,
|
|
bool only_read_checksums = false) {
|
|
ASSERT_TRUE(context != nullptr);
|
|
if (only_read_checksums) {
|
|
ASSERT_EQ(context->dex_files_state_,
|
|
ClassLoaderContext::ContextDexFilesState::kDexFilesChecksumsRead);
|
|
} else {
|
|
ASSERT_EQ(context->dex_files_state_,
|
|
ClassLoaderContext::ContextDexFilesState::kDexFilesOpened);
|
|
}
|
|
ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(index);
|
|
ASSERT_EQ(all_dex_files->size(), info.classpath.size());
|
|
ASSERT_EQ(all_dex_files->size(), info.checksums.size());
|
|
if (only_read_checksums) {
|
|
ASSERT_EQ(0u, info.opened_dex_files.size());
|
|
} else {
|
|
ASSERT_EQ(all_dex_files->size(), info.opened_dex_files.size());
|
|
}
|
|
|
|
for (size_t k = 0, cur_open_dex_index = 0;
|
|
k < all_dex_files->size();
|
|
k++, cur_open_dex_index++) {
|
|
const std::string& opened_location = only_read_checksums
|
|
? info.classpath[cur_open_dex_index]
|
|
: info.opened_dex_files[cur_open_dex_index]->GetLocation();
|
|
uint32_t opened_checksum = only_read_checksums
|
|
? info.checksums[cur_open_dex_index]
|
|
: info.opened_dex_files[cur_open_dex_index]->GetLocationChecksum();
|
|
|
|
std::unique_ptr<const DexFile>& expected_dex_file = (*all_dex_files)[k];
|
|
std::string expected_location = expected_dex_file->GetLocation();
|
|
|
|
if (!IsAbsoluteLocation(opened_location)) {
|
|
// If the opened location is relative (it was open from a relative path without a
|
|
// classpath_dir) it might not match the expected location which is absolute in tests).
|
|
// So we compare the endings (the checksum will validate it's actually the same file).
|
|
ASSERT_EQ(0, expected_location.compare(
|
|
expected_location.length() - opened_location.length(),
|
|
opened_location.length(),
|
|
opened_location));
|
|
} else {
|
|
ASSERT_EQ(expected_location, opened_location);
|
|
}
|
|
ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_checksum);
|
|
if (classpath_matches_dex_location) {
|
|
ASSERT_EQ(info.classpath[k], opened_location);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader) {
|
|
return ClassLoaderContext::CreateContextForClassLoader(class_loader, nullptr);
|
|
}
|
|
|
|
std::unique_ptr<ClassLoaderContext> ParseContextWithChecksums(const std::string& context_spec) {
|
|
std::unique_ptr<ClassLoaderContext> context(new ClassLoaderContext());
|
|
if (!context->Parse(context_spec, /*parse_checksums=*/ true)) {
|
|
return nullptr;
|
|
}
|
|
return context;
|
|
}
|
|
|
|
void VerifyContextForClassLoader(ClassLoaderContext* context) {
|
|
ASSERT_TRUE(context != nullptr);
|
|
ASSERT_EQ(context->dex_files_state_, ClassLoaderContext::ContextDexFilesState::kDexFilesOpened);
|
|
ASSERT_FALSE(context->owns_the_dex_files_);
|
|
}
|
|
|
|
void VerifyClassLoaderDexFiles(ScopedObjectAccess& soa,
|
|
Handle<mirror::ClassLoader> class_loader,
|
|
jclass type,
|
|
std::vector<const DexFile*>& expected_dex_files)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
ASSERT_TRUE(class_loader->GetClass() == soa.Decode<mirror::Class>(type));
|
|
|
|
std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(soa, class_loader);
|
|
ASSERT_EQ(expected_dex_files.size(), class_loader_dex_files.size());
|
|
|
|
for (size_t i = 0; i < expected_dex_files.size(); i++) {
|
|
ASSERT_EQ(expected_dex_files[i]->GetLocation(),
|
|
class_loader_dex_files[i]->GetLocation());
|
|
ASSERT_EQ(expected_dex_files[i]->GetLocationChecksum(),
|
|
class_loader_dex_files[i]->GetLocationChecksum());
|
|
}
|
|
}
|
|
|
|
void PretendContextOpenedDexFiles(ClassLoaderContext* context) {
|
|
context->dex_files_state_ = ClassLoaderContext::ContextDexFilesState::kDexFilesOpened;
|
|
}
|
|
|
|
void PretendContextOpenedDexFilesForChecksums(ClassLoaderContext* context) {
|
|
context->dex_files_state_ = ClassLoaderContext::ContextDexFilesState::kDexFilesChecksumsRead;
|
|
}
|
|
|
|
void TestOpenDexFiles(bool only_read_checksums) {
|
|
std::string multidex_name = GetTestDexFileName("MultiDex");
|
|
std::string myclass_dex_name = GetTestDexFileName("MyClass");
|
|
std::string dex_name = GetTestDexFileName("Main");
|
|
|
|
std::unique_ptr<ClassLoaderContext> context =
|
|
ClassLoaderContext::Create(
|
|
"PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
|
|
"DLC[" + dex_name + "]");
|
|
|
|
ASSERT_TRUE(context->OpenDexFiles(
|
|
/*classpath_dir=*/ "",
|
|
/*context_fds=*/ std::vector<int>(),
|
|
only_read_checksums));
|
|
|
|
VerifyContextSize(context.get(), 2);
|
|
|
|
std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex");
|
|
std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
|
|
for (size_t i = 0; i < myclass_dex_files.size(); i++) {
|
|
all_dex_files0.emplace_back(myclass_dex_files[i].release());
|
|
}
|
|
VerifyOpenDexFiles(context.get(),
|
|
/*index=*/ 0,
|
|
&all_dex_files0,
|
|
/*classpath_matches_dex_location=*/ false,
|
|
only_read_checksums);
|
|
std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main");
|
|
VerifyOpenDexFiles(context.get(),
|
|
/*index=*/ 1,
|
|
&all_dex_files1,
|
|
/*classpath_matches_dex_location=*/ false,
|
|
only_read_checksums);
|
|
}
|
|
|
|
void TestOpenValidDexFilesRelative(bool use_classpath_dir, bool only_read_checksums) {
|
|
char cwd_buf[4096];
|
|
if (getcwd(cwd_buf, arraysize(cwd_buf)) == nullptr) {
|
|
PLOG(FATAL) << "Could not get working directory";
|
|
}
|
|
std::string multidex_name;
|
|
std::string myclass_dex_name;
|
|
std::string dex_name;
|
|
if (!CreateRelativeString(GetTestDexFileName("MultiDex"), cwd_buf, &multidex_name) ||
|
|
!CreateRelativeString(GetTestDexFileName("MyClass"), cwd_buf, &myclass_dex_name) ||
|
|
!CreateRelativeString(GetTestDexFileName("Main"), cwd_buf, &dex_name)) {
|
|
LOG(ERROR) << "Test OpenValidDexFilesRelative cannot be run because target dex files have no "
|
|
<< "relative path.";
|
|
SUCCEED();
|
|
return;
|
|
}
|
|
|
|
std::unique_ptr<ClassLoaderContext> context =
|
|
ClassLoaderContext::Create(
|
|
"PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
|
|
"DLC[" + dex_name + "]");
|
|
|
|
ASSERT_TRUE(context->OpenDexFiles(
|
|
/*classpath_dir=*/ use_classpath_dir ? cwd_buf : "",
|
|
/*context_fds=*/ std::vector<int>(),
|
|
only_read_checksums));
|
|
VerifyContextSize(context.get(), 2);
|
|
|
|
std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex");
|
|
std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
|
|
for (size_t i = 0; i < myclass_dex_files.size(); i++) {
|
|
all_dex_files0.emplace_back(myclass_dex_files[i].release());
|
|
}
|
|
VerifyOpenDexFiles(context.get(),
|
|
/*index=*/ 0,
|
|
&all_dex_files0,
|
|
/*classpath_matches_dex_location=*/ false,
|
|
only_read_checksums);
|
|
|
|
std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main");
|
|
VerifyOpenDexFiles(context.get(),
|
|
/*index=*/ 1,
|
|
&all_dex_files1,
|
|
/*classpath_matches_dex_location=*/ false,
|
|
only_read_checksums);
|
|
}
|
|
|
|
// Creates a relative path from cwd to 'in'. Returns false if it cannot be done.
|
|
// TODO We should somehow support this in all situations. b/72042237.
|
|
bool CreateRelativeString(const std::string& in, const char* cwd, std::string* out) {
|
|
int cwd_len = strlen(cwd);
|
|
if (!android::base::StartsWith(in, cwd) || (cwd_len < 1)) {
|
|
return false;
|
|
}
|
|
bool contains_trailing_slash = (cwd[cwd_len - 1] == '/');
|
|
int start_position = cwd_len + (contains_trailing_slash ? 0 : 1);
|
|
*out = in.substr(start_position);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
void VerifyClassLoaderInfo(ClassLoaderContext* context,
|
|
size_t index,
|
|
ClassLoaderContext::ClassLoaderType type,
|
|
const std::string& classpath) {
|
|
ASSERT_TRUE(context != nullptr);
|
|
ASSERT_GT(context->GetParentChainSize(), index);
|
|
ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(index);
|
|
ASSERT_EQ(type, info.type);
|
|
std::vector<std::string> expected_classpath;
|
|
Split(classpath, ':', &expected_classpath);
|
|
ASSERT_EQ(expected_classpath, info.classpath);
|
|
}
|
|
|
|
void VerifyClassLoaderInfoSL(ClassLoaderContext* context,
|
|
size_t loader_index,
|
|
size_t shared_library_index,
|
|
ClassLoaderContext::ClassLoaderType type,
|
|
const std::string& classpath) {
|
|
ASSERT_TRUE(context != nullptr);
|
|
ASSERT_GT(context->GetParentChainSize(), loader_index);
|
|
const ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(loader_index);
|
|
ASSERT_GT(info.shared_libraries.size(), shared_library_index);
|
|
const ClassLoaderContext::ClassLoaderInfo& sl =
|
|
*info.shared_libraries[shared_library_index].get();
|
|
ASSERT_EQ(type, info.type);
|
|
std::vector<std::string> expected_classpath;
|
|
Split(classpath, ':', &expected_classpath);
|
|
ASSERT_EQ(expected_classpath, sl.classpath);
|
|
}
|
|
|
|
void VerifyClassLoaderFromTestDex(ClassLoaderContext* context,
|
|
size_t index,
|
|
ClassLoaderContext::ClassLoaderType type,
|
|
const std::string& test_name,
|
|
const std::string& classpath = "") {
|
|
std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(test_name.c_str());
|
|
|
|
// If `classpath` is set, override the expected value of ClassLoaderInfo::classpath.
|
|
// Otherwise assume it is equal to dex location (here test dex file name).
|
|
VerifyClassLoaderInfo(context,
|
|
index,
|
|
type,
|
|
classpath.empty() ? GetTestDexFileName(test_name.c_str()) : classpath);
|
|
VerifyOpenDexFiles(context,
|
|
index,
|
|
&dex_files,
|
|
/* classpath_matches_dex_location= */ classpath.empty());
|
|
}
|
|
};
|
|
|
|
TEST_F(ClassLoaderContextTest, ParseValidEmptyContext) {
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("");
|
|
// An empty context should create a single empty PathClassLoader.
|
|
VerifyContextSize(context.get(), 1);
|
|
VerifyClassLoaderPCL(context.get(), 0, "");
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ParseInvalidSharedLibraryContext) {
|
|
// '&' used to be a special context.
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("&");
|
|
ASSERT_TRUE(context == nullptr);
|
|
}
|
|
|
|
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, ParseValidContextIMC) {
|
|
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums("IMC[<unknown>*111]");
|
|
ASSERT_FALSE(context == nullptr);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ParseInvalidContextIMCNoChecksum) {
|
|
// IMC is treated as an unknown class loader unless a checksum is provided.
|
|
// This is because the dex location is always bogus.
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("IMC[<unknown>]");
|
|
ASSERT_TRUE(context == nullptr);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ParseInvalidContextIMCWrongClasspathMagic) {
|
|
// IMC does not support arbitrary dex location. A magic marker must be used
|
|
// otherwise the spec should be rejected.
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("IMC[a.dex*111]");
|
|
ASSERT_TRUE(context == nullptr);
|
|
}
|
|
|
|
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, ParseSharedLibraries) {
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(
|
|
"PCL[a.dex:b.dex]{PCL[s1.dex]#PCL[s2.dex:s3.dex]};DLC[c.dex:d.dex]{DLC[s4.dex]}");
|
|
VerifyContextSize(context.get(), 2);
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex");
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "s2.dex:s3.dex");
|
|
VerifyClassLoaderDLC(context.get(), 1, "c.dex:d.dex");
|
|
VerifyClassLoaderSharedLibraryDLC(context.get(), 1, 0, "s4.dex");
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ParseEnclosingSharedLibraries) {
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(
|
|
"PCL[a.dex:b.dex]{PCL[s1.dex]{PCL[s2.dex:s3.dex];PCL[s4.dex]}}");
|
|
VerifyContextSize(context.get(), 1);
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex");
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries1) {
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(
|
|
"PCL[]{PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}}");
|
|
VerifyContextSize(context.get(), 1);
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s4.dex");
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries2) {
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(
|
|
"PCL[]{PCL[s1.dex]{PCL[s2.dex]}#PCL[s2.dex]#"
|
|
"PCL[s3.dex]#PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}#PCL[s5.dex]{PCL[s6.dex]}}");
|
|
VerifyContextSize(context.get(), 1);
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex");
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "s2.dex");
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 2, "s3.dex");
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 3, "s4.dex");
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 4, "s5.dex");
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ParseValidEmptyContextDLC) {
|
|
std::unique_ptr<ClassLoaderContext> context =
|
|
ClassLoaderContext::Create("DLC[]");
|
|
VerifyContextSize(context.get(), 1);
|
|
VerifyClassLoaderDLC(context.get(), 0, "");
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ParseValidEmptyContextSharedLibrary) {
|
|
std::unique_ptr<ClassLoaderContext> context =
|
|
ClassLoaderContext::Create("DLC[]{}");
|
|
VerifyContextSize(context.get(), 1);
|
|
VerifySharedLibrariesSize(context.get(), 0, 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"));
|
|
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex]{ABC};DLC[b.dex"));
|
|
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex]{};DLC[b.dex"));
|
|
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]}"));
|
|
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]{"));
|
|
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC{DLC[s4.dex]}"));
|
|
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{##}"));
|
|
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]#}"));
|
|
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]##}"));
|
|
ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]{PCL[s3.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("."));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ReadChecksumsInvalidDexFiles) {
|
|
std::unique_ptr<ClassLoaderContext> context =
|
|
ClassLoaderContext::Create("PCL[does_not_exist.dex]");
|
|
VerifyContextSize(context.get(), 1);
|
|
ASSERT_FALSE(context->OpenDexFiles(
|
|
/*classpath_dir=*/ ".",
|
|
/*context_fds=*/ std::vector<int>(),
|
|
/*only_read_checksums=*/ true));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, OpenValidDexFiles) {
|
|
TestOpenDexFiles(/*only_read_checksums=*/ false);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ReadDexFileChecksums) {
|
|
TestOpenDexFiles(/*only_read_checksums=*/ true);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, OpenValidDexFilesRelative) {
|
|
TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ false, /*only_read_checksums=*/ false);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ReadChecksumsValidDexFilesRelative) {
|
|
TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ false, /*only_read_checksums=*/ true);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, OpenValidDexFilesClasspathDir) {
|
|
TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ true, /*only_read_checksums=*/ false);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ReadChecksumsValidDexFilesClasspathDir) {
|
|
TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ true, /*only_read_checksums=*/ true);
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, ReadChecksumsInvalidDexFilesMix) {
|
|
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(
|
|
/*classpath_dir=*/ "",
|
|
/*context_fds=*/ std::vector<int>(),
|
|
/*only_read_checksums=*/ true));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, OpenDexFilesForIMCFails) {
|
|
std::unique_ptr<ClassLoaderContext> context;
|
|
std::string dex_name = GetTestDexFileName("Main");
|
|
|
|
context = ParseContextWithChecksums("IMC[<unknown>*111]");
|
|
VerifyContextSize(context.get(), 1);
|
|
ASSERT_FALSE(context->OpenDexFiles("."));
|
|
}
|
|
|
|
// Verify that we can fully open the dex files after only reading their checksums.
|
|
TEST_F(ClassLoaderContextTest, SubsequentOpenDexFilesOperations) {
|
|
std::string dex_name = GetTestDexFileName("Main");
|
|
std::unique_ptr<ClassLoaderContext> context =
|
|
ClassLoaderContext::Create("PCL[" + dex_name + "]");
|
|
|
|
std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("Main");
|
|
|
|
ASSERT_TRUE(context->OpenDexFiles(
|
|
/*classpath_dir=*/ "",
|
|
/*context_fds=*/ std::vector<int>(),
|
|
/*only_read_checksums=*/ true));
|
|
|
|
VerifyOpenDexFiles(
|
|
context.get(),
|
|
/*index=*/ 0,
|
|
&all_dex_files0,
|
|
/*classpath_matches_dex_location=*/ false,
|
|
/*only_read_checksums=*/ true);
|
|
|
|
ASSERT_TRUE(context->OpenDexFiles(
|
|
/*classpath_dir=*/ "",
|
|
/*context_fds=*/ std::vector<int>(),
|
|
/*only_read_checksums=*/ false));
|
|
|
|
VerifyOpenDexFiles(
|
|
context.get(),
|
|
/*index=*/ 0,
|
|
&all_dex_files0,
|
|
/*classpath_matches_dex_location=*/ false,
|
|
/*only_read_checksums=*/ false);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CreateClassLoader) {
|
|
std::string dex_name = GetTestDexFileName("Main");
|
|
std::unique_ptr<ClassLoaderContext> context =
|
|
ClassLoaderContext::Create("PCL[" + dex_name + "]");
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
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<1> 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));
|
|
|
|
// For the first class loader the class path dex files must come first and then the
|
|
// compilation sources.
|
|
std::vector<const DexFile*> expected_classpath = MakeNonOwningPointerVector(classpath_dex);
|
|
for (auto& dex : compilation_sources_raw) {
|
|
expected_classpath.push_back(dex);
|
|
}
|
|
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
expected_classpath);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) {
|
|
std::unique_ptr<ClassLoaderContext> context =
|
|
ClassLoaderContext::Create("");
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
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<1> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
|
|
soa.Decode<mirror::ClassLoader>(jclass_loader));
|
|
|
|
// An empty context should create a single PathClassLoader with only the compilation sources.
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
compilation_sources_raw);
|
|
ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) {
|
|
// Setup the context.
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD");
|
|
|
|
std::string context_spec =
|
|
"PCL[" + CreateClassPath(classpath_dex_a) + ":" + CreateClassPath(classpath_dex_b) + "];" +
|
|
"DLC[" + CreateClassPath(classpath_dex_c) + "];" +
|
|
"PCL[" + CreateClassPath(classpath_dex_d) + "]";
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec);
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
// Setup the compilation sources.
|
|
std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
|
|
std::vector<const DexFile*> compilation_sources_raw =
|
|
MakeNonOwningPointerVector(compilation_sources);
|
|
|
|
// Create the class loader.
|
|
jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
|
|
ASSERT_TRUE(jclass_loader != nullptr);
|
|
|
|
// Verify the class loader.
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
|
|
StackHandleScope<3> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
|
|
soa.Decode<mirror::ClassLoader>(jclass_loader));
|
|
|
|
// Verify the first class loader
|
|
|
|
// For the first class loader the class path dex files must come first and then the
|
|
// compilation sources.
|
|
std::vector<const DexFile*> class_loader_1_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_a);
|
|
for (auto& dex : classpath_dex_b) {
|
|
class_loader_1_dex_files.push_back(dex.get());
|
|
}
|
|
for (auto& dex : compilation_sources_raw) {
|
|
class_loader_1_dex_files.push_back(dex);
|
|
}
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_1,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_1_dex_files);
|
|
|
|
// Verify the second class loader
|
|
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(class_loader_1->GetParent());
|
|
std::vector<const DexFile*> class_loader_2_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_c);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_2,
|
|
WellKnownClasses::dalvik_system_DelegateLastClassLoader,
|
|
class_loader_2_dex_files);
|
|
|
|
// Verify the third class loader
|
|
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_2->GetParent());
|
|
std::vector<const DexFile*> class_loader_3_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_d);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_3,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_3_dex_files);
|
|
// The last class loader should have the BootClassLoader as a parent.
|
|
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraries) {
|
|
// Setup the context.
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD");
|
|
|
|
std::string context_spec =
|
|
"PCL[" + CreateClassPath(classpath_dex_a) + ":" + CreateClassPath(classpath_dex_b) + "]{" +
|
|
"DLC[" + CreateClassPath(classpath_dex_c) + "]#" +
|
|
"PCL[" + CreateClassPath(classpath_dex_d) + "]}";
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec);
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
// Setup the compilation sources.
|
|
std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
|
|
std::vector<const DexFile*> compilation_sources_raw =
|
|
MakeNonOwningPointerVector(compilation_sources);
|
|
|
|
// Create the class loader.
|
|
jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
|
|
ASSERT_TRUE(jclass_loader != nullptr);
|
|
|
|
// Verify the class loader.
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
|
|
StackHandleScope<4> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
|
|
soa.Decode<mirror::ClassLoader>(jclass_loader));
|
|
|
|
// For the first class loader the class path dex files must come first and then the
|
|
// compilation sources.
|
|
std::vector<const DexFile*> class_loader_1_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_a);
|
|
for (auto& dex : classpath_dex_b) {
|
|
class_loader_1_dex_files.push_back(dex.get());
|
|
}
|
|
for (auto& dex : compilation_sources_raw) {
|
|
class_loader_1_dex_files.push_back(dex);
|
|
}
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_1,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_1_dex_files);
|
|
|
|
// Verify the shared libraries.
|
|
ArtField* field =
|
|
jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
|
|
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
|
|
ASSERT_TRUE(raw_shared_libraries != nullptr);
|
|
|
|
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
|
|
hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
|
|
ASSERT_EQ(shared_libraries->GetLength(), 2);
|
|
|
|
// Verify the first shared library.
|
|
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
|
|
std::vector<const DexFile*> class_loader_2_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_c);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_2,
|
|
WellKnownClasses::dalvik_system_DelegateLastClassLoader,
|
|
class_loader_2_dex_files);
|
|
raw_shared_libraries = field->GetObject(class_loader_2.Get());
|
|
ASSERT_TRUE(raw_shared_libraries == nullptr);
|
|
|
|
// Verify the second shared library.
|
|
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries->Get(1));
|
|
std::vector<const DexFile*> class_loader_3_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_d);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_3,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_3_dex_files);
|
|
raw_shared_libraries = field->GetObject(class_loader_3.Get());
|
|
ASSERT_TRUE(raw_shared_libraries == nullptr);
|
|
|
|
// All class loaders should have the BootClassLoader as a parent.
|
|
ASSERT_TRUE(class_loader_1->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesInParentToo) {
|
|
// Setup the context.
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD");
|
|
|
|
std::string context_spec =
|
|
"PCL[" + CreateClassPath(classpath_dex_a) + "]{" +
|
|
"PCL[" + CreateClassPath(classpath_dex_b) + "]};" +
|
|
"PCL[" + CreateClassPath(classpath_dex_c) + "]{" +
|
|
"PCL[" + CreateClassPath(classpath_dex_d) + "]}";
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec);
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
// Setup the compilation sources.
|
|
std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
|
|
std::vector<const DexFile*> compilation_sources_raw =
|
|
MakeNonOwningPointerVector(compilation_sources);
|
|
|
|
// Create the class loader.
|
|
jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
|
|
ASSERT_TRUE(jclass_loader != nullptr);
|
|
|
|
// Verify the class loader.
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
|
|
StackHandleScope<6> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
|
|
soa.Decode<mirror::ClassLoader>(jclass_loader));
|
|
|
|
// For the first class loader the class path dex files must come first and then the
|
|
// compilation sources.
|
|
std::vector<const DexFile*> class_loader_1_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_a);
|
|
for (auto& dex : compilation_sources_raw) {
|
|
class_loader_1_dex_files.push_back(dex);
|
|
}
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_1,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_1_dex_files);
|
|
|
|
// Verify its shared library.
|
|
ArtField* field =
|
|
jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
|
|
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
|
|
ASSERT_TRUE(raw_shared_libraries != nullptr);
|
|
|
|
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
|
|
hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
|
|
ASSERT_EQ(shared_libraries->GetLength(), 1);
|
|
|
|
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
|
|
std::vector<const DexFile*> class_loader_2_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_b);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_2,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_2_dex_files);
|
|
raw_shared_libraries = field->GetObject(class_loader_2.Get());
|
|
ASSERT_TRUE(raw_shared_libraries == nullptr);
|
|
|
|
// Verify the parent.
|
|
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent());
|
|
std::vector<const DexFile*> class_loader_3_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_c);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_3,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_3_dex_files);
|
|
|
|
// Verify its shared library.
|
|
raw_shared_libraries = field->GetObject(class_loader_3.Get());
|
|
ASSERT_TRUE(raw_shared_libraries != nullptr);
|
|
|
|
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2(
|
|
hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
|
|
ASSERT_EQ(shared_libraries->GetLength(), 1);
|
|
|
|
Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(shared_libraries_2->Get(0));
|
|
std::vector<const DexFile*> class_loader_4_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_d);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_4,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_4_dex_files);
|
|
raw_shared_libraries = field->GetObject(class_loader_4.Get());
|
|
ASSERT_TRUE(raw_shared_libraries == nullptr);
|
|
|
|
// Class loaders should have the BootClassLoader as a parent.
|
|
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
ASSERT_TRUE(class_loader_4->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesDependencies) {
|
|
// Setup the context.
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD");
|
|
|
|
std::string context_spec =
|
|
"PCL[" + CreateClassPath(classpath_dex_a) + "]{" +
|
|
"PCL[" + CreateClassPath(classpath_dex_b) + "]{" +
|
|
"PCL[" + CreateClassPath(classpath_dex_c) + "]}};" +
|
|
"PCL[" + CreateClassPath(classpath_dex_d) + "]";
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec);
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
// Setup the compilation sources.
|
|
std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
|
|
std::vector<const DexFile*> compilation_sources_raw =
|
|
MakeNonOwningPointerVector(compilation_sources);
|
|
|
|
// Create the class loader.
|
|
jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
|
|
ASSERT_TRUE(jclass_loader != nullptr);
|
|
|
|
// Verify the class loader.
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
|
|
StackHandleScope<6> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
|
|
soa.Decode<mirror::ClassLoader>(jclass_loader));
|
|
|
|
// For the first class loader the class path dex files must come first and then the
|
|
// compilation sources.
|
|
std::vector<const DexFile*> class_loader_1_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_a);
|
|
for (auto& dex : compilation_sources_raw) {
|
|
class_loader_1_dex_files.push_back(dex);
|
|
}
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_1,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_1_dex_files);
|
|
|
|
// Verify its shared library.
|
|
ArtField* field =
|
|
jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
|
|
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
|
|
ASSERT_TRUE(raw_shared_libraries != nullptr);
|
|
|
|
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
|
|
hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
|
|
ASSERT_EQ(shared_libraries->GetLength(), 1);
|
|
|
|
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
|
|
std::vector<const DexFile*> class_loader_2_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_b);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_2,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_2_dex_files);
|
|
|
|
// Verify the shared library dependency of the shared library.
|
|
raw_shared_libraries = field->GetObject(class_loader_2.Get());
|
|
ASSERT_TRUE(raw_shared_libraries != nullptr);
|
|
|
|
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2(
|
|
hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
|
|
ASSERT_EQ(shared_libraries_2->GetLength(), 1);
|
|
|
|
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries_2->Get(0));
|
|
std::vector<const DexFile*> class_loader_3_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_c);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_3,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_3_dex_files);
|
|
raw_shared_libraries = field->GetObject(class_loader_3.Get());
|
|
ASSERT_TRUE(raw_shared_libraries == nullptr);
|
|
|
|
// Verify the parent.
|
|
Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(class_loader_1->GetParent());
|
|
std::vector<const DexFile*> class_loader_4_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_d);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_4,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_4_dex_files);
|
|
raw_shared_libraries = field->GetObject(class_loader_4.Get());
|
|
ASSERT_TRUE(raw_shared_libraries == nullptr);
|
|
|
|
// Class loaders should have the BootClassLoader as a parent.
|
|
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
ASSERT_TRUE(class_loader_4->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
}
|
|
|
|
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, CreateClassLoaderWithSameSharedLibraries) {
|
|
// Setup the context.
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB");
|
|
std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC");
|
|
|
|
std::string context_spec =
|
|
"PCL[" + CreateClassPath(classpath_dex_a) + "]{" +
|
|
"PCL[" + CreateClassPath(classpath_dex_b) + "]};" +
|
|
"PCL[" + CreateClassPath(classpath_dex_c) + "]{" +
|
|
"PCL[" + CreateClassPath(classpath_dex_b) + "]}";
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec);
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
// Setup the compilation sources.
|
|
std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
|
|
std::vector<const DexFile*> compilation_sources_raw =
|
|
MakeNonOwningPointerVector(compilation_sources);
|
|
|
|
// Create the class loader.
|
|
jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
|
|
ASSERT_TRUE(jclass_loader != nullptr);
|
|
|
|
// Verify the class loader.
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
|
|
StackHandleScope<6> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
|
|
soa.Decode<mirror::ClassLoader>(jclass_loader));
|
|
|
|
// For the first class loader the class path dex files must come first and then the
|
|
// compilation sources.
|
|
std::vector<const DexFile*> class_loader_1_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_a);
|
|
for (auto& dex : compilation_sources_raw) {
|
|
class_loader_1_dex_files.push_back(dex);
|
|
}
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_1,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_1_dex_files);
|
|
|
|
// Verify its shared library.
|
|
ArtField* field =
|
|
jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
|
|
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
|
|
ASSERT_TRUE(raw_shared_libraries != nullptr);
|
|
|
|
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
|
|
hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
|
|
ASSERT_EQ(shared_libraries->GetLength(), 1);
|
|
|
|
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
|
|
std::vector<const DexFile*> class_loader_2_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_b);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_2,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_2_dex_files);
|
|
|
|
// Verify the parent.
|
|
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent());
|
|
std::vector<const DexFile*> class_loader_3_dex_files =
|
|
MakeNonOwningPointerVector(classpath_dex_c);
|
|
VerifyClassLoaderDexFiles(soa,
|
|
class_loader_3,
|
|
WellKnownClasses::dalvik_system_PathClassLoader,
|
|
class_loader_3_dex_files);
|
|
|
|
// Verify its shared library is the same as the child.
|
|
raw_shared_libraries = field->GetObject(class_loader_3.Get());
|
|
ASSERT_TRUE(raw_shared_libraries != nullptr);
|
|
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2(
|
|
hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
|
|
ASSERT_EQ(shared_libraries_2->GetLength(), 1);
|
|
ASSERT_OBJ_PTR_EQ(shared_libraries_2->Get(0), class_loader_2.Get());
|
|
|
|
// Class loaders should have the BootClassLoader as a parent.
|
|
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
|
|
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
|
|
}
|
|
|
|
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());
|
|
|
|
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[" + CreateClassPathWithChecksums(dex1) + ":" +
|
|
CreateClassPathWithChecksums(dex2) + "]";
|
|
ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, EncodeInOatFileIMC) {
|
|
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);
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main");
|
|
std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MyClass");
|
|
ASSERT_EQ(dex2.size(), 1u);
|
|
|
|
std::string encoding = context->EncodeContextForOatFile("");
|
|
std::string expected_encoding = "IMC[<unknown>*" + std::to_string(dex2[0]->GetLocationChecksum())
|
|
+ "];PCL[" + CreateClassPathWithChecksums(dex1) + "]";
|
|
ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, EncodeForDex2oat) {
|
|
std::string dex1_name = GetTestDexFileName("Main");
|
|
std::string dex2_name = GetTestDexFileName("MultiDex");
|
|
std::unique_ptr<ClassLoaderContext> context =
|
|
ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]");
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
std::string encoding = context->EncodeContextForDex2oat("");
|
|
std::string expected_encoding = "PCL[" + dex1_name + ":" + dex2_name + "]";
|
|
ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat(""));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, EncodeForDex2oatIMC) {
|
|
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);
|
|
ASSERT_TRUE(context->OpenDexFiles());
|
|
|
|
std::string encoding = context->EncodeContextForDex2oat("");
|
|
std::string expected_encoding = "IMC[<unknown>];PCL[" + GetTestDexFileName("Main") + "]";
|
|
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")));
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, IsValidEncoding) {
|
|
ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("PCL[]"));
|
|
ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("PCL[foo.dex]"));
|
|
ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("PCL[foo.dex];PCL[bar.dex]"));
|
|
ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("DLC[];PCL[bar.dex]"));
|
|
ASSERT_TRUE(
|
|
ClassLoaderContext::IsValidEncoding(
|
|
ClassLoaderContext::kUnsupportedClassLoaderContextEncoding));
|
|
ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("not_valid"));
|
|
ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("[]"));
|
|
ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("FCL[]"));
|
|
ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("foo.dex:bar.dex"));
|
|
}
|
|
|
|
// TODO(calin) add a test which creates the context for a class loader together with dex_elements.
|
|
TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) {
|
|
// The chain is
|
|
// ClassLoaderA (PathClassLoader)
|
|
// ^
|
|
// |
|
|
// ClassLoaderB (DelegateLastClassLoader)
|
|
// ^
|
|
// |
|
|
// ClassLoaderC (PathClassLoader)
|
|
// ^
|
|
// |
|
|
// ClassLoaderD (DelegateLastClassLoader)
|
|
|
|
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);
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
|
|
|
|
VerifyContextForClassLoader(context.get());
|
|
VerifyContextSize(context.get(), 4);
|
|
|
|
VerifyClassLoaderDLCFromTestDex(context.get(), 0, "ForClassLoaderD");
|
|
VerifyClassLoaderPCLFromTestDex(context.get(), 1, "ForClassLoaderC");
|
|
VerifyClassLoaderDLCFromTestDex(context.get(), 2, "ForClassLoaderB");
|
|
VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA");
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CreateContextForClassLoaderIMC) {
|
|
// The chain is
|
|
// ClassLoaderA (PathClassLoader)
|
|
// ^
|
|
// |
|
|
// ClassLoaderB (InMemoryDexClassLoader)
|
|
// ^
|
|
// |
|
|
// ClassLoaderC (InMemoryDexClassLoader)
|
|
// ^
|
|
// |
|
|
// ClassLoaderD (DelegateLastClassLoader)
|
|
|
|
jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
|
|
jobject class_loader_b = LoadDexInInMemoryDexClassLoader("ForClassLoaderB", class_loader_a);
|
|
jobject class_loader_c = LoadDexInInMemoryDexClassLoader("ForClassLoaderC", class_loader_b);
|
|
jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
|
|
|
|
VerifyContextForClassLoader(context.get());
|
|
VerifyContextSize(context.get(), 4);
|
|
|
|
VerifyClassLoaderDLCFromTestDex(context.get(), 0, "ForClassLoaderD");
|
|
VerifyClassLoaderIMCFromTestDex(context.get(), 1, "ForClassLoaderC");
|
|
VerifyClassLoaderIMCFromTestDex(context.get(), 2, "ForClassLoaderB");
|
|
VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA");
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) {
|
|
std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]";
|
|
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
|
|
// Pretend that we successfully open the dex files to pass the DCHECKS.
|
|
// (as it's much easier to test all the corner cases without relying on actual dex files).
|
|
PretendContextOpenedDexFilesForChecksums(context.get());
|
|
|
|
VerifyContextSize(context.get(), 2);
|
|
VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
|
|
VerifyClassLoaderDLC(context.get(), 1, "c.dex");
|
|
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
|
|
std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC[";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextWithIMCMatch) {
|
|
std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];IMC[<unknown>*111]";
|
|
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
|
|
// Pretend that we successfully open the dex files to pass the DCHECKS.
|
|
// (as it's much easier to test all the corner cases without relying on actual dex files).
|
|
PretendContextOpenedDexFiles(context.get());
|
|
|
|
VerifyContextSize(context.get(), 3);
|
|
VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
|
|
VerifyClassLoaderDLC(context.get(), 1, "c.dex");
|
|
VerifyClassLoaderIMC(context.get(), 2, "<unknown>");
|
|
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithSL) {
|
|
std::string context_spec =
|
|
"PCL[a.dex*123:b.dex*456]{PCL[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}"
|
|
";DLC[c.dex*890]";
|
|
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
|
|
// Pretend that we successfully open the dex files to pass the DCHECKS.
|
|
// (as it's much easier to test all the corner cases without relying on actual dex files).
|
|
PretendContextOpenedDexFiles(context.get());
|
|
|
|
VerifyContextSize(context.get(), 2);
|
|
VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
|
|
VerifyClassLoaderDLC(context.get(), 1, "c.dex");
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "d.dex");
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "f.dex:g.dex");
|
|
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
|
|
std::string wrong_class_loader_type =
|
|
"PCL[a.dex*123:b.dex*456]{DLC[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}"
|
|
";DLC[c.dex*890]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_class_loader_order =
|
|
"PCL[a.dex*123:b.dex*456]{PCL[f.dex#098:g.dex#999}#PCL[d.dex*321];PCL[e.dex*654]}"
|
|
";DLC[c.dex*890]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_classpath_order =
|
|
"PCL[a.dex*123:b.dex*456]{PCL[d.dex*321];PCL[e.dex*654]#PCL[g.dex*999:f.dex*098]}"
|
|
";DLC[c.dex*890]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_checksum =
|
|
"PCL[a.dex*123:b.dex*456]{PCL[d.dex*333];PCL[e.dex*654]#PCL[g.dex*999:f.dex*098]}"
|
|
";DLC[c.dex*890]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_extra_class_loader =
|
|
"PCL[a.dex*123:b.dex*456]"
|
|
"{PCL[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999];PCL[i.dex#444]}"
|
|
";DLC[c.dex*890]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
|
|
std::string wrong_extra_classpath =
|
|
"PCL[a.dex*123:b.dex*456]{PCL[d.dex*321:i.dex#444];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}"
|
|
";DLC[c.dex*890]";
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath),
|
|
ClassLoaderContext::VerificationResult::kMismatch);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithIMCSL) {
|
|
std::string context_spec =
|
|
"IMC[<unknown>*123:<unknown>*456]"
|
|
"{IMC[<unknown>*321];IMC[<unknown>*654]#IMC[<unknown>*098:<unknown>*999]};"
|
|
"DLC[c.dex*890]";
|
|
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
|
|
// Pretend that we successfully open the dex files to pass the DCHECKS.
|
|
// (as it's much easier to test all the corner cases without relying on actual dex files).
|
|
PretendContextOpenedDexFiles(context.get());
|
|
|
|
VerifyContextSize(context.get(), 2);
|
|
VerifyClassLoaderIMC(context.get(), 0, "<unknown>:<unknown>");
|
|
VerifyClassLoaderDLC(context.get(), 1, "c.dex");
|
|
VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 0, "<unknown>");
|
|
VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 1, "<unknown>:<unknown>");
|
|
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) {
|
|
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);
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
|
|
|
|
std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
|
|
std::string dex_location = GetTestDexFileName("ForClassLoaderA");
|
|
size_t pos = dex_location.rfind('/');
|
|
ASSERT_NE(std::string::npos, pos);
|
|
std::string parent = dex_location.substr(0, pos);
|
|
|
|
std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
|
|
ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingIMC) {
|
|
jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
|
|
jobject class_loader_b = LoadDexInInMemoryDexClassLoader("ForClassLoaderB", class_loader_a);
|
|
jobject class_loader_c = LoadDexInInMemoryDexClassLoader("ForClassLoaderC", class_loader_b);
|
|
jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
|
|
|
|
std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
|
|
std::string dex_location = GetTestDexFileName("ForClassLoaderA");
|
|
size_t pos = dex_location.rfind('/');
|
|
ASSERT_NE(std::string::npos, pos);
|
|
std::string parent = dex_location.substr(0, pos);
|
|
|
|
std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
|
|
ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) {
|
|
jobject class_loader = LoadDexInPathClassLoader("MultiDex", nullptr);
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader);
|
|
|
|
std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
|
|
std::string dex_location = GetTestDexFileName("MultiDex");
|
|
size_t pos = dex_location.rfind('/');
|
|
ASSERT_NE(std::string::npos, pos);
|
|
std::string parent = dex_location.substr(0, pos);
|
|
|
|
std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
|
|
ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CreateContextForClassLoaderWithSharedLibraries) {
|
|
jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", 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(
|
|
"ForClassLoaderB", nullptr, soa.AddLocalReference<jobject>(libraries.Get()));
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_b);
|
|
ASSERT_TRUE(context != nullptr);
|
|
std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ForClassLoaderB");
|
|
VerifyClassLoaderPCL(context.get(), 0, dex_files[0]->GetLocation());
|
|
dex_files = OpenTestDexFiles("ForClassLoaderA");
|
|
VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, dex_files[0]->GetLocation());
|
|
|
|
ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")),
|
|
ClassLoaderContext::VerificationResult::kVerifies);
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CheckForDuplicateDexFilesNotFoundSingleCL) {
|
|
jobject class_loader = LoadDexInPathClassLoader("Main", nullptr);
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader);
|
|
|
|
std::set<const DexFile*> result = context->CheckForDuplicateDexFiles(
|
|
std::vector<const DexFile*>());
|
|
ASSERT_EQ(0u, result.size());
|
|
|
|
std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("ForClassLoaderA");
|
|
std::vector<const DexFile*> dex1_raw = MakeNonOwningPointerVector(dex1);
|
|
result = context->CheckForDuplicateDexFiles(dex1_raw);
|
|
ASSERT_EQ(0u, result.size());
|
|
}
|
|
|
|
TEST_F(ClassLoaderContextTest, CheckForDuplicateDexFilesFound) {
|
|
jobject class_loader = LoadDexInPathClassLoader(std::vector<std::string> { "Main", "Main" }, nullptr);
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader);
|
|
|
|
std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main");
|
|
std::vector<const DexFile*> dex1_raw = MakeNonOwningPointerVector(dex1);
|
|
std::set<const DexFile*> result = context->CheckForDuplicateDexFiles(dex1_raw);
|
|
ASSERT_EQ(1u, result.size()) << context->EncodeContextForOatFile("");
|
|
ASSERT_EQ(dex1_raw[0], *(result.begin()));
|
|
}
|
|
|
|
|
|
TEST_F(ClassLoaderContextTest, CheckForDuplicateCrossCLNotFound) {
|
|
jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
|
|
jobject class_loader_b = LoadDexInInMemoryDexClassLoader("ForClassLoaderB", class_loader_a);
|
|
|
|
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_b);
|
|
|
|
std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("ForClassLoaderA");
|
|
std::vector<const DexFile*> dex1_raw = MakeNonOwningPointerVector(dex1);
|
|
std::set<const DexFile*> result = context->CheckForDuplicateDexFiles(dex1_raw);
|
|
ASSERT_EQ(0u, result.size());
|
|
}
|
|
|
|
} // namespace art
|