Merge "ART: Add FileNotFoundException to preloads for test 912"
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/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/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"]
}
]