summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--oatdump/oatdump.cc5
-rw-r--r--runtime/art_method.cc19
-rw-r--r--runtime/art_method.h1
-rw-r--r--runtime/class_linker.cc9
-rw-r--r--runtime/common_throws.cc50
-rw-r--r--runtime/oat_file_assistant.cc44
-rw-r--r--runtime/verifier/method_verifier-inl.h2
-rw-r--r--runtime/verifier/method_verifier.cc175
-rw-r--r--runtime/verifier/method_verifier.h31
-rw-r--r--test/678-quickening/expected.txt1
-rw-r--r--test/678-quickening/info.txt1
-rw-r--r--test/678-quickening/run18
-rw-r--r--test/678-quickening/src-art/Main.java79
-rwxr-xr-x[-rw-r--r--]test/979-const-method-handle/build43
-rw-r--r--test/979-const-method-handle/classes/Main.classbin943 -> 0 bytes
-rw-r--r--test/979-const-method-handle/classes/constmethodhandle/ConstTest.classbin1411 -> 0 bytes
-rw-r--r--test/979-const-method-handle/expected.txt8
-rw-r--r--test/979-const-method-handle/info.txt6
-rw-r--r--test/979-const-method-handle/src/Main.java102
-rw-r--r--test/979-const-method-handle/util-src/annotations/ConstantMethodHandle.java58
-rw-r--r--test/979-const-method-handle/util-src/annotations/ConstantMethodType.java38
-rw-r--r--test/979-const-method-handle/util-src/transformer/ConstantTransformer.java229
-rw-r--r--test/knownfailures.json6
-rwxr-xr-xtools/run-jdwp-tests.sh5
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_ = &reg_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
deleted file mode 100644
index 8d6b7d88bb..0000000000
--- a/test/979-const-method-handle/classes/Main.class
+++ /dev/null
Binary files differ
diff --git a/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class b/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class
deleted file mode 100644
index a21b0a336c..0000000000
--- a/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class
+++ /dev/null
Binary files differ
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