Merge "ARM: VIXL32: Improve codegen on CBZ/CBNZ"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4fce235..9902628 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -116,10 +116,14 @@
ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
$(HOST_CORE_IMAGE_optimizing_pic_64) \
$(HOST_CORE_IMAGE_optimizing_pic_32) \
+ $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
+ $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
$(HOST_OUT_EXECUTABLES)/patchoatd
ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_optimizing_pic_64) \
$(TARGET_CORE_IMAGE_optimizing_pic_32) \
+ $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
+ $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
$(TARGET_OUT_EXECUTABLES)/patchoatd
ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e155e10..ad75ec4 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -45,6 +45,7 @@
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
#include "dex/dex_to_dex_compiler.h"
+#include "dex/dex_to_dex_decompiler.h"
#include "dex/verification_results.h"
#include "dex/verified_method.h"
#include "driver/compiler_options.h"
@@ -72,6 +73,7 @@
#include "transaction.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "utils/swap_space.h"
+#include "vdex_file.h"
#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
#include "verifier/verifier_log_mode.h"
@@ -394,7 +396,6 @@
void CompilerDriver::CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings) {
DCHECK(!Runtime::Current()->IsStarted());
@@ -406,7 +407,7 @@
// 2) Resolve all classes
// 3) Attempt to verify all classes
// 4) Attempt to initialize image classes, and trivially initialized classes
- PreCompile(class_loader, dex_files, verifier_deps, timings);
+ PreCompile(class_loader, dex_files, timings);
if (GetCompilerOptions().IsBootImage()) {
// We don't need to setup the intrinsics for non boot image compilation, as
// those compilations will pick up a boot image that have the ArtMethod already
@@ -433,6 +434,72 @@
FreeThreadPools();
}
+// In-place unquicken the given `dex_files` based on `quickening_info`.
+static void Unquicken(const std::vector<const DexFile*>& dex_files,
+ const ArrayRef<const uint8_t>& quickening_info) {
+ const uint8_t* quickening_info_ptr = quickening_info.data();
+ const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
+ for (const DexFile* dex_file : dex_files) {
+ for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data == nullptr) {
+ continue;
+ }
+ ClassDataItemIterator it(*dex_file, class_data);
+ // Skip fields
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
+
+ // Unquicken each method.
+ while (it.HasNextDirectMethod()) {
+ const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ if (code_item != nullptr) {
+ uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+ quickening_info_ptr += sizeof(uint32_t);
+ optimizer::ArtDecompileDEX(
+ *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+ quickening_info_ptr += quickening_size;
+ }
+ it.Next();
+ }
+
+ while (it.HasNextVirtualMethod()) {
+ const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ if (code_item != nullptr) {
+ uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+ quickening_info_ptr += sizeof(uint32_t);
+ optimizer::ArtDecompileDEX(
+ *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+ quickening_info_ptr += quickening_size;
+ }
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+ }
+ DCHECK_EQ(quickening_info_ptr, quickening_info_end) << "Failed to use all quickening info";
+}
+
+void CompilerDriver::CompileAll(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ VdexFile* vdex_file,
+ TimingLogger* timings) {
+ if (vdex_file != nullptr) {
+ // TODO: we unquicken unconditionnally, as we don't know
+ // if the boot image has changed. How exactly we'll know is under
+ // experimentation.
+ Unquicken(dex_files, vdex_file->GetQuickeningInfo());
+ Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
+ new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
+ }
+ CompileAll(class_loader, dex_files, timings);
+}
+
static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
Thread* self, const CompilerDriver& driver, Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file, const DexFile::ClassDef& class_def)
@@ -673,7 +740,7 @@
InitializeThreadPools();
- PreCompile(jclass_loader, dex_files, /* verifier_deps */ nullptr, timings);
+ PreCompile(jclass_loader, dex_files, timings);
// Can we run DEX-to-DEX compiler on this class ?
optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
@@ -870,7 +937,6 @@
void CompilerDriver::PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings) {
CheckThreadPools();
@@ -904,7 +970,7 @@
VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false);
}
- Verify(class_loader, dex_files, verifier_deps, timings);
+ Verify(class_loader, dex_files, timings);
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -1936,8 +2002,10 @@
void CompilerDriver::Verify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings) {
+ verifier::VerifierDeps* verifier_deps =
+ Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
+ // If there is an existing `VerifierDeps`, try to use it for fast verification.
if (verifier_deps != nullptr) {
TimingLogger::ScopedTiming t("Fast Verify", timings);
ScopedObjectAccess soa(Thread::Current());
@@ -1975,16 +2043,15 @@
}
}
- // If there is no passed `verifier_deps` (because of non-existing vdex), or
- // the passed `verifier_deps` is not valid anymore, create a new one for
+ // If there is no existing `verifier_deps` (because of non-existing vdex), or
+ // the existing `verifier_deps` is not valid anymore, create a new one for
// non boot image compilation. The verifier will need it to record the new dependencies.
// Then dex2oat can update the vdex file with these new dependencies.
if (!GetCompilerOptions().IsBootImage()) {
// Create the main VerifierDeps, and set it to this thread.
- Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
- new verifier::VerifierDeps(dex_files));
- Thread::Current()->SetVerifierDeps(
- Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps());
+ verifier_deps = new verifier::VerifierDeps(dex_files);
+ Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps);
+ Thread::Current()->SetVerifierDeps(verifier_deps);
// Create per-thread VerifierDeps to avoid contention on the main one.
// We will merge them after verification.
for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
@@ -2005,13 +2072,11 @@
}
if (!GetCompilerOptions().IsBootImage()) {
- verifier::VerifierDeps* main_deps =
- Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
// Merge all VerifierDeps into the main one.
for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
worker->GetThread()->SetVerifierDeps(nullptr);
- main_deps->MergeWith(*thread_deps, dex_files);;
+ verifier_deps->MergeWith(*thread_deps, dex_files);;
delete thread_deps;
}
Thread::Current()->SetVerifierDeps(nullptr);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index c7719fb..7418b00 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -51,7 +51,6 @@
namespace verifier {
class MethodVerifier;
-class VerifierDeps;
class VerifierDepsTest;
} // namespace verifier
@@ -69,6 +68,7 @@
using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>;
template<class T> class Handle;
class TimingLogger;
+class VdexFile;
class VerificationResults;
class VerifiedMethod;
@@ -119,7 +119,12 @@
void CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
+ TimingLogger* timings)
+ REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
+
+ void CompileAll(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ VdexFile* vdex_file,
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
@@ -420,7 +425,6 @@
private:
void PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
@@ -443,7 +447,6 @@
void Verify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings);
void VerifyDexFile(jobject class_loader,
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 0f41cbf..a706697 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -838,64 +838,19 @@
return true;
}
-class ImageWriter::PruneClassesVisitor : public ClassVisitor {
+class ImageWriter::NonImageClassesVisitor : public ClassVisitor {
public:
- PruneClassesVisitor(ImageWriter* image_writer, ObjPtr<mirror::ClassLoader> class_loader)
- : image_writer_(image_writer),
- class_loader_(class_loader),
- classes_to_prune_(),
- defined_class_count_(0u) { }
+ explicit NonImageClassesVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
- bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool operator()(ObjPtr<Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
if (!image_writer_->KeepClass(klass.Ptr())) {
classes_to_prune_.insert(klass.Ptr());
- if (klass->GetClassLoader() == class_loader_) {
- ++defined_class_count_;
- }
}
return true;
}
- size_t Prune() REQUIRES_SHARED(Locks::mutator_lock_) {
- ClassTable* class_table =
- Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader_);
- for (mirror::Class* klass : classes_to_prune_) {
- std::string storage;
- const char* descriptor = klass->GetDescriptor(&storage);
- bool result = class_table->Remove(descriptor);
- DCHECK(result);
- }
- return defined_class_count_;
- }
-
- private:
- ImageWriter* const image_writer_;
- const ObjPtr<mirror::ClassLoader> class_loader_;
std::unordered_set<mirror::Class*> classes_to_prune_;
- size_t defined_class_count_;
-};
-
-class ImageWriter::PruneClassLoaderClassesVisitor : public ClassLoaderVisitor {
- public:
- explicit PruneClassLoaderClassesVisitor(ImageWriter* image_writer)
- : image_writer_(image_writer), removed_class_count_(0) {}
-
- virtual void Visit(ObjPtr<mirror::ClassLoader> class_loader) OVERRIDE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- PruneClassesVisitor classes_visitor(image_writer_, class_loader);
- ClassTable* class_table =
- Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader);
- class_table->Visit(classes_visitor);
- removed_class_count_ += classes_visitor.Prune();
- }
-
- size_t GetRemovedClassCount() const {
- return removed_class_count_;
- }
-
- private:
ImageWriter* const image_writer_;
- size_t removed_class_count_;
};
void ImageWriter::PruneNonImageClasses() {
@@ -907,13 +862,21 @@
// path dex caches.
class_linker->ClearClassTableStrongRoots();
+ // Make a list of classes we would like to prune.
+ NonImageClassesVisitor visitor(this);
+ class_linker->VisitClasses(&visitor);
+
// Remove the undesired classes from the class roots.
- {
- ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
- PruneClassLoaderClassesVisitor class_loader_visitor(this);
- class_loader_visitor.Visit(nullptr); // Visit boot class loader.
- class_linker->VisitClassLoaders(&class_loader_visitor);
- VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes";
+ VLOG(compiler) << "Pruning " << visitor.classes_to_prune_.size() << " classes";
+ for (mirror::Class* klass : visitor.classes_to_prune_) {
+ std::string temp;
+ const char* name = klass->GetDescriptor(&temp);
+ VLOG(compiler) << "Pruning class " << name;
+ if (!compile_app_image_) {
+ DCHECK(IsBootClassLoaderClass(klass));
+ }
+ bool result = class_linker->RemoveClass(name, klass->GetClassLoader());
+ DCHECK(result);
}
// Clear references to removed classes from the DexCaches.
@@ -1544,10 +1507,8 @@
}
// Calculate the size of the class table.
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
- CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
- mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr;
- DCHECK_EQ(image_info.class_table_->NumZygoteClasses(class_loader), 0u);
- if (image_info.class_table_->NumNonZygoteClasses(class_loader) != 0u) {
+ DCHECK_EQ(image_info.class_table_->NumZygoteClasses(), 0u);
+ if (image_info.class_table_->NumNonZygoteClasses() != 0u) {
image_info.class_table_bytes_ += image_info.class_table_->WriteToMemory(nullptr);
}
}
@@ -1892,10 +1853,8 @@
// above comment for intern tables.
ClassTable temp_class_table;
temp_class_table.ReadFromMemory(class_table_memory_ptr);
- CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
- mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr;
- CHECK_EQ(temp_class_table.NumZygoteClasses(class_loader),
- table->NumNonZygoteClasses(class_loader) + table->NumZygoteClasses(class_loader));
+ CHECK_EQ(temp_class_table.NumZygoteClasses(), table->NumNonZygoteClasses() +
+ table->NumZygoteClasses());
BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(&root_visitor,
RootInfo(kRootUnknown));
temp_class_table.VisitRoots(buffered_visitor);
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index ad6ffd8..24fad46 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -588,8 +588,7 @@
class FixupVisitor;
class GetRootsVisitor;
class NativeLocationVisitor;
- class PruneClassesVisitor;
- class PruneClassLoaderClassesVisitor;
+ class NonImageClassesVisitor;
class VisitReferencesVisitor;
DISALLOW_COPY_AND_ASSIGN(ImageWriter);
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index eed9d11..153aff4 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -403,6 +403,35 @@
return true;
}
+// Add dex file source(s) from a vdex file specified by a file handle.
+bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table) {
+ DCHECK(write_state_ == WriteState::kAddingDexFileSources);
+ const uint8_t* current_dex_data = nullptr;
+ for (size_t i = 0; ; ++i) {
+ current_dex_data = vdex_file.GetNextDexFileData(current_dex_data);
+ if (current_dex_data == nullptr) {
+ break;
+ }
+ if (!DexFile::IsMagicValid(current_dex_data)) {
+ LOG(ERROR) << "Invalid magic in vdex file created from " << location;
+ return false;
+ }
+ // We used `zipped_dex_file_locations_` to keep the strings in memory.
+ zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location));
+ const char* full_location = zipped_dex_file_locations_.back().c_str();
+ oat_dex_files_.emplace_back(full_location,
+ DexFileSource(current_dex_data),
+ create_type_lookup_table);
+ }
+ if (oat_dex_files_.empty()) {
+ LOG(ERROR) << "No dex files in vdex file created from " << location;
+ return false;
+ }
+ return true;
+}
+
// Add dex file source from raw memory.
bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data,
const char* location,
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index f9671d7..0dcf79e 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -42,6 +42,7 @@
class OutputStream;
class TimingLogger;
class TypeLookupTable;
+class VdexFile;
class ZipEntry;
namespace debug {
@@ -116,7 +117,8 @@
// To produce a valid oat file, the user must first add sources with any combination of
// - AddDexFileSource(),
// - AddZippedDexFilesSource(),
- // - AddRawDexFileSource().
+ // - AddRawDexFileSource(),
+ // - AddVdexDexFilesSource().
// Then the user must call in order
// - WriteAndOpenDexFiles()
// - Initialize()
@@ -145,6 +147,11 @@
const char* location,
uint32_t location_checksum,
CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
+ // Add dex file source(s) from a vdex file.
+ bool AddVdexDexFilesSource(
+ const VdexFile& vdex_file,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
dchecked_vector<const char*> GetSourceLocations() const;
// Write raw dex files to the vdex file, mmap the file and open the dex files from it.
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index dcf3619..525a2ee 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -83,10 +83,13 @@
// The compiler driver handles the verifier deps in the callbacks, so
// remove what this class did for unit testing.
verifier_deps_.reset(nullptr);
- callbacks_->SetVerifierDeps(nullptr);
- compiler_driver_->Verify(class_loader_, dex_files_, deps, &timings);
+ callbacks_->SetVerifierDeps(deps);
+ compiler_driver_->Verify(class_loader_, dex_files_, &timings);
// The compiler driver may have updated the VerifierDeps in the callback object.
- verifier_deps_.reset(callbacks_->GetVerifierDeps());
+ if (callbacks_->GetVerifierDeps() != deps) {
+ verifier_deps_.reset(callbacks_->GetVerifierDeps());
+ }
+ callbacks_->SetVerifierDeps(nullptr);
}
void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 81baa80..9e6032f 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -78,6 +78,7 @@
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
#include "utils.h"
+#include "vdex_file.h"
#include "verifier/verifier_deps.h"
#include "well_known_classes.h"
#include "zip_archive.h"
@@ -520,6 +521,7 @@
oat_fd_(-1),
input_vdex_fd_(-1),
output_vdex_fd_(-1),
+ input_vdex_file_(nullptr),
zip_fd_(-1),
image_base_(0U),
image_classes_zip_filename_(nullptr),
@@ -710,6 +712,10 @@
Usage("Output must be supplied with either --oat-file or --oat-fd");
}
+ if (input_vdex_fd_ != -1 && !input_vdex_.empty()) {
+ Usage("Can't have both --input-vdex-fd and --input-vdex");
+ }
+
if (!oat_filenames_.empty() && oat_fd_ != -1) {
Usage("--oat-file should not be used with --oat-fd");
}
@@ -1123,6 +1129,8 @@
zip_location_ = option.substr(strlen("--zip-location=")).data();
} else if (option.starts_with("--input-vdex-fd=")) {
ParseInputVdexFd(option);
+ } else if (option.starts_with("--input-vdex=")) {
+ input_vdex_ = option.substr(strlen("--input-vdex=")).data();
} else if (option.starts_with("--output-vdex-fd=")) {
ParseOutputVdexFd(option);
} else if (option.starts_with("--oat-file=")) {
@@ -1266,6 +1274,17 @@
return false;
}
oat_files_.push_back(std::move(oat_file));
+ DCHECK_EQ(input_vdex_fd_, -1);
+ if (!input_vdex_.empty()) {
+ std::string error_msg;
+ input_vdex_file_.reset(VdexFile::Open(input_vdex_,
+ /* writable */ false,
+ /* low_4gb */ false,
+ &error_msg));
+ if (input_vdex_file_ != nullptr && !input_vdex_file_->IsValid()) {
+ input_vdex_file_.reset(nullptr);
+ }
+ }
DCHECK_EQ(output_vdex_fd_, -1);
std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
@@ -1293,6 +1312,31 @@
}
oat_files_.push_back(std::move(oat_file));
+ DCHECK_NE(input_vdex_fd_, output_vdex_fd_);
+ if (input_vdex_fd_ != -1) {
+ struct stat s;
+ int rc = TEMP_FAILURE_RETRY(fstat(input_vdex_fd_, &s));
+ if (rc == -1) {
+ PLOG(WARNING) << "Failed getting length of vdex file";
+ } else {
+ std::string error_msg;
+ input_vdex_file_.reset(VdexFile::Open(input_vdex_fd_,
+ s.st_size,
+ "vdex",
+ /* writable */ false,
+ /* low_4gb */ false,
+ &error_msg));
+ // If there's any problem with the passed vdex, just warn and proceed
+ // without it.
+ if (input_vdex_file_ == nullptr) {
+ PLOG(WARNING) << "Failed opening vdex file " << error_msg;
+ } else if (!input_vdex_file_->IsValid()) {
+ PLOG(WARNING) << "Existing vdex file is invalid";
+ input_vdex_file_.reset(nullptr);
+ }
+ }
+ }
+
DCHECK_NE(output_vdex_fd_, -1);
std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex");
std::unique_ptr<File> vdex_file(new File(output_vdex_fd_, vdex_location, /* check_usage */ true));
@@ -1388,7 +1432,6 @@
// boot class path.
bool Setup() {
TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
- art::MemMap::Init(); // For ZipEntry::ExtractToMemMap.
if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
return false;
@@ -1480,8 +1523,11 @@
// Unzip or copy dex files straight to the oat file.
std::unique_ptr<MemMap> opened_dex_files_map;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
- // Dexlayout verifies the dex file, so disable dex file verification in that case.
- bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile;
+ // No need to verify the dex file for:
+ // 1) dexlayout, which already verified it
+ // 2) when we have a vdex file, which means it was already verified.
+ bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile &&
+ (input_vdex_file_ == nullptr);
if (!oat_writers_[i]->WriteAndOpenDexFiles(
kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(),
rodata_.back(),
@@ -1668,7 +1714,7 @@
swap_fd_,
profile_compilation_info_.get()));
driver_->SetDexFilesForOatFile(dex_files_);
- driver_->CompileAll(class_loader_, dex_files_, /* verifier_deps */ nullptr, timings_);
+ driver_->CompileAll(class_loader_, dex_files_, input_vdex_file_.get(), timings_);
}
// Notes on the interleaving of creating the images and oat files to
@@ -2241,7 +2287,14 @@
bool AddDexFileSources() {
TimingLogger::ScopedTiming t2("AddDexFileSources", timings_);
- if (zip_fd_ != -1) {
+ if (input_vdex_file_ != nullptr) {
+ DCHECK_EQ(oat_writers_.size(), 1u);
+ const std::string& name = zip_location_.empty() ? dex_locations_[0] : zip_location_;
+ DCHECK(!name.empty());
+ if (!oat_writers_[0]->AddVdexDexFilesSource(*input_vdex_file_.get(), name.c_str())) {
+ return false;
+ }
+ } else if (zip_fd_ != -1) {
DCHECK_EQ(oat_writers_.size(), 1u);
if (!oat_writers_[0]->AddZippedDexFilesSource(File(zip_fd_, /* check_usage */ false),
zip_location_.c_str())) {
@@ -2599,6 +2652,8 @@
int oat_fd_;
int input_vdex_fd_;
int output_vdex_fd_;
+ std::string input_vdex_;
+ std::unique_ptr<VdexFile> input_vdex_file_;
std::vector<const char*> dex_filenames_;
std::vector<const char*> dex_locations_;
int zip_fd_;
@@ -2795,6 +2850,8 @@
}
}
+ art::MemMap::Init(); // For ZipEntry::ExtractToMemMap, and vdex.
+
// Check early that the result of compilation can be written
if (!dex2oat->OpenFile()) {
return EXIT_FAILURE;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 216ec9e..f3aba97 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1949,36 +1949,13 @@
void Visit(ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
ClassTable* const class_table = class_loader->GetClassTable();
- if (!done_ && class_table != nullptr) {
- DefiningClassLoaderFilterVisitor visitor(class_loader, visitor_);
- if (!class_table->Visit(visitor)) {
- // If the visitor ClassTable returns false it means that we don't need to continue.
- done_ = true;
- }
+ if (!done_ && class_table != nullptr && !class_table->Visit(*visitor_)) {
+ // If the visitor ClassTable returns false it means that we don't need to continue.
+ done_ = true;
}
}
private:
- // Class visitor that limits the class visits from a ClassTable to the classes with
- // the provided defining class loader. This filter is used to avoid multiple visits
- // of the same class which can be recorded for multiple initiating class loaders.
- class DefiningClassLoaderFilterVisitor : public ClassVisitor {
- public:
- DefiningClassLoaderFilterVisitor(ObjPtr<mirror::ClassLoader> defining_class_loader,
- ClassVisitor* visitor)
- : defining_class_loader_(defining_class_loader), visitor_(visitor) { }
-
- bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
- if (klass->GetClassLoader() != defining_class_loader_) {
- return true;
- }
- return (*visitor_)(klass);
- }
-
- ObjPtr<mirror::ClassLoader> const defining_class_loader_;
- ClassVisitor* const visitor_;
- };
-
ClassVisitor* const visitor_;
// If done is true then we don't need to do any more visiting.
bool done_;
@@ -2563,109 +2540,56 @@
}
} else {
ScopedObjectAccessUnchecked soa(self);
- ObjPtr<mirror::Class> result_ptr;
- bool descriptor_equals;
- bool known_hierarchy =
- FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr);
- if (result_ptr != nullptr) {
- // The chain was understood and we found the class. We still need to add the class to
- // the class table to protect from racy programs that can try and redefine the path list
- // which would change the Class<?> returned for subsequent evaluation of const-class.
- DCHECK(known_hierarchy);
- DCHECK(result_ptr->DescriptorEquals(descriptor));
- descriptor_equals = true;
- } else {
- // Either the chain wasn't understood or the class wasn't found.
- //
- // If the chain was understood but we did not find the class, let the Java-side
- // rediscover all this and throw the exception with the right stack trace. Note that
- // the Java-side could still succeed for racy programs if another thread is actively
- // modifying the class loader's path list.
+ ObjPtr<mirror::Class> cp_klass;
+ if (FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) {
+ // The chain was understood. So the value in cp_klass is either the class we were looking
+ // for, or not found.
+ if (cp_klass != nullptr) {
+ return cp_klass.Ptr();
+ }
+ // TODO: We handle the boot classpath loader in FindClassInBaseDexClassLoader. Try to unify
+ // this and the branch above. TODO: throw the right exception here.
- if (Runtime::Current()->IsAotCompiler()) {
- // Oops, compile-time, can't run actual class-loader code.
- ObjPtr<mirror::Throwable> pre_allocated =
- Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
- self->SetException(pre_allocated);
- return nullptr;
- }
-
- ScopedLocalRef<jobject> class_loader_object(
- soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
- std::string class_name_string(DescriptorToDot(descriptor));
- ScopedLocalRef<jobject> result(soa.Env(), nullptr);
- {
- ScopedThreadStateChange tsc(self, kNative);
- ScopedLocalRef<jobject> class_name_object(
- soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str()));
- if (class_name_object.get() == nullptr) {
- DCHECK(self->IsExceptionPending()); // OOME.
- return nullptr;
- }
- CHECK(class_loader_object.get() != nullptr);
- result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
- WellKnownClasses::java_lang_ClassLoader_loadClass,
- class_name_object.get()));
- }
- if (self->IsExceptionPending()) {
- // If the ClassLoader threw, pass that exception up.
- // However, to comply with the RI behavior, first check if another thread succeeded.
- result_ptr = LookupClass(self, descriptor, hash, class_loader.Get());
- if (result_ptr != nullptr && !result_ptr->IsErroneous()) {
- self->ClearException();
- return EnsureResolved(self, descriptor, result_ptr);
- }
- return nullptr;
- } else if (result.get() == nullptr) {
- // broken loader - throw NPE to be compatible with Dalvik
- ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
- class_name_string.c_str()).c_str());
- return nullptr;
- }
- result_ptr = soa.Decode<mirror::Class>(result.get());
- // Check the name of the returned class.
- descriptor_equals = result_ptr->DescriptorEquals(descriptor);
+ // We'll let the Java-side rediscover all this and throw the exception with the right stack
+ // trace.
}
- // Try to insert the class to the class table, checking for mismatch.
- ObjPtr<mirror::Class> old;
- {
- ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
- ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get());
- old = class_table->Lookup(descriptor, hash);
- if (old == nullptr) {
- old = result_ptr; // For the comparison below, after releasing the lock.
- if (descriptor_equals) {
- class_table->InsertWithHash(result_ptr.Ptr(), hash);
- Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
- } // else throw below, after releasing the lock.
- }
- }
- if (UNLIKELY(old != result_ptr)) {
- // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel
- // capable class loaders. (All class loaders are considered parallel capable on Android.)
- mirror::Class* loader_class = class_loader->GetClass();
- const char* loader_class_name =
- loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex());
- LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name)
- << " is not well-behaved; it returned a different Class for racing loadClass(\""
- << DescriptorToDot(descriptor) << "\").";
- return EnsureResolved(self, descriptor, old);
- }
- if (UNLIKELY(!descriptor_equals)) {
- std::string result_storage;
- const char* result_name = result_ptr->GetDescriptor(&result_storage);
- std::string loader_storage;
- const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage);
- ThrowNoClassDefFoundError(
- "Initiating class loader of type %s returned class %s instead of %s.",
- DescriptorToDot(loader_class_name).c_str(),
- DescriptorToDot(result_name).c_str(),
- DescriptorToDot(descriptor).c_str());
+ if (Runtime::Current()->IsAotCompiler()) {
+ // Oops, compile-time, can't run actual class-loader code.
+ ObjPtr<mirror::Throwable> pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+ self->SetException(pre_allocated);
return nullptr;
}
- // success, return mirror::Class*
- return result_ptr.Ptr();
+
+ ScopedLocalRef<jobject> class_loader_object(soa.Env(),
+ soa.AddLocalReference<jobject>(class_loader.Get()));
+ std::string class_name_string(DescriptorToDot(descriptor));
+ ScopedLocalRef<jobject> result(soa.Env(), nullptr);
+ {
+ ScopedThreadStateChange tsc(self, kNative);
+ ScopedLocalRef<jobject> class_name_object(soa.Env(),
+ soa.Env()->NewStringUTF(class_name_string.c_str()));
+ if (class_name_object.get() == nullptr) {
+ DCHECK(self->IsExceptionPending()); // OOME.
+ return nullptr;
+ }
+ CHECK(class_loader_object.get() != nullptr);
+ result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
+ WellKnownClasses::java_lang_ClassLoader_loadClass,
+ class_name_object.get()));
+ }
+ if (self->IsExceptionPending()) {
+ // If the ClassLoader threw, pass that exception up.
+ return nullptr;
+ } else if (result.get() == nullptr) {
+ // broken loader - throw NPE to be compatible with Dalvik
+ ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
+ class_name_string.c_str()).c_str());
+ return nullptr;
+ } else {
+ // success, return mirror::Class*
+ return soa.Decode<mirror::Class>(result.get()).Ptr();
+ }
}
UNREACHABLE();
}
@@ -3746,6 +3670,12 @@
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass);
}
+bool ClassLinker::RemoveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> class_loader) {
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ ClassTable* const class_table = ClassTableForClassLoader(class_loader);
+ return class_table != nullptr && class_table->Remove(descriptor);
+}
+
mirror::Class* ClassLinker::LookupClass(Thread* self,
const char* descriptor,
size_t hash,
@@ -3796,8 +3726,7 @@
REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
ClassTable* const class_table = class_loader->GetClassTable();
ObjPtr<mirror::Class> klass = class_table->Lookup(descriptor_, hash_);
- // Add `klass` only if `class_loader` is its defining (not just initiating) class loader.
- if (klass != nullptr && klass->GetClassLoader() == class_loader) {
+ if (klass != nullptr) {
result_->push_back(klass);
}
}
@@ -3816,7 +3745,6 @@
const size_t hash = ComputeModifiedUtf8Hash(descriptor);
ObjPtr<mirror::Class> klass = boot_class_table_.Lookup(descriptor, hash);
if (klass != nullptr) {
- DCHECK(klass->GetClassLoader() == nullptr);
result.push_back(klass);
}
LookupClassesVisitor visitor(descriptor, hash, &result);
@@ -8124,8 +8052,8 @@
REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
ClassTable* const class_table = class_loader->GetClassTable();
if (class_table != nullptr) {
- num_zygote_classes += class_table->NumZygoteClasses(class_loader);
- num_non_zygote_classes += class_table->NumNonZygoteClasses(class_loader);
+ num_zygote_classes += class_table->NumZygoteClasses();
+ num_non_zygote_classes += class_table->NumNonZygoteClasses();
}
}
@@ -8136,13 +8064,13 @@
size_t ClassLinker::NumZygoteClasses() const {
CountClassesVisitor visitor;
VisitClassLoaders(&visitor);
- return visitor.num_zygote_classes + boot_class_table_.NumZygoteClasses(nullptr);
+ return visitor.num_zygote_classes + boot_class_table_.NumZygoteClasses();
}
size_t ClassLinker::NumNonZygoteClasses() const {
CountClassesVisitor visitor;
VisitClassLoaders(&visitor);
- return visitor.num_non_zygote_classes + boot_class_table_.NumNonZygoteClasses(nullptr);
+ return visitor.num_non_zygote_classes + boot_class_table_.NumNonZygoteClasses();
}
size_t ClassLinker::NumLoadedClasses() {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 88028ea..9563448 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -218,6 +218,12 @@
mirror::Class* FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_);
+ // General class unloading is not supported, this is used to prune
+ // unwanted classes during image writing.
+ bool RemoveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES(!Locks::classlinker_classes_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
void DumpAllClasses(int flags)
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index 81cdce5..0fcce6b 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -83,29 +83,18 @@
#pragma clang diagnostic pop // http://b/31104323
-size_t ClassTable::CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader,
- const ClassSet& set) const {
- size_t count = 0;
- for (const GcRoot<mirror::Class>& klass : set) {
- if (klass.Read()->GetClassLoader() == defining_loader) {
- ++count;
- }
- }
- return count;
-}
-
-size_t ClassTable::NumZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const {
+size_t ClassTable::NumZygoteClasses() const {
ReaderMutexLock mu(Thread::Current(), lock_);
size_t sum = 0;
for (size_t i = 0; i < classes_.size() - 1; ++i) {
- sum += CountDefiningLoaderClasses(defining_loader, classes_[i]);
+ sum += classes_[i].Size();
}
return sum;
}
-size_t ClassTable::NumNonZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const {
+size_t ClassTable::NumNonZygoteClasses() const {
ReaderMutexLock mu(Thread::Current(), lock_);
- return CountDefiningLoaderClasses(defining_loader, classes_.back());
+ return classes_.back().Size();
}
mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) {
@@ -113,7 +102,7 @@
for (ClassSet& class_set : classes_) {
auto it = class_set.FindWithHash(descriptor, hash);
if (it != class_set.end()) {
- return it->Read();
+ return it->Read();
}
}
return nullptr;
@@ -153,6 +142,7 @@
bool ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
const GcRoot<mirror::Class>& b) const {
+ DCHECK_EQ(a.Read()->GetClassLoader(), b.Read()->GetClassLoader());
std::string temp;
return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp));
}
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 92634a4..558c144 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -84,14 +84,10 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns the number of classes in previous snapshots.
- size_t NumZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const
- REQUIRES(!lock_)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ size_t NumZygoteClasses() const REQUIRES(!lock_);
// Returns all off the classes in the lastest snapshot.
- size_t NumNonZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const
- REQUIRES(!lock_)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ size_t NumNonZygoteClasses() const REQUIRES(!lock_);
// Update a class in the table with the new class. Returns the existing class which was replaced.
mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash)
@@ -177,11 +173,6 @@
private:
void InsertWithoutLocks(ObjPtr<mirror::Class> klass) NO_THREAD_SAFETY_ANALYSIS;
- size_t CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader,
- const ClassSet& set) const
- REQUIRES(lock_)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Return true if we inserted the oat file, false if it already exists.
bool InsertOatFileLocked(const OatFile* oat_file)
REQUIRES(lock_)
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index d717ec0..b0c4597 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -136,7 +136,9 @@
+ "/core.art";
}
- bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const {
+ bool GetCachedImageFile(const std::string& image_location,
+ /*out*/std::string* image,
+ /*out*/std::string* error_msg) const {
std::string cache;
bool have_android_data;
bool dalvik_cache_exists;
@@ -151,7 +153,14 @@
*error_msg = "Failed to create dalvik cache";
return false;
}
- return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
+ return GetDalvikCacheFilename(image_location.c_str(), cache.c_str(), image, error_msg);
+ }
+
+ // Returns the path to an image location whose contents differ from the
+ // image at GetImageLocation(). This is used for testing mismatched
+ // image checksums in the oat_file_assistant_tests.
+ std::string GetImageLocation2() const {
+ return GetImageDirectory() + "/core-npic.art";
}
std::string GetDexSrc1() const {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 05f74d6..d098ee2 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -922,7 +922,7 @@
ThrowWrongMethodTypeException(check_type.Ptr(), callsite_type.Get());
return false;
}
- } else {
+ } else if (!IsInvokeTransform(handle_kind)) {
if (UNLIKELY(!IsCallerTransformer(callsite_type) &&
!callsite_type->IsConvertible(check_type.Ptr()))) {
ThrowWrongMethodTypeException(check_type.Ptr(), callsite_type.Get());
@@ -990,33 +990,37 @@
CHECK(called_method != nullptr);
}
- bool call_success;
- if (handle_kind == kInvokeTransform) {
- call_success = DoCallTransform<is_range>(called_method,
- callsite_type,
- handle_type,
- self,
- shadow_frame,
- method_handle /* receiver */,
- result,
- arg,
- first_src_reg);
+ if (IsInvokeTransform(handle_kind)) {
+ // There are two cases here - method handles representing regular
+ // transforms and those representing call site transforms. Method
+ // handles for call site transforms adapt their MethodType to match
+ // the call site. For these, the |callee_type| is the same as the
+ // |callsite_type|. The VarargsCollector is such a tranform, its
+ // method type depends on the call site, ie. x(a) or x(a, b), or
+ // x(a, b, c). The VarargsCollector invokes a variable arity method
+ // with the arity arguments in an array.
+ Handle<mirror::MethodType> callee_type =
+ (handle_kind == kInvokeCallSiteTransform) ? callsite_type : handle_type;
+ return DoCallTransform<is_range>(called_method,
+ callsite_type,
+ callee_type,
+ self,
+ shadow_frame,
+ method_handle /* receiver */,
+ result,
+ arg,
+ first_src_reg);
} else {
- call_success = DoCallPolymorphic<is_range>(called_method,
- callsite_type,
- handle_type,
- self,
- shadow_frame,
- result,
- arg,
- first_src_reg,
- handle_kind);
+ return DoCallPolymorphic<is_range>(called_method,
+ callsite_type,
+ handle_type,
+ self,
+ shadow_frame,
+ result,
+ arg,
+ first_src_reg,
+ handle_kind);
}
- if (LIKELY(call_success && ConvertReturnValue(callsite_type, handle_type, result))) {
- return true;
- }
- DCHECK(self->IsExceptionPending());
- return false;
} else {
DCHECK(!is_range);
ArtField* field = method_handle->GetTargetField();
@@ -1097,7 +1101,6 @@
return num_ins;
}
-
inline void PerformCall(Thread* self,
const DexFile::CodeItem* code_item,
ArtMethod* caller_method,
@@ -1251,18 +1254,31 @@
}
PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
+ if (self->IsExceptionPending()) {
+ return false;
+ }
// If the caller of this signature polymorphic method was a transformer,
// we need to copy the result back out to the emulated stack frame.
- if (is_caller_transformer && !self->IsExceptionPending()) {
- ObjPtr<mirror::EmulatedStackFrame> emulated_stack_frame(
- reinterpret_cast<mirror::EmulatedStackFrame*>(
- shadow_frame.GetVRegReference(first_src_reg)));
+ if (is_caller_transformer) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::EmulatedStackFrame> emulated_stack_frame(
+ hs.NewHandle(reinterpret_cast<mirror::EmulatedStackFrame*>(
+ shadow_frame.GetVRegReference(first_src_reg))));
+ Handle<mirror::MethodType> emulated_stack_type(hs.NewHandle(emulated_stack_frame->GetType()));
+ JValue local_result;
+ local_result.SetJ(result->GetJ());
- emulated_stack_frame->SetReturnValue(self, *result);
+ if (ConvertReturnValue(emulated_stack_type, target_type, &local_result)) {
+ emulated_stack_frame->SetReturnValue(self, local_result);
+ return true;
+ } else {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ } else {
+ return ConvertReturnValue(callsite_type, target_type, result);
}
-
- return !self->IsExceptionPending();
}
template <bool is_range>
@@ -1329,14 +1345,14 @@
0 /* first dest reg */,
new_shadow_frame,
result);
+ if (self->IsExceptionPending()) {
+ return false;
+ }
// If the called transformer method we called has returned a value, then we
// need to copy it back to |result|.
- if (!self->IsExceptionPending()) {
- sf->GetReturnValue(self, result);
- }
-
- return !self->IsExceptionPending();
+ sf->GetReturnValue(self, result);
+ return ConvertReturnValue(callsite_type, callee_type, result);
}
template <bool is_range,
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 54c772a..d0a4902 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -46,12 +46,13 @@
kInvokeStatic,
kInvokeInterface,
kInvokeTransform,
+ kInvokeCallSiteTransform,
kInstanceGet,
kInstancePut,
kStaticGet,
kStaticPut,
kLastValidKind = kStaticPut,
- kLastInvokeKind = kInvokeTransform
+ kLastInvokeKind = kInvokeCallSiteTransform
};
// Whether the given method handle kind is some variant of an invoke.
@@ -59,6 +60,11 @@
return handle_kind <= kLastInvokeKind;
}
+// Whether the given method handle kind is some variant of a tranform.
+inline bool IsInvokeTransform(const MethodHandleKind handle_kind) {
+ return handle_kind == kInvokeTransform || handle_kind == kInvokeCallSiteTransform;
+}
+
// Returns true if there is a possible conversion from |from| to |to|
// for a MethodHandle parameter.
bool IsParameterTypeConvertible(ObjPtr<mirror::Class> from,
diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h
index 9fa06b7..d83a536 100644
--- a/runtime/mirror/emulated_stack_frame.h
+++ b/runtime/mirror/emulated_stack_frame.h
@@ -58,6 +58,10 @@
// Sets the return value slot of this emulated stack frame to |value|.
void SetReturnValue(Thread* self, const JValue& value) REQUIRES_SHARED(Locks::mutator_lock_);
+ mirror::MethodType* GetType() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, type_));
+ }
+
static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -67,10 +71,6 @@
return static_class_.Read();
}
- mirror::MethodType* GetType() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, type_));
- }
-
mirror::ObjectArray<mirror::Object>* GetReferences() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldObject<mirror::ObjectArray<mirror::Object>>(
OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, references_));
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index df0849a..1a77072 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -413,33 +413,7 @@
OatFileAssistant oat_file_assistant(filename.c_str(), target_instruction_set,
false /* load_executable */);
-
- std::ostringstream status;
- bool oat_file_exists = false;
- bool odex_file_exists = false;
- if (oat_file_assistant.OatFileExists()) {
- oat_file_exists = true;
- status << *oat_file_assistant.OatFileName() << " [compilation_filter=";
- status << CompilerFilter::NameOfFilter(oat_file_assistant.OatFileCompilerFilter());
- status << ", status=" << oat_file_assistant.OatFileStatus();
- }
-
- if (oat_file_assistant.OdexFileExists()) {
- odex_file_exists = true;
- if (oat_file_exists) {
- status << "] ";
- }
- status << *oat_file_assistant.OdexFileName() << " [compilation_filter=";
- status << CompilerFilter::NameOfFilter(oat_file_assistant.OdexFileCompilerFilter());
- status << ", status=" << oat_file_assistant.OdexFileStatus();
- }
-
- if (!oat_file_exists && !odex_file_exists) {
- status << "invalid[";
- }
-
- status << "]";
- return env->NewStringUTF(status.str().c_str());
+ return env->NewStringUTF(oat_file_assistant.GetStatusDump().c_str());
}
static jint DexFile_getDexOptNeeded(JNIEnv* env,
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 0679360..6197120 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -16,6 +16,8 @@
#include "oat_file_assistant.h"
+#include <sstream>
+
#include <sys/stat.h>
#include "base/logging.h"
#include "base/stringprintf.h"
@@ -34,15 +36,21 @@
std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
switch (status) {
- case OatFileAssistant::kOatOutOfDate:
- stream << "kOatOutOfDate";
+ case OatFileAssistant::kOatCannotOpen:
+ stream << "kOatCannotOpen";
+ break;
+ case OatFileAssistant::kOatDexOutOfDate:
+ stream << "kOatDexOutOfDate";
+ break;
+ case OatFileAssistant::kOatBootImageOutOfDate:
+ stream << "kOatBootImageOutOfDate";
+ break;
+ case OatFileAssistant::kOatRelocationOutOfDate:
+ stream << "kOatRelocationOutOfDate";
break;
case OatFileAssistant::kOatUpToDate:
stream << "kOatUpToDate";
break;
- case OatFileAssistant::kOatNeedsRelocation:
- stream << "kOatNeedsRelocation";
- break;
default:
UNREACHABLE();
}
@@ -60,7 +68,10 @@
const char* oat_location,
const InstructionSet isa,
bool load_executable)
- : isa_(isa), load_executable_(load_executable), odex_(this), oat_(this) {
+ : isa_(isa),
+ load_executable_(load_executable),
+ odex_(this, /*is_oat_location*/ false),
+ oat_(this, /*is_oat_location*/ true) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
dex_location_.assign(dex_location);
@@ -137,49 +148,12 @@
OatFileAssistant::DexOptNeeded
OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
- bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
-
- // See if the oat file is in good shape as is.
- bool oat_okay = oat_.CompilerFilterIsOkay(target, profile_changed);
- if (oat_okay) {
- if (compilation_desired) {
- if (oat_.IsUpToDate()) {
- return kNoDexOptNeeded;
- }
- } else {
- if (!oat_.IsOutOfDate()) {
- return kNoDexOptNeeded;
- }
- }
+ OatFileInfo& info = GetBestInfo();
+ DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed);
+ if (dexopt_needed == kPatchOatNeeded && info.IsOatLocation()) {
+ dexopt_needed = kSelfPatchOatNeeded;
}
-
- // See if the odex file is in good shape as is.
- bool odex_okay = odex_.CompilerFilterIsOkay(target, profile_changed);
- if (odex_okay) {
- if (compilation_desired) {
- if (odex_.IsUpToDate()) {
- return kNoDexOptNeeded;
- }
- } else {
- if (!odex_.IsOutOfDate()) {
- return kNoDexOptNeeded;
- }
- }
- }
-
- // See if we can get an up-to-date file by running patchoat.
- if (compilation_desired) {
- if (odex_okay && odex_.NeedsRelocation() && odex_.HasPatchInfo()) {
- return kPatchOatNeeded;
- }
-
- if (oat_okay && oat_.NeedsRelocation() && oat_.HasPatchInfo()) {
- return kSelfPatchOatNeeded;
- }
- }
-
- // We can only run dex2oat if there are original dex files.
- return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
+ return dexopt_needed;
}
// Figure out the currently specified compile filter option in the runtime.
@@ -205,7 +179,7 @@
}
bool OatFileAssistant::IsUpToDate() {
- return OatFileIsUpToDate() || OdexFileIsUpToDate();
+ return GetBestInfo().Status() == kOatUpToDate;
}
OatFileAssistant::ResultOfAttemptToUpdate
@@ -215,59 +189,58 @@
return kUpdateNotAttempted;
}
- switch (GetDexOptNeeded(target, profile_changed)) {
+ OatFileInfo& info = GetBestInfo();
+ switch (info.GetDexOptNeeded(target, profile_changed)) {
case kNoDexOptNeeded: return kUpdateSucceeded;
case kDex2OatNeeded: return GenerateOatFile(error_msg);
- case kPatchOatNeeded: return RelocateOatFile(odex_.Filename(), error_msg);
- case kSelfPatchOatNeeded: return RelocateOatFile(oat_.Filename(), error_msg);
+ case kPatchOatNeeded: return RelocateOatFile(info.Filename(), error_msg);
+
+ // kSelfPatchOatNeeded will never be returned by GetDexOptNeeded for an
+ // individual OatFileInfo.
+ case kSelfPatchOatNeeded: UNREACHABLE();
}
UNREACHABLE();
}
std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() {
- // The best oat files are, in descending order of bestness:
- // 1. Properly relocated files. These may be opened executable.
- // 2. Not out-of-date files that are already opened non-executable.
- // 3. Not out-of-date files that we must reopen non-executable.
+ return GetBestInfo().ReleaseFileForUse();
+}
- if (oat_.IsUpToDate()) {
- return oat_.ReleaseFile();
+std::string OatFileAssistant::GetStatusDump() {
+ std::ostringstream status;
+ bool oat_file_exists = false;
+ bool odex_file_exists = false;
+ if (oat_.Status() != kOatCannotOpen) {
+ // If we can open the file, neither Filename nor GetFile should return null.
+ CHECK(oat_.Filename() != nullptr);
+ CHECK(oat_.GetFile() != nullptr);
+
+ oat_file_exists = true;
+ status << *oat_.Filename() << " [compilation_filter=";
+ status << CompilerFilter::NameOfFilter(oat_.GetFile()->GetCompilerFilter());
+ status << ", status=" << oat_.Status();
}
- if (odex_.IsUpToDate()) {
- return odex_.ReleaseFile();
- }
+ if (odex_.Status() != kOatCannotOpen) {
+ // If we can open the file, neither Filename nor GetFile should return null.
+ CHECK(odex_.Filename() != nullptr);
+ CHECK(odex_.GetFile() != nullptr);
- VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
- << " attempting to fall back to interpreting oat file instead.";
-
- if (!oat_.IsOutOfDate() && !oat_.IsExecutable()) {
- return oat_.ReleaseFile();
- }
-
- if (!odex_.IsOutOfDate() && !odex_.IsExecutable()) {
- return odex_.ReleaseFile();
- }
-
- if (!oat_.IsOutOfDate()) {
- load_executable_ = false;
- oat_.Reset();
- if (!oat_.IsOutOfDate()) {
- CHECK(!oat_.IsExecutable());
- return oat_.ReleaseFile();
+ odex_file_exists = true;
+ if (oat_file_exists) {
+ status << "] ";
}
+ status << *odex_.Filename() << " [compilation_filter=";
+ status << CompilerFilter::NameOfFilter(odex_.GetFile()->GetCompilerFilter());
+ status << ", status=" << odex_.Status();
}
- if (!odex_.IsOutOfDate()) {
- load_executable_ = false;
- odex_.Reset();
- if (!odex_.IsOutOfDate()) {
- CHECK(!odex_.IsExecutable());
- return odex_.ReleaseFile();
- }
+ if (!oat_file_exists && !odex_file_exists) {
+ status << "invalid[";
}
- return std::unique_ptr<OatFile>();
+ status << "]";
+ return status.str();
}
std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
@@ -317,62 +290,14 @@
return has_original_dex_files_;
}
-const std::string* OatFileAssistant::OdexFileName() {
- return odex_.Filename();
-}
-
-bool OatFileAssistant::OdexFileExists() {
- return odex_.Exists();
-}
-
OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() {
return odex_.Status();
}
-bool OatFileAssistant::OdexFileIsOutOfDate() {
- return odex_.IsOutOfDate();
-}
-
-bool OatFileAssistant::OdexFileNeedsRelocation() {
- return odex_.NeedsRelocation();
-}
-
-bool OatFileAssistant::OdexFileIsUpToDate() {
- return odex_.IsUpToDate();
-}
-
-CompilerFilter::Filter OatFileAssistant::OdexFileCompilerFilter() {
- return odex_.CompilerFilter();
-}
-
-const std::string* OatFileAssistant::OatFileName() {
- return oat_.Filename();
-}
-
-bool OatFileAssistant::OatFileExists() {
- return oat_.Exists();
-}
-
OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() {
return oat_.Status();
}
-bool OatFileAssistant::OatFileIsOutOfDate() {
- return oat_.IsOutOfDate();
-}
-
-bool OatFileAssistant::OatFileNeedsRelocation() {
- return oat_.NeedsRelocation();
-}
-
-bool OatFileAssistant::OatFileIsUpToDate() {
- return oat_.IsUpToDate();
-}
-
-CompilerFilter::Filter OatFileAssistant::OatFileCompilerFilter() {
- return oat_.CompilerFilter();
-}
-
OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
// Verify the dex checksum.
// Note: GetOatDexFile will return null if the dex checksum doesn't match
@@ -383,7 +308,7 @@
dex_location_.c_str(), dex_checksum_pointer, &error_msg);
if (oat_dex_file == nullptr) {
VLOG(oat) << error_msg;
- return kOatOutOfDate;
+ return kOatDexOutOfDate;
}
// Verify the dex checksums for any secondary multidex files
@@ -406,7 +331,7 @@
<< secondary_dex_location
<< ". Expected: " << expected_secondary_checksum
<< ", Actual: " << actual_secondary_checksum;
- return kOatOutOfDate;
+ return kOatDexOutOfDate;
}
} else {
// If we can't get the checksum for the secondary location, we assume
@@ -425,7 +350,7 @@
VLOG(oat) << "No image for oat image checksum to match against.";
if (HasOriginalDexFiles()) {
- return kOatOutOfDate;
+ return kOatBootImageOutOfDate;
}
// If there is no original dex file to fall back to, grudgingly accept
@@ -439,7 +364,7 @@
} else if (file.GetOatHeader().GetImageFileLocationOatChecksum()
!= GetCombinedImageChecksum()) {
VLOG(oat) << "Oat image checksum does not match image checksum.";
- return kOatOutOfDate;
+ return kOatBootImageOutOfDate;
}
} else {
VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
@@ -450,7 +375,7 @@
const ImageInfo* image_info = GetImageInfo();
if (image_info == nullptr) {
VLOG(oat) << "No image to check oat relocation against.";
- return kOatNeedsRelocation;
+ return kOatRelocationOutOfDate;
}
// Verify the oat_data_begin recorded for the image in the oat file matches
@@ -462,7 +387,7 @@
": Oat file image oat_data_begin (" << oat_data_begin << ")"
<< " does not match actual image oat_data_begin ("
<< image_info->oat_data_begin << ")";
- return kOatNeedsRelocation;
+ return kOatRelocationOutOfDate;
}
// Verify the oat_patch_delta recorded for the image in the oat file matches
@@ -473,7 +398,7 @@
": Oat file image patch delta (" << oat_patch_delta << ")"
<< " does not match actual image patch delta ("
<< image_info->patch_delta << ")";
- return kOatNeedsRelocation;
+ return kOatRelocationOutOfDate;
}
} else {
// Oat files compiled in PIC mode do not require relocation.
@@ -841,6 +766,11 @@
return combined_image_checksum_;
}
+OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
+ bool use_oat = oat_.IsUseable() || odex_.Status() == kOatCannotOpen;
+ return use_oat ? oat_ : odex_;
+}
+
std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
DCHECK(oat_file != nullptr);
std::string art_file = ReplaceFileExtension(oat_file->GetLocation(), "art");
@@ -857,16 +787,29 @@
return ret;
}
-OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant)
- : oat_file_assistant_(oat_file_assistant)
+OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant,
+ bool is_oat_location)
+ : oat_file_assistant_(oat_file_assistant), is_oat_location_(is_oat_location)
{}
+bool OatFileAssistant::OatFileInfo::IsOatLocation() {
+ return is_oat_location_;
+}
+
const std::string* OatFileAssistant::OatFileInfo::Filename() {
return filename_provided_ ? &filename_ : nullptr;
}
-bool OatFileAssistant::OatFileInfo::Exists() {
- return GetFile() != nullptr;
+bool OatFileAssistant::OatFileInfo::IsUseable() {
+ switch (Status()) {
+ case kOatCannotOpen:
+ case kOatDexOutOfDate:
+ case kOatBootImageOutOfDate: return false;
+
+ case kOatRelocationOutOfDate:
+ case kOatUpToDate: return true;
+ }
+ UNREACHABLE();
}
OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() {
@@ -874,7 +817,7 @@
status_attempted_ = true;
const OatFile* file = GetFile();
if (file == nullptr) {
- status_ = kOatOutOfDate;
+ status_ = kOatCannotOpen;
} else {
status_ = oat_file_assistant_->GivenOatFileStatus(*file);
VLOG(oat) << file->GetLocation() << " is " << status_
@@ -884,22 +827,32 @@
return status_;
}
-bool OatFileAssistant::OatFileInfo::IsOutOfDate() {
- return Status() == kOatOutOfDate;
-}
+OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded(
+ CompilerFilter::Filter target, bool profile_changed) {
+ bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
-bool OatFileAssistant::OatFileInfo::NeedsRelocation() {
- return Status() == kOatNeedsRelocation;
-}
+ if (CompilerFilterIsOkay(target, profile_changed)) {
+ if (Status() == kOatUpToDate) {
+ // The oat file is in good shape as is.
+ return kNoDexOptNeeded;
+ }
-bool OatFileAssistant::OatFileInfo::IsUpToDate() {
- return Status() == kOatUpToDate;
-}
+ if (Status() == kOatRelocationOutOfDate) {
+ if (!compilation_desired) {
+ // If no compilation is desired, then it doesn't matter if the oat
+ // file needs relocation. It's in good shape as is.
+ return kNoDexOptNeeded;
+ }
-CompilerFilter::Filter OatFileAssistant::OatFileInfo::CompilerFilter() {
- const OatFile* file = GetFile();
- CHECK(file != nullptr);
- return file->GetCompilerFilter();
+ if (compilation_desired && HasPatchInfo()) {
+ // Relocate if we can.
+ return kPatchOatNeeded;
+ }
+ }
+ }
+
+ // We can only run dex2oat if there are original dex files.
+ return oat_file_assistant_->HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
}
const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
@@ -967,5 +920,31 @@
return std::move(file_);
}
+std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() {
+ if (Status() == kOatUpToDate) {
+ return ReleaseFile();
+ }
+
+ VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
+ << " attempting to fall back to interpreting oat file instead.";
+
+ if (Status() == kOatRelocationOutOfDate && !IsExecutable()) {
+ return ReleaseFile();
+ }
+
+ if (Status() == kOatRelocationOutOfDate) {
+ // We are loading an oat file for runtime use that needs relocation.
+ // Reload the file non-executable to ensure that we interpret out of the
+ // dex code in the oat file rather than trying to execute the unrelocated
+ // compiled code.
+ oat_file_assistant_->load_executable_ = false;
+ Reset();
+ if (IsUseable()) {
+ CHECK(!IsExecutable());
+ return ReleaseFile();
+ }
+ }
+ return std::unique_ptr<OatFile>();
+}
} // namespace art
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 3f018dc..3a838d7 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -70,19 +70,26 @@
};
enum OatStatus {
- // kOatOutOfDate - An oat file is said to be out of date if the file does
- // not exist, is out of date with respect to the dex file or boot image,
- // or does not meet the target compilation type.
- kOatOutOfDate,
+ // kOatCannotOpen - The oat file cannot be opened, because it does not
+ // exist, is unreadable, or otherwise corrupted.
+ kOatCannotOpen,
- // kOatNeedsRelocation - An oat file is said to need relocation if the
- // code is up to date, but not yet properly relocated for address space
- // layout randomization (ASLR). In this case, the oat file is neither
- // "out of date" nor "up to date".
- kOatNeedsRelocation,
+ // kOatDexOutOfDate - The oat file is out of date with respect to the dex file.
+ kOatDexOutOfDate,
- // kOatUpToDate - An oat file is said to be up to date if it is not out of
- // date and has been properly relocated for the purposes of ASLR.
+ // kOatBootImageOutOfDate - The oat file is up to date with respect to the
+ // dex file, but is out of date with respect to the boot image.
+ kOatBootImageOutOfDate,
+
+ // kOatRelocationOutOfDate - The oat file is up to date with respect to
+ // the dex file and boot image, but contains compiled code that has the
+ // wrong patch delta with respect to the boot image. Patchoat should be
+ // run on the oat file to update the patch delta of the compiled code to
+ // match the boot image.
+ kOatRelocationOutOfDate,
+
+ // kOatUpToDate - The oat file is completely up to date with respect to
+ // the dex file and boot image.
kOatUpToDate,
};
@@ -179,6 +186,10 @@
// the OatFileAssistant object.
std::unique_ptr<OatFile> GetBestOatFile();
+ // Returns a human readable description of the status of the code for the
+ // dex file. The returned description is for debugging purposes only.
+ std::string GetStatusDump();
+
// Open and returns an image space associated with the oat file.
static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
@@ -205,43 +216,16 @@
// really an oat file. The odex file will often, but not always, have a
// patch delta of 0 and need to be relocated before use for the purposes of
// ASLR. The odex file is treated as if it were read-only.
- // These methods return the location and status of the odex file for the dex
- // location.
- // Notes:
- // * OdexFileName may return null if the odex file name could not be
- // determined.
- const std::string* OdexFileName();
- bool OdexFileExists();
+ //
+ // Returns the status of the odex file for the dex location.
OatStatus OdexFileStatus();
- bool OdexFileIsOutOfDate();
- bool OdexFileNeedsRelocation();
- bool OdexFileIsUpToDate();
- // Must only be called if the associated odex file exists, i.e, if
- // |OdexFileExists() == true|.
- CompilerFilter::Filter OdexFileCompilerFilter();
// When the dex files is compiled on the target device, the oat file is the
// result. The oat file will have been relocated to some
// (possibly-out-of-date) offset for ASLR.
- // These methods return the location and status of the target oat file for
- // the dex location.
//
- // Notes:
- // * OatFileName may return null if the oat file name could not be
- // determined.
- const std::string* OatFileName();
- bool OatFileExists();
+ // Returns the status of the oat file for the dex location.
OatStatus OatFileStatus();
- bool OatFileIsOutOfDate();
- bool OatFileNeedsRelocation();
- bool OatFileIsUpToDate();
- // Must only be called if the associated oat file exists, i.e, if
- // |OatFileExists() == true|.
- CompilerFilter::Filter OatFileCompilerFilter();
-
- // Return the status for a given opened oat file with respect to the dex
- // location.
- OatStatus GivenOatFileStatus(const OatFile& file);
// Generates the oat file by relocation from the named input file.
// This does not check the current status before attempting to relocate the
@@ -311,29 +295,39 @@
// Initially the info is for no file in particular. It will treat the
// file as out of date until Reset is called with a real filename to use
// the cache for.
- explicit OatFileInfo(OatFileAssistant* oat_file_assistant);
+ // Pass true for is_oat_location if the information associated with this
+ // OatFileInfo is for the oat location, as opposed to the odex location.
+ OatFileInfo(OatFileAssistant* oat_file_assistant, bool is_oat_location);
+
+ bool IsOatLocation();
const std::string* Filename();
- bool Exists();
+
+ // Returns true if this oat file can be used for running code. The oat
+ // file can be used for running code as long as it is not out of date with
+ // respect to the dex code or boot image. An oat file that is out of date
+ // with respect to relocation is considered useable, because it's possible
+ // to interpret the dex code rather than run the unrelocated compiled
+ // code.
+ bool IsUseable();
+
+ // Returns the status of this oat file.
OatStatus Status();
- bool IsOutOfDate();
- bool NeedsRelocation();
- bool IsUpToDate();
- // Must only be called if the associated file exists, i.e, if
- // |Exists() == true|.
- CompilerFilter::Filter CompilerFilter();
+
+ // Return the DexOptNeeded value for this oat file with respect to the
+ // given target_compilation_filter.
+ // profile_changed should be true to indicate the profile has recently
+ // changed for this dex location.
+ // If patchoat is needed, this function will return the kPatchOatNeeded
+ // status, not the kSelfPatchOatNeeded status.
+ DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
+ bool profile_changed);
// Returns the loaded file.
// Loads the file if needed. Returns null if the file failed to load.
// The caller shouldn't clean up or free the returned pointer.
const OatFile* GetFile();
- // Returns true if the compiler filter used to generate the file is at
- // least as good as the given target filter. profile_changed should be
- // true to indicate the profile has recently changed for this dex
- // location.
- bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
-
// Returns true if the file is opened executable.
bool IsExecutable();
@@ -348,6 +342,23 @@
// file with the given filename.
void Reset(const std::string& filename);
+ // Release the loaded oat file for runtime use.
+ // Returns null if the oat file hasn't been loaded or is out of date.
+ // Ensures the returned file is not loaded executable if it has unuseable
+ // compiled code.
+ //
+ // After this call, no other methods of the OatFileInfo should be
+ // called, because access to the loaded oat file has been taken away from
+ // the OatFileInfo object.
+ std::unique_ptr<OatFile> ReleaseFileForUse();
+
+ private:
+ // Returns true if the compiler filter used to generate the file is at
+ // least as good as the given target filter. profile_changed should be
+ // true to indicate the profile has recently changed for this dex
+ // location.
+ bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
+
// Release the loaded oat file.
// Returns null if the oat file hasn't been loaded.
//
@@ -356,8 +367,8 @@
// the OatFileInfo object.
std::unique_ptr<OatFile> ReleaseFile();
- private:
OatFileAssistant* oat_file_assistant_;
+ const bool is_oat_location_;
bool filename_provided_ = false;
std::string filename_;
@@ -374,6 +385,13 @@
bool file_released_ = false;
};
+ // Return info for the best oat file.
+ OatFileInfo& GetBestInfo();
+
+ // Return the status for a given opened oat file with respect to the dex
+ // location.
+ OatStatus GivenOatFileStatus(const OatFile& file);
+
// Returns the current image location.
// Returns an empty string if the image location could not be retrieved.
//
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d4337b9..1848255 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -49,9 +49,9 @@
// Pre-Relocate the image to a known non-zero offset so we don't have to
// deal with the runtime randomly relocating the image by 0 and messing up
// the expected results of the tests.
- bool PreRelocateImage(std::string* error_msg) {
+ bool PreRelocateImage(const std::string& image_location, std::string* error_msg) {
std::string image;
- if (!GetCachedImageFile(&image, error_msg)) {
+ if (!GetCachedImageFile(image_location, &image, error_msg)) {
return false;
}
@@ -60,7 +60,7 @@
std::vector<std::string> argv;
argv.push_back(patchoat);
- argv.push_back("--input-image-location=" + GetImageLocation());
+ argv.push_back("--input-image-location=" + image_location);
argv.push_back("--output-image-file=" + image);
argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
argv.push_back("--base-offset-delta=0x00008000");
@@ -69,8 +69,8 @@
virtual void PreRuntimeCreate() {
std::string error_msg;
- ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg;
-
+ ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg;
+ ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg;
UnreserveImageSpace();
}
@@ -78,24 +78,32 @@
ReserveImageSpace();
}
- // Generate a non-PIC odex file for the purposes of test.
- // The generated odex file will be un-relocated.
- void GenerateOdexForTest(const std::string& dex_location,
- const std::string& odex_location,
- CompilerFilter::Filter filter,
- bool pic = false,
- bool with_patch_info = true) {
- // Temporarily redirect the dalvik cache so dex2oat doesn't find the
- // relocated image file.
+ // Generate an oat file for the purposes of test.
+ void GenerateOatForTest(const std::string& dex_location,
+ const std::string& oat_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_patch_info,
+ bool with_alternate_image) {
std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
- ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+
+ if (!relocate) {
+ // Temporarily redirect the dalvik cache so dex2oat doesn't find the
+ // relocated image file.
+ ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+ }
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location);
- args.push_back("--oat-file=" + odex_location);
+ args.push_back("--oat-file=" + oat_location);
args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
args.push_back("--runtime-arg");
+
+ // Use -Xnorelocate regardless of the relocate argument.
+ // We control relocation by redirecting the dalvik cache when needed
+ // rather than use this flag.
args.push_back("-Xnorelocate");
if (pic) {
@@ -106,14 +114,22 @@
args.push_back("--include-patch-information");
}
+ std::string image_location = GetImageLocation();
+ if (with_alternate_image) {
+ args.push_back("--boot-image=" + GetImageLocation2());
+ }
+
std::string error_msg;
ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
- ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
- // Verify the odex file was generated as expected and really is
- // unrelocated.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
- odex_location.c_str(),
+ if (!relocate) {
+ // Restore the dalvik cache if needed.
+ ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
+ }
+
+ // Verify the odex file was generated as expected.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
nullptr,
nullptr,
false,
@@ -125,24 +141,59 @@
EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo());
EXPECT_EQ(filter, odex_file->GetCompilerFilter());
- if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
- const std::vector<gc::space::ImageSpace*> image_spaces =
- Runtime::Current()->GetHeap()->GetBootImageSpaces();
- ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr);
- const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
- const OatHeader& oat_header = odex_file->GetOatHeader();
- uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
- EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
- EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta());
+ std::unique_ptr<ImageHeader> image_header(
+ gc::space::ImageSpace::ReadImageHeader(image_location.c_str(),
+ kRuntimeISA,
+ &error_msg));
+ ASSERT_TRUE(image_header != nullptr) << error_msg;
+ const OatHeader& oat_header = odex_file->GetOatHeader();
+ uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
+
+ if (CompilerFilter::DependsOnImageChecksum(filter)) {
+ if (with_alternate_image) {
+ EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+ } else {
+ EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+ }
}
+
+ if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
+ if (relocate) {
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ } else {
+ EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ }
+ }
+ }
+
+ // Generate a non-PIC odex file for the purposes of test.
+ // The generated odex file will be un-relocated.
+ void GenerateOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/false,
+ /*with_patch_info*/true,
+ /*with_alternate_image*/false);
}
void GeneratePicOdexForTest(const std::string& dex_location,
const std::string& odex_location,
CompilerFilter::Filter filter) {
- GenerateOdexForTest(dex_location, odex_location, filter, true, false);
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/true,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/false);
}
// Generate a non-PIC odex file without patch information for the purposes
@@ -150,7 +201,43 @@
void GenerateNoPatchOdexForTest(const std::string& dex_location,
const std::string& odex_location,
CompilerFilter::Filter filter) {
- GenerateOdexForTest(dex_location, odex_location, filter, false, false);
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/false,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/false);
+ }
+
+ // Generate an oat file in the oat location.
+ void GenerateOatForTest(const char* dex_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_patch_info,
+ bool with_alternate_image) {
+ std::string oat_location;
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+ dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+ GenerateOatForTest(dex_location,
+ oat_location,
+ filter,
+ relocate,
+ pic,
+ with_patch_info,
+ with_alternate_image);
+ }
+
+ // Generate a standard oat file in the oat location.
+ void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ filter,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/false);
}
private:
@@ -211,36 +298,6 @@
}
};
-// Generate an oat file for the purposes of test, as opposed to testing
-// generation of oat files.
-static void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
- // Use an oat file assistant to find the proper oat location.
- std::string oat_location;
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
- dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
-
- std::vector<std::string> args;
- args.push_back("--dex-file=" + std::string(dex_location));
- args.push_back("--oat-file=" + oat_location);
- args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
- args.push_back("--runtime-arg");
- args.push_back("-Xnorelocate");
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
- // Verify the oat file was generated as expected.
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location.c_str(),
- oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- dex_location,
- &error_msg));
- ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
- EXPECT_EQ(filter, oat_file->GetCompilerFilter());
-}
-
// Case: We have a DEX file, but no OAT file for it.
// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, DexNoOat) {
@@ -259,16 +316,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OdexFileStatus());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
- EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -311,13 +360,7 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -341,13 +384,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
// Case: We have a DEX file and speed-profile OAT file for it.
@@ -370,13 +408,7 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly, true));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -453,10 +485,10 @@
EXPECT_EQ(2u, dex_files.size());
}
-// Case: We have a DEX file and out-of-date OAT file.
-// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, OatOutOfDate) {
- std::string dex_location = GetScratchDir() + "/OatOutOfDate.jar";
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// dex checksum.
+TEST_F(OatFileAssistantTest, OatDexOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
// We create a dex, generate an oat for it, then overwrite the dex with a
// different dex to make the oat out of date.
@@ -471,12 +503,62 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
+ EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// boot image.
+TEST_F(OatFileAssistantTest, OatImageOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ CompilerFilter::kSpeed,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/true);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+ 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.HasOriginalDexFiles());
+}
+
+// Case: We have a DEX file and a verify-at-runtime OAT file out of date with
+// respect to the boot image.
+// It shouldn't matter that the OAT file is out of date, because it is
+// verify-at-runtime.
+TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ CompilerFilter::kVerifyAtRuntime,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/true);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+
+ 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.HasOriginalDexFiles());
}
@@ -499,13 +581,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
// We should still be able to get the non-executable odex file to run from.
@@ -533,12 +610,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Make the oat file up to date.
@@ -551,12 +624,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Verify we can load the dex files from it.
@@ -596,13 +665,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Make the oat file up to date.
@@ -617,14 +681,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Verify we can load the dex files from it.
@@ -654,13 +712,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Make the oat file up to date. This should have no effect.
@@ -673,13 +726,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -705,14 +753,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
// Make the oat file up to date.
@@ -725,14 +767,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -778,7 +814,7 @@
// Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
// OAT files both have patch delta of 0.
-// Expect: It shouldn't crash, and status is kPatchOatNeeded.
+// Expect: It shouldn't crash, and status is kSelfPatchOatNeeded.
TEST_F(OatFileAssistantTest, OdexOatOverlap) {
std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
@@ -796,16 +832,15 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(),
oat_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+ // kSelfPatchOatNeeded is expected rather than kPatchOatNeeded based on the
+ // assumption that the oat location is more up-to-date than the odex
+ // location, even if they both need relocation.
+ EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
// Things aren't relocated, so it should fall back to interpreted.
@@ -837,12 +872,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -865,12 +896,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -958,7 +985,7 @@
// Verify it didn't create an oat in the default location.
OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_FALSE(ofm.OatFileExists());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OatFileStatus());
}
// Case: We have a DEX file but can't write the oat file.
@@ -1045,12 +1072,8 @@
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
// Case: Very short, non-existent Dex location.
@@ -1063,12 +1086,8 @@
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Trying to make it up to date should have no effect.
@@ -1091,12 +1110,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
// A task to generate a dex location. Used by the RaceToGenerate test.
@@ -1297,9 +1312,6 @@
}
// TODO: More Tests:
-// * Image checksum change is out of date for kIntepretOnly, but not
-// kVerifyAtRuntime. But target of kVerifyAtRuntime still says current
-// kInterpretOnly is out of date.
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
// * Test using secondary isa
@@ -1313,5 +1325,4 @@
// because it's unrelocated and no dex2oat
// * Test unrelocated specific target compilation type can be relocated to
// make it up to date.
-
} // namespace art
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index b3dab58..843be92 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -20,6 +20,7 @@
#include "base/logging.h"
#include "base/unix_file/fd_file.h"
+#include "dex_file.h"
namespace art {
@@ -73,10 +74,19 @@
return nullptr;
}
+ return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, error_msg);
+}
+
+VdexFile* VdexFile::Open(int file_fd,
+ size_t vdex_length,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ std::string* error_msg) {
std::unique_ptr<MemMap> mmap(MemMap::MapFile(vdex_length,
writable ? PROT_READ | PROT_WRITE : PROT_READ,
MAP_SHARED,
- vdex_file->Fd(),
+ file_fd,
0 /* start offset */,
low_4gb,
vdex_filename.c_str(),
@@ -90,4 +100,16 @@
return new VdexFile(mmap.release());
}
+const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const {
+ DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End()));
+ if (cursor == nullptr) {
+ // Beginning of the iteration, return the first dex file if there is one.
+ return HasDexSection() ? DexBegin() : nullptr;
+ } else {
+ // Fetch the next dex file. Return null if there is none.
+ const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_;
+ return (data == DexEnd()) ? nullptr : data;
+ }
+}
+
} // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index edd6ffe..75a0d5e 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -71,6 +71,13 @@
bool low_4gb,
std::string* error_msg);
+ static VdexFile* Open(int file_fd,
+ size_t vdex_length,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ std::string* error_msg);
+
const uint8_t* Begin() const { return mmap_->Begin(); }
const uint8_t* End() const { return mmap_->End(); }
size_t Size() const { return mmap_->Size(); }
@@ -84,9 +91,37 @@
Begin() + sizeof(Header) + GetHeader().GetDexSize(), GetHeader().GetVerifierDepsSize());
}
+ ArrayRef<const uint8_t> GetQuickeningInfo() const {
+ return ArrayRef<const uint8_t>(
+ GetVerifierDepsData().data() + GetHeader().GetVerifierDepsSize(),
+ GetHeader().GetQuickeningInfoSize());
+ }
+
+ bool IsValid() const {
+ return mmap_->Size() >= sizeof(Header) && GetHeader().IsValid();
+ }
+
+ // This method is for iterating over the dex files in the vdex. If `cursor` is null,
+ // the first dex file is returned. If `cursor` is not null, it must point to a dex
+ // file and this method returns the next dex file if there is one, or null if there
+ // is none.
+ const uint8_t* GetNextDexFileData(const uint8_t* cursor) const;
+
private:
explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
+ bool HasDexSection() const {
+ return GetHeader().GetDexSize() != 0;
+ }
+
+ const uint8_t* DexBegin() const {
+ return Begin() + sizeof(Header);
+ }
+
+ const uint8_t* DexEnd() const {
+ return Begin() + sizeof(Header) + GetHeader().GetDexSize();
+ }
+
std::unique_ptr<MemMap> mmap_;
DISALLOW_COPY_AND_ASSIGN(VdexFile);
diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc
deleted file mode 100644
index ff33709..0000000
--- a/test/626-const-class-linking/clear_dex_cache_types.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 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 "jni.h"
-#include "scoped_thread_state_change-inl.h"
-
-namespace art {
-
-namespace {
-
-extern "C" JNIEXPORT void JNICALL Java_Main_clearResolvedTypes(JNIEnv*, jclass, jclass cls) {
- ScopedObjectAccess soa(Thread::Current());
- mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
- for (size_t i = 0, num_types = dex_cache->NumResolvedTypes(); i != num_types; ++i) {
- dex_cache->SetResolvedType(dex::TypeIndex(i), ObjPtr<mirror::Class>(nullptr));
- }
-}
-
-} // namespace
-
-} // namespace art
diff --git a/test/626-const-class-linking/expected.txt b/test/626-const-class-linking/expected.txt
deleted file mode 100644
index 1243226..0000000
--- a/test/626-const-class-linking/expected.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-JNI_OnLoad called
-first: Helper1 class loader: DelegatingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: DelegatingLoader
-second: Test class loader: DefiningLoader
-testClearDexCache done
-first: Helper1 class loader: DelegatingLoader
-second: Test class loader: DefiningLoader
-first: Helper2 class loader: DelegatingLoader
-second: Test class loader: DefiningLoader
-testMultiDex done
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-total: 4
- throwables: 0
- class_weaks: 4 (1 unique)
-testRacyLoader done
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper3 class loader: RacyLoader
-second: Test3 class loader: DefiningLoader
-first: Helper3 class loader: RacyLoader
-second: Test3 class loader: DefiningLoader
-total: 4
- throwables: 0
- class_weaks: 4 (2 unique)
-testRacyLoader2 done
-java.lang.NoClassDefFoundError: Initiating class loader of type MisbehavingLoader returned class Helper2 instead of Test.
-testMisbehavingLoader done
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-total: 4
- throwables: 0
- class_weaks: 4 (1 unique)
-testRacyMisbehavingLoader done
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-total: 4
- throwables: 0
- class_weaks: 4 (1 unique)
-testRacyMisbehavingLoader2 done
diff --git a/test/626-const-class-linking/info.txt b/test/626-const-class-linking/info.txt
deleted file mode 100644
index 9c19a46..0000000
--- a/test/626-const-class-linking/info.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Test that once a const-class instruction is linked, it will keep referring
-to the same class even in the presence of custom class loaders even after
-clearing the dex cache type array.
diff --git a/test/626-const-class-linking/multidex.jpp b/test/626-const-class-linking/multidex.jpp
deleted file mode 100644
index c7a6648..0000000
--- a/test/626-const-class-linking/multidex.jpp
+++ /dev/null
@@ -1,27 +0,0 @@
-ClassPair:
- @@com.android.jack.annotations.ForceInMainDex
- class ClassPair
-DefiningLoader:
- @@com.android.jack.annotations.ForceInMainDex
- class DefiningLoader
-DelegatingLoader:
- @@com.android.jack.annotations.ForceInMainDex
- class DelegatingLoader
-Helper1:
- @@com.android.jack.annotations.ForceInMainDex
- class Helper1
-Main:
- @@com.android.jack.annotations.ForceInMainDex
- class Main
-MisbehavingLoader:
- @@com.android.jack.annotations.ForceInMainDex
- class MisbehavingLoader
-RacyLoader:
- @@com.android.jack.annotations.ForceInMainDex
- class RacyLoader
-RacyMisbehavingHelper:
- @@com.android.jack.annotations.ForceInMainDex
- class RacyMisbehavingHelper
-RacyMisbehavingLoader:
- @@com.android.jack.annotations.ForceInMainDex
- class RacyMisbehavingLoader
diff --git a/test/626-const-class-linking/src-multidex/Helper2.java b/test/626-const-class-linking/src-multidex/Helper2.java
deleted file mode 100644
index 5bb31ee..0000000
--- a/test/626-const-class-linking/src-multidex/Helper2.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-public class Helper2 {
- public static ClassPair get() {
- Class<?> helper2_class = Helper2.class;
- Class<?> test_class = Test.class;
- return new ClassPair(helper2_class, test_class);
- }
-}
diff --git a/test/626-const-class-linking/src-multidex/Helper3.java b/test/626-const-class-linking/src-multidex/Helper3.java
deleted file mode 100644
index af996de..0000000
--- a/test/626-const-class-linking/src-multidex/Helper3.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-public class Helper3 {
- public static ClassPair get() {
- Class<?> helper3_class = Helper3.class;
- Class<?> test3_class = Test3.class;
- return new ClassPair(helper3_class, test3_class);
- }
-}
diff --git a/test/626-const-class-linking/src-multidex/Test3.java b/test/626-const-class-linking/src-multidex/Test3.java
deleted file mode 100644
index c4b134d..0000000
--- a/test/626-const-class-linking/src-multidex/Test3.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-public class Test3 {
-}
diff --git a/test/626-const-class-linking/src/ClassPair.java b/test/626-const-class-linking/src/ClassPair.java
deleted file mode 100644
index b07036c..0000000
--- a/test/626-const-class-linking/src/ClassPair.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-public class ClassPair {
- public Class<?> first;
- public Class<?> second;
-
- public ClassPair(Class<?> first, Class<?> second) {
- this.first = first;
- this.second = second;
- }
-
- public void print() {
- String first_loader_name = first.getClassLoader().getClass().getName();
- System.out.println("first: " + first.getName() + " class loader: " + first_loader_name);
- String second_loader_name = second.getClassLoader().getClass().getName();
- System.out.println("second: " + second.getName() + " class loader: " + second_loader_name);
- }
-}
diff --git a/test/626-const-class-linking/src/DefiningLoader.java b/test/626-const-class-linking/src/DefiningLoader.java
deleted file mode 100644
index b17ab77..0000000
--- a/test/626-const-class-linking/src/DefiningLoader.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * A class loader with atypical behavior: we try to load a private
- * class implementation before asking the system or boot loader. This
- * is used to create multiple classes with identical names in a single VM.
- *
- * If DexFile is available, we use that; if not, we assume we're not in
- * Dalvik and instantiate the class with defineClass().
- *
- * The location of the DEX files and class data is dependent upon the
- * test framework.
- */
-public class DefiningLoader extends ClassLoader {
- static {
- // For JVM, register as parallel capable.
- // Android treats all class loaders as parallel capable and makes this a no-op.
- registerAsParallelCapable();
- }
-
- /* this is where the .class files live */
- static final String CLASS_PATH1 = "classes/";
- static final String CLASS_PATH2 = "classes2/";
-
- /* this is the DEX/Jar file */
- static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/626-const-class-linking.jar";
-
- /* on Dalvik, this is a DexFile; otherwise, it's null */
- private Class<?> mDexClass;
-
- private Object mDexFile;
-
- /**
- * Construct DefiningLoader, grabbing a reference to the DexFile class
- * if we're running under Dalvik.
- */
- public DefiningLoader(ClassLoader parent) {
- super(parent);
-
- try {
- mDexClass = parent.loadClass("dalvik.system.DexFile");
- } catch (ClassNotFoundException cnfe) {
- // ignore -- not running Dalvik
- }
- }
-
- /**
- * Finds the class with the specified binary name.
- *
- * We search for a file in CLASS_PATH or pull an entry from DEX_FILE.
- * If we don't find a match, we throw an exception.
- */
- protected Class<?> findClass(String name) throws ClassNotFoundException
- {
- if (mDexClass != null) {
- return findClassDalvik(name);
- } else {
- return findClassNonDalvik(name);
- }
- }
-
- /**
- * Finds the class with the specified binary name, from a DEX file.
- */
- private Class<?> findClassDalvik(String name)
- throws ClassNotFoundException {
-
- if (mDexFile == null) {
- synchronized (DefiningLoader.class) {
- Constructor<?> ctor;
- /*
- * Construct a DexFile object through reflection.
- */
- try {
- ctor = mDexClass.getConstructor(String.class);
- } catch (NoSuchMethodException nsme) {
- throw new ClassNotFoundException("getConstructor failed",
- nsme);
- }
-
- try {
- mDexFile = ctor.newInstance(DEX_FILE);
- } catch (InstantiationException ie) {
- throw new ClassNotFoundException("newInstance failed", ie);
- } catch (IllegalAccessException iae) {
- throw new ClassNotFoundException("newInstance failed", iae);
- } catch (InvocationTargetException ite) {
- throw new ClassNotFoundException("newInstance failed", ite);
- }
- }
- }
-
- /*
- * Call DexFile.loadClass(String, ClassLoader).
- */
- Method meth;
-
- try {
- meth = mDexClass.getMethod("loadClass", String.class, ClassLoader.class);
- } catch (NoSuchMethodException nsme) {
- throw new ClassNotFoundException("getMethod failed", nsme);
- }
-
- try {
- meth.invoke(mDexFile, name, this);
- } catch (IllegalAccessException iae) {
- throw new ClassNotFoundException("loadClass failed", iae);
- } catch (InvocationTargetException ite) {
- throw new ClassNotFoundException("loadClass failed",
- ite.getCause());
- }
-
- return null;
- }
-
- /**
- * Finds the class with the specified binary name, from .class files.
- */
- private Class<?> findClassNonDalvik(String name)
- throws ClassNotFoundException {
-
- String[] pathNames = { CLASS_PATH1 + name + ".class", CLASS_PATH2 + name + ".class" };
-
- String pathName = null;
- RandomAccessFile raf = null;
-
- for (String pn : pathNames) {
- pathName = pn;
- try {
- //System.out.println("--- Defining: looking for " + pathName);
- raf = new RandomAccessFile(new File(pathName), "r");
- break;
- } catch (FileNotFoundException fnfe) {
- }
- }
- if (raf == null) {
- throw new ClassNotFoundException("Not found: " + pathNames[0] + ":" + pathNames[1]);
- }
-
- /* read the entire file in */
- byte[] fileData;
- try {
- fileData = new byte[(int) raf.length()];
- raf.readFully(fileData);
- } catch (IOException ioe) {
- throw new ClassNotFoundException("Read error: " + pathName);
- } finally {
- try {
- raf.close();
- } catch (IOException ioe) {
- // drop
- }
- }
-
- /* create the class */
- //System.out.println("--- Defining: defining " + name);
- try {
- return defineClass(name, fileData, 0, fileData.length);
- } catch (Throwable th) {
- throw new ClassNotFoundException("defineClass failed", th);
- }
- }
-
- /**
- * Load a class.
- *
- * Normally a class loader wouldn't override this, but we want our
- * version of the class to take precedence over an already-loaded
- * version.
- *
- * We still want the system classes (e.g. java.lang.Object) from the
- * bootstrap class loader.
- */
- synchronized protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- Class<?> res;
-
- /*
- * 1. Invoke findLoadedClass(String) to check if the class has
- * already been loaded.
- *
- * This doesn't change.
- */
- res = findLoadedClass(name);
- if (res != null) {
- // System.out.println("FancyLoader.loadClass: " + name + " already loaded");
- if (resolve)
- resolveClass(res);
- return res;
- }
-
- /*
- * 3. Invoke the findClass(String) method to find the class.
- */
- try {
- res = findClass(name);
- if (resolve)
- resolveClass(res);
- }
- catch (ClassNotFoundException e) {
- // we couldn't find it, so eat the exception and keep going
- }
-
- /*
- * 2. Invoke the loadClass method on the parent class loader. If
- * the parent loader is null the class loader built-in to the
- * virtual machine is used, instead.
- *
- * (Since we're not in java.lang, we can't actually invoke the
- * parent's loadClass() method, but we passed our parent to the
- * super-class which can take care of it for us.)
- */
- res = super.loadClass(name, resolve); // returns class or throws
- return res;
- }
-}
diff --git a/test/626-const-class-linking/src/DelegatingLoader.java b/test/626-const-class-linking/src/DelegatingLoader.java
deleted file mode 100644
index 49955d4..0000000
--- a/test/626-const-class-linking/src/DelegatingLoader.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-public class DelegatingLoader extends DefiningLoader {
- private DefiningLoader defining_loader;
-
- public DelegatingLoader(ClassLoader parent, DefiningLoader defining_loader) {
- super(parent);
- this.defining_loader = defining_loader;
- }
-
- public void resetDefiningLoader(DefiningLoader defining_loader) {
- this.defining_loader = defining_loader;
- }
-
- protected Class<?> findClass(String name) throws ClassNotFoundException
- {
- if (name.equals("Test")) {
- throw new Error("Unexpected DelegatingLoader.findClass(\"Test\")");
- }
- return super.findClass(name);
- }
-
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- if (name.equals("Test")) {
- return defining_loader.loadClass(name, resolve);
- }
- return super.loadClass(name, resolve);
- }
-}
diff --git a/test/626-const-class-linking/src/Helper1.java b/test/626-const-class-linking/src/Helper1.java
deleted file mode 100644
index ff9cd1a..0000000
--- a/test/626-const-class-linking/src/Helper1.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-public class Helper1 {
- public static ClassPair get() {
- Class<?> helper1_class = Helper1.class;
- Class<?> test_class = Test.class;
- return new ClassPair(helper1_class, test_class);
- }
-}
diff --git a/test/626-const-class-linking/src/Main.java b/test/626-const-class-linking/src/Main.java
deleted file mode 100644
index 05a6bac..0000000
--- a/test/626-const-class-linking/src/Main.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-public class Main {
- public static void main(String[] args) throws Exception {
- try {
- System.loadLibrary(args[0]);
- } catch (UnsatisfiedLinkError ule) {
- usingRI = true;
- // Add expected JNI_OnLoad log line to match expected.txt.
- System.out.println("JNI_OnLoad called");
- }
-
- testClearDexCache();
- testMultiDex();
- testRacyLoader();
- testRacyLoader2();
- testMisbehavingLoader();
- testRacyMisbehavingLoader();
- testRacyMisbehavingLoader2();
- }
-
- private static void testClearDexCache() throws Exception {
- DelegatingLoader delegating_loader = createDelegatingLoader();
- Class<?> helper = delegating_loader.loadClass("Helper1");
-
- WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper);
- changeInner(delegating_loader);
- if (!usingRI) {
- clearResolvedTypes(helper);
- }
- Runtime.getRuntime().gc();
- WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper);
- Runtime.getRuntime().gc();
-
- Class<?> test1 = weak_test1.get();
- if (test1 == null) {
- System.out.println("test1 disappeared");
- }
- Class<?> test2 = weak_test2.get();
- if (test2 == null) {
- System.out.println("test2 disappeared");
- }
- if (test1 != test2) {
- System.out.println("test1 != test2");
- }
-
- System.out.println("testClearDexCache done");
- }
-
- private static void testMultiDex() throws Exception {
- DelegatingLoader delegating_loader = createDelegatingLoader();
-
- Class<?> helper1 = delegating_loader.loadClass("Helper1");
- WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper1);
-
- changeInner(delegating_loader);
-
- Class<?> helper2 = delegating_loader.loadClass("Helper2");
- WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper2);
-
- Runtime.getRuntime().gc();
-
- Class<?> test1 = weak_test1.get();
- if (test1 == null) {
- System.out.println("test1 disappeared");
- }
- Class<?> test2 = weak_test2.get();
- if (test2 == null) {
- System.out.println("test2 disappeared");
- }
- if (test1 != test2) {
- System.out.println("test1 != test2");
- }
-
- System.out.println("testMultiDex done");
- }
-
- private static void testMisbehavingLoader() throws Exception {
- ClassLoader system_loader = ClassLoader.getSystemClassLoader();
- DefiningLoader defining_loader = new DefiningLoader(system_loader);
- MisbehavingLoader misbehaving_loader =
- new MisbehavingLoader(system_loader, defining_loader);
- Class<?> helper = misbehaving_loader.loadClass("Helper1");
-
- try {
- WeakReference<Class<?>> weak_test = wrapHelperGet(helper);
- } catch (InvocationTargetException ite) {
- String message = ite.getCause().getMessage();
- if (usingRI && "Test".equals(message)) {
- // Replace RI message with dalvik message to match expected.txt.
- message = "Initiating class loader of type " +
- misbehaving_loader.getClass().getName() +
- " returned class Helper2 instead of Test.";
- }
- System.out.println(ite.getCause().getClass().getName() + ": " + message);
- }
- System.out.println("testMisbehavingLoader done");
- }
-
- private static void testRacyLoader() throws Exception {
- final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-
- final Thread[] threads = new Thread[4];
- final Object[] results = new Object[threads.length];
-
- final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
- final Class<?> helper1 = racy_loader.loadClass("Helper1");
-
- for (int i = 0; i != threads.length; ++i) {
- final int my_index = i;
- Thread t = new Thread() {
- public void run() {
- try {
- Method get = helper1.getDeclaredMethod("get");
- results[my_index] = get.invoke(null);
- } catch (InvocationTargetException ite) {
- results[my_index] = ite.getCause();
- } catch (Throwable t) {
- results[my_index] = t;
- }
- }
- };
- t.start();
- threads[i] = t;
- }
- for (Thread t : threads) {
- t.join();
- }
- dumpResultStats(results);
- System.out.println("testRacyLoader done");
- }
-
- private static void testRacyLoader2() throws Exception {
- final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-
- final Thread[] threads = new Thread[4];
- final Object[] results = new Object[threads.length];
-
- final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
- final Class<?> helper1 = racy_loader.loadClass("Helper1");
- final Class<?> helper3 = racy_loader.loadClass("Helper3");
-
- for (int i = 0; i != threads.length; ++i) {
- final int my_index = i;
- Thread t = new Thread() {
- public void run() {
- try {
- Class<?> helper = (my_index < threads.length / 2) ? helper1 : helper3;
- Method get = helper.getDeclaredMethod("get");
- results[my_index] = get.invoke(null);
- } catch (InvocationTargetException ite) {
- results[my_index] = ite.getCause();
- } catch (Throwable t) {
- results[my_index] = t;
- }
- }
- };
- t.start();
- threads[i] = t;
- }
- for (Thread t : threads) {
- t.join();
- }
- dumpResultStats(results);
- System.out.println("testRacyLoader2 done");
- }
-
- private static void dumpResultStats(Object[] results) throws Exception {
- int throwables = 0;
- int class_weaks = 0;
- int unique_class_weaks = 0;
- for (int i = 0; i != results.length; ++i) {
- Object r = results[i];
- if (r instanceof Throwable) {
- ++throwables;
- System.out.println(((Throwable) r).getMessage());
- } else if (isClassPair(r)) {
- printPair(r);
- Object ref = getSecond(r);
- ++class_weaks;
- ++unique_class_weaks;
- for (int j = 0; j != i; ++j) {
- Object rj = results[j];
- if (isClassPair(results[j]) && getSecond(results[j]) == ref) {
- --unique_class_weaks;
- break;
- }
- }
- }
- }
- System.out.println("total: " + results.length);
- System.out.println(" throwables: " + throwables);
- System.out.println(" class_weaks: " + class_weaks
- + " (" + unique_class_weaks + " unique)");
- }
-
- private static void testRacyMisbehavingLoader() throws Exception {
- final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-
- final Thread[] threads = new Thread[4];
- final Object[] results = new Object[threads.length];
-
- final RacyMisbehavingLoader racy_loader =
- new RacyMisbehavingLoader(system_loader, threads.length, false);
- final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
-
- for (int i = 0; i != threads.length; ++i) {
- final int my_index = i;
- Thread t = new Thread() {
- public void run() {
- try {
- Method get = helper1.getDeclaredMethod("get");
- results[my_index] = get.invoke(null);
- } catch (InvocationTargetException ite) {
- results[my_index] = ite.getCause();
- } catch (Throwable t) {
- results[my_index] = t;
- }
- }
- };
- t.start();
- threads[i] = t;
- }
- for (Thread t : threads) {
- t.join();
- }
- dumpResultStats(results);
- System.out.println("testRacyMisbehavingLoader done");
- }
-
- private static void testRacyMisbehavingLoader2() throws Exception {
- final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-
- final Thread[] threads = new Thread[4];
- final Object[] results = new Object[threads.length];
-
- final RacyMisbehavingLoader racy_loader =
- new RacyMisbehavingLoader(system_loader, threads.length, true);
- final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
-
- for (int i = 0; i != threads.length; ++i) {
- final int my_index = i;
- Thread t = new Thread() {
- public void run() {
- try {
- Method get = helper1.getDeclaredMethod("get");
- results[my_index] = get.invoke(null);
- } catch (InvocationTargetException ite) {
- results[my_index] = ite.getCause();
- } catch (Throwable t) {
- results[my_index] = t;
- }
- }
- };
- t.start();
- threads[i] = t;
- }
- for (Thread t : threads) {
- t.join();
- }
- dumpResultStats(results);
- System.out.println("testRacyMisbehavingLoader2 done");
- }
-
- private static DelegatingLoader createDelegatingLoader() {
- ClassLoader system_loader = ClassLoader.getSystemClassLoader();
- DefiningLoader defining_loader = new DefiningLoader(system_loader);
- return new DelegatingLoader(system_loader, defining_loader);
- }
-
- private static void changeInner(DelegatingLoader delegating_loader) {
- ClassLoader system_loader = ClassLoader.getSystemClassLoader();
- DefiningLoader defining_loader = new DefiningLoader(system_loader);
- delegating_loader.resetDefiningLoader(defining_loader);
- }
-
- private static WeakReference<Class<?>> wrapHelperGet(Class<?> helper) throws Exception {
- Method get = helper.getDeclaredMethod("get");
- Object pair = get.invoke(null);
- printPair(pair);
- return new WeakReference<Class<?>>(getSecond(pair));
- }
-
- private static void printPair(Object pair) throws Exception {
- Method print = pair.getClass().getDeclaredMethod("print");
- print.invoke(pair);
- }
-
- private static Class<?> getSecond(Object pair) throws Exception {
- Field second = pair.getClass().getDeclaredField("second");
- return (Class<?>) second.get(pair);
- }
-
- private static boolean isClassPair(Object r) {
- return r != null && r.getClass().getName().equals("ClassPair");
- }
-
- public static native void clearResolvedTypes(Class<?> c);
-
- static boolean usingRI = false;
-}
diff --git a/test/626-const-class-linking/src/MisbehavingLoader.java b/test/626-const-class-linking/src/MisbehavingLoader.java
deleted file mode 100644
index ca9783e..0000000
--- a/test/626-const-class-linking/src/MisbehavingLoader.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-// Class loader that returns Helper2.class when asked to load "Test".
-public class MisbehavingLoader extends DefiningLoader {
- private DefiningLoader defining_loader;
-
- public MisbehavingLoader(ClassLoader parent, DefiningLoader defining_loader) {
- super(parent);
- this.defining_loader = defining_loader;
- }
-
- protected Class<?> findClass(String name) throws ClassNotFoundException
- {
- if (name.equals("Helper1") || name.equals("Helper2")) {
- return super.findClass(name);
- } else if (name.equals("Test")) {
- throw new Error("Unexpected MisbehavingLoader.findClass(\"Test\")");
- }
- return super.findClass(name);
- }
-
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- if (name.equals("Helper1") || name.equals("Helper2")) {
- return super.loadClass(name, resolve);
- } else if (name.equals("Test")) {
- // Ask for a different class.
- return defining_loader.loadClass("Helper2", resolve);
- }
- return super.loadClass(name, resolve);
- }
-}
diff --git a/test/626-const-class-linking/src/RacyLoader.java b/test/626-const-class-linking/src/RacyLoader.java
deleted file mode 100644
index abfc8ed..0000000
--- a/test/626-const-class-linking/src/RacyLoader.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-public class RacyLoader extends DefiningLoader {
- static {
- // For JVM, register as parallel capable.
- // Android treats all class loaders as parallel capable and makes this a no-op.
- registerAsParallelCapable();
- }
-
- private Object lock = new Object();
- private int index = 0;
- private int count;
-
- private DefiningLoader[] defining_loaders;
-
- public RacyLoader(ClassLoader parent, int count) {
- super(parent);
- this.count = count;
- defining_loaders = new DefiningLoader[2];
- for (int i = 0; i != defining_loaders.length; ++i) {
- defining_loaders[i] = new DefiningLoader(parent);
- }
- }
-
- protected Class<?> findClass(String name) throws ClassNotFoundException
- {
- if (name.equals("Test") || name.equals("Test3")) {
- throw new Error("Unexpected RacyLoader.findClass(\"" + name + "\")");
- }
- return super.findClass(name);
- }
-
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- if (name.equals("Test") || name.equals("Test3")) {
- int my_index = syncWithOtherInstances(count);
- Class<?> result = defining_loaders[my_index & 1].loadClass(name, resolve);
- syncWithOtherInstances(2 * count);
- return result;
- }
- return super.loadClass(name, resolve);
- }
-
- private int syncWithOtherInstances(int limit) {
- int my_index;
- synchronized (lock) {
- my_index = index;
- ++index;
- if (index != limit) {
- try {
- lock.wait(2000);
- if (index < limit) {
- throw new Error("Timed out; my_index=" + my_index + ", index=" + index);
- }
- } catch (InterruptedException ie) {
- throw new Error(ie);
- }
- } else {
- lock.notifyAll();
- }
- }
- return my_index;
- }
-}
diff --git a/test/626-const-class-linking/src/RacyMisbehavingHelper.java b/test/626-const-class-linking/src/RacyMisbehavingHelper.java
deleted file mode 100644
index 4525278..0000000
--- a/test/626-const-class-linking/src/RacyMisbehavingHelper.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-import java.lang.reflect.Method;
-
-public class RacyMisbehavingHelper {
- public static ClassPair get() {
- Class<?> helper1_class = Helper1.class;
- Class<?> test_class = Test.class;
- try {
- // After loading the correct class, allow loading the incorrect class.
- ClassLoader loader = helper1_class.getClassLoader();
- Method reportAfterLoading = loader.getClass().getDeclaredMethod("reportAfterLoading");
- reportAfterLoading.invoke(loader);
- } catch (Throwable t) {
- t.printStackTrace();
- }
- return new ClassPair(helper1_class, test_class);
- }
-}
diff --git a/test/626-const-class-linking/src/RacyMisbehavingLoader.java b/test/626-const-class-linking/src/RacyMisbehavingLoader.java
deleted file mode 100644
index d09888e..0000000
--- a/test/626-const-class-linking/src/RacyMisbehavingLoader.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-public class RacyMisbehavingLoader extends DefiningLoader {
- static {
- // For JVM, register as parallel capable.
- // Android treats all class loaders as parallel capable and makes this a no-op.
- registerAsParallelCapable();
- }
-
- private Object lock = new Object();
- private int index = 0;
- private int count;
- private boolean throw_error;
-
- private DefiningLoader[] defining_loaders;
-
- public RacyMisbehavingLoader(ClassLoader parent, int count, boolean throw_error) {
- super(parent);
- this.count = count;
- this.throw_error = throw_error;
- defining_loaders = new DefiningLoader[2];
- for (int i = 0; i != defining_loaders.length; ++i) {
- defining_loaders[i] = new DefiningLoader(parent);
- }
- }
-
- public void reportAfterLoading() {
- synchronized (lock) {
- ++index;
- if (index == 2 * count) {
- lock.notifyAll();
- }
- }
- }
-
- protected Class<?> findClass(String name) throws ClassNotFoundException
- {
- if (name.equals("Test")) {
- throw new Error("Unexpected RacyLoader.findClass(\"" + name + "\")");
- }
- return super.findClass(name);
- }
-
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- if (name.equals("Test")) {
- int my_index = syncWithOtherInstances(count);
- Class<?> result;
- if ((my_index & 1) == 0) {
- // Do not delay loading the correct class.
- result = defining_loaders[my_index & 1].loadClass(name, resolve);
- } else {
- // Delay loading the wrong class.
- syncWithOtherInstances(2 * count);
- if (throw_error) {
- throw new Error("RacyMisbehavingLoader throw_error=true");
- }
- result = defining_loaders[my_index & 1].loadClass("Test3", resolve);
- }
- return result;
- }
- return super.loadClass(name, resolve);
- }
-
- private int syncWithOtherInstances(int limit) {
- int my_index;
- synchronized (lock) {
- my_index = index;
- ++index;
- if (index != limit) {
- try {
- lock.wait(2000);
- if (index < limit) {
- throw new Error("Timed out; my_index=" + my_index + ", index=" + index);
- }
- } catch (InterruptedException ie) {
- throw new Error(ie);
- }
- } else {
- lock.notifyAll();
- }
- }
- return my_index;
- }
-}
diff --git a/test/628-vdex/expected.txt b/test/628-vdex/expected.txt
new file mode 100644
index 0000000..d0f61f6
--- /dev/null
+++ b/test/628-vdex/expected.txt
@@ -0,0 +1,2 @@
+In foo
+In foo
diff --git a/test/628-vdex/info.txt b/test/628-vdex/info.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/628-vdex/info.txt
diff --git a/test/628-vdex/run b/test/628-vdex/run
new file mode 100644
index 0000000..f1b0a95
--- /dev/null
+++ b/test/628-vdex/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 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.
+
+exec ${RUN} --vdex "${@}"
diff --git a/test/626-const-class-linking/src-multidex/Test.java b/test/628-vdex/src/Main.java
similarity index 61%
rename from test/626-const-class-linking/src-multidex/Test.java
rename to test/628-vdex/src/Main.java
index 1b0cc2a..7ceab2c 100644
--- a/test/626-const-class-linking/src-multidex/Test.java
+++ b/test/628-vdex/src/Main.java
@@ -14,5 +14,24 @@
* limitations under the License.
*/
-public class Test {
+public class Main {
+ Main() {
+ // Will be quickened with RETURN_VOID_NO_BARRIER.
+ }
+
+ public static void main(String[] args) {
+ Main m = new Main();
+ Object o = m;
+ // The call and field accesses will be quickened.
+ m.foo(m.a);
+
+ // The checkcast will be quickened.
+ m.foo(((Main)o).a);
+ }
+
+ int a;
+ void foo(int a) {
+ System.out.println("In foo");
+ }
}
+
diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt
index 0a5caa1..9b09327 100644
--- a/test/956-methodhandles/expected.txt
+++ b/test/956-methodhandles/expected.txt
@@ -7,3 +7,11 @@
String constructors done.
testReferenceReturnValueConversions done.
testPrimitiveReturnValueConversions done.
+Hi
+Hi
+Hi
+Hi
+Expect Hi here: Hi
+Don't expect Hi now
+[3, 2, 1]
+[1, 2, 3]
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index 8713caa..ee9c436 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -19,13 +19,14 @@
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.WrongMethodTypeException;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
public class Main {
@@ -74,6 +75,7 @@
testConstructors();
testStringConstructors();
testReturnValueConversions();
+ testVariableArity();
}
public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -555,6 +557,34 @@
}
}
+ public static void assertTrue(boolean value) {
+ if (!value) {
+ throw new AssertionError("assertTrue value: " + value);
+ }
+ }
+
+ public static void assertFalse(boolean value) {
+ if (value) {
+ throw new AssertionError("assertTrue value: " + value);
+ }
+ }
+
+ public static void assertEquals(int i1, int i2) {
+ if (i1 == i2) { return; }
+ throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+ }
+
+ public static void assertEquals(long i1, long i2) {
+ if (i1 == i2) { return; }
+ throw new AssertionError("assertEquals l1: " + i1 + ", l2: " + i2);
+ }
+
+ public static void assertEquals(Object o, Object p) {
+ if (o == p) { return; }
+ if (o != null && p != null && o.equals(p)) { return; }
+ throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+ }
+
public static void assertEquals(String s1, String s2) {
if (s1 == s2) {
return;
@@ -960,4 +990,480 @@
testReferenceReturnValueConversions();
testPrimitiveReturnValueConversions();
}
+
+ public static class BaseVariableArityTester {
+ public String update(Float f0, Float... floats) {
+ return "base " + f0 + ", " + Arrays.toString(floats);
+ }
+ }
+
+ public static class VariableArityTester extends BaseVariableArityTester {
+ private String lastResult;
+
+ // Constructors
+ public VariableArityTester() {}
+ public VariableArityTester(boolean... booleans) { update(booleans); }
+ public VariableArityTester(byte... bytes) { update(bytes); }
+ public VariableArityTester(char... chars) { update(chars); }
+ public VariableArityTester(short... shorts) { update(shorts); }
+ public VariableArityTester(int... ints) { update(ints); }
+ public VariableArityTester(long... longs) { update(longs); }
+ public VariableArityTester(float... floats) { update(floats); }
+ public VariableArityTester(double... doubles) { update(doubles); }
+ public VariableArityTester(Float f0, Float... floats) { update(f0, floats); }
+ public VariableArityTester(String s0, String... strings) { update(s0, strings); }
+ public VariableArityTester(char c, Number... numbers) { update(c, numbers); }
+ @SafeVarargs
+ public VariableArityTester(ArrayList<Integer> l0, ArrayList<Integer>... lists) {
+ update(l0, lists);
+ }
+ public VariableArityTester(List l0, List... lists) { update(l0, lists); }
+
+ // Methods
+ public String update(boolean... booleans) { return lastResult = tally(booleans); }
+ public String update(byte... bytes) { return lastResult = tally(bytes); }
+ public String update(char... chars) { return lastResult = tally(chars); }
+ public String update(short... shorts) { return lastResult = tally(shorts); }
+ public String update(int... ints) {
+ lastResult = tally(ints);
+ return lastResult;
+ }
+ public String update(long... longs) { return lastResult = tally(longs); }
+ public String update(float... floats) { return lastResult = tally(floats); }
+ public String update(double... doubles) { return lastResult = tally(doubles); }
+ @Override
+ public String update(Float f0, Float... floats) { return lastResult = tally(f0, floats); }
+ public String update(String s0, String... strings) { return lastResult = tally(s0, strings); }
+ public String update(char c, Number... numbers) { return lastResult = tally(c, numbers); }
+ @SafeVarargs
+ public final String update(ArrayList<Integer> l0, ArrayList<Integer>... lists) {
+ lastResult = tally(l0, lists);
+ return lastResult;
+ }
+ public String update(List l0, List... lists) { return lastResult = tally(l0, lists); }
+
+ public String arrayMethod(Object[] o) {
+ return Arrays.deepToString(o);
+ }
+
+ public String lastResult() { return lastResult; }
+
+ // Static Methods
+ public static String tally(boolean... booleans) { return Arrays.toString(booleans); }
+ public static String tally(byte... bytes) { return Arrays.toString(bytes); }
+ public static String tally(char... chars) { return Arrays.toString(chars); }
+ public static String tally(short... shorts) { return Arrays.toString(shorts); }
+ public static String tally(int... ints) { return Arrays.toString(ints); }
+ public static String tally(long... longs) { return Arrays.toString(longs); }
+ public static String tally(float... floats) { return Arrays.toString(floats); }
+ public static String tally(double... doubles) { return Arrays.toString(doubles); }
+ public static String tally(Float f0, Float... floats) {
+ return f0 + ", " + Arrays.toString(floats);
+ }
+ public static String tally(String s0, String... strings) {
+ return s0 + ", " + Arrays.toString(strings);
+ }
+ public static String tally(char c, Number... numbers) {
+ return c + ", " + Arrays.toString(numbers);
+ }
+ @SafeVarargs
+ public static String tally(ArrayList<Integer> l0, ArrayList<Integer>... lists) {
+ return Arrays.toString(l0.toArray()) + ", " + Arrays.deepToString(lists);
+ }
+ public static String tally(List l0, List... lists) {
+ return Arrays.deepToString(l0.toArray()) + ", " + Arrays.deepToString(lists);
+ }
+ public static void foo(int... ints) { System.out.println(Arrays.toString(ints)); }
+ public static long sumToPrimitive(int... ints) {
+ long result = 0;
+ for (int i : ints) result += i;
+ return result;
+ }
+ public static Long sumToReference(int... ints) {
+ System.err.println("Hi");
+ return new Long(sumToPrimitive(ints));
+ }
+ public static MethodHandles.Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+ }
+
+ // This method only exists to fool Jack's handling of types. See b/32536744.
+ public static Object getAsObject(String[] strings) {
+ return (Object) strings;
+ }
+
+ public static void testVariableArity() throws Throwable {
+ MethodHandle mh;
+ VariableArityTester vat = new VariableArityTester();
+
+ assertEquals("[1]", vat.update(1));
+ assertEquals("[1, 1]", vat.update(1, 1));
+ assertEquals("[1, 1, 1]", vat.update(1, 1, 1));
+
+ // Methods - boolean
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, boolean[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertFalse(mh.asFixedArity().isVarargsCollector());
+ assertEquals("[]", mh.invoke(vat));
+ assertEquals("[true, false, true]", mh.invoke(vat, true, false, true));
+ assertEquals("[true, false, true]", mh.invoke(vat, new boolean[] { true, false, true}));
+ assertEquals("[false, true]", mh.invoke(vat, Boolean.valueOf(false), Boolean.valueOf(true)));
+ try {
+ mh.invoke(vat, true, true, 0);
+ fail();
+ } catch (WrongMethodTypeException e) {}
+ try {
+ assertEquals("[false, true]", mh.invoke(vat, Boolean.valueOf(false), (Boolean) null));
+ fail();
+ } catch (NullPointerException e) {}
+
+ // Methods - byte
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, byte[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[]", mh.invoke(vat));
+ assertEquals("[32, 64, 97]", mh.invoke(vat, (byte) 32, Byte.valueOf((byte) 64), (byte) 97));
+ assertEquals("[32, 64, 97]", mh.invoke(vat, new byte[] {(byte) 32, (byte) 64, (byte) 97}));
+ try {
+ mh.invoke(vat, (byte) 1, Integer.valueOf(3), (byte) 0);
+ fail();
+ } catch (WrongMethodTypeException e) {}
+
+ // Methods - char
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, char[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[]", mh.invoke(vat));
+ assertEquals("[A, B, C]", mh.invoke(vat, 'A', Character.valueOf('B'), 'C'));
+ assertEquals("[W, X, Y, Z]", mh.invoke(vat, new char[] { 'W', 'X', 'Y', 'Z' }));
+
+ // Methods - short
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, short[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[]", mh.invoke(vat));
+ assertEquals("[32767, -32768, 0]",
+ mh.invoke(vat, Short.MAX_VALUE, Short.MIN_VALUE, Short.valueOf((short) 0)));
+ assertEquals("[1, -1]", mh.invoke(vat, new short[] { (short) 1, (short) -1 }));
+
+ // Methods - int
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, int[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[]", mh.invoke(vat));
+ assertEquals("[0, 2147483647, -2147483648, 0]",
+ mh.invoke(vat, Integer.valueOf(0), Integer.MAX_VALUE, Integer.MIN_VALUE, 0));
+ assertEquals("[0, -1, 1, 0]", mh.invoke(vat, new int[] { 0, -1, 1, 0 }));
+
+ assertEquals("[5, 4, 3, 2, 1]", (String) mh.invokeExact(vat, new int [] { 5, 4, 3, 2, 1 }));
+ try {
+ assertEquals("[5, 4, 3, 2, 1]", (String) mh.invokeExact(vat, 5, 4, 3, 2, 1));
+ fail();
+ } catch (WrongMethodTypeException e) {}
+ assertEquals("[5, 4, 3, 2, 1]", (String) mh.invoke(vat, 5, 4, 3, 2, 1));
+
+ // Methods - long
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, long[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[]", mh.invoke(vat));
+ assertEquals("[0, 9223372036854775807, -9223372036854775808]",
+ mh.invoke(vat, Long.valueOf(0), Long.MAX_VALUE, Long.MIN_VALUE));
+ assertEquals("[0, -1, 1, 0]", mh.invoke(vat, new long[] { 0, -1, 1, 0 }));
+
+ // Methods - float
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, float[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[]", mh.invoke(vat));
+ assertEquals("[0.0, 1.25, -1.25]",
+ mh.invoke(vat, 0.0f, Float.valueOf(1.25f), Float.valueOf(-1.25f)));
+ assertEquals("[0.0, -1.0, 1.0, 0.0]",
+ mh.invoke(vat, new float[] { 0.0f, -1.0f, 1.0f, 0.0f }));
+
+ // Methods - double
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, double[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[]", mh.invoke(vat));
+ assertEquals("[0.0, 1.25, -1.25]",
+ mh.invoke(vat, 0.0, Double.valueOf(1.25), Double.valueOf(-1.25)));
+ assertEquals("[0.0, -1.0, 1.0, 0.0]",
+ mh.invoke(vat, new double[] { 0.0, -1.0, 1.0, 0.0 }));
+ mh.invoke(vat, 0.3f, 1.33, 1.33);
+
+ // Methods - String
+ mh = MethodHandles.lookup().
+ findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, String.class, String[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("Echidna, []", mh.invoke(vat, "Echidna"));
+ assertEquals("Bongo, [Jerboa, Okapi]",
+ mh.invoke(vat, "Bongo", "Jerboa", "Okapi"));
+
+ // Methods - Float
+ mh = MethodHandles.lookup().
+ findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, Float.class, Float[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invoke(vat, Float.valueOf(9.99f),
+ new Float[] { Float.valueOf(0.0f),
+ Float.valueOf(0.1f),
+ Float.valueOf(1.1f) }));
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invoke(vat, Float.valueOf(9.99f), Float.valueOf(0.0f),
+ Float.valueOf(0.1f), Float.valueOf(1.1f)));
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invoke(vat, Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f));
+ try {
+ assertEquals("9.99, [77.0, 33.0, 64.0]",
+ (String) mh.invoke(vat, Float.valueOf(9.99f), 77, 33, 64));
+ fail();
+ } catch (WrongMethodTypeException e) {}
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invokeExact(vat, Float.valueOf(9.99f),
+ new Float[] { Float.valueOf(0.0f),
+ Float.valueOf(0.1f),
+ Float.valueOf(1.1f) }));
+ assertEquals("9.99, [0.0, null, 1.1]",
+ (String) mh.invokeExact(vat, Float.valueOf(9.99f),
+ new Float[] { Float.valueOf(0.0f),
+ null,
+ Float.valueOf(1.1f) }));
+ try {
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invokeExact(vat, Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f));
+ fail();
+ } catch (WrongMethodTypeException e) {}
+
+ // Methods - Number
+ mh = MethodHandles.lookup().
+ findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, char.class, Number[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertFalse(mh.asFixedArity().isVarargsCollector());
+ assertEquals("x, []", (String) mh.invoke(vat, 'x'));
+ assertEquals("x, [3.141]", (String) mh.invoke(vat, 'x', 3.141));
+ assertEquals("x, [null, 3.131, 37]",
+ (String) mh.invoke(vat, 'x', null, 3.131, new Integer(37)));
+ try {
+ assertEquals("x, [null, 3.131, bad, 37]",
+ (String) mh.invoke(vat, 'x', null, 3.131, "bad", new Integer(37)));
+ assertTrue(false);
+ fail();
+ } catch (ClassCastException e) {}
+ try {
+ assertEquals("x, [null, 3.131, bad, 37]",
+ (String) mh.invoke(
+ vat, 'x', (Process) null, 3.131, "bad", new Integer(37)));
+ assertTrue(false);
+ fail();
+ } catch (ClassCastException e) {}
+
+ // Methods - an array method that is not variable arity.
+ mh = MethodHandles.lookup().findVirtual(
+ VariableArityTester.class, "arrayMethod",
+ MethodType.methodType(String.class, Object[].class));
+ assertFalse(mh.isVarargsCollector());
+ mh.invoke(vat, new Object[] { "123" });
+ try {
+ assertEquals("-", mh.invoke(vat, new Float(3), new Float(4)));
+ fail();
+ } catch (WrongMethodTypeException e) {}
+ mh = mh.asVarargsCollector(Object[].class);
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[3.0, 4.0]", (String) mh.invoke(vat, new Float(3), new Float(4)));
+
+ // Constructors - default
+ mh = MethodHandles.lookup().findConstructor(
+ VariableArityTester.class, MethodType.methodType(void.class));
+ assertFalse(mh.isVarargsCollector());
+
+ // Constructors - boolean
+ mh = MethodHandles.lookup().findConstructor(
+ VariableArityTester.class, MethodType.methodType(void.class, boolean[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[true, true, false]",
+ ((VariableArityTester) mh.invoke(new boolean[] {true, true, false})).lastResult());
+ assertEquals("[true, true, false]",
+ ((VariableArityTester) mh.invoke(true, true, false)).lastResult());
+ try {
+ assertEquals("[true, true, false]",
+ ((VariableArityTester) mh.invokeExact(true, true, false)).lastResult());
+ fail();
+ } catch (WrongMethodTypeException e) {}
+
+ // Constructors - byte
+ mh = MethodHandles.lookup().findConstructor(
+ VariableArityTester.class, MethodType.methodType(void.class, byte[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[55, 66, 60]",
+ ((VariableArityTester)
+ mh.invoke(new byte[] {(byte) 55, (byte) 66, (byte) 60})).lastResult());
+ assertEquals("[55, 66, 60]",
+ ((VariableArityTester) mh.invoke(
+ (byte) 55, (byte) 66, (byte) 60)).lastResult());
+ try {
+ assertEquals("[55, 66, 60]",
+ ((VariableArityTester) mh.invokeExact(
+ (byte) 55, (byte) 66, (byte) 60)).lastResult());
+ fail();
+ } catch (WrongMethodTypeException e) {}
+ try {
+ assertEquals("[3, 3]",
+ ((VariableArityTester) mh.invoke(
+ new Number[] { Byte.valueOf((byte) 3), (byte) 3})).lastResult());
+ fail();
+ } catch (WrongMethodTypeException e) {}
+
+ // Constructors - String (have a different path than other reference types).
+ mh = MethodHandles.lookup().findConstructor(
+ VariableArityTester.class, MethodType.methodType(void.class, String.class, String[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("x, []", ((VariableArityTester) mh.invoke("x")).lastResult());
+ assertEquals("x, [y]", ((VariableArityTester) mh.invoke("x", "y")).lastResult());
+ assertEquals("x, [y, z]",
+ ((VariableArityTester) mh.invoke("x", new String[] { "y", "z" })).lastResult());
+ try {
+ assertEquals("x, [y]", ((VariableArityTester) mh.invokeExact("x", "y")).lastResult());
+ fail();
+ } catch (WrongMethodTypeException e) {}
+ assertEquals("x, [null, z]",
+ ((VariableArityTester) mh.invoke("x", new String[] { null, "z" })).lastResult());
+
+ // Constructors - Number
+ mh = MethodHandles.lookup().findConstructor(
+ VariableArityTester.class, MethodType.methodType(void.class, char.class, Number[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertFalse(mh.asFixedArity().isVarargsCollector());
+ assertEquals("x, []", ((VariableArityTester) mh.invoke('x')).lastResult());
+ assertEquals("x, [3.141]", ((VariableArityTester) mh.invoke('x', 3.141)).lastResult());
+ assertEquals("x, [null, 3.131, 37]",
+ ((VariableArityTester) mh.invoke('x', null, 3.131, new Integer(37))).lastResult());
+ try {
+ assertEquals("x, [null, 3.131, bad, 37]",
+ ((VariableArityTester) mh.invoke(
+ 'x', null, 3.131, "bad", new Integer(37))).lastResult());
+ assertTrue(false);
+ fail();
+ } catch (ClassCastException e) {}
+ try {
+ assertEquals("x, [null, 3.131, bad, 37]",
+ ((VariableArityTester) mh.invoke(
+ 'x', (Process) null, 3.131, "bad", new Integer(37))).lastResult());
+ assertTrue(false);
+ fail();
+ } catch (ClassCastException e) {}
+
+ // Static Methods - Float
+ mh = MethodHandles.lookup().
+ findStatic(VariableArityTester.class, "tally",
+ MethodType.methodType(String.class, Float.class, Float[].class));
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invoke(Float.valueOf(9.99f),
+ new Float[] { Float.valueOf(0.0f),
+ Float.valueOf(0.1f),
+ Float.valueOf(1.1f) }));
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invoke(Float.valueOf(9.99f), Float.valueOf(0.0f),
+ Float.valueOf(0.1f), Float.valueOf(1.1f)));
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invoke(Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f));
+ try {
+ assertEquals("9.99, [77.0, 33.0, 64.0]",
+ (String) mh.invoke(Float.valueOf(9.99f), 77, 33, 64));
+ fail();
+ } catch (WrongMethodTypeException e) {}
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invokeExact(Float.valueOf(9.99f),
+ new Float[] { Float.valueOf(0.0f),
+ Float.valueOf(0.1f),
+ Float.valueOf(1.1f) }));
+ assertEquals("9.99, [0.0, null, 1.1]",
+ (String) mh.invokeExact(Float.valueOf(9.99f),
+ new Float[] { Float.valueOf(0.0f),
+ null,
+ Float.valueOf(1.1f) }));
+ try {
+ assertEquals("9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invokeExact(Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f));
+ fail();
+ } catch (WrongMethodTypeException e) {}
+
+ // Special methods - Float
+ mh = VariableArityTester.lookup().
+ findSpecial(BaseVariableArityTester.class, "update",
+ MethodType.methodType(String.class, Float.class, Float[].class),
+ VariableArityTester.class);
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("base 9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invoke(vat,
+ Float.valueOf(9.99f),
+ new Float[] { Float.valueOf(0.0f),
+ Float.valueOf(0.1f),
+ Float.valueOf(1.1f) }));
+ assertEquals("base 9.99, [0.0, 0.1, 1.1]",
+ (String) mh.invoke(vat, Float.valueOf(9.99f), Float.valueOf(0.0f),
+ Float.valueOf(0.1f), Float.valueOf(1.1f)));
+
+ // Return value conversions.
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, int[].class));
+ assertEquals("[1, 2, 3]", (String) mh.invoke(vat, 1, 2, 3));
+ assertEquals("[1, 2, 3]", (Object) mh.invoke(vat, 1, 2, 3));
+ try {
+ assertEquals("[1, 2, 3, 4]", (long) mh.invoke(vat, 1, 2, 3));
+ fail();
+ } catch (WrongMethodTypeException e) {}
+ assertEquals("[1, 2, 3]", vat.lastResult());
+ mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "sumToPrimitive",
+ MethodType.methodType(long.class, int[].class));
+ assertEquals(10l, (long) mh.invoke(1, 2, 3, 4));
+ assertEquals(Long.valueOf(10l), (Long) mh.invoke(1, 2, 3, 4));
+ mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "sumToReference",
+ MethodType.methodType(Long.class, int[].class));
+ Object o = mh.invoke(1, 2, 3, 4);
+ long l = (long) mh.invoke(1, 2, 3, 4);
+ assertEquals(10l, (long) mh.invoke(1, 2, 3, 4));
+ assertEquals(Long.valueOf(10l), (Long) mh.invoke(1, 2, 3, 4));
+ try {
+ // WrongMethodTypeException should be raised before invoke here.
+ System.err.print("Expect Hi here: ");
+ assertEquals(Long.valueOf(10l), (Byte) mh.invoke(1, 2, 3, 4));
+ fail();
+ } catch (ClassCastException e) {}
+ try {
+ // WrongMethodTypeException should be raised before invoke here.
+ System.err.println("Don't expect Hi now");
+ byte b = (byte) mh.invoke(1, 2, 3, 4);
+ fail();
+ } catch (WrongMethodTypeException e) {}
+
+ // Return void produces 0 / null.
+ mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "foo",
+ MethodType.methodType(void.class, int[].class));
+ assertEquals(null, (Object) mh.invoke(3, 2, 1));
+ assertEquals(0l, (long) mh.invoke(1, 2, 3));
+
+ // Combinators
+ mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+ MethodType.methodType(String.class, boolean[].class));
+ assertTrue(mh.isVarargsCollector());
+ mh = mh.bindTo(vat);
+ assertFalse(mh.isVarargsCollector());
+ mh = mh.asVarargsCollector(boolean[].class);
+ assertTrue(mh.isVarargsCollector());
+ assertEquals("[]", mh.invoke());
+ assertEquals("[true, false, true]", mh.invoke(true, false, true));
+ assertEquals("[true, false, true]", mh.invoke(new boolean[] { true, false, true}));
+ assertEquals("[false, true]", mh.invoke(Boolean.valueOf(false), Boolean.valueOf(true)));
+ try {
+ mh.invoke(true, true, 0);
+ fail();
+ } catch (WrongMethodTypeException e) {}
+ }
}
diff --git a/test/Android.bp b/test/Android.bp
index 39a4059..fe20f29 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -314,7 +314,6 @@
"595-profile-saving/profile-saving.cc",
"596-app-images/app_images.cc",
"597-deopt-new-string/deopt.cc",
- "626-const-class-linking/clear_dex_cache_types.cc",
],
shared_libs: [
"libbacktrace",
diff --git a/test/etc/default-build b/test/etc/default-build
index 408dcfd..e663496 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -273,10 +273,8 @@
fi
# Create a single jar with two dex files for multidex.
-if [ ${NEED_DEX} = "true" ]; then
- if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
- zip $TEST_NAME.jar classes.dex classes2.dex
- else
- zip $TEST_NAME.jar classes.dex
- fi
+if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
+ zip $TEST_NAME.jar classes.dex classes2.dex
+elif [ ${NEED_DEX} = "true" ]; then
+ zip $TEST_NAME.jar classes.dex
fi
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index a71593c..bb3a3ad 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -58,6 +58,7 @@
ARGS=""
EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
DRY_RUN="n" # if y prepare to run the test but don't run it.
+TEST_VDEX="n"
while true; do
if [ "x$1" = "x--quiet" ]; then
@@ -243,6 +244,9 @@
elif [ "x$1" = "x--dry-run" ]; then
DRY_RUN="y"
shift
+ elif [ "x$1" = "x--vdex" ]; then
+ TEST_VDEX="y"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
@@ -312,8 +316,7 @@
if [ "$USE_JVM" = "y" ]; then
export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
# Xmx is necessary since we don't pass down the ART flags to JVM.
- # We pass the classes2 path whether it's used (src-multidex) or not.
- cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes:classes2 ${FLAGS} $MAIN $@ ${ARGS}"
+ cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes ${FLAGS} $MAIN $@ ${ARGS}"
if [ "$DEV_MODE" = "y" ]; then
echo $cmdline
fi
@@ -445,6 +448,7 @@
fi
dex2oat_cmdline="true"
+vdex_cmdline="true"
mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
strip_cmdline="true"
@@ -474,6 +478,9 @@
# Use -k 1m to SIGKILL it a minute later if it hasn't ended.
dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}"
fi
+ if [ "$TEST_VDEX" = "y" ]; then
+ vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+ fi
fi
if [ "$STRIP_DEX" = "y" ]; then
@@ -514,6 +521,7 @@
# Remove whitespace.
dex2oat_cmdline=$(echo $dex2oat_cmdline)
dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
+vdex_cmdline=$(echo $vdex_cmdline)
if [ "$HOST" = "n" ]; then
adb root > /dev/null
@@ -554,6 +562,7 @@
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
export PATH=$ANDROID_ROOT/bin:$PATH && \
$dex2oat_cmdline && \
+ $vdex_cmdline && \
$strip_cmdline && \
$dalvikvm_cmdline"
@@ -627,7 +636,7 @@
fi
if [ "$DEV_MODE" = "y" ]; then
- echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $strip_cmdline && $cmdline"
+ echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $cmdline"
fi
cd $ANDROID_BUILD_TOP
@@ -635,6 +644,7 @@
rm -rf ${DEX_LOCATION}/dalvik-cache/
mkdir -p ${mkdir_locations} || exit 1
$dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
+ $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
$strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
# For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use
diff --git a/test/run-test b/test/run-test
index 37eefb3..ea9622a 100755
--- a/test/run-test
+++ b/test/run-test
@@ -351,6 +351,9 @@
elif [ "x$1" = "x--bisection-search" ]; then
bisection_search="yes"
shift
+ elif [ "x$1" = "x--vdex" ]; then
+ run_args="${run_args} --vdex"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
usage="yes"
@@ -640,6 +643,7 @@
echo " --pic-test Compile the test code position independent."
echo " --quiet Don't print anything except failure messages"
echo " --bisection-search Perform bisection bug search."
+ echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild."
) 1>&2 # Direct to stderr so usage is not printed if --quiet is set.
exit 1
fi