diff options
author | 2022-11-23 10:57:22 +0000 | |
---|---|---|
committer | 2022-11-25 16:38:58 +0000 | |
commit | c372e79522f8126d241a6f6b52fc9a1145b03d1e (patch) | |
tree | 21f9d050e3fa5b41c93b557438b5dfb5cd143498 | |
parent | 4f301422a579fee785849afadd623c180da32e61 (diff) |
Make ArtDexFileLoader accept an zip file with no dex code.
We pass config split APKs as part of the class loader context to
OatFileAssistant, in both the legacy dexopt code path and the new ART
Services code path. Those config split APKs eventually go to
ArtDexFileLoader. Therefore, ArtDexFileLoader should not regard this as
an error.
Before this change, this was regarded as an error. The legacy dexopt
code path could work because it uses the legacy
`DexFile.GetDexOptNeeded` Java API, which doesn't check the result of
opening dex files. The new ART Services code path couldn't work because
it checks the result.
Bug: 229268202
Test: m test-art-host-gtest
Test: (on internal master) -
1. Run `pm art optimize-package` for a split APK that depends on a
config split APK.
2. See dexopt being successful.
Change-Id: I08a2a5ad7bea82c3f534ed4de2935fc400652361
-rw-r--r-- | libdexfile/Android.bp | 1 | ||||
-rw-r--r-- | libdexfile/art_standalone_libdexfile_tests.xml | 1 | ||||
-rw-r--r-- | libdexfile/dex/art_dex_file_loader.cc | 22 | ||||
-rw-r--r-- | libdexfile/dex/art_dex_file_loader_test.cc | 14 | ||||
-rw-r--r-- | runtime/dex2oat_environment_test.h | 7 | ||||
-rw-r--r-- | runtime/oat_file_assistant.cc | 71 | ||||
-rw-r--r-- | runtime/oat_file_assistant.h | 19 | ||||
-rw-r--r-- | runtime/oat_file_assistant_test.cc | 98 |
8 files changed, 163 insertions, 70 deletions
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index c6f18b62af..b5a411bdd7 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -288,6 +288,7 @@ art_cc_defaults { ":art-gtest-jars-GetMethodSignature", ":art-gtest-jars-Lookup", ":art-gtest-jars-Main", + ":art-gtest-jars-MainEmptyUncompressed", ":art-gtest-jars-MultiDex", ":art-gtest-jars-Nested", ], diff --git a/libdexfile/art_standalone_libdexfile_tests.xml b/libdexfile/art_standalone_libdexfile_tests.xml index e7089dd51a..8e3ef2de50 100644 --- a/libdexfile/art_standalone_libdexfile_tests.xml +++ b/libdexfile/art_standalone_libdexfile_tests.xml @@ -26,6 +26,7 @@ <option name="push" value="art-gtest-jars-GetMethodSignature.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-GetMethodSignature.jar" /> <option name="push" value="art-gtest-jars-Lookup.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-Lookup.jar" /> <option name="push" value="art-gtest-jars-Main.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-Main.jar" /> + <option name="push" value="art-gtest-jars-MainEmptyUncompressed.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-MainEmptyUncompressed.jar" /> <option name="push" value="art-gtest-jars-MultiDex.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-MultiDex.jar" /> <option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-Nested.jar" /> </target_preparer> diff --git a/libdexfile/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc index a7dd13e1ed..1057bbcc65 100644 --- a/libdexfile/dex/art_dex_file_loader.cc +++ b/libdexfile/dex/art_dex_file_loader.cc @@ -19,9 +19,9 @@ #include <sys/stat.h> #include "android-base/stringprintf.h" - #include "base/file_magic.h" #include "base/file_utils.h" +#include "base/logging.h" #include "base/mem_map.h" #include "base/mman.h" // For the PROT_* and MAP_* constants. #include "base/stl_util.h" @@ -114,18 +114,22 @@ bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename, return false; } + if (zip_file_only_contains_uncompressed_dex != nullptr) { + // Start by assuming everything is uncompressed. + *zip_file_only_contains_uncompressed_dex = true; + } + uint32_t idx = 0; std::string zip_entry_name = GetMultiDexClassesDexName(idx); std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg)); if (zip_entry.get() == nullptr) { - *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename, - zip_entry_name.c_str(), error_msg->c_str()); - return false; - } - - if (zip_file_only_contains_uncompressed_dex != nullptr) { - // Start by assuming everything is uncompressed. - *zip_file_only_contains_uncompressed_dex = true; + // A zip file with no dex code should be accepted. It's likely a config split APK, which we + // are currently passing from higher levels. + VLOG(dex) << StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", + filename, + zip_entry_name.c_str(), + error_msg->c_str()); + return true; } do { diff --git a/libdexfile/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc index 1863c1bf57..bf1d306e65 100644 --- a/libdexfile/dex/art_dex_file_loader_test.cc +++ b/libdexfile/dex/art_dex_file_loader_test.cc @@ -103,6 +103,20 @@ TEST_F(ArtDexFileLoaderTest, GetMultiDexChecksums) { EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]); } +TEST_F(ArtDexFileLoaderTest, GetMultiDexChecksumsEmptyZip) { + std::string error_msg; + std::vector<uint32_t> checksums; + std::vector<std::string> dex_locations; + std::string multidex_file = GetTestDexFileName("MainEmptyUncompressed"); + const ArtDexFileLoader dex_file_loader; + EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums( + multidex_file.c_str(), &checksums, &dex_locations, &error_msg)) + << error_msg; + + EXPECT_EQ(dex_locations.size(), 0); + EXPECT_EQ(checksums.size(), 0); +} + TEST_F(ArtDexFileLoaderTest, ClassDefs) { std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested")); ASSERT_TRUE(raw.get() != nullptr); diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index 9ebc68dd21..bc25231ea1 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -109,10 +109,9 @@ class Dex2oatEnvironmentTest : public Dex2oatScratchDirs, public CommonRuntimeTe << "Expected dex file to be at: " << GetDexSrc1(); ASSERT_TRUE(OS::FileExists(GetResourceOnlySrc1().c_str())) << "Expected stripped dex file to be at: " << GetResourceOnlySrc1(); - ASSERT_FALSE( - dex_file_loader.GetMultiDexChecksums( - GetResourceOnlySrc1().c_str(), &checksums, &dex_locations, &error_msg)) - << "Expected stripped dex file to be stripped: " << GetResourceOnlySrc1(); + ASSERT_TRUE(dex_file_loader.GetMultiDexChecksums( + GetResourceOnlySrc1().c_str(), &checksums, &dex_locations, &error_msg)) + << "Expected stripped dex file to be stripped: " << GetResourceOnlySrc1(); ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str())) << "Expected dex file to be at: " << GetDexSrc2(); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 320e827bd5..109d2ba182 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -19,6 +19,7 @@ #include <sys/stat.h> #include <memory> +#include <optional> #include <sstream> #include <vector> @@ -446,13 +447,13 @@ bool OatFileAssistant::LoadDexFiles( return true; } -bool OatFileAssistant::HasDexFiles() { +std::optional<bool> OatFileAssistant::HasDexFiles(std::string* error_msg) { ScopedTrace trace("HasDexFiles"); - // Ensure GetRequiredDexChecksums has been run so that - // has_original_dex_files_ is initialized. We don't care about the result of - // GetRequiredDexChecksums. - GetRequiredDexChecksums(); - return has_original_dex_files_; + const std::vector<std::uint32_t>* checksums = GetRequiredDexChecksums(error_msg); + if (checksums == nullptr) { + return std::nullopt; + } + return !checksums->empty(); } OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() { @@ -465,8 +466,12 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() { bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) { ScopedTrace trace("DexChecksumUpToDate(vdex)"); - const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(); + const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(error_msg); if (required_dex_checksums == nullptr) { + // TODO(jiakaiz): Change to false. + return true; + } + if (required_dex_checksums->empty()) { LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date."; return true; } @@ -498,8 +503,12 @@ bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* er bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) { ScopedTrace trace("DexChecksumUpToDate(oat)"); - const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(); + const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(error_msg); if (required_dex_checksums == nullptr) { + // TODO(jiakaiz): Change to false. + return true; + } + if (required_dex_checksums->empty()) { LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date."; return true; } @@ -746,30 +755,34 @@ bool OatFileAssistant::DexLocationToOatFilename(const std::string& location, return GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(), oat_filename, error_msg); } -const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() { +const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums(std::string* error_msg) { if (!required_dex_checksums_attempted_) { required_dex_checksums_attempted_ = true; - required_dex_checksums_found_ = false; - cached_required_dex_checksums_.clear(); - std::string error_msg; + std::vector<uint32_t> checksums; const ArtDexFileLoader dex_file_loader; std::vector<std::string> dex_locations_ignored; if (dex_file_loader.GetMultiDexChecksums(dex_location_.c_str(), - &cached_required_dex_checksums_, + &checksums, &dex_locations_ignored, - &error_msg, + &cached_required_dex_checksums_error_, zip_fd_, &zip_file_only_contains_uncompressed_dex_)) { - required_dex_checksums_found_ = true; - has_original_dex_files_ = true; - } else { - // The only valid case here is for APKs without dex files. - required_dex_checksums_found_ = false; - has_original_dex_files_ = false; - VLOG(oat) << "Could not get required checksum: " << error_msg; + if (checksums.empty()) { + // The only valid case here is for APKs without dex files. + VLOG(oat) << "No dex file found in " << dex_location_; + } + + cached_required_dex_checksums_ = std::move(checksums); } } - return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr; + + if (cached_required_dex_checksums_.has_value()) { + return &*cached_required_dex_checksums_; + } else { + *error_msg = cached_required_dex_checksums_error_; + DCHECK(!error_msg->empty()); + return nullptr; + } } bool OatFileAssistant::ValidateBootClassPathChecksums(OatFileAssistantContext* ofa_context, @@ -1036,10 +1049,18 @@ OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded( return kDex2OatForBootImage; } - if (oat_file_assistant_->HasDexFiles()) { - return kDex2OatFromScratch; + std::string error_msg; + std::optional<bool> has_dex_files = oat_file_assistant_->HasDexFiles(&error_msg); + if (has_dex_files.has_value()) { + if (*has_dex_files) { + return kDex2OatFromScratch; + } else { + // No dex file, so there is nothing we need to do. + return kNoDexOptNeeded; + } } else { - // No dex file, there is nothing we need to do. + // Unable to open the dex file, so there is nothing we can do. + LOG(WARNING) << error_msg; return kNoDexOptNeeded; } } diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 2d0a1506ad..33708ce6c1 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -284,8 +284,8 @@ class OatFileAssistant { const std::string& dex_location, std::vector<std::unique_ptr<const DexFile>>* out_dex_files); - // Returns whether this is an apk/zip wit a classes.dex entry. - bool HasDexFiles(); + // Returns whether this is an apk/zip wit a classes.dex entry, or nullopt if an error occurred. + std::optional<bool> HasDexFiles(std::string* error_msg); // If the dex file has been installed with a compiled oat file alongside // it, the compiled oat file will have the extension .odex, and is referred @@ -489,12 +489,10 @@ class OatFileAssistant { OatStatus GivenOatFileStatus(const OatFile& file); // Gets the dex checksums required for an up-to-date oat file. - // Returns cached_required_dex_checksums if the required checksums were - // located. Returns null if the required checksums were not found. The - // caller shouldn't clean up or free the returned pointer. This sets the - // has_original_dex_files_ field to true if the checksums were found for the - // dex_location_ dex file. - const std::vector<uint32_t>* GetRequiredDexChecksums(); + // Returns cached_required_dex_checksums if the required checksums were located. Returns an empty + // list if `dex_location_` refers to a zip and there is no dex file in it. Returns nullptr if an + // error occurred. The caller shouldn't clean up or free the returned pointer. + const std::vector<uint32_t>* GetRequiredDexChecksums(std::string* error_msg); // Returns whether there is at least one boot image usable. bool IsPrimaryBootImageUsable(); @@ -547,10 +545,9 @@ class OatFileAssistant { // Cached value of the required dex checksums. // This should be accessed only by the GetRequiredDexChecksums() method. - std::vector<uint32_t> cached_required_dex_checksums_; + std::optional<std::vector<uint32_t>> cached_required_dex_checksums_; + std::string cached_required_dex_checksums_error_; bool required_dex_checksums_attempted_ = false; - bool required_dex_checksums_found_; - bool has_original_dex_files_; // The AOT-compiled file of an app when the APK of the app is in /data. OatFileInfo odex_; diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 090532c592..6cd04c7819 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -221,6 +221,13 @@ class OatFileAssistantTest : public OatFileAssistantBaseTest, zip_fd); } + void ExpectHasDexFiles(OatFileAssistant* oat_file_assistant, bool expected_value) { + std::string error_msg; + std::optional<bool> has_dex_files = oat_file_assistant->HasDexFiles(&error_msg); + ASSERT_TRUE(has_dex_files.has_value()) << error_msg; + EXPECT_EQ(*has_dex_files, expected_value); + } + std::unique_ptr<ClassLoaderContext> default_context_ = InitializeDefaultContext(); bool with_runtime_; const OatFileAssistant::DexOptTrigger default_trigger_{.targetFilterIsBetter = true, @@ -402,7 +409,7 @@ TEST_P(OatFileAssistantTest, DexNoOat) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); VerifyOptimizationStatus( dex_location, default_context_.get(), "run-from-apk", "unknown", "io-error-no-oat"); @@ -423,7 +430,8 @@ TEST_P(OatFileAssistantTest, NoDexNoOat) { /*expected_is_vdex_usable=*/false, /*expected_location=*/OatFileAssistant::kLocationNoneOrError, /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded); - EXPECT_FALSE(oat_file_assistant.HasDexFiles()); + std::string error_msg_ignored; + EXPECT_FALSE(oat_file_assistant.HasDexFiles(&error_msg_ignored).has_value()); // Trying to get the best oat file should fail, but not crash. std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); @@ -471,7 +479,7 @@ TEST_P(OatFileAssistantTest, OdexUpToDate) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); VerifyOptimizationStatus( dex_location, default_context_.get(), CompilerFilter::kSpeed, "install", "up-to-date"); @@ -522,7 +530,7 @@ TEST_P(OatFileAssistantTest, OdexUpToDatePartialBootImage) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); VerifyOptimizationStatus( dex_location, default_context_.get(), CompilerFilter::kSpeed, "install", "up-to-date"); @@ -576,7 +584,7 @@ TEST_P(OatFileAssistantTest, OdexUpToDateSymLink) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); } // Case: We have a DEX file and up-to-date OAT file for it. @@ -628,7 +636,7 @@ TEST_P(OatFileAssistantTest, OatUpToDate) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); VerifyOptimizationStatus( dex_location, default_context_.get(), CompilerFilter::kSpeed, "unknown", "up-to-date"); @@ -687,7 +695,7 @@ TEST_P(OatFileAssistantTest, GetDexOptNeededWithFd) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); } // Case: Passing invalid odex fd and valid vdex and zip fds. @@ -736,7 +744,7 @@ TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); } // Case: Passing invalid vdex fd and valid odex and zip fds. @@ -772,7 +780,7 @@ TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); } // Case: Passing invalid vdex and odex fd with valid zip fd. @@ -962,7 +970,7 @@ TEST_P(OatFileAssistantTest, ProfileOatUpToDate) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); } // Case: We have a MultiDEX file and up-to-date OAT file for it. @@ -992,7 +1000,7 @@ TEST_P(OatFileAssistantTest, MultiDexOatUpToDate) { /*expected_is_vdex_usable=*/true, /*expected_location=*/OatFileAssistant::kLocationOat, /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); // Verify we can load both dex files. std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); @@ -1036,7 +1044,7 @@ TEST_P(OatFileAssistantTest, MultiDexNonMainOutOfDate) { /*expected_is_vdex_usable=*/false, /*expected_location=*/OatFileAssistant::kLocationNoneOrError, /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); } // Case: We have a DEX file and an OAT file out of date with respect to the @@ -1078,7 +1086,7 @@ TEST_P(OatFileAssistantTest, OatDexOutOfDate) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); VerifyOptimizationStatus( dex_location, default_context_.get(), "run-from-apk-fallback", "unknown", "apk-more-recent"); @@ -1174,7 +1182,7 @@ TEST_P(OatFileAssistantTest, OatImageOutOfDate) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); VerifyOptimizationStatus(dex_location, default_context_.get(), "verify", "vdex", "up-to-date"); } @@ -1248,7 +1256,7 @@ TEST_P(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); } // Case: We have a DEX file and an ODEX file, but no OAT file. @@ -1281,7 +1289,7 @@ TEST_P(OatFileAssistantTest, DexOdexNoOat) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); // We should still be able to get the non-executable odex file to run from. std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); @@ -1322,7 +1330,7 @@ TEST_P(OatFileAssistantTest, ResourceOnlyDex) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_FALSE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, false); VerifyGetDexOptNeededDefault(&oat_file_assistant, CompilerFilter::kSpeed, @@ -1334,7 +1342,7 @@ TEST_P(OatFileAssistantTest, ResourceOnlyDex) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_FALSE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, false); } // Case: We have a DEX file, an ODEX file and an OAT file. @@ -1365,7 +1373,7 @@ TEST_P(OatFileAssistantTest, OdexOatOverlap) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -1409,7 +1417,7 @@ TEST_P(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasDexFiles()); + ExpectHasDexFiles(&oat_file_assistant, true); } // Case: We have a DEX file and up-to-date OAT file for it. @@ -1591,7 +1599,8 @@ TEST_P(OatFileAssistantTest, ShortDexLocation) { /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_FALSE(oat_file_assistant.HasDexFiles()); + std::string error_msg_ignored; + EXPECT_FALSE(oat_file_assistant.HasDexFiles(&error_msg_ignored).has_value()); } // Case: Non-standard extension for dex file. @@ -2148,6 +2157,53 @@ TEST_P(OatFileAssistantTest, DmUpToDate) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify)); } +// Case: We have an ODEX file, but the DEX file is gone. +// Expect: No dexopt is needed, as there's nothing we can do. +// TODO(jiakaiz): Fix this. The result should be the same as `NoDexNoOat`. +TEST_P(OatFileAssistantTest, OdexNoDex) { + std::string dex_location = GetScratchDir() + "/OdexNoDex.jar"; + std::string odex_location = GetOdexDir() + "/OdexNoDex.oat"; + + Copy(GetDexSrc1(), dex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(dex_location.c_str())); + + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); + + VerifyGetDexOptNeededDefault(&oat_file_assistant, + CompilerFilter::kSpeed, + /*expected_dexopt_needed=*/true, + /*expected_is_vdex_usable=*/true, + /*expected_location=*/OatFileAssistant::kLocationOdex, + /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter); +} + +// Case: We have a VDEX file, but the DEX file is gone. +// Expect: No dexopt is needed, as there's nothing we can do. +// TODO(jiakaiz): Fix this. The result should be the same as `NoDexNoOat`. +TEST_P(OatFileAssistantTest, VdexNoDex) { + std::string dex_location = GetScratchDir() + "/VdexNoDex.jar"; + std::string odex_location = GetOdexDir() + "/VdexNoDex.oat"; + + Copy(GetDexSrc1(), dex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(odex_location.c_str())); + ASSERT_EQ(0, unlink(dex_location.c_str())); + + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); + + VerifyGetDexOptNeededDefault(&oat_file_assistant, + CompilerFilter::kSpeed, + /*expected_dexopt_needed=*/true, + /*expected_is_vdex_usable=*/true, + /*expected_location=*/OatFileAssistant::kLocationOdex, + /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter); +} + // Test that GetLocation of a dex file is the same whether the dex // filed is backed by an oat file or not. TEST_F(OatFileAssistantBaseTest, GetDexLocation) { |