Merge "Use input-vdex-fd, or input-vdex in dex2oat."
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/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 511a787..3fb10d8 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -31,49 +31,68 @@
VerificationResults::VerificationResults(const CompilerOptions* compiler_options)
: compiler_options_(compiler_options),
verified_methods_lock_("compiler verified methods lock"),
- verified_methods_(),
- rejected_classes_lock_("compiler rejected classes lock"),
- rejected_classes_() {
-}
+ rejected_classes_lock_("compiler rejected classes lock") {}
VerificationResults::~VerificationResults() {
- Thread* self = Thread::Current();
- {
- WriterMutexLock mu(self, verified_methods_lock_);
- STLDeleteValues(&verified_methods_);
- }
+ WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
+ DeleteResults(preregistered_dex_files_);
+ STLDeleteValues(&verified_methods_);
}
void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
DCHECK(method_verifier != nullptr);
MethodReference ref = method_verifier->GetMethodReference();
bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
- const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile);
+ std::unique_ptr<const VerifiedMethod> verified_method(
+ VerifiedMethod::Create(method_verifier, compile));
if (verified_method == nullptr) {
// We'll punt this later.
return;
}
-
- WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
- auto it = verified_methods_.find(ref);
- if (it != verified_methods_.end()) {
+ bool inserted;
+ DexFileMethodArray* const array = GetMethodArray(ref.dex_file);
+ const VerifiedMethod* existing = nullptr;
+ if (array != nullptr) {
+ DCHECK(array != nullptr);
+ Atomic<const VerifiedMethod*>* slot = &(*array)[ref.dex_method_index];
+ inserted = slot->CompareExchangeStrongSequentiallyConsistent(nullptr, verified_method.get());
+ if (!inserted) {
+ existing = slot->LoadSequentiallyConsistent();
+ DCHECK_NE(verified_method.get(), existing);
+ }
+ } else {
+ WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
+ auto it = verified_methods_.find(ref);
+ inserted = it == verified_methods_.end();
+ if (inserted) {
+ verified_methods_.Put(ref, verified_method.get());
+ DCHECK(verified_methods_.find(ref) != verified_methods_.end());
+ } else {
+ existing = it->second;
+ }
+ }
+ if (inserted) {
+ // Successfully added, release the unique_ptr since we no longer have ownership.
+ DCHECK_EQ(GetVerifiedMethod(ref), verified_method.get());
+ verified_method.release();
+ } else {
// TODO: Investigate why are we doing the work again for this method and try to avoid it.
LOG(WARNING) << "Method processed more than once: " << ref.PrettyMethod();
if (!Runtime::Current()->UseJitCompilation()) {
- DCHECK_EQ(it->second->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
- DCHECK_EQ(it->second->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
+ DCHECK_EQ(existing->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
+ DCHECK_EQ(existing->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
}
- // Delete the new verified method since there was already an existing one registered. It
- // is unsafe to replace the existing one since the JIT may be using it to generate a
- // native GC map.
- delete verified_method;
- return;
+ // Let the unique_ptr delete the new verified method since there was already an existing one
+ // registered. It is unsafe to replace the existing one since the JIT may be using it to
+ // generate a native GC map.
}
- verified_methods_.Put(ref, verified_method);
- DCHECK(verified_methods_.find(ref) != verified_methods_.end());
}
const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
+ DexFileMethodArray* array = GetMethodArray(ref.dex_file);
+ if (array != nullptr) {
+ return (*array)[ref.dex_method_index].LoadRelaxed();
+ }
ReaderMutexLock mu(Thread::Current(), verified_methods_lock_);
auto it = verified_methods_.find(ref);
return (it != verified_methods_.end()) ? it->second : nullptr;
@@ -105,4 +124,42 @@
return true;
}
+void VerificationResults::PreRegisterDexFile(const DexFile* dex_file) {
+ CHECK(preregistered_dex_files_.find(dex_file) == preregistered_dex_files_.end())
+ << dex_file->GetLocation();
+ DexFileMethodArray array(dex_file->NumMethodIds());
+ WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
+ // There can be some verified methods that are already registered for the dex_file since we set
+ // up well known classes earlier. Remove these and put them in the array so that we don't
+ // accidentally miss seeing them.
+ for (auto it = verified_methods_.begin(); it != verified_methods_.end(); ) {
+ MethodReference ref = it->first;
+ if (ref.dex_file == dex_file) {
+ array[ref.dex_method_index].StoreSequentiallyConsistent(it->second);
+ it = verified_methods_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ preregistered_dex_files_.emplace(dex_file, std::move(array));
+}
+
+void VerificationResults::DeleteResults(DexFileResults& array) {
+ for (auto& pair : array) {
+ for (Atomic<const VerifiedMethod*>& method : pair.second) {
+ delete method.LoadSequentiallyConsistent();
+ }
+ }
+ array.clear();
+}
+
+VerificationResults::DexFileMethodArray* VerificationResults::GetMethodArray(
+ const DexFile* dex_file) {
+ auto it = preregistered_dex_files_.find(dex_file);
+ if (it != preregistered_dex_files_.end()) {
+ return &it->second;
+ }
+ return nullptr;
+}
+
} // namespace art
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 6afd1ab..b3356e0 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -19,8 +19,8 @@
#include <stdint.h>
#include <set>
-#include <vector>
+#include "base/dchecked_vector.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "class_reference.h"
@@ -38,35 +38,48 @@
// Used by CompilerCallbacks to track verification information from the Runtime.
class VerificationResults {
- public:
- explicit VerificationResults(const CompilerOptions* compiler_options);
- ~VerificationResults();
+ public:
+ explicit VerificationResults(const CompilerOptions* compiler_options);
+ ~VerificationResults();
- void ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!verified_methods_lock_);
+ void ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!verified_methods_lock_);
- const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
- REQUIRES(!verified_methods_lock_);
+ const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
+ REQUIRES(!verified_methods_lock_);
- void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
- bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_);
+ void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
+ bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_);
- bool IsCandidateForCompilation(MethodReference& method_ref,
- const uint32_t access_flags);
+ bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags);
- private:
- const CompilerOptions* const compiler_options_;
+ // Add a dex file array to the preregistered_dex_files_ array. These dex files require no locks to
+ // access. It is not safe to call if other callers are calling GetVerifiedMethod concurrently.
+ void PreRegisterDexFile(const DexFile* dex_file) REQUIRES(!verified_methods_lock_);
- // Verified methods.
- typedef SafeMap<MethodReference, const VerifiedMethod*,
- MethodReferenceComparator> VerifiedMethodMap;
- ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
+ private:
+ // Verified methods. The method array is fixed to avoid needing a lock to extend it.
+ using DexFileMethodArray = dchecked_vector<Atomic<const VerifiedMethod*>>;
+ using DexFileResults = std::map<const DexFile*, DexFileMethodArray>;
+ using VerifiedMethodMap = SafeMap<MethodReference,
+ const VerifiedMethod*,
+ MethodReferenceComparator>;
- // Rejected classes.
- ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
+ static void DeleteResults(DexFileResults& array);
+
+ DexFileMethodArray* GetMethodArray(const DexFile* dex_file) REQUIRES(!verified_methods_lock_);
+ VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
+ const CompilerOptions* const compiler_options_;
+
+ // Dex2oat can preregister dex files to avoid locking when calling GetVerifiedMethod.
+ DexFileResults preregistered_dex_files_;
+
+ ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+
+ // Rejected classes.
+ ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
};
} // namespace art
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 194c458..9e6032f 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1634,6 +1634,9 @@
dex_caches_.push_back(soa.AddLocalReference<jobject>(
class_linker->RegisterDexFile(*dex_file,
soa.Decode<mirror::ClassLoader>(class_loader_).Ptr())));
+ // Pre-register dex files so that we can access verification results without locks during
+ // compilation and verification.
+ verification_results_->PreRegisterDexFile(dex_file);
}
return true;
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/thread.h b/runtime/thread.h
index 3f13db1..97093a6 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -39,6 +39,7 @@
#include "jvalue.h"
#include "object_callbacks.h"
#include "offsets.h"
+#include "runtime.h"
#include "runtime_stats.h"
#include "stack.h"
#include "thread_state.h"
@@ -948,15 +949,17 @@
}
std::vector<ArtMethod*>* GetStackTraceSample() const {
+ DCHECK(!Runtime::Current()->IsAotCompiler());
return tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
}
void SetStackTraceSample(std::vector<ArtMethod*>* sample) {
- DCHECK(sample == nullptr || tlsPtr_.deps_or_stack_trace_sample.verifier_deps == nullptr);
+ DCHECK(!Runtime::Current()->IsAotCompiler());
tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample = sample;
}
verifier::VerifierDeps* GetVerifierDeps() const {
+ DCHECK(Runtime::Current()->IsAotCompiler());
return tlsPtr_.deps_or_stack_trace_sample.verifier_deps;
}
@@ -964,8 +967,8 @@
// entry in the thread is cleared before destruction of the actual VerifierDeps
// object, or the thread.
void SetVerifierDeps(verifier::VerifierDeps* verifier_deps) {
- DCHECK(verifier_deps == nullptr ||
- tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample == nullptr);
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ DCHECK(verifier_deps == nullptr || tlsPtr_.deps_or_stack_trace_sample.verifier_deps == nullptr);
tlsPtr_.deps_or_stack_trace_sample.verifier_deps = verifier_deps;
}
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index fefe5a3..01af5ec 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -84,16 +84,18 @@
ObjPtr<mirror::Class> klass) {
DCHECK(klass != nullptr);
ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache();
- // Array classes do not have a dex cache.
+ // Array and proxy classes do not have a dex cache.
if (!klass->IsArrayClass() && !klass->IsProxyClass()) {
DCHECK(dex_cache != nullptr) << klass->PrettyClass();
if (dex_cache->GetDexFile() == &dex_file) {
// FindStringId is slow, try to go through the class def if we have one.
const DexFile::ClassDef* class_def = klass->GetClassDef();
DCHECK(class_def != nullptr) << klass->PrettyClass();
- std::string temp;
const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def->class_idx_);
- DCHECK_EQ(GetIdFromString(dex_file, klass->GetDescriptor(&temp)), type_id.descriptor_idx_);
+ if (kIsDebugBuild) {
+ std::string temp;
+ CHECK_EQ(GetIdFromString(dex_file, klass->GetDescriptor(&temp)), type_id.descriptor_idx_);
+ }
return type_id.descriptor_idx_;
}
}
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.run-test.mk b/test/Android.run-test.mk
index af4ef91..554f66d 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -792,12 +792,8 @@
endif
# Also need libopenjdkjvmti.
-TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libopenjdkjvmti)
-TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libopenjdkjvmtid)
-ifdef TARGET_2ND_ARCH
-TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libopenjdkjvmti)
-TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libopenjdkjvmtid)
-endif
+TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmti
+TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmtid
# All tests require the host executables. The tests also depend on the core images, but on
# specific version depending on the compiler.