summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.gtest.mk1
-rw-r--r--runtime/Android.bp2
-rw-r--r--runtime/class_loader_context.cc285
-rw-r--r--runtime/class_loader_context.h150
-rw-r--r--runtime/class_loader_context_test.cc265
-rw-r--r--runtime/oat_file.cc2
-rw-r--r--runtime/oat_file.h2
-rw-r--r--runtime/oat_file_assistant.cc30
-rw-r--r--runtime/oat_file_assistant.h7
9 files changed, 732 insertions, 12 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index b6ffcc54f7..ff4f9016c0 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -104,6 +104,7 @@ ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex Multi
ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_loader_context_test_DEX_DEPS := Main MultiDex MyClass
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 0dfc60d88a..8d15c349dd 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -48,6 +48,7 @@ cc_defaults {
"cha.cc",
"check_jni.cc",
"class_linker.cc",
+ "class_loader_context.cc",
"class_table.cc",
"code_simulator_container.cc",
"common_throws.cc",
@@ -542,6 +543,7 @@ art_cc_test {
"base/unix_file/fd_file_test.cc",
"cha_test.cc",
"class_linker_test.cc",
+ "class_loader_context_test.cc",
"class_table_test.cc",
"compiler_filter_test.cc",
"dex_file_test.cc",
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
new file mode 100644
index 0000000000..5cbcd8f1ff
--- /dev/null
+++ b/runtime/class_loader_context.cc
@@ -0,0 +1,285 @@
+/*
+ * 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 "base/dchecked_vector.h"
+#include "base/stl_util.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "oat_file_assistant.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+
+namespace art {
+
+static constexpr char kPathClassLoaderString[] = "PCL";
+static constexpr char kDelegateLastClassLoaderString[] = "DLC";
+static constexpr char kClassLoaderOpeningMark = '[';
+static constexpr char kClassLoaderClosingMark = ']';
+static constexpr char kClassLoaderSep = ';';
+static constexpr char kClasspathSep = ':';
+
+ClassLoaderContext::ClassLoaderContext()
+ : special_shared_library_(false),
+ dex_files_open_attempted_(false),
+ dex_files_open_result_(false) {}
+
+std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string& spec) {
+ std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext());
+ if (result->Parse(spec)) {
+ return result;
+ } else {
+ return nullptr;
+ }
+}
+
+// The expected format is: "ClassLoaderType1[ClasspathElem1:ClasspathElem2...]".
+bool ClassLoaderContext::ParseClassLoaderSpec(const std::string& class_loader_spec,
+ ClassLoaderType class_loader_type) {
+ const char* class_loader_type_str = GetClassLoaderTypeName(class_loader_type);
+ size_t type_str_size = strlen(class_loader_type_str);
+
+ CHECK_EQ(0, class_loader_spec.compare(0, type_str_size, class_loader_type_str));
+
+ // Check the opening and closing markers.
+ if (class_loader_spec[type_str_size] != kClassLoaderOpeningMark) {
+ return false;
+ }
+ if (class_loader_spec[class_loader_spec.length() - 1] != kClassLoaderClosingMark) {
+ return false;
+ }
+
+ // At this point we know the format is ok; continue and extract the classpath.
+ // Note that class loaders with an empty class path are allowed.
+ std::string classpath = class_loader_spec.substr(type_str_size + 1,
+ class_loader_spec.length() - type_str_size - 2);
+
+ class_loader_chain_.push_back(ClassLoaderInfo(class_loader_type));
+ Split(classpath, kClasspathSep, &class_loader_chain_.back().classpath);
+
+ return true;
+}
+
+// Extracts the class loader type from the given spec.
+// Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not
+// recognized.
+ClassLoaderContext::ClassLoaderType
+ClassLoaderContext::ExtractClassLoaderType(const std::string& class_loader_spec) {
+ const ClassLoaderType kValidTypes[] = {kPathClassLoader, kDelegateLastClassLoader};
+ for (const ClassLoaderType& type : kValidTypes) {
+ const char* type_str = GetClassLoaderTypeName(type);
+ if (class_loader_spec.compare(0, strlen(type_str), type_str) == 0) {
+ return type;
+ }
+ }
+ return kInvalidClassLoader;
+}
+
+// The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
+// ClassLoaderType is either "PCL" (PathClassLoader) or "DLC" (DelegateLastClassLoader).
+// ClasspathElem is the path of dex/jar/apk file.
+bool ClassLoaderContext::Parse(const std::string& spec) {
+ if (spec.empty()) {
+ LOG(ERROR) << "Empty string passed to Parse";
+ return false;
+ }
+ // Stop early if we detect the special shared library, which may be passed as the classpath
+ // for dex2oat when we want to skip the shared libraries check.
+ if (spec == OatFile::kSpecialSharedLibrary) {
+ LOG(INFO) << "The ClassLoaderContext is a special shared library.";
+ special_shared_library_ = true;
+ return true;
+ }
+
+ std::vector<std::string> class_loaders;
+ Split(spec, kClassLoaderSep, &class_loaders);
+
+ for (const std::string& class_loader : class_loaders) {
+ ClassLoaderType type = ExtractClassLoaderType(class_loader);
+ if (type == kInvalidClassLoader) {
+ LOG(ERROR) << "Invalid class loader type: " << class_loader;
+ return false;
+ }
+ if (!ParseClassLoaderSpec(class_loader, type)) {
+ LOG(ERROR) << "Invalid class loader spec: " << class_loader;
+ return false;
+ }
+ }
+ return true;
+}
+
+// Opens requested class path files and appends them to opened_dex_files. If the dex files have
+// been stripped, this opens them from their oat files (which get added to opened_oat_files).
+bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& classpath_dir) {
+ CHECK(!dex_files_open_attempted_) << "OpenDexFiles should not be called twice";
+
+ dex_files_open_attempted_ = true;
+ // Assume we can open all dex files. If not, we will set this to false as we go.
+ dex_files_open_result_ = true;
+
+ if (special_shared_library_) {
+ // Nothing to open if the context is a special shared library.
+ return true;
+ }
+
+ // Note that we try to open all dex files even if some fail.
+ // We may get resource-only apks which we cannot load.
+ // TODO(calin): Refine the dex opening interface to be able to tell if an archive contains
+ // no dex files. So that we can distinguish the real failures...
+ for (ClassLoaderInfo& info : class_loader_chain_) {
+ for (const std::string& cp_elem : info.classpath) {
+ // If path is relative, append it to the provided base directory.
+ std::string location = cp_elem;
+ if (location[0] != '/') {
+ location = classpath_dir + '/' + location;
+ }
+ std::string error_msg;
+ // When opening the dex files from the context we expect their checksum to match their
+ // contents. So pass true to verify_checksum.
+ if (!DexFile::Open(location.c_str(),
+ location.c_str(),
+ /*verify_checksum*/ true,
+ &error_msg,
+ &info.opened_dex_files)) {
+ // If we fail to open the dex file because it's been stripped, try to open the dex file
+ // from its corresponding oat file.
+ // This could happen when we need to recompile a pre-build whose dex code has been stripped.
+ // (for example, if the pre-build is only quicken and we want to re-compile it
+ // speed-profile).
+ // TODO(calin): Use the vdex directly instead of going through the oat file.
+ OatFileAssistant oat_file_assistant(location.c_str(), isa, false);
+ std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile());
+ std::vector<std::unique_ptr<const DexFile>> oat_dex_files;
+ if (oat_file != nullptr &&
+ OatFileAssistant::LoadDexFiles(*oat_file, location, &oat_dex_files)) {
+ info.opened_oat_files.push_back(std::move(oat_file));
+ info.opened_dex_files.insert(info.opened_dex_files.end(),
+ std::make_move_iterator(oat_dex_files.begin()),
+ std::make_move_iterator(oat_dex_files.end()));
+ } else {
+ LOG(WARNING) << "Could not open dex files from location: " << location;
+ dex_files_open_result_ = false;
+ }
+ }
+ }
+ }
+
+ return dex_files_open_result_;
+}
+
+bool ClassLoaderContext::RemoveLocationsFromClassPaths(
+ const dchecked_vector<std::string>& locations) {
+ CHECK(!dex_files_open_attempted_)
+ << "RemoveLocationsFromClasspaths cannot be call after OpenDexFiles";
+
+ std::set<std::string> canonical_locations;
+ for (const std::string& location : locations) {
+ canonical_locations.insert(DexFile::GetDexCanonicalLocation(location.c_str()));
+ }
+ bool removed_locations = false;
+ for (ClassLoaderInfo& info : class_loader_chain_) {
+ size_t initial_size = info.classpath.size();
+ auto kept_it = std::remove_if(
+ info.classpath.begin(),
+ info.classpath.end(),
+ [canonical_locations](const std::string& location) {
+ return ContainsElement(canonical_locations,
+ DexFile::GetDexCanonicalLocation(location.c_str()));
+ });
+ info.classpath.erase(kept_it, info.classpath.end());
+ if (initial_size != info.classpath.size()) {
+ removed_locations = true;
+ }
+ }
+ return removed_locations;
+}
+
+std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir) const {
+ CheckDexFilesOpened("EncodeContextForOatFile");
+ if (special_shared_library_) {
+ return OatFile::kSpecialSharedLibrary;
+ }
+
+ if (class_loader_chain_.empty()) {
+ return "";
+ }
+
+ // TODO(calin): Transition period: assume we only have a classloader until
+ // the oat file assistant implements the full class loader check.
+ CHECK_EQ(1u, class_loader_chain_.size());
+
+ return OatFile::EncodeDexFileDependencies(MakeNonOwningPointerVector(
+ class_loader_chain_[0].opened_dex_files), base_dir);
+}
+
+jobject ClassLoaderContext::CreateClassLoader(
+ const std::vector<const DexFile*>& compilation_sources) const {
+ CheckDexFilesOpened("CreateClassLoader");
+
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ std::vector<const DexFile*> class_path_files;
+
+ // TODO(calin): Transition period: assume we only have a classloader until
+ // the oat file assistant implements the full class loader check.
+ if (!class_loader_chain_.empty()) {
+ CHECK_EQ(1u, class_loader_chain_.size());
+ CHECK_EQ(kPathClassLoader, class_loader_chain_[0].type);
+ class_path_files = MakeNonOwningPointerVector(class_loader_chain_[0].opened_dex_files);
+ }
+
+ // Classpath: first the class-path given; then the dex files we'll compile.
+ // Thus we'll resolve the class-path first.
+ class_path_files.insert(class_path_files.end(),
+ compilation_sources.begin(),
+ compilation_sources.end());
+
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ return class_linker->CreatePathClassLoader(self, class_path_files);
+}
+
+std::vector<const DexFile*> ClassLoaderContext::FlattenOpenedDexFiles() const {
+ CheckDexFilesOpened("FlattenOpenedDexFiles");
+
+ std::vector<const DexFile*> result;
+ for (const ClassLoaderInfo& info : class_loader_chain_) {
+ for (const std::unique_ptr<const DexFile>& dex_file : info.opened_dex_files) {
+ result.push_back(dex_file.get());
+ }
+ }
+ return result;
+}
+
+const char* ClassLoaderContext::GetClassLoaderTypeName(ClassLoaderType type) {
+ switch (type) {
+ case kPathClassLoader: return kPathClassLoaderString;
+ case kDelegateLastClassLoader: return kDelegateLastClassLoaderString;
+ default:
+ LOG(FATAL) << "Invalid class loader type " << type;
+ UNREACHABLE();
+ }
+}
+
+void ClassLoaderContext::CheckDexFilesOpened(const std::string& calling_method) const {
+ CHECK(dex_files_open_attempted_)
+ << "Dex files were not successfully opened before the call to " << calling_method
+ << "attempt=" << dex_files_open_attempted_ << ", result=" << dex_files_open_result_;
+}
+} // namespace art
+
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
new file mode 100644
index 0000000000..4af5017ef2
--- /dev/null
+++ b/runtime/class_loader_context.h
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_CLASS_LOADER_CONTEXT_H_
+#define ART_RUNTIME_CLASS_LOADER_CONTEXT_H_
+
+#include <string>
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "base/dchecked_vector.h"
+#include "jni.h"
+
+namespace art {
+
+class DexFile;
+class OatFile;
+
+// Utility class which holds the class loader context used during compilation/verification.
+class ClassLoaderContext {
+ public:
+ // Creates an empty context (with no class loaders).
+ ClassLoaderContext();
+
+ // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
+ // If the dex files have been stripped, the method opens them from their oat files which are added
+ // to ClassLoaderInfo::opened_oat_files. The 'classpath_dir' argument specifies the directory to
+ // use for the relative class paths.
+ // Returns true if all dex files where successfully opened.
+ // It may be called only once per ClassLoaderContext. The second call will abort.
+ //
+ // Note that a "false" return could mean that either an apk/jar contained no dex files or
+ // that we hit a I/O or checksum mismatch error.
+ // TODO(calin): Currently there's no easy way to tell the difference.
+ //
+ // TODO(calin): we're forced to complicate the flow in this class with a different
+ // OpenDexFiles step because the current dex2oat flow requires the dex files be opened before
+ // the class loader is created. Consider reworking the dex2oat part.
+ bool OpenDexFiles(InstructionSet isa, const std::string& classpath_dir);
+
+ // Remove the specified compilation sources from all classpaths present in this context.
+ // Should only be called before the first call to OpenDexFiles().
+ bool RemoveLocationsFromClassPaths(const dchecked_vector<std::string>& compilation_sources);
+
+ // Creates the entire class loader hierarchy according to the current context.
+ // The compilation sources are appended to the classpath of the top class loader
+ // (i.e the class loader whose parent is the BootClassLoader).
+ // Should only be called if OpenDexFiles() returned true.
+ jobject CreateClassLoader(const std::vector<const DexFile*>& compilation_sources) const;
+
+ // Encodes the context as a string suitable to be added in oat files.
+ // (so that it can be read and verified at runtime against the actual class
+ // loader hierarchy).
+ // Should only be called if OpenDexFiles() returned true.
+ // E.g. if the context is PCL[a.dex:b.dex] this will return "a.dex*a_checksum*b.dex*a_checksum".
+ std::string EncodeContextForOatFile(const std::string& base_dir) const;
+
+ // Flattens the opened dex files into the given vector.
+ // Should only be called if OpenDexFiles() returned true.
+ std::vector<const DexFile*> FlattenOpenedDexFiles() const;
+
+ // Creates the class loader context from the given string.
+ // The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
+ // ClassLoaderType is either "PCL" (PathClassLoader) or "DLC" (DelegateLastClassLoader).
+ // ClasspathElem is the path of dex/jar/apk file.
+ // Note that we allowed class loaders with an empty class path in order to support a custom
+ // class loader for the source dex files.
+ static std::unique_ptr<ClassLoaderContext> Create(const std::string& spec);
+
+ private:
+ enum ClassLoaderType {
+ kInvalidClassLoader = 0,
+ kPathClassLoader = 1,
+ kDelegateLastClassLoader = 2
+ };
+
+ struct ClassLoaderInfo {
+ // The type of this class loader.
+ ClassLoaderType type;
+ // The list of class path elements that this loader loads.
+ // Note that this list may contain relative paths.
+ std::vector<std::string> classpath;
+ // After OpenDexFiles is called this holds the opened dex files.
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
+ // After OpenDexFiles, in case some of the dex files were opened from their oat files
+ // this holds the list of opened oat files.
+ std::vector<std::unique_ptr<OatFile>> opened_oat_files;
+
+ explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {}
+ };
+
+ // Reads the class loader spec in place and returns true if the spec is valid and the
+ // compilation context was constructed.
+ bool Parse(const std::string& spec);
+
+ // Attempts to parse a single class loader spec for the given class_loader_type.
+ // If successful the class loader spec will be added to the chain.
+ // Returns whether or not the operation was successful.
+ bool ParseClassLoaderSpec(const std::string& class_loader_spec,
+ ClassLoaderType class_loader_type);
+
+ // Extracts the class loader type from the given spec.
+ // Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not
+ // recognized.
+ static ClassLoaderType ExtractClassLoaderType(const std::string& class_loader_spec);
+
+ // Returns the string representation of the class loader type.
+ // The returned format can be used when parsing a context spec.
+ static const char* GetClassLoaderTypeName(ClassLoaderType type);
+
+ // CHECKs that the dex files were opened (OpenDexFiles was called). Aborts if not.
+ void CheckDexFilesOpened(const std::string& calling_method) const;
+
+ // The class loader chain represented as a vector.
+ // The parent of class_loader_chain_[i] is class_loader_chain_[i++].
+ // The parent of the last element is assumed to be the boot class loader.
+ std::vector<ClassLoaderInfo> class_loader_chain_;
+
+ // Whether or not the class loader context should be ignored at runtime when loading the oat
+ // files. When true, dex2oat will use OatFile::kSpecialSharedLibrary as the classpath key in
+ // the oat file.
+ // TODO(calin): Can we get rid of this and cover all relevant use cases?
+ // (e.g. packages using prebuild system packages as shared libraries b/36480683)
+ bool special_shared_library_;
+
+ // Whether or not OpenDexFiles() was called.
+ bool dex_files_open_attempted_;
+ // The result of the last OpenDexFiles() operation.
+ bool dex_files_open_result_;
+
+ friend class ClassLoaderContextTest;
+
+ DISALLOW_COPY_AND_ASSIGN(ClassLoaderContext);
+};
+
+} // namespace art
+#endif // ART_RUNTIME_CLASS_LOADER_CONTEXT_H_
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
new file mode 100644
index 0000000000..4643e78226
--- /dev/null
+++ b/runtime/class_loader_context_test.cc
@@ -0,0 +1,265 @@
+/*
+ * 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, 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 =
+ dex1[0]->GetLocation() + "*" + std::to_string(dex1[0]->GetLocationChecksum()) + "*" +
+ dex2[0]->GetLocation() + "*" + std::to_string(dex2[0]->GetLocationChecksum()) + "*";
+ ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
+}
+
+} // namespace art
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 888de457dc..2ed30df372 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1577,7 +1577,7 @@ CompilerFilter::Filter OatFile::GetCompilerFilter() const {
static constexpr char kDexClassPathEncodingSeparator = '*';
std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files,
- std::string& base_dir) {
+ const std::string& base_dir) {
std::ostringstream out;
for (const DexFile* dex_file : dex_files) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 66ed44f1b9..6393e091db 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -299,7 +299,7 @@ class OatFile {
// Create a dependency list (dex locations and checksums) for the given dex files.
// Removes dex file paths prefixed with base_dir to convert them back to relative paths.
static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files,
- std::string& base_dir);
+ const std::string& base_dir);
// Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on
// error and sets found to false.
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 4820feb56c..c8766578c4 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -298,28 +298,38 @@ std::string OatFileAssistant::GetStatusDump() {
}
std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
- const OatFile& oat_file, const char* dex_location) {
+ const OatFile &oat_file, const char *dex_location) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
+ if (LoadDexFiles(oat_file, dex_location, &dex_files)) {
+ return dex_files;
+ } else {
+ return std::vector<std::unique_ptr<const DexFile>>();
+ }
+}
+bool OatFileAssistant::LoadDexFiles(
+ const OatFile &oat_file,
+ const std::string& dex_location,
+ std::vector<std::unique_ptr<const DexFile>>* out_dex_files) {
// Load the main dex file.
std::string error_msg;
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
- dex_location, nullptr, &error_msg);
+ dex_location.c_str(), nullptr, &error_msg);
if (oat_dex_file == nullptr) {
LOG(WARNING) << error_msg;
- return std::vector<std::unique_ptr<const DexFile>>();
+ return false;
}
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file.get() == nullptr) {
LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
- return std::vector<std::unique_ptr<const DexFile>>();
+ return false;
}
- dex_files.push_back(std::move(dex_file));
+ out_dex_files->push_back(std::move(dex_file));
// Load the rest of the multidex entries
- for (size_t i = 1; ; i++) {
- std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
+ for (size_t i = 1;; i++) {
+ std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location.c_str());
oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
if (oat_dex_file == nullptr) {
// There are no more multidex entries to load.
@@ -329,11 +339,11 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file.get() == nullptr) {
LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
- return std::vector<std::unique_ptr<const DexFile>>();
+ return false;
}
- dex_files.push_back(std::move(dex_file));
+ out_dex_files->push_back(std::move(dex_file));
}
- return dex_files;
+ return true;
}
bool OatFileAssistant::HasOriginalDexFiles() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 03d9ca38a8..92d87eaeae 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -207,6 +207,13 @@ class OatFileAssistant {
static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(
const OatFile& oat_file, const char* dex_location);
+ // Same as `std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(...)` with the difference:
+ // - puts the dex files in the given vector
+ // - returns whether or not all dex files were successfully opened
+ static bool LoadDexFiles(const OatFile& oat_file,
+ const std::string& dex_location,
+ std::vector<std::unique_ptr<const DexFile>>* out_dex_files);
+
// Returns true if there are dex files in the original dex location that can
// be compiled with dex2oat for this dex location.
// Returns false if there is no original dex file, or if the original dex