Merge "signal_catcher: Only notify tombstoned when we're using it."
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 0ed230c..8a8df36 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,3 +1,4 @@
[Hook Scripts]
check_generated_files_up_to_date = tools/cpp-define-generator/presubmit-check-files-up-to-date
+check_generated_tests_up_to_date = tools/test_presubmit.py
check_cpplint_on_changed_files = tools/cpplint_presubmit.py
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index b6ffcc5..ff4f901 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -104,6 +104,7 @@
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/build/art.go b/build/art.go
index 1b9c646..c06be0a 100644
--- a/build/art.go
+++ b/build/art.go
@@ -97,6 +97,12 @@
"-DART_STACK_OVERFLOW_GAP_x86_64=8192")
}
+ if envTrue(ctx, "ART_ENABLE_ADDRESS_SANITIZER") {
+ // Used to enable full sanitization, i.e., user poisoning, under ASAN.
+ cflags = append(cflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
+ asflags = append(asflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
+ }
+
return cflags, asflags
}
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index f7465c0..7cb3166 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -473,8 +473,8 @@
return true;
}
-dchecked_vector<const char*> OatWriter::GetSourceLocations() const {
- dchecked_vector<const char*> locations;
+dchecked_vector<std::string> OatWriter::GetSourceLocations() const {
+ dchecked_vector<std::string> locations;
locations.reserve(oat_dex_files_.size());
for (const OatDexFile& oat_dex_file : oat_dex_files_) {
locations.push_back(oat_dex_file.GetLocation());
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 9217701..024a3e8 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -153,7 +153,7 @@
const VdexFile& vdex_file,
const char* location,
CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
- dchecked_vector<const char*> GetSourceLocations() const;
+ dchecked_vector<std::string> GetSourceLocations() const;
// Write raw dex files to the vdex file, mmap the file and open the dex files from it.
// Supporting data structures are written into the .rodata section of the oat file.
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ca0bae1..6257c7c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -49,6 +49,7 @@
#include "base/timing_logger.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
+#include "class_loader_context.h"
#include "compiler.h"
#include "compiler_callbacks.h"
#include "debug/elf_debug_writer.h"
@@ -400,6 +401,27 @@
UsageError("");
UsageError(" --classpath-dir=<directory-path>: directory used to resolve relative class paths.");
UsageError("");
+ UsageError(" --class-loader-context=<string spec>: a string specifying the intended");
+ UsageError(" runtime loading context for the compiled dex files.");
+ UsageError(" ");
+ UsageError(" It describes how the class loader chain should be built in order to ensure");
+ UsageError(" classes are resolved during dex2aot as they would be resolved at runtime.");
+ UsageError(" This spec will be encoded in the oat file. If at runtime the dex file is");
+ UsageError(" loaded in a different context, the oat file will be rejected.");
+ UsageError(" ");
+ UsageError(" The chain is interpreted in the natural 'parent order', meaning that class");
+ UsageError(" loader 'i+1' will be the parent of class loader 'i'.");
+ UsageError(" The compilation sources will be added to the classpath of the last class");
+ UsageError(" loader. This allows the compiled dex files to be loaded at runtime in");
+ UsageError(" a class loader that contains other dex files as well (e.g. shared libraries).");
+ UsageError(" ");
+ UsageError(" Note that the compiler will be tolerant if the source dex files specified");
+ UsageError(" with --dex-file are found in the classpath. The source dex files will be");
+ UsageError(" removed from any class loader's classpath possibly resulting in empty");
+ UsageError(" class loaders.");
+ UsageError(" ");
+ UsageError(" Example: --classloader-spec=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -1271,6 +1293,12 @@
force_determinism_ = true;
} else if (option.starts_with("--classpath-dir=")) {
classpath_dir_ = option.substr(strlen("--classpath-dir=")).data();
+ } else if (option.starts_with("--class-loader-context=")) {
+ class_loader_context_ = ClassLoaderContext::Create(
+ option.substr(strlen("--class-loader-context=")).data());
+ if (class_loader_context_== nullptr) {
+ Usage("Option --class-loader-context has an incorrect format: %s", option.data());
+ }
} else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
Usage("Unknown argument %s", option.data());
}
@@ -1542,25 +1570,45 @@
}
// Open dex files for class path.
- std::vector<std::string> class_path_locations =
- GetClassPathLocations(runtime_->GetClassPathString());
- OpenClassPathFiles(class_path_locations,
- &class_path_files_,
- &opened_oat_files_,
- runtime_->GetInstructionSet(),
- classpath_dir_);
-
- // Store the classpath we have right now.
- std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
- std::string encoded_class_path;
- if (class_path_locations.size() == 1 &&
- class_path_locations[0] == OatFile::kSpecialSharedLibrary) {
- // When passing the special shared library as the classpath, it is the only path.
- encoded_class_path = OatFile::kSpecialSharedLibrary;
- } else {
- encoded_class_path = OatFile::EncodeDexFileDependencies(class_path_files, classpath_dir_);
+ if (class_loader_context_ == nullptr) {
+ // TODO(calin): Temporary workaround while we transition to use
+ // --class-loader-context instead of --runtime-arg -cp
+ if (runtime_->GetClassPathString().empty()) {
+ class_loader_context_ = std::unique_ptr<ClassLoaderContext>(
+ new ClassLoaderContext());
+ } else {
+ std::string spec = runtime_->GetClassPathString() == OatFile::kSpecialSharedLibrary
+ ? OatFile::kSpecialSharedLibrary
+ : "PCL[" + runtime_->GetClassPathString() + "]";
+ class_loader_context_ = ClassLoaderContext::Create(spec);
+ }
}
- key_value_store_->Put(OatHeader::kClassPathKey, encoded_class_path);
+ CHECK(class_loader_context_ != nullptr);
+ DCHECK_EQ(oat_writers_.size(), 1u);
+
+ // Note: Ideally we would reject context where the source dex files are also
+ // specified in the classpath (as it doesn't make sense). However this is currently
+ // needed for non-prebuild tests and benchmarks which expects on the fly compilation.
+ // Also, for secondary dex files we do not have control on the actual classpath.
+ // Instead of aborting, remove all the source location from the context classpaths.
+ if (class_loader_context_->RemoveLocationsFromClassPaths(
+ oat_writers_[0]->GetSourceLocations())) {
+ LOG(WARNING) << "The source files to be compiled are also in the classpath.";
+ }
+
+ // We need to open the dex files before encoding the context in the oat file.
+ // (because the encoding adds the dex checksum...)
+ // TODO(calin): consider redesigning this so we don't have to open the dex files before
+ // creating the actual class loader.
+ if (!class_loader_context_->OpenDexFiles(runtime_->GetInstructionSet(), classpath_dir_)) {
+ // Do not abort if we couldn't open files from the classpath. They might be
+ // apks without dex files and right now are opening flow will fail them.
+ LOG(WARNING) << "Failed to open classpath dex files";
+ }
+
+ // Store the class loader context in the oat header.
+ key_value_store_->Put(OatHeader::kClassPathKey,
+ class_loader_context_->EncodeContextForOatFile(classpath_dir_));
}
// Now that we have finalized key_value_store_, start writing the oat file.
@@ -1656,17 +1704,7 @@
if (kSaveDexInput) {
SaveDexInput();
}
-
- // Handle and ClassLoader creation needs to come after Runtime::Create.
- ScopedObjectAccess soa(self);
-
- // Classpath: first the class-path given.
- std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
-
- // Then the dex files we'll compile. Thus we'll resolve the class-path first.
- class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end());
-
- class_loader_ = class_linker->CreatePathClassLoader(self, class_path_files);
+ class_loader_ = class_loader_context_->CreateClassLoader(dex_files_);
}
// Ensure opened dex files are writable for dex-to-dex transformations.
@@ -1721,7 +1759,12 @@
if (!no_inline_filters.empty()) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
+ std::vector<const DexFile*> class_path_files;
+ if (!IsBootImage()) {
+ // The class loader context is used only for apps.
+ class_path_files = class_loader_context_->FlattenOpenedDexFiles();
+ }
+
std::vector<const std::vector<const DexFile*>*> dex_file_vectors = {
&class_linker->GetBootClassPath(),
&class_path_files,
@@ -2224,8 +2267,8 @@
DCHECK(!IsBootImage());
DCHECK_EQ(oat_writers_.size(), 1u);
std::vector<std::string> dex_files_canonical_locations;
- for (const char* location : oat_writers_[0]->GetSourceLocations()) {
- dex_files_canonical_locations.push_back(DexFile::GetDexCanonicalLocation(location));
+ for (const std::string& location : oat_writers_[0]->GetSourceLocations()) {
+ dex_files_canonical_locations.push_back(DexFile::GetDexCanonicalLocation(location.c_str()));
}
std::vector<std::string> parsed;
@@ -2240,48 +2283,6 @@
return parsed;
}
- // 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 and appends them to opened_oat_files.
- static void OpenClassPathFiles(std::vector<std::string>& class_path_locations,
- std::vector<std::unique_ptr<const DexFile>>* opened_dex_files,
- std::vector<std::unique_ptr<OatFile>>* opened_oat_files,
- InstructionSet isa,
- std::string& classpath_dir) {
- DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles dex out-param is nullptr";
- DCHECK(opened_oat_files != nullptr) << "OpenClassPathFiles oat out-param is nullptr";
- for (std::string& location : class_path_locations) {
- // 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 (location == OatFile::kSpecialSharedLibrary) {
- break;
- }
- // If path is relative, append it to the provided base directory.
- if (!classpath_dir.empty() && location[0] != '/') {
- location = classpath_dir + '/' + location;
- }
- static constexpr bool kVerifyChecksum = true;
- std::string error_msg;
- if (!DexFile::Open(
- location.c_str(), location.c_str(), kVerifyChecksum, &error_msg, 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.
- OatFileAssistant oat_file_assistant(location.c_str(), isa, false);
- std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile());
- if (oat_file == nullptr) {
- LOG(WARNING) << "Failed to open dex file and associated oat file for '" << location
- << "': " << error_msg;
- } else {
- std::vector<std::unique_ptr<const DexFile>> oat_dex_files =
- oat_file_assistant.LoadDexFiles(*oat_file, location.c_str());
- opened_oat_files->push_back(std::move(oat_file));
- opened_dex_files->insert(opened_dex_files->end(),
- std::make_move_iterator(oat_dex_files.begin()),
- std::make_move_iterator(oat_dex_files.end()));
- }
- }
- }
- }
-
bool PrepareImageClasses() {
// If --image-classes was specified, calculate the full list of classes to include in the image.
if (image_classes_filename_ != nullptr) {
@@ -2737,8 +2738,8 @@
std::unique_ptr<Runtime> runtime_;
- // Ownership for the class path files.
- std::vector<std::unique_ptr<const DexFile>> class_path_files_;
+ // The spec describing how the class loader should be setup for compilation.
+ std::unique_ptr<ClassLoaderContext> class_loader_context_;
size_t thread_count_;
uint64_t start_ns_;
@@ -2792,9 +2793,9 @@
std::unique_ptr<CompilerDriver> driver_;
std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
- std::vector<std::unique_ptr<OatFile>> opened_oat_files_;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
+ // Note that this might contain pointers owned by class_loader_context_.
std::vector<const DexFile*> no_inline_from_dex_files_;
bool dump_stats_;
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index b604e8b..1505eb5 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -89,7 +89,8 @@
CompilerFilter::Filter filter,
const std::vector<std::string>& extra_args = {},
bool expect_success = true,
- bool use_fd = false) {
+ bool use_fd = false,
+ std::function<void(const OatFile&)> check_oat = [](const OatFile&) {}) {
std::string error_msg;
int status = GenerateOdexForTestWithStatus(dex_location,
odex_location,
@@ -113,6 +114,7 @@
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
CheckFilter(filter, odex_file->GetCompilerFilter());
+ check_oat(*(odex_file.get()));
} else {
ASSERT_FALSE(success) << output_;
@@ -895,4 +897,123 @@
EXPECT_EQ(static_cast<int>(dex2oat::ReturnCode::kCreateRuntime), WEXITSTATUS(status)) << output_;
}
+class Dex2oatClassLoaderContextTest : public Dex2oatTest {
+ protected:
+ void RunTest(const char* class_loader_context,
+ const char* expected_classpath_key,
+ bool expected_success,
+ bool use_second_source = false) {
+ std::string dex_location = GetUsedDexLocation();
+ std::string odex_location = GetUsedOatLocation();
+
+ Copy(use_second_source ? GetDexSrc2() : GetDexSrc1(), dex_location);
+
+ std::string error_msg;
+ std::vector<std::string> extra_args;
+ if (class_loader_context != nullptr) {
+ extra_args.push_back(std::string("--class-loader-context=") + class_loader_context);
+ }
+ auto check_oat = [expected_classpath_key](const OatFile& oat_file) {
+ ASSERT_TRUE(expected_classpath_key != nullptr);
+ const char* classpath = oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey);
+ ASSERT_TRUE(classpath != nullptr);
+ ASSERT_STREQ(expected_classpath_key, classpath);
+ };
+
+ GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kQuicken,
+ extra_args,
+ expected_success,
+ /*use_fd*/ false,
+ check_oat);
+ }
+
+ std::string GetUsedDexLocation() {
+ return GetScratchDir() + "/Context.jar";
+ }
+
+ std::string GetUsedOatLocation() {
+ return GetOdexDir() + "/Context.odex";
+ }
+
+ const char* kEmptyClassPathKey = "";
+};
+
+TEST_F(Dex2oatClassLoaderContextTest, InvalidContext) {
+ RunTest("Invalid[]", /*expected_classpath_key*/ nullptr, /*expected_success*/ false);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, EmptyContext) {
+ RunTest("PCL[]", kEmptyClassPathKey, /*expected_success*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, SpecialContext) {
+ RunTest(OatFile::kSpecialSharedLibrary,
+ OatFile::kSpecialSharedLibrary,
+ /*expected_success*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithTheSourceDexFiles) {
+ std::string context = "PCL[" + GetUsedDexLocation() + "]";
+ RunTest(context.c_str(), kEmptyClassPathKey, /*expected_success*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithOtherDexFiles) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Nested");
+ std::string expected_classpath_key =
+ OatFile::EncodeDexFileDependencies(MakeNonOwningPointerVector(dex_files), "");
+
+ std::string context = "PCL[" + dex_files[0]->GetLocation() + "]";
+ RunTest(context.c_str(), expected_classpath_key.c_str(), true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFiles) {
+ std::string stripped_classpath = GetScratchDir() + "/stripped_classpath.jar";
+ Copy(GetStrippedDexSrc1(), stripped_classpath);
+
+ std::string context = "PCL[" + stripped_classpath + "]";
+ // Expect an empty context because stripped dex files cannot be open.
+ RunTest(context.c_str(), /*expected_classpath_key*/ "" , /*expected_success*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFilesBackedByOdex) {
+ std::string stripped_classpath = GetScratchDir() + "/stripped_classpath.jar";
+ std::string odex_for_classpath = GetOdexDir() + "/stripped_classpath.odex";
+
+ Copy(GetDexSrc1(), stripped_classpath);
+
+ GenerateOdexForTest(stripped_classpath,
+ odex_for_classpath,
+ CompilerFilter::kQuicken,
+ {},
+ true);
+
+ // Strip the dex file
+ Copy(GetStrippedDexSrc1(), stripped_classpath);
+
+ std::string context = "PCL[" + stripped_classpath + "]";
+ std::string expected_classpath;
+ {
+ // Open the oat file to get the expected classpath.
+ OatFileAssistant oat_file_assistant(stripped_classpath.c_str(), kRuntimeISA, false);
+ std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile());
+ std::vector<std::unique_ptr<const DexFile>> oat_dex_files =
+ OatFileAssistant::LoadDexFiles(*oat_file, stripped_classpath.c_str());
+ expected_classpath = OatFile::EncodeDexFileDependencies(
+ MakeNonOwningPointerVector(oat_dex_files), "");
+ }
+
+ RunTest(context.c_str(),
+ expected_classpath.c_str(),
+ /*expected_success*/ true,
+ /*use_second_source*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithNotExistentDexFiles) {
+ std::string context = "PCL[does_not_exists.dex]";
+ // Expect an empty context because stripped dex files cannot be open.
+ RunTest(context.c_str(), kEmptyClassPathKey, /*expected_success*/ true);
+}
+
} // namespace art
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 0dfc60d..8d15c34 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -48,6 +48,7 @@
"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 @@
"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/base/arena_allocator.h b/runtime/base/arena_allocator.h
index ebde82d..a484c5c 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -336,7 +336,8 @@
auto* end = reinterpret_cast<uint8_t*>(ptr) + aligned_ptr_size;
// If we haven't allocated anything else, we can safely extend.
if (end == ptr_) {
- DCHECK(!IsRunningOnMemoryTool()); // Red zone prevents end == ptr_.
+ // Red zone prevents end == ptr_ (unless input = allocator state = null).
+ DCHECK(!IsRunningOnMemoryTool() || ptr_ == nullptr);
const size_t aligned_new_size = RoundUp(new_size, kAlignment);
const size_t size_delta = aligned_new_size - aligned_ptr_size;
// Check remain space.
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
new file mode 100644
index 0000000..5cbcd8f
--- /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 0000000..4af5017
--- /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 0000000..4643e78
--- /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/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 38edc7a..74fec48 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -241,7 +241,7 @@
}
CHECK(receiver->GetClass()->ShouldHaveEmbeddedVTable());
ArtMethod* const called_method = receiver->GetClass()->GetEmbeddedVTableEntry(
- vtable_idx, kRuntimePointerSize);
+ vtable_idx, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
if (UNLIKELY(called_method == nullptr)) {
CHECK(self->IsExceptionPending());
result->SetJ(0);
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index c314f3c..c2ef724 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -386,8 +386,9 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
mirror::Class* klass = mirror::String::GetJavaLangString();
- ArtMethod* method = klass->FindDeclaredDirectMethod("<init>", "(Ljava/lang/String;)V",
- kRuntimePointerSize);
+ ArtMethod* method =
+ klass->FindDeclaredDirectMethod("<init>", "(Ljava/lang/String;)V",
+ Runtime::Current()->GetClassLinker()->GetImagePointerSize());
// create instruction data for invoke-direct {v0, v1} of method with fake index
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
@@ -1335,10 +1336,16 @@
ArtMethod* throw_cons = throw_class->FindDeclaredDirectMethod(
"<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
ASSERT_TRUE(throw_cons != nullptr);
-
- Handle<mirror::Constructor> cons = hs.NewHandle(
- mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(self, throw_cons));
- ASSERT_TRUE(cons != nullptr);
+ Handle<mirror::Constructor> cons;
+ if (class_linker->GetImagePointerSize() == PointerSize::k64) {
+ cons = hs.NewHandle(
+ mirror::Constructor::CreateFromArtMethod<PointerSize::k64, false>(self, throw_cons));
+ ASSERT_TRUE(cons != nullptr);
+ } else {
+ cons = hs.NewHandle(
+ mirror::Constructor::CreateFromArtMethod<PointerSize::k32, false>(self, throw_cons));
+ ASSERT_TRUE(cons != nullptr);
+ }
Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle(
mirror::ObjectArray<mirror::Object>::Alloc(
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 888de45..2ed30df 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1577,7 +1577,7 @@
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 66ed44f..6393e09 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -299,7 +299,7 @@
// 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 4820feb..c876657 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -298,28 +298,38 @@
}
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 @@
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 03d9ca3..92d87ea 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -207,6 +207,13 @@
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
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 36ecd33..fc30832 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2134,6 +2134,10 @@
ScopedObjectAccess soa(self);
// We may need to call user-supplied managed code, do this before final clean-up.
HandleUncaughtExceptions(soa);
+ Runtime* runtime = Runtime::Current();
+ if (runtime != nullptr) {
+ runtime->GetRuntimeCallbacks()->ThreadDeath(self);
+ }
RemoveFromThreadGroup(soa);
// this.nativePeer = 0;
@@ -2144,11 +2148,6 @@
jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
->SetLong<false>(tlsPtr_.opeer, 0);
}
- Runtime* runtime = Runtime::Current();
- if (runtime != nullptr) {
- runtime->GetRuntimeCallbacks()->ThreadDeath(self);
- }
-
// Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone
// who is waiting.
diff --git a/test/912-classes/src-art/art/Test912.java b/test/912-classes/src-art/art/Test912.java
index 9896eac..fbf8794 100644
--- a/test/912-classes/src-art/art/Test912.java
+++ b/test/912-classes/src-art/art/Test912.java
@@ -228,7 +228,8 @@
// The JIT may deeply inline and load some classes. Preload these for test determinism.
final String PRELOAD_FOR_JIT[] = {
"java.nio.charset.CoderMalfunctionError",
- "java.util.NoSuchElementException"
+ "java.util.NoSuchElementException",
+ "java.io.FileNotFoundException", // b/63581208
};
for (String s : PRELOAD_FOR_JIT) {
Class.forName(s);
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 8a4c2df..c6553f8 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -223,5 +223,10 @@
result: EXEC_FAILED,
bug: 62408076,
names: ["libcore.java.lang.reflect.annotations.AnnotatedElementParameterTest#testImplicitConstructorParameters_singleAnnotation"]
+},
+{
+ description: "java.io.IOException: Error writing ASN.1 encoding",
+ result: EXEC_FAILED,
+ names: ["libcore.javax.crypto.spec.AlgorithmParametersTestGCM#testEncoding"]
}
]
diff --git a/tools/test_presubmit.py b/tools/test_presubmit.py
new file mode 100755
index 0000000..f6e6df9
--- /dev/null
+++ b/tools/test_presubmit.py
@@ -0,0 +1,159 @@
+#!/usr/bin/python3
+#
+# Copyright 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.
+
+#
+# There are many run-tests which generate their sources automatically.
+# It is desirable to keep the checked-in source code, as we re-run generators very rarely.
+#
+# This script will re-run the generators only if their dependent files have changed and then
+# complain if the outputs no longer matched what's in the source tree.
+#
+
+import os
+import pathlib
+import subprocess
+import sys
+import tempfile
+
+THIS_PATH = os.path.dirname(os.path.realpath(__file__))
+
+TOOLS_GEN_SRCS = [
+ # tool -> path to a script to generate a file
+ # reference_files -> list of files that the script can generate
+ # args -> lambda(path) that generates arguments the 'tool' in order to output to 'path'
+ # interesting_files -> which files much change in order to re-run the tool.
+ # interesting_to_reference_files: lambda(x,reference_files)
+ # given the interesting file 'x' and a list of reference_files,
+ # return exactly one reference file that corresponds to it.
+ { 'tool' : 'test/988-method-trace/gen_srcs.py',
+ 'reference_files' : ['test/988-method-trace/src/art/Test988Intrinsics.java'],
+ 'args' : lambda output_path: [output_path],
+ 'interesting_files' : ['compiler/intrinsics_list.h'],
+ 'interesting_to_reference_file' : lambda interesting, references: references[0],
+ },
+]
+
+DEBUG = False
+
+def debug_print(msg):
+ if DEBUG:
+ print("[DEBUG]: " + msg, file=sys.stderr)
+
+def is_interesting(f, tool_dict):
+ """
+ Returns true if this is a file we want to run this tool before uploading. False otherwise.
+ """
+ path = pathlib.Path(f)
+ return str(path) in tool_dict['interesting_files']
+
+def get_changed_files(commit):
+ """
+ Gets the files changed in the given commit.
+ """
+ return subprocess.check_output(
+ ["git", 'diff-tree', '--no-commit-id', '--name-only', '-r', commit],
+ stderr=subprocess.STDOUT,
+ universal_newlines=True).split()
+
+def command_line_for_tool(tool_dict, output):
+ """
+ Calculate the command line for this tool when ran against the output file 'output'.
+ """
+ proc_args = [tool_dict['tool']] + tool_dict['args'](output)
+ return proc_args
+
+def run_tool(tool_dict, output):
+ """
+ Execute this tool by passing the tool args to the tool.
+ """
+ proc_args = command_line_for_tool(tool_dict, output)
+ debug_print("PROC_ARGS: %s" %(proc_args))
+ succ = subprocess.call(proc_args)
+ return succ
+
+def get_reference_file(changed_file, tool_dict):
+ """
+ Lookup the file that the tool is generating in response to changing an interesting file
+ """
+ return tool_dict['interesting_to_reference_file'](changed_file, tool_dict['reference_files'])
+
+def run_diff(changed_file, tool_dict, original_file):
+ ref_file = get_reference_file(changed_file, tool_dict)
+
+ return subprocess.call(["diff", ref_file, original_file]) != 0
+
+def run_gen_srcs(files):
+ """
+ Runs test tools only for interesting files that were changed in this commit.
+ """
+ if len(files) == 0:
+ return
+
+ success = 0 # exit code 0 = success, >0 error.
+ had_diffs = False
+
+ for tool_dict in TOOLS_GEN_SRCS:
+ tool_ran_at_least_once = False
+ for f in files:
+ if is_interesting(f, tool_dict):
+ tmp_file = tempfile.mktemp()
+ reference_file = get_reference_file(f, tool_dict)
+
+ # Generate the source code with a temporary file as the output.
+ success = run_tool(tool_dict, tmp_file)
+ if success != 0:
+ # Immediately abort if the tool fails with a non-0 exit code, do not go any further.
+ print("[FATAL] Error when running tool (return code %s)" %(success), file=sys.stderr)
+ print("$> %s" %(" ".join(command_line_for_tool(tool_dict, tmp_file))), file=sys.stderr)
+ sys.exit(success)
+ if run_diff(f, tool_dict, tmp_file):
+ # If the tool succeeded, but there was a diff, then the generated code has diverged.
+ # Output the diff information and continue to the next files/tools.
+ had_diffs = True
+ print("-----------------------------------------------------------", file=sys.stderr)
+ print("File '%s' diverged from generated file; please re-run tools:" %(reference_file), file=sys.stderr)
+ print("$> %s" %(" ".join(command_line_for_tool(tool_dict, reference_file))), file=sys.stderr)
+ else:
+ debug_print("File %s is consistent with tool %s" %(reference_file, tool_dict['tool']))
+
+ tool_ran_at_least_once = True
+
+ if not tool_ran_at_least_once:
+ debug_print("Interesting files %s unchanged, skipping tool '%s'" %(tool_dict['interesting_files'], tool_dict['tool']))
+
+ if had_diffs:
+ success = 1
+ # Always return non-0 exit code when there were diffs so that the presubmit hooks are FAILED.
+
+ return success
+
+
+def main():
+ if 'PREUPLOAD_COMMIT' in os.environ:
+ commit = os.environ['PREUPLOAD_COMMIT']
+ else:
+ print("WARNING: Not running as a pre-upload hook. Assuming commit to check = 'HEAD'", file=sys.stderr)
+ commit = "HEAD"
+
+ os.chdir(os.path.join(THIS_PATH, '..')) # run tool relative to 'art' directory
+ debug_print("CWD: %s" %(os.getcwd()))
+
+ changed_files = get_changed_files(commit)
+ debug_print("Changed files: %s" %(changed_files))
+ return run_gen_srcs(changed_files)
+
+if __name__ == '__main__':
+ sys.exit(main())