diff options
24 files changed, 698 insertions, 232 deletions
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index c4bf1c7889..10da2eaa83 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1695,9 +1695,12 @@ class OatDumper { Handle<mirror::DexCache> dex_cache = hs->NewHandle( runtime->GetClassLinker()->RegisterDexFile(*dex_file, options_.class_loader_->Get())); CHECK(dex_cache != nullptr); + ArtMethod* method = runtime->GetClassLinker()->ResolveMethodWithoutInvokeType( + dex_method_idx, dex_cache, *options_.class_loader_); + CHECK(method != nullptr); return verifier::MethodVerifier::VerifyMethodAndDump( soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_, - class_def, code_item, nullptr, method_access_flags); + class_def, code_item, method, method_access_flags); } return nullptr; diff --git a/runtime/art_method.cc b/runtime/art_method.cc index f3c495957f..41b01c251b 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -43,6 +43,7 @@ #include "mirror/object_array-inl.h" #include "mirror/string.h" #include "oat_file-inl.h" +#include "quicken_info.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" @@ -578,6 +579,24 @@ ArrayRef<const uint8_t> ArtMethod::GetQuickenedInfo() { GetDexMethodIndex()); } +uint16_t ArtMethod::GetIndexFromQuickening(uint32_t dex_pc) { + ArrayRef<const uint8_t> data = GetQuickenedInfo(); + if (data.empty()) { + return DexFile::kDexNoIndex16; + } + QuickenInfoTable table(data); + uint32_t quicken_index = 0; + for (const DexInstructionPcPair& pair : DexInstructions()) { + if (pair.DexPc() == dex_pc) { + return table.GetData(quicken_index); + } + if (QuickenInfoTable::NeedsIndexForInstruction(&pair.Inst())) { + ++quicken_index; + } + } + return DexFile::kDexNoIndex16; +} + const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { // Our callers should make sure they don't pass the instrumentation exit pc, // as this method does not look at the side instrumentation stack. diff --git a/runtime/art_method.h b/runtime/art_method.h index bd9b64df36..64d293200f 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -683,6 +683,7 @@ class ArtMethod FINAL { } ArrayRef<const uint8_t> GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_); + uint16_t GetIndexFromQuickening(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the method header for the compiled code containing 'pc'. Note that runtime // methods will return null for this method, as they are not oat based. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 549e6a4dbc..8a29ff33b7 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -8287,29 +8287,34 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( DexFile::MethodHandleType handle_type = static_cast<DexFile::MethodHandleType>(method_handle.method_handle_type_); mirror::MethodHandle::Kind kind; + bool is_put; bool is_static; int32_t num_params; switch (handle_type) { case DexFile::MethodHandleType::kStaticPut: { kind = mirror::MethodHandle::Kind::kStaticPut; + is_put = true; is_static = true; num_params = 1; break; } case DexFile::MethodHandleType::kStaticGet: { kind = mirror::MethodHandle::Kind::kStaticGet; + is_put = false; is_static = true; num_params = 0; break; } case DexFile::MethodHandleType::kInstancePut: { kind = mirror::MethodHandle::Kind::kInstancePut; + is_put = true; is_static = false; num_params = 2; break; } case DexFile::MethodHandleType::kInstanceGet: { kind = mirror::MethodHandle::Kind::kInstanceGet; + is_put = false; is_static = false; num_params = 1; break; @@ -8331,6 +8336,10 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( ThrowIllegalAccessErrorField(referring_class, target_field); return nullptr; } + if (UNLIKELY(is_put && target_field->IsFinal())) { + ThrowIllegalAccessErrorField(referring_class, target_field); + return nullptr; + } } else { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 7484dd9207..945442d539 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -34,6 +34,7 @@ #include "nativehelper/scoped_local_ref.h" #include "obj_ptr-inl.h" #include "thread.h" +#include "vdex_file.h" #include "verifier/method_verifier.h" #include "well_known_classes.h" @@ -608,13 +609,10 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { break; case Instruction::INVOKE_VIRTUAL_QUICK: case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { - // Since we replaced the method index, we ask the verifier to tell us which - // method is invoked at this location. - ArtMethod* invoked_method = - verifier::MethodVerifier::FindInvokedMethodAtDexPc(method, throw_dex_pc); - if (invoked_method != nullptr) { + uint16_t method_idx = method->GetIndexFromQuickening(throw_dex_pc); + if (method_idx != DexFile::kDexNoIndex16) { // NPE with precise message. - ThrowNullPointerExceptionForMethodAccess(invoked_method, kVirtual); + ThrowNullPointerExceptionForMethodAccess(method_idx, kVirtual); } else { // NPE with imprecise message. ThrowNullPointerException("Attempt to invoke a virtual method on a null object reference"); @@ -641,17 +639,13 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IGET_SHORT_QUICK: case Instruction::IGET_WIDE_QUICK: case Instruction::IGET_OBJECT_QUICK: { - // Since we replaced the field index, we ask the verifier to tell us which - // field is accessed at this location. - ArtField* field = - verifier::MethodVerifier::FindAccessedFieldAtDexPc(method, throw_dex_pc); - if (field != nullptr) { - // NPE with precise message. - ThrowNullPointerExceptionForFieldAccess(field, true /* read */); - } else { - // NPE with imprecise message. - ThrowNullPointerException("Attempt to read from a field on a null object reference"); - } + uint16_t field_idx = method->GetIndexFromQuickening(throw_dex_pc); + ArtField* field = nullptr; + CHECK_NE(field_idx, DexFile::kDexNoIndex16); + field = Runtime::Current()->GetClassLinker()->ResolveField( + field_idx, method, /* is_static */ false); + Thread::Current()->ClearException(); // Resolution may fail, ignore. + ThrowNullPointerExceptionForFieldAccess(field, true /* read */); break; } case Instruction::IPUT: @@ -661,8 +655,8 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { - ArtField* field = - Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false); + ArtField* field = Runtime::Current()->GetClassLinker()->ResolveField( + instr.VRegC_22c(), method, /* is_static */ false); Thread::Current()->ClearException(); // Resolution may fail, ignore. ThrowNullPointerExceptionForFieldAccess(field, false /* write */); break; @@ -674,17 +668,13 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IPUT_SHORT_QUICK: case Instruction::IPUT_WIDE_QUICK: case Instruction::IPUT_OBJECT_QUICK: { - // Since we replaced the field index, we ask the verifier to tell us which - // field is accessed at this location. - ArtField* field = - verifier::MethodVerifier::FindAccessedFieldAtDexPc(method, throw_dex_pc); - if (field != nullptr) { - // NPE with precise message. - ThrowNullPointerExceptionForFieldAccess(field, false /* write */); - } else { - // NPE with imprecise message. - ThrowNullPointerException("Attempt to write to a field on a null object reference"); - } + uint16_t field_idx = method->GetIndexFromQuickening(throw_dex_pc); + ArtField* field = nullptr; + CHECK_NE(field_idx, DexFile::kDexNoIndex16); + field = Runtime::Current()->GetClassLinker()->ResolveField( + field_idx, method, /* is_static */ false); + Thread::Current()->ClearException(); // Resolution may fail, ignore. + ThrowNullPointerExceptionForFieldAccess(field, false /* write */); break; } case Instruction::AGET: diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 38ca4c9623..718f9176e9 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -25,6 +25,7 @@ #include "base/file_utils.h" #include "base/logging.h" // For VLOG. +#include "base/macros.h" #include "base/os.h" #include "base/stl_util.h" #include "base/utils.h" @@ -1303,18 +1304,49 @@ void OatFileAssistant::GetOptimizationStatus( InstructionSet isa, std::string* out_compilation_filter, std::string* out_compilation_reason) { - // Try to load the oat file as we would do at runtime. - OatFileAssistant oat_file_assistant(filename.c_str(), isa, true /* load_executable */); + // It may not be possible to load an oat file executable (e.g., selinux restrictions). Load + // non-executable and check the status manually. + OatFileAssistant oat_file_assistant(filename.c_str(), isa, false /* load_executable */); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); if (oat_file == nullptr) { *out_compilation_filter = "run-from-apk"; *out_compilation_reason = "unknown"; - } else { - *out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter()); - const char* reason = oat_file->GetCompilationReason(); - *out_compilation_reason = reason == nullptr ? "unknown" : reason; + return; } + + OatStatus status = oat_file_assistant.GivenOatFileStatus(*oat_file); + const char* reason = oat_file->GetCompilationReason(); + *out_compilation_reason = reason == nullptr ? "unknown" : reason; + switch (status) { + case OatStatus::kOatUpToDate: + *out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter()); + return; + + case kOatCannotOpen: // This should never happen, but be robust. + *out_compilation_filter = "error"; + *out_compilation_reason = "error"; + return; + + // 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. + case kOatBootImageOutOfDate: + FALLTHROUGH_INTENDED; + case kOatDexOutOfDate: + if (oat_file_assistant.HasOriginalDexFiles()) { + *out_compilation_filter = "run-from-apk-fallback"; + } else { + *out_compilation_filter = "run-from-vdex-fallback"; + } + return; + + case kOatRelocationOutOfDate: + // On relocation-out-of-date, we'd run the dex code. + *out_compilation_filter = "run-from-vdex-fallback"; + return; + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); } } // namespace art diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index 445a6ff7de..4b92c56b79 100644 --- a/runtime/verifier/method_verifier-inl.h +++ b/runtime/verifier/method_verifier-inl.h @@ -49,7 +49,7 @@ inline mirror::DexCache* MethodVerifier::GetDexCache() { } inline ArtMethod* MethodVerifier::GetMethod() const { - return mirror_method_; + return method_being_verified_; } inline MethodReference MethodVerifier::GetMethodReference() const { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 2a2afc4a27..b07001e595 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -56,6 +56,7 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" +#include "vdex_file.h" #include "verifier_compiler_binding.h" #include "verifier_deps.h" @@ -565,7 +566,7 @@ MethodVerifier::MethodVerifier(Thread* self, reg_table_(allocator_), work_insn_idx_(dex::kDexNoIndex), dex_method_idx_(dex_method_idx), - mirror_method_(method), + method_being_verified_(method), method_access_flags_(method_access_flags), return_type_(nullptr), dex_file_(dex_file), @@ -643,87 +644,6 @@ void MethodVerifier::FindLocksAtDexPc() { } } -ArtField* MethodVerifier::FindAccessedFieldAtDexPc(ArtMethod* m, uint32_t dex_pc) { - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader())); - MethodVerifier verifier(hs.Self(), - m->GetDexFile(), - dex_cache, - class_loader, - m->GetClassDef(), - m->GetCodeItem(), - m->GetDexMethodIndex(), - m, - m->GetAccessFlags(), - true /* can_load_classes */, - true /* allow_soft_failures */, - false /* need_precise_constants */, - false /* verify_to_dump */, - true /* allow_thread_suspension */); - return verifier.FindAccessedFieldAtDexPc(dex_pc); -} - -ArtField* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) { - CHECK(code_item_accessor_.HasCodeItem()); // This only makes sense for methods with code. - - // Strictly speaking, we ought to be able to get away with doing a subset of the full method - // verification. In practice, the phase we want relies on data structures set up by all the - // earlier passes, so we just run the full method verification and bail out early when we've - // got what we wanted. - bool success = Verify(); - if (!success) { - return nullptr; - } - RegisterLine* register_line = reg_table_.GetLine(dex_pc); - if (register_line == nullptr) { - return nullptr; - } - const Instruction* inst = &code_item_accessor_.InstructionAt(dex_pc); - return GetQuickFieldAccess(inst, register_line); -} - -ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_pc) { - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader())); - MethodVerifier verifier(hs.Self(), - m->GetDexFile(), - dex_cache, - class_loader, - m->GetClassDef(), - m->GetCodeItem(), - m->GetDexMethodIndex(), - m, - m->GetAccessFlags(), - true /* can_load_classes */, - true /* allow_soft_failures */, - false /* need_precise_constants */, - false /* verify_to_dump */, - true /* allow_thread_suspension */); - return verifier.FindInvokedMethodAtDexPc(dex_pc); -} - -ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { - CHECK(code_item_accessor_.HasCodeItem()); // This only makes sense for methods with code. - - // Strictly speaking, we ought to be able to get away with doing a subset of the full method - // verification. In practice, the phase we want relies on data structures set up by all the - // earlier passes, so we just run the full method verification and bail out early when we've - // got what we wanted. - bool success = Verify(); - if (!success) { - return nullptr; - } - RegisterLine* register_line = reg_table_.GetLine(dex_pc); - if (register_line == nullptr) { - return nullptr; - } - const Instruction* inst = &code_item_accessor_.InstructionAt(dex_pc); - const bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); - return GetQuickInvokedMethod(inst, register_line, is_range, false); -} - bool MethodVerifier::Verify() { // Some older code doesn't correctly mark constructors as such. Test for this case by looking at // the name. @@ -4414,62 +4334,24 @@ bool MethodVerifier::CheckSignaturePolymorphicReceiver(const Instruction* inst) return true; } -ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, RegisterLine* reg_line, - bool is_range, bool allow_failure) { +ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, bool is_range) { if (is_range) { DCHECK_EQ(inst->Opcode(), Instruction::INVOKE_VIRTUAL_RANGE_QUICK); } else { DCHECK_EQ(inst->Opcode(), Instruction::INVOKE_VIRTUAL_QUICK); } - const RegType& actual_arg_type = reg_line->GetInvocationThis(this, inst, allow_failure); - if (!actual_arg_type.HasClass()) { - VLOG(verifier) << "Failed to get mirror::Class* from '" << actual_arg_type << "'"; - return nullptr; - } - mirror::Class* klass = actual_arg_type.GetClass(); - mirror::Class* dispatch_class; - if (klass->IsInterface()) { - // Derive Object.class from Class.class.getSuperclass(). - mirror::Class* object_klass = klass->GetClass()->GetSuperClass(); - if (FailOrAbort(object_klass->IsObjectClass(), - "Failed to find Object class in quickened invoke receiver", - work_insn_idx_)) { - return nullptr; - } - dispatch_class = object_klass; - } else { - dispatch_class = klass; - } - if (!dispatch_class->HasVTable()) { - FailOrAbort(allow_failure, - "Receiver class has no vtable for quickened invoke at ", - work_insn_idx_); - return nullptr; - } - uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - auto* cl = Runtime::Current()->GetClassLinker(); - auto pointer_size = cl->GetImagePointerSize(); - if (static_cast<int32_t>(vtable_index) >= dispatch_class->GetVTableLength()) { - FailOrAbort(allow_failure, - "Receiver class has not enough vtable slots for quickened invoke at ", - work_insn_idx_); - return nullptr; - } - ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index, pointer_size); - if (self_->IsExceptionPending()) { - FailOrAbort(allow_failure, - "Unexpected exception pending for quickened invoke at ", - work_insn_idx_); - return nullptr; - } - return res_method; + + DCHECK(method_being_verified_ != nullptr); + uint16_t method_idx = method_being_verified_->GetIndexFromQuickening(work_insn_idx_); + CHECK_NE(method_idx, DexFile::kDexNoIndex16); + return ResolveMethodAndCheckAccess(method_idx, METHOD_VIRTUAL); } ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, bool is_range) { DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_) << dex_file_->PrettyMethod(dex_method_idx_, true) << "@" << work_insn_idx_; - ArtMethod* res_method = GetQuickInvokedMethod(inst, work_line_.get(), is_range, false); + ArtMethod* res_method = GetQuickInvokedMethod(inst, is_range); if (res_method == nullptr) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name(); return nullptr; @@ -5090,22 +4972,17 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& } } -ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) { - DCHECK(IsInstructionIGetQuickOrIPutQuick(inst->Opcode())) << inst->Opcode(); - const RegType& object_type = reg_line->GetRegisterType(this, inst->VRegB_22c()); - if (!object_type.HasClass()) { - VLOG(verifier) << "Failed to get mirror::Class* from '" << object_type << "'"; - return nullptr; - } - uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c()); - ArtField* const f = ArtField::FindInstanceFieldWithOffset(object_type.GetClass(), field_offset); - if (f == nullptr) { - VLOG(verifier) << "Failed to find instance field at offset '" << field_offset - << "' from '" << mirror::Class::PrettyDescriptor(object_type.GetClass()) << "'"; - } else { - DCHECK_EQ(f->GetOffset().Uint32Value(), field_offset); +ArtField* MethodVerifier::GetQuickAccessedField() { + DCHECK(method_being_verified_ != nullptr); + uint16_t field_idx = method_being_verified_->GetIndexFromQuickening(work_insn_idx_); + CHECK_NE(field_idx, DexFile::kDexNoIndex16); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ArtField* field = class_linker->ResolveFieldJLS(field_idx, dex_cache_, class_loader_); + if (field == nullptr) { + DCHECK(self_->IsExceptionPending()); + self_->ClearException(); } - return f; + return field; } template <MethodVerifier::FieldAccessType kAccType> @@ -5113,7 +4990,7 @@ void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, const RegTy bool is_primitive) { DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_); - ArtField* field = GetQuickFieldAccess(inst, work_line_.get()); + ArtField* field = GetQuickAccessedField(); if (field == nullptr) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); return; @@ -5318,12 +5195,12 @@ InstructionFlags* MethodVerifier::CurrentInsnFlags() { const RegType& MethodVerifier::GetMethodReturnType() { if (return_type_ == nullptr) { - if (mirror_method_ != nullptr) { + if (method_being_verified_ != nullptr) { ObjPtr<mirror::Class> return_type_class = can_load_classes_ - ? mirror_method_->ResolveReturnType() - : mirror_method_->LookupResolvedReturnType(); + ? method_being_verified_->ResolveReturnType() + : method_being_verified_->LookupResolvedReturnType(); if (return_type_class != nullptr) { - return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(), + return_type_ = &FromClass(method_being_verified_->GetReturnTypeDescriptor(), return_type_class.Ptr(), return_type_class->CannotBeAssignedFromOtherTypes()); } else { @@ -5347,8 +5224,8 @@ const RegType& MethodVerifier::GetDeclaringClass() { const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); - if (mirror_method_ != nullptr) { - mirror::Class* klass = mirror_method_->GetDeclaringClass(); + if (method_being_verified_ != nullptr) { + mirror::Class* klass = method_being_verified_->GetDeclaringClass(); declaring_class_ = &FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()); } else { declaring_class_ = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 9a94297942..4c9518b0ec 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -162,20 +162,11 @@ class MethodVerifier { }; // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding // to the locks held at 'dex_pc' in method 'm'. + // Note: this is the only situation where the verifier will visit quickened instructions. static void FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc, std::vector<DexLockInfo>* monitor_enter_dex_pcs) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns the accessed field corresponding to the quick instruction's field - // offset at 'dex_pc' in method 'm'. - static ArtField* FindAccessedFieldAtDexPc(ArtMethod* m, uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Returns the invoked method corresponding to the quick instruction's vtable - // index at 'dex_pc' in method 'm'. - static ArtMethod* FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_); - static void Init() REQUIRES_SHARED(Locks::mutator_lock_); static void Shutdown(); @@ -206,7 +197,7 @@ class MethodVerifier { ALWAYS_INLINE InstructionFlags& GetInstructionFlags(size_t index); mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); mirror::DexCache* GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* GetMethod() const REQUIRES_SHARED(Locks::mutator_lock_); + ArtMethod* GetMethod() const; MethodReference GetMethodReference() const; uint32_t GetAccessFlags() const; bool HasCheckCasts() const; @@ -219,13 +210,11 @@ class MethodVerifier { const RegType& ResolveCheckedClass(dex::TypeIndex class_idx) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the method of a quick invoke or null if it cannot be found. - ArtMethod* GetQuickInvokedMethod(const Instruction* inst, RegisterLine* reg_line, - bool is_range, bool allow_failure) + ArtMethod* GetQuickInvokedMethod(const Instruction* inst, bool is_range) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the access field of a quick field access (iget/iput-quick) or null // if it cannot be found. - ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) - REQUIRES_SHARED(Locks::mutator_lock_); + ArtField* GetQuickAccessedField() REQUIRES_SHARED(Locks::mutator_lock_); uint32_t GetEncounteredFailureTypes() { return encountered_failure_types_; @@ -332,15 +321,6 @@ class MethodVerifier { void FindLocksAtDexPc() REQUIRES_SHARED(Locks::mutator_lock_); - ArtField* FindAccessedFieldAtDexPc(uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_); - - ArtMethod* FindInvokedMethodAtDexPc(uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_); - - SafeMap<uint32_t, std::set<uint32_t>>& FindStringInitMap() - REQUIRES_SHARED(Locks::mutator_lock_); - /* * Compute the width of the instruction at each address in the instruction stream, and store it in * insn_flags_. Addresses that are in the middle of an instruction, or that are part of switch @@ -745,8 +725,7 @@ class MethodVerifier { RegisterLineArenaUniquePtr saved_line_; const uint32_t dex_method_idx_; // The method we're working on. - // Its object representation if known. - ArtMethod* mirror_method_ GUARDED_BY(Locks::mutator_lock_); + ArtMethod* method_being_verified_; // Its ArtMethod representation if known. const uint32_t method_access_flags_; // Method's access flags. const RegType* return_type_; // Lazily computed return type of the method. const DexFile* const dex_file_; // The dex file containing the method. diff --git a/test/678-quickening/expected.txt b/test/678-quickening/expected.txt new file mode 100644 index 0000000000..a965a70ed4 --- /dev/null +++ b/test/678-quickening/expected.txt @@ -0,0 +1 @@ +Done diff --git a/test/678-quickening/info.txt b/test/678-quickening/info.txt new file mode 100644 index 0000000000..e08eaeb285 --- /dev/null +++ b/test/678-quickening/info.txt @@ -0,0 +1 @@ +Test for FindLocksAtDexPc running with quickened opcodes. diff --git a/test/678-quickening/run b/test/678-quickening/run new file mode 100644 index 0000000000..0cc87f3168 --- /dev/null +++ b/test/678-quickening/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License.i + +# Run without an app image to prevent the class NotLoaded to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/678-quickening/src-art/Main.java b/test/678-quickening/src-art/Main.java new file mode 100644 index 0000000000..5ae88d60d7 --- /dev/null +++ b/test/678-quickening/src-art/Main.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +class NotLoaded { + public void foo() {} +} + +public class Main { + public static void main(String[] args) throws Exception { + runTest(null); + } + + // This method being synchronized means the SIGQUIT code in ART will call + // FindLocksAtDexPc (we check for the presence of try blocks and monitor-enter), + // which triggered a DCHECK of an invariant. + public static synchronized void runTest(Object m) throws Exception { + if (m != null) { + // We used to crash while trying to resolve NotLoaded and beint interrupted + // by the SIGQUIT. + if (m instanceof NotLoaded) { + ((NotLoaded)m).foo(); + } + } + SigQuit.doKill(); + // Sleep some time to get the kill while executing this method. + Thread.sleep(2); + System.out.println("Done"); + } + + private final static class SigQuit { + private final static int sigquit; + private final static Method kill; + private final static int pid; + + static { + int pidTemp = -1; + int sigquitTemp = -1; + Method killTemp = null; + + try { + Class<?> osClass = Class.forName("android.system.Os"); + Method getpid = osClass.getDeclaredMethod("getpid"); + pidTemp = (Integer)getpid.invoke(null); + + Class<?> osConstants = Class.forName("android.system.OsConstants"); + Field sigquitField = osConstants.getDeclaredField("SIGQUIT"); + sigquitTemp = (Integer)sigquitField.get(null); + + killTemp = osClass.getDeclaredMethod("kill", int.class, int.class); + } catch (Exception e) { + throw new Error(e); + } + + pid = pidTemp; + sigquit = sigquitTemp; + kill = killTemp; + } + + public static void doKill() throws Exception { + kill.invoke(null, pid, sigquit); + } + } +} diff --git a/test/979-const-method-handle/build b/test/979-const-method-handle/build index 495557e3df..ce931a96d1 100644..100755 --- a/test/979-const-method-handle/build +++ b/test/979-const-method-handle/build @@ -1,12 +1,12 @@ #!/bin/bash # -# Copyright (C) 2017 The Android Open Source Project +# Copyright 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,9 +14,42 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Stop if something fails. +# make us exit on a failure set -e -${DX} --dex --min-sdk-version=28 --output=classes.dex classes +ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-6.0.jar" +INTERMEDIATE_CLASSES=classes-intermediate +TRANSFORMER_CLASSES=classes-transformer +CLASSES=classes -zip $TEST_NAME.jar classes.dex +DEXER="${DX:-dx}" +if [ "${USE_D8=false}" = "true" ]; then + DEXER="${ANDROID_HOST_OUT}/bin/d8-compat-dx" +fi + +# Create directories for classes +for class_dir in "${INTERMEDIATE_CLASSES}" "${TRANSFORMER_CLASSES}" "${CLASSES}"; do + rm -rf "${class_dir}" + mkdir "${class_dir}" +done + +# Build transformer +${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d ${TRANSFORMER_CLASSES} $(find util-src -name '*.java') + +# Generate intermediate classes that will allow transform to be applied to test classes +JAVAC_ARGS="${JAVAC_ARGS} -source 1.8 -target 1.8" +${JAVAC:-javac} ${JAVAC_ARGS} -cp ${TRANSFORMER_CLASSES} -d ${INTERMEDIATE_CLASSES} $(find src -name '*.java') + +# Run transform +for class in ${INTERMEDIATE_CLASSES}/*.class ; do + transformed_class=${CLASSES}/$(basename ${class}) + ${JAVA:-java} -cp "${ASM_JAR}:${TRANSFORMER_CLASSES}" \ + transformer.ConstantTransformer ${class} ${transformed_class} +done + +# Create DEX +DX_FLAGS="${DX_FLAGS} --min-sdk-version=28 --debug --dump-width=1000" +${DEXER} -JXmx256m --dex ${DX_FLAGS} --dump-to=${CLASSES}.lst --output=classes.dex ${CLASSES} ${TRANSFORMER_CLASSES} + +# Zip DEX to file name expected by test runner +zip ${TEST_NAME:-classes-dex}.jar classes.dex diff --git a/test/979-const-method-handle/classes/Main.class b/test/979-const-method-handle/classes/Main.class Binary files differdeleted file mode 100644 index 8d6b7d88bb..0000000000 --- a/test/979-const-method-handle/classes/Main.class +++ /dev/null diff --git a/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class b/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class Binary files differdeleted file mode 100644 index a21b0a336c..0000000000 --- a/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class +++ /dev/null diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt index 573b80da99..bc943e368e 100644 --- a/test/979-const-method-handle/expected.txt +++ b/test/979-const-method-handle/expected.txt @@ -1,2 +1,6 @@ -MethodHandle MethodHandle(Object)Class => class java.lang.Float -MethodType (char,short,int,long,float,double,Object)boolean +(int,Integer,System)String +Hello World! And Hello Zog +Hello World! And Hello Zorba +name is HoverFly +2.718281828459045 +Attempting to set Math.E raised IAE diff --git a/test/979-const-method-handle/info.txt b/test/979-const-method-handle/info.txt index e8514ce91f..fc909db299 100644 --- a/test/979-const-method-handle/info.txt +++ b/test/979-const-method-handle/info.txt @@ -1,7 +1 @@ This test checks const-method-handle and const-method-type bytecodes. - -The class files in this test come from: - - dalvik/dx/tests/142-const-method-handle - -and are built using ASM bytecode manipulation library. diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java new file mode 100644 index 0000000000..663814f232 --- /dev/null +++ b/test/979-const-method-handle/src/Main.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import annotations.ConstantMethodHandle; +import annotations.ConstantMethodType; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +class Main { + private static String name = "default"; + + private static void unreachable() { + throw new Error("Unreachable"); + } + + @ConstantMethodType( + returnType = String.class, + parameterTypes = {int.class, Integer.class, System.class} + ) + private static MethodType methodType0() { + unreachable(); + return null; + } + + static void helloWorld(String who) { + System.out.print("Hello World! And Hello "); + System.out.println(who); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_STATIC, + owner = "Main", + fieldOrMethodName = "helloWorld", + descriptor = "(Ljava/lang/String;)V" + ) + private static MethodHandle printHelloHandle() { + unreachable(); + return null; + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.STATIC_PUT, + owner = "Main", + fieldOrMethodName = "name", + descriptor = "Ljava/lang/String;" + ) + private static MethodHandle setNameHandle() { + unreachable(); + return null; + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.STATIC_GET, + owner = "java/lang/Math", + fieldOrMethodName = "E", + descriptor = "D" + ) + private static MethodHandle getMathE() { + unreachable(); + return null; + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.STATIC_PUT, + owner = "java/lang/Math", + fieldOrMethodName = "E", + descriptor = "D" + ) + private static MethodHandle putMathE() { + unreachable(); + return null; + } + + public static void main(String[] args) throws Throwable { + System.out.println(methodType0()); + printHelloHandle().invokeExact("Zog"); + printHelloHandle().invokeExact("Zorba"); + setNameHandle().invokeExact("HoverFly"); + System.out.print("name is "); + System.out.println(name); + System.out.println(getMathE().invoke()); + try { + putMathE().invokeExact(Math.PI); + unreachable(); + } catch (IllegalAccessError expected) { + System.out.println("Attempting to set Math.E raised IAE"); + } + } +} diff --git a/test/979-const-method-handle/util-src/annotations/ConstantMethodHandle.java b/test/979-const-method-handle/util-src/annotations/ConstantMethodHandle.java new file mode 100644 index 0000000000..40785ebc6e --- /dev/null +++ b/test/979-const-method-handle/util-src/annotations/ConstantMethodHandle.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation can be set on method to specify that if this method + * is statically invoked then the invocation is replaced by a + * load-constant bytecode with the MethodHandle constant described by + * the annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ConstantMethodHandle { + /* Method handle kinds */ + public static final int STATIC_PUT = 0; + public static final int STATIC_GET = 1; + public static final int INSTANCE_PUT = 2; + public static final int INSTANCE_GET = 3; + public static final int INVOKE_STATIC = 4; + public static final int INVOKE_VIRTUAL = 5; + public static final int INVOKE_SPECIAL = 6; + public static final int NEW_INVOKE_SPECIAL = 7; + public static final int INVOKE_INTERFACE = 8; + + /** Kind of method handle. */ + int kind(); + + /** Class name owning the field or method. */ + String owner(); + + /** The field or method name addressed by the MethodHandle. */ + String fieldOrMethodName(); + + /** Descriptor for the field (type) or method (method-type) */ + String descriptor(); + + /** Whether the owner is an interface. */ + boolean ownerIsInterface() default false; +} diff --git a/test/979-const-method-handle/util-src/annotations/ConstantMethodType.java b/test/979-const-method-handle/util-src/annotations/ConstantMethodType.java new file mode 100644 index 0000000000..c89fa013fe --- /dev/null +++ b/test/979-const-method-handle/util-src/annotations/ConstantMethodType.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation can be set on method to specify that if this method + * is statically invoked then the invocation is replaced by a + * load-constant bytecode with the MethodType constant described by + * the annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ConstantMethodType { + /** Return type of method() or field getter() */ + Class<?> returnType() default void.class; + + /** Types of parameters for method or field setter() */ + Class<?>[] parameterTypes() default {}; +} diff --git a/test/979-const-method-handle/util-src/transformer/ConstantTransformer.java b/test/979-const-method-handle/util-src/transformer/ConstantTransformer.java new file mode 100644 index 0000000000..9356426492 --- /dev/null +++ b/test/979-const-method-handle/util-src/transformer/ConstantTransformer.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package transformer; + +import annotations.ConstantMethodHandle; +import annotations.ConstantMethodType; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * Class for transforming invoke static bytecodes into constant method handle loads and and constant + * method type loads. + * + * <p>When a parameterless private static method returning a MethodHandle is defined and annotated + * with {@code ConstantMethodHandle}, this transformer will replace static invocations of the method + * with a load constant bytecode with a method handle in the constant pool. + * + * <p>Suppose a method is annotated as: <code> + * @ConstantMethodHandle( + * kind = ConstantMethodHandle.STATIC_GET, + * owner = "java/lang/Math", + * fieldOrMethodName = "E", + * descriptor = "D" + * ) + * private static MethodHandle getMathE() { + * unreachable(); + * return null; + * } + * </code> Then invocations of {@code getMathE} will be replaced by a load from the constant pool + * with the constant method handle described in the {@code ConstantMethodHandle} annotation. + * + * <p>Similarly, a parameterless private static method returning a {@code MethodType} and annotated + * with {@code ConstantMethodType}, will have invocations replaced by a load constant bytecode with + * a method type in the constant pool. + */ +class ConstantTransformer { + static class ConstantBuilder extends ClassVisitor { + private final Map<String, ConstantMethodHandle> constantMethodHandles; + private final Map<String, ConstantMethodType> constantMethodTypes; + + ConstantBuilder( + int api, + ClassVisitor cv, + Map<String, ConstantMethodHandle> constantMethodHandles, + Map<String, ConstantMethodType> constantMethodTypes) { + super(api, cv); + this.constantMethodHandles = constantMethodHandles; + this.constantMethodTypes = constantMethodTypes; + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); + return new MethodVisitor(this.api, mv) { + @Override + public void visitMethodInsn( + int opcode, String owner, String name, String desc, boolean itf) { + if (opcode == org.objectweb.asm.Opcodes.INVOKESTATIC) { + ConstantMethodHandle constantMethodHandle = constantMethodHandles.get(name); + if (constantMethodHandle != null) { + insertConstantMethodHandle(constantMethodHandle); + return; + } + ConstantMethodType constantMethodType = constantMethodTypes.get(name); + if (constantMethodType != null) { + insertConstantMethodType(constantMethodType); + return; + } + } + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } + + private Type buildMethodType(Class<?> returnType, Class<?>[] parameterTypes) { + Type rType = Type.getType(returnType); + Type[] pTypes = new Type[parameterTypes.length]; + for (int i = 0; i < pTypes.length; ++i) { + pTypes[i] = Type.getType(parameterTypes[i]); + } + return Type.getMethodType(rType, pTypes); + } + + private int getHandleTag(int kind) { + switch (kind) { + case ConstantMethodHandle.STATIC_PUT: + return Opcodes.H_PUTSTATIC; + case ConstantMethodHandle.STATIC_GET: + return Opcodes.H_GETSTATIC; + case ConstantMethodHandle.INSTANCE_PUT: + return Opcodes.H_PUTFIELD; + case ConstantMethodHandle.INSTANCE_GET: + return Opcodes.H_GETFIELD; + case ConstantMethodHandle.INVOKE_STATIC: + return Opcodes.H_INVOKESTATIC; + case ConstantMethodHandle.INVOKE_VIRTUAL: + return Opcodes.H_INVOKEVIRTUAL; + case ConstantMethodHandle.INVOKE_SPECIAL: + return Opcodes.H_INVOKESPECIAL; + case ConstantMethodHandle.NEW_INVOKE_SPECIAL: + return Opcodes.H_NEWINVOKESPECIAL; + case ConstantMethodHandle.INVOKE_INTERFACE: + return Opcodes.H_INVOKEINTERFACE; + } + throw new Error("Unhandled kind " + kind); + } + + private void insertConstantMethodHandle(ConstantMethodHandle constantMethodHandle) { + Handle handle = + new Handle( + getHandleTag(constantMethodHandle.kind()), + constantMethodHandle.owner(), + constantMethodHandle.fieldOrMethodName(), + constantMethodHandle.descriptor(), + constantMethodHandle.ownerIsInterface()); + mv.visitLdcInsn(handle); + } + + private void insertConstantMethodType(ConstantMethodType constantMethodType) { + Type methodType = + buildMethodType( + constantMethodType.returnType(), + constantMethodType.parameterTypes()); + mv.visitLdcInsn(methodType); + } + }; + } + } + + private static void throwAnnotationError( + Method method, Class<?> annotationClass, String reason) { + StringBuilder sb = new StringBuilder(); + sb.append("Error in annotation ") + .append(annotationClass) + .append(" on method ") + .append(method) + .append(": ") + .append(reason); + throw new Error(sb.toString()); + } + + private static void checkMethodToBeReplaced( + Method method, Class<?> annotationClass, Class<?> returnType) { + final int PRIVATE_STATIC = Modifier.STATIC | Modifier.PRIVATE; + if ((method.getModifiers() & PRIVATE_STATIC) != PRIVATE_STATIC) { + throwAnnotationError(method, annotationClass, " method is not private and static"); + } + if (method.getTypeParameters().length != 0) { + throwAnnotationError(method, annotationClass, " method expects parameters"); + } + if (!method.getReturnType().equals(returnType)) { + throwAnnotationError(method, annotationClass, " wrong return type"); + } + } + + private static void transform(Path inputClassPath, Path outputClassPath) throws Throwable { + Path classLoadPath = inputClassPath.toAbsolutePath().getParent(); + URLClassLoader classLoader = + new URLClassLoader(new URL[] {classLoadPath.toUri().toURL()}, + ClassLoader.getSystemClassLoader()); + String inputClassName = inputClassPath.getFileName().toString().replace(".class", ""); + Class<?> inputClass = classLoader.loadClass(inputClassName); + + final Map<String, ConstantMethodHandle> constantMethodHandles = new HashMap<>(); + final Map<String, ConstantMethodType> constantMethodTypes = new HashMap<>(); + + for (Method m : inputClass.getDeclaredMethods()) { + ConstantMethodHandle constantMethodHandle = m.getAnnotation(ConstantMethodHandle.class); + if (constantMethodHandle != null) { + checkMethodToBeReplaced(m, ConstantMethodHandle.class, MethodHandle.class); + constantMethodHandles.put(m.getName(), constantMethodHandle); + continue; + } + + ConstantMethodType constantMethodType = m.getAnnotation(ConstantMethodType.class); + if (constantMethodType != null) { + checkMethodToBeReplaced(m, ConstantMethodType.class, MethodType.class); + constantMethodTypes.put(m.getName(), constantMethodType); + continue; + } + } + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + try (InputStream is = Files.newInputStream(inputClassPath)) { + ClassReader cr = new ClassReader(is); + ConstantBuilder cb = + new ConstantBuilder( + Opcodes.ASM6, cw, constantMethodHandles, constantMethodTypes); + cr.accept(cb, 0); + } + try (OutputStream os = Files.newOutputStream(outputClassPath)) { + os.write(cw.toByteArray()); + } + } + + public static void main(String[] args) throws Throwable { + transform(Paths.get(args[0]), Paths.get(args[1])); + } +} diff --git a/test/knownfailures.json b/test/knownfailures.json index 9f0f95411c..a7e76d131e 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -337,7 +337,8 @@ "variant": "optimizing | regalloc_gc" }, { - "tests": ["537-checker-arraycopy"], + "tests": ["537-checker-arraycopy", + "641-checker-arraycopy"], "env_vars": {"ART_READ_BARRIER_TYPE": "TABLELOOKUP"} }, { @@ -952,7 +953,8 @@ "description": ["Failing on RI. Needs further investigating."] }, { - "tests": ["616-cha-unloading"], + "tests": ["616-cha-unloading", + "678-quickening"], "variant": "jvm", "description": ["Doesn't run on RI."] }, diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 53c6fb63f9..de07a47df7 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -206,10 +206,7 @@ else if [[ "$mode" == "host" ]]; then dump_command="/bin/kill -3" else - # TODO It would be great to be able to use this on target too but we need to - # be able to walk /proc to figure out what the actual child pid is. - dump_command="/system/bin/true" - # dump_command="/system/xbin/su root /data/local/tmp/system/bin/debuggerd -b" + dump_command="/system/xbin/su root /data/local/tmp/system/bin/debuggerd" fi if [[ $has_gdb = "yes" ]]; then if [[ $mode == "target" ]]; then |