summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common_throws.cc80
-rw-r--r--src/common_throws.h5
-rw-r--r--src/compiler/dex/dex_to_dex_compiler.cc258
-rw-r--r--src/compiler/driver/compiler_driver.cc68
-rw-r--r--src/compiler/driver/compiler_driver.h5
-rw-r--r--src/dex_file.cc22
-rw-r--r--src/dex_file.h17
-rw-r--r--src/dex_instruction.cc36
-rw-r--r--src/dex_instruction.h24
-rw-r--r--src/dex_instruction_list.h16
-rw-r--r--src/interpreter/interpreter.cc204
-rw-r--r--src/mem_map.cc22
-rw-r--r--src/mem_map.h2
-rw-r--r--src/verifier/method_verifier.cc420
-rw-r--r--src/verifier/method_verifier.h41
15 files changed, 1172 insertions, 48 deletions
diff --git a/src/common_throws.cc b/src/common_throws.cc
index dc3627a0b2..1e114bb6dd 100644
--- a/src/common_throws.cc
+++ b/src/common_throws.cc
@@ -27,6 +27,7 @@
#include "mirror/object_array-inl.h"
#include "object_utils.h"
#include "thread.h"
+#include "verifier/method_verifier.h"
#include <sstream>
@@ -283,16 +284,34 @@ void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location
ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
}
-void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx,
- InvokeType type) {
- mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache();
- const DexFile& dex_file = *dex_cache->GetDexFile();
+static void ThrowNullPointerExceptionForMethodAccessImpl(const ThrowLocation& throw_location,
+ uint32_t method_idx,
+ const DexFile& dex_file,
+ InvokeType type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::ostringstream msg;
msg << "Attempt to invoke " << type << " method '"
<< PrettyMethod(method_idx, dex_file, true) << "' on a null object reference";
ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
}
+void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx,
+ InvokeType type) {
+ mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache();
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method_idx,
+ dex_file, type);
+}
+
+void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
+ mirror::AbstractMethod* method,
+ InvokeType type) {
+ mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method->GetDexMethodIndex(),
+ dex_file, type);
+}
+
void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) {
const DexFile::CodeItem* code = MethodHelper(throw_location.GetMethod()).GetCodeItem();
uint32_t throw_dex_pc = throw_location.GetDexPc();
@@ -317,6 +336,23 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) {
case Instruction::INVOKE_INTERFACE_RANGE:
ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_3rc(), kInterface);
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.
+ mirror::AbstractMethod* method =
+ verifier::MethodVerifier::FindInvokedMethodAtDexPc(throw_location.GetMethod(),
+ throw_location.GetDexPc());
+ if (method != NULL) {
+ // NPE with precise message.
+ ThrowNullPointerExceptionForMethodAccess(throw_location, method, kVirtual);
+ } else {
+ // NPE with imprecise message.
+ ThrowNullPointerException(&throw_location,
+ "Attempt to invoke a virtual method on a null object reference");
+ }
+ break;
+ }
case Instruction::IGET:
case Instruction::IGET_WIDE:
case Instruction::IGET_OBJECT:
@@ -330,6 +366,24 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) {
ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */);
break;
}
+ case Instruction::IGET_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.
+ mirror::Field* field =
+ verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(),
+ throw_location.GetDexPc());
+ if (field != NULL) {
+ // NPE with precise message.
+ ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */);
+ } else {
+ // NPE with imprecise message.
+ ThrowNullPointerException(&throw_location,
+ "Attempt to read from a field on a null object reference");
+ }
+ break;
+ }
case Instruction::IPUT:
case Instruction::IPUT_WIDE:
case Instruction::IPUT_OBJECT:
@@ -343,6 +397,24 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) {
ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */);
break;
}
+ case Instruction::IPUT_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.
+ mirror::Field* field =
+ verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(),
+ throw_location.GetDexPc());
+ if (field != NULL) {
+ // NPE with precise message.
+ ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */);
+ } else {
+ // NPE with imprecise message.
+ ThrowNullPointerException(&throw_location,
+ "Attempt to write to a field on a null object reference");
+ }
+ break;
+ }
case Instruction::AGET:
case Instruction::AGET_WIDE:
case Instruction::AGET_OBJECT:
diff --git a/src/common_throws.h b/src/common_throws.h
index 5555435051..00d89f45f6 100644
--- a/src/common_throws.h
+++ b/src/common_throws.h
@@ -153,6 +153,11 @@ void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_locatio
InvokeType type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
+ mirror::AbstractMethod* method,
+ InvokeType type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/src/compiler/dex/dex_to_dex_compiler.cc b/src/compiler/dex/dex_to_dex_compiler.cc
new file mode 100644
index 0000000000..afb29f4163
--- /dev/null
+++ b/src/compiler/dex/dex_to_dex_compiler.cc
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+#include "base/mutex.h"
+#include "compiler/driver/compiler_driver.h"
+#include "compiler/driver/dex_compilation_unit.h"
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+#include "mirror/abstract_method-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/field-inl.h"
+
+namespace art {
+namespace optimizer {
+
+// Controls quickening activation.
+const bool kEnableQuickening = true;
+// Controls logging.
+const bool kEnableLogging = false;
+
+class DexCompiler {
+ public:
+ DexCompiler(art::CompilerDriver& compiler,
+ const DexCompilationUnit& unit)
+ : driver_(compiler),
+ unit_(unit) {};
+
+ ~DexCompiler() {};
+
+ void Compile();
+
+ private:
+ const DexFile& GetDexFile() const {
+ return *unit_.GetDexFile();
+ }
+
+ // TODO: since the whole compilation pipeline uses a "const DexFile", we need
+ // to "unconst" here. The DEX-to-DEX compiler should work on a non-const DexFile.
+ DexFile& GetModifiableDexFile() {
+ return *const_cast<DexFile*>(unit_.GetDexFile());
+ }
+
+ void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc,
+ Instruction::Code new_opcode, bool is_put);
+ void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
+ Instruction::Code new_opcode, bool is_range);
+
+ CompilerDriver& driver_;
+ const DexCompilationUnit& unit_;
+
+ DISALLOW_COPY_AND_ASSIGN(DexCompiler);
+};
+
+// Ensures write access to a part of DEX file.
+//
+// If a DEX file is read-only, it modifies its protection (mprotect) so it allows
+// write access to the part of DEX file defined by an address and a length.
+// In this case, it also takes the DexFile::modification_lock to prevent from
+// concurrent protection modification from a parallel DEX-to-DEX compilation on
+// the same DEX file.
+// When the instance is destroyed, it recovers original protection and releases
+// the lock.
+// TODO: as this scoped class is similar to a MutexLock we should use annotalysis
+// to capture the locking behavior.
+class ScopedDexWriteAccess {
+ public:
+ ScopedDexWriteAccess(DexFile& dex_file, Instruction* inst,
+ size_t length)
+ : dex_file_(dex_file),
+ address_(reinterpret_cast<uint8_t*>(inst)),
+ length_(length),
+ is_read_only_(dex_file_.IsReadOnly()) {
+ if (is_read_only_) {
+ // We need to enable DEX write access. To avoid concurrent DEX write access
+ // modification, we take the DexFile::modification_lock before.
+ dex_file_.GetModificationLock().ExclusiveLock(Thread::Current());
+ bool success = dex_file_.EnableWrite(address_, length_);
+ DCHECK(success) << "Failed to enable DEX write access";
+ }
+ }
+
+ ~ScopedDexWriteAccess() {
+ DCHECK_EQ(is_read_only_, dex_file_.IsReadOnly());
+ if (is_read_only_) {
+ bool success = dex_file_.DisableWrite(address_, length_);
+ DCHECK(success) << "Failed to disable DEX write access";
+ // Now we recovered original read-only protection, we can release the
+ // DexFile::modification_lock.
+ dex_file_.GetModificationLock().ExclusiveUnlock(Thread::Current());
+ }
+ }
+
+ private:
+ DexFile& dex_file_;
+ // TODO: make address_ const.
+ uint8_t* address_;
+ const size_t length_;
+ const bool is_read_only_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDexWriteAccess);
+};
+
+void DexCompiler::Compile() {
+ const DexFile::CodeItem* code_item = unit_.GetCodeItem();
+ const uint16_t* insns = code_item->insns_;
+ const uint32_t insns_size = code_item->insns_size_in_code_units_;
+ Instruction* inst = const_cast<Instruction*>(Instruction::At(insns));
+
+ for (uint32_t dex_pc = 0; dex_pc < insns_size;
+ inst = const_cast<Instruction*>(inst->Next()), dex_pc = inst->GetDexPc(insns)) {
+ switch (inst->Opcode()) {
+ case Instruction::IGET:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
+ break;
+ case Instruction::IGET_WIDE:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false);
+ break;
+
+ case Instruction::IGET_OBJECT:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
+ break;
+
+ case Instruction::IPUT:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_SHORT:
+ // These opcodes have the same implementation in interpreter so group
+ // them under IPUT_QUICK.
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
+ break;
+
+ case Instruction::IPUT_WIDE:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
+ break;
+
+ case Instruction::IPUT_OBJECT:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true);
+ break;
+
+ case Instruction::INVOKE_VIRTUAL:
+ CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false);
+ break;
+
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true);
+ break;
+
+ default:
+ // No optimization.
+ break;
+ }
+ }
+}
+
+void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
+ uint32_t dex_pc,
+ Instruction::Code new_opcode,
+ bool is_put) {
+ if (!kEnableQuickening) {
+ return;
+ }
+ uint32_t field_idx = inst->VRegC_22c();
+ int field_offset;
+ bool is_volatile;
+ bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset,
+ is_volatile, is_put);
+ if (fast_path && !is_volatile && IsUint(16, field_offset)) {
+ // TODO: use VLOG ?
+ if (kEnableLogging) {
+ LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
+ << " to " << Instruction::Name(new_opcode)
+ << " by replacing field index " << field_idx
+ << " by field offset " << field_offset
+ << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+ << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+ }
+ // We are modifying 4 consecutive bytes.
+ ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
+ inst->SetOpcode(new_opcode);
+ // Replace field index by field offset.
+ inst->SetVRegC_22c(static_cast<uint16_t>(field_offset));
+ }
+}
+
+void DexCompiler::CompileInvokeVirtual(Instruction* inst,
+ uint32_t dex_pc,
+ Instruction::Code new_opcode,
+ bool is_range) {
+ if (!kEnableQuickening) {
+ return;
+ }
+ uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ CompilerDriver::MethodReference target_method(&GetDexFile(), method_idx);
+ InvokeType invoke_type = kVirtual;
+ InvokeType original_invoke_type = invoke_type;
+ int vtable_idx;
+ uintptr_t direct_code;
+ uintptr_t direct_method;
+ bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type,
+ target_method, vtable_idx,
+ direct_code, direct_method,
+ false);
+ // TODO: support devirtualization.
+ if (fast_path && original_invoke_type == invoke_type) {
+ if (vtable_idx >= 0 && IsUint(16, vtable_idx)) {
+ // TODO: use VLOG ?
+ if (kEnableLogging) {
+ LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
+ << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
+ << " to " << Instruction::Name(new_opcode)
+ << " by replacing method index " << method_idx
+ << " by vtable index " << vtable_idx
+ << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+ << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+ }
+ // We are modifying 4 consecutive bytes.
+ ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
+ inst->SetOpcode(new_opcode);
+ // Replace method index by vtable index.
+ if (is_range) {
+ inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
+ } else {
+ inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
+ }
+ }
+ }
+}
+
+} // namespace optimizer
+} // namespace art
+
+extern "C" art::CompiledMethod*
+ ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item,
+ uint32_t access_flags, art::InvokeType invoke_type,
+ uint32_t class_def_idx, uint32_t method_idx, jobject class_loader,
+ const art::DexFile& dex_file) {
+ art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(),
+ dex_file, code_item, class_def_idx, method_idx, access_flags);
+ art::optimizer::DexCompiler dex_compiler(compiler, unit);
+ dex_compiler.Compile();
+ return NULL;
+}
diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc
index 6050108d48..4a6eb962e3 100644
--- a/src/compiler/driver/compiler_driver.cc
+++ b/src/compiler/driver/compiler_driver.cc
@@ -372,6 +372,8 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet
compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtQuickCompileMethod");
}
+ dex_to_dex_compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtCompileDEX");
+
init_compiler_context(*this);
if (compiler_backend_ == kPortable) {
@@ -531,10 +533,33 @@ void CompilerDriver::CompileAll(jobject class_loader,
}
}
+static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader,
+ const DexFile& dex_file,
+ const DexFile::ClassDef& class_def)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Do not allow DEX-to-DEX compilation of image classes. This is to prevent the
+ // verifier from passing on "quick" instruction at compilation time. It must
+ // only pass on quick instructions at runtime.
+ if (class_loader == NULL) {
+ return false;
+ }
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ mirror::Class* klass = class_linker->FindClass(descriptor, class_loader);
+ if (klass == NULL) {
+ Thread* self = Thread::Current();
+ CHECK(self->IsExceptionPending());
+ self->ClearException();
+ return false;
+ }
+ // DEX-to-DEX compilation is only allowed on preverified classes.
+ return klass->IsVerified();
+}
+
void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) {
DCHECK(!Runtime::Current()->IsStarted());
Thread* self = Thread::Current();
- jobject class_loader;
+ jobject jclass_loader;
const DexFile* dex_file;
uint32_t class_def_idx;
{
@@ -542,7 +567,7 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) {
ScopedLocalRef<jobject>
local_class_loader(soa.Env(),
soa.AddLocalReference<jobject>(method->GetDeclaringClass()->GetClassLoader()));
- class_loader = soa.Env()->NewGlobalRef(local_class_loader.get());
+ jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get());
// Find the dex_file
MethodHelper mh(method);
dex_file = &mh.GetDexFile();
@@ -555,14 +580,22 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) {
UniquePtr<ThreadPool> thread_pool(new ThreadPool(1U));
TimingLogger timings("CompileOne", false);
- PreCompile(class_loader, dex_files, *thread_pool.get(), timings);
+ PreCompile(jclass_loader, dex_files, *thread_pool.get(), timings);
uint32_t method_idx = method->GetDexMethodIndex();
const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset());
+ // Can we run DEX-to-DEX compiler on this class ?
+ bool allow_dex_compilation;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
+ mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
+ allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, *dex_file, class_def);
+ }
CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(),
- class_def_idx, method_idx, class_loader, *dex_file);
+ class_def_idx, method_idx, jclass_loader, *dex_file, allow_dex_compilation);
- self->GetJniEnv()->DeleteGlobalRef(class_loader);
+ self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
self->TransitionFromSuspendedToRunnable();
}
@@ -2015,12 +2048,12 @@ void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFi
}
void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, size_t class_def_index) {
- jobject class_loader = manager->GetClassLoader();
+ jobject jclass_loader = manager->GetClassLoader();
const DexFile& dex_file = *manager->GetDexFile();
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
{
ScopedObjectAccess soa(Thread::Current());
- mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader());
+ mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
if (SkipClass(class_loader, dex_file, class_def)) {
return;
}
@@ -2035,6 +2068,13 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz
// empty class, probably a marker interface
return;
}
+ // Can we run DEX-to-DEX compiler on this class ?
+ bool allow_dex_compilation;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
+ allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, dex_file, class_def);
+ }
ClassDataItemIterator it(dex_file, class_data);
// Skip fields
while (it.HasNextStaticField()) {
@@ -2056,7 +2096,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz
previous_direct_method_idx = method_idx;
manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
it.GetMethodInvokeType(class_def), class_def_index,
- method_idx, class_loader, dex_file);
+ method_idx, jclass_loader, dex_file, allow_dex_compilation);
it.Next();
}
// Compile virtual methods
@@ -2072,7 +2112,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz
previous_virtual_method_idx = method_idx;
manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
it.GetMethodInvokeType(class_def), class_def_index,
- method_idx, class_loader, dex_file);
+ method_idx, jclass_loader, dex_file, allow_dex_compilation);
it.Next();
}
DCHECK(!it.HasNext());
@@ -2088,7 +2128,8 @@ void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_fil
void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
InvokeType invoke_type, uint32_t class_def_idx,
uint32_t method_idx, jobject class_loader,
- const DexFile& dex_file) {
+ const DexFile& dex_file,
+ bool allow_dex_to_dex_compilation) {
CompiledMethod* compiled_method = NULL;
uint64_t start_ns = NanoTime();
@@ -2113,6 +2154,13 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t
compiled_method = (*compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx,
method_idx, class_loader, dex_file);
CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file);
+ } else if (allow_dex_to_dex_compilation) {
+ // TODO: add a mode to disable DEX-to-DEX compilation ?
+ compiled_method = (*dex_to_dex_compiler_)(*this, code_item, access_flags,
+ invoke_type, class_def_idx,
+ method_idx, class_loader, dex_file);
+ // No native code is generated.
+ CHECK(compiled_method == NULL) << PrettyMethod(method_idx, dex_file);
}
}
uint64_t duration_ns = NanoTime() - start_ns;
diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h
index fbfcadb8dd..fdd2149f7f 100644
--- a/src/compiler/driver/compiler_driver.h
+++ b/src/compiler/driver/compiler_driver.h
@@ -349,7 +349,8 @@ class CompilerDriver {
LOCKS_EXCLUDED(Locks::mutator_lock_);
void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx,
- jobject class_loader, const DexFile& dex_file)
+ jobject class_loader, const DexFile& dex_file,
+ bool allow_dex_to_dex_compilation)
LOCKS_EXCLUDED(compiled_methods_lock_);
static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
@@ -404,6 +405,8 @@ class CompilerDriver {
jobject class_loader, const DexFile& dex_file);
CompilerFn compiler_;
+ CompilerFn dex_to_dex_compiler_;
+
void* compiler_context_;
typedef CompiledMethod* (*JniCompilerFn)(CompilerDriver& driver,
diff --git a/src/dex_file.cc b/src/dex_file.cc
index dad083cc3f..80465f2fed 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -108,6 +108,28 @@ int DexFile::GetPermissions() const {
}
}
+bool DexFile::IsReadOnly() const {
+ return GetPermissions() == PROT_READ;
+}
+
+bool DexFile::EnableWrite(uint8_t* addr, size_t length) const {
+ CHECK(IsReadOnly());
+ if (mem_map_.get() == NULL) {
+ return false;
+ } else {
+ return mem_map_->ProtectRegion(addr, length, PROT_READ | PROT_WRITE);
+ }
+}
+
+bool DexFile::DisableWrite(uint8_t* addr, size_t length) const {
+ CHECK(!IsReadOnly());
+ if (mem_map_.get() == NULL) {
+ return false;
+ } else {
+ return mem_map_->ProtectRegion(addr, length, PROT_READ);
+ }
+}
+
const DexFile* DexFile::OpenFile(const std::string& filename,
const std::string& location,
bool verify) {
diff --git a/src/dex_file.h b/src/dex_file.h
index ecc985fb3a..e09270e018 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -21,6 +21,7 @@
#include <vector>
#include "base/logging.h"
+#include "base/mutex.h"
#include "base/stringpiece.h"
#include "globals.h"
#include "invoke_type.h"
@@ -384,6 +385,10 @@ class DexFile {
return *header_;
}
+ Mutex& GetModificationLock() {
+ return modification_lock;
+ }
+
// Decode the dex magic version
uint32_t GetVersion() const;
@@ -798,6 +803,12 @@ class DexFile {
int GetPermissions() const;
+ bool IsReadOnly() const;
+
+ bool EnableWrite(uint8_t* addr, size_t size) const;
+
+ bool DisableWrite(uint8_t* addr, size_t size) const;
+
private:
// Opens a .dex file
static const DexFile* OpenFile(const std::string& filename,
@@ -830,6 +841,7 @@ class DexFile {
location_checksum_(location_checksum),
mem_map_(mem_map),
dex_object_(NULL),
+ modification_lock("DEX modification lock"),
header_(0),
string_ids_(0),
type_ids_(0),
@@ -890,6 +902,11 @@ class DexFile {
// TODO: this is mutable as it shouldn't be here. We should move it to the dex cache or similar.
mutable jobject dex_object_;
+ // The DEX-to-DEX compiler uses this lock to ensure thread safety when
+ // enabling write access to a read-only DEX file.
+ // TODO: move to Locks::dex_file_modification_lock.
+ Mutex modification_lock;
+
// Points to the header section.
const Header* header_;
diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc
index b18b4d0226..e00272796c 100644
--- a/src/dex_instruction.cc
+++ b/src/dex_instruction.cc
@@ -354,6 +354,14 @@ std::string Instruction::DumpString(const DexFile* file) const {
<< PrettyField(field_idx, *file, true) << " // field@" << field_idx;
break;
} // else fall-through
+ case IGET_QUICK:
+ case IGET_OBJECT_QUICK:
+ if (file != NULL) {
+ uint32_t field_idx = VRegC_22c();
+ os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
+ << "// offset@" << field_idx;
+ break;
+ } // else fall-through
case IPUT:
case IPUT_WIDE:
case IPUT_OBJECT:
@@ -367,6 +375,14 @@ std::string Instruction::DumpString(const DexFile* file) const {
<< PrettyField(field_idx, *file, true) << " // field@" << field_idx;
break;
} // else fall-through
+ case IPUT_QUICK:
+ case IPUT_OBJECT_QUICK:
+ if (file != NULL) {
+ uint32_t field_idx = VRegC_22c();
+ os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
+ << "// offset@" << field_idx;
+ break;
+ } // else fall-through
case INSTANCE_OF:
if (file != NULL) {
uint32_t type_idx = VRegC_22c();
@@ -413,6 +429,19 @@ std::string Instruction::DumpString(const DexFile* file) const {
os << "}, " << PrettyMethod(method_idx, *file) << " // method@" << method_idx;
break;
} // else fall-through
+ case INVOKE_VIRTUAL_QUICK:
+ if (file != NULL) {
+ os << opcode << " {";
+ uint32_t method_idx = VRegB_35c();
+ for (size_t i = 0; i < VRegA_35c(); ++i) {
+ if (i != 0) {
+ os << ", ";
+ }
+ os << "v" << arg[i];
+ }
+ os << "}, // vtable@" << method_idx;
+ break;
+ } // else fall-through
default:
os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2]
<< ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c();
@@ -433,6 +462,13 @@ std::string Instruction::DumpString(const DexFile* file) const {
<< PrettyMethod(method_idx, *file) << " // method@" << method_idx;
break;
} // else fall-through
+ case INVOKE_VIRTUAL_RANGE_QUICK:
+ if (file != NULL) {
+ uint32_t method_idx = VRegB_3rc();
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+ << "// vtable@" << method_idx;
+ break;
+ } // else fall-through
default:
os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(),
(VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc());
diff --git a/src/dex_instruction.h b/src/dex_instruction.h
index adaada7f87..8a49216e4d 100644
--- a/src/dex_instruction.h
+++ b/src/dex_instruction.h
@@ -278,6 +278,30 @@ class Instruction {
return static_cast<Code>(opcode);
}
+ void SetOpcode(Code opcode) {
+ DCHECK_LT(static_cast<uint16_t>(opcode), 256u);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode);
+ }
+
+ void SetVRegB_3rc(uint16_t val) {
+ DCHECK(FormatOf(Opcode()) == k3rc);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[1] = val;
+ }
+
+ void SetVRegB_35c(uint16_t val) {
+ DCHECK(FormatOf(Opcode()) == k35c);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[1] = val;
+ }
+
+ void SetVRegC_22c(uint16_t val) {
+ DCHECK(FormatOf(Opcode()) == k22c);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[1] = val;
+ }
+
// Returns the format of the given opcode.
static Format FormatOf(Code opcode) {
return kInstructionFormats[opcode];
diff --git a/src/dex_instruction_list.h b/src/dex_instruction_list.h
index 3083d224e9..9daec61ba5 100644
--- a/src/dex_instruction_list.h
+++ b/src/dex_instruction_list.h
@@ -242,14 +242,14 @@
V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
- V(0xE3, UNUSED_E3, "unused-e3", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xE4, UNUSED_E4, "unused-e4", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xE5, UNUSED_E5, "unused-e5", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xE6, UNUSED_E6, "unused-e6", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xE7, UNUSED_E7, "unused-e7", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xE8, UNUSED_E8, "unused-e8", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xE9, UNUSED_E9, "unused-e9", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xEA, UNUSED_EA, "unused-ea", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xE3, IGET_QUICK, "iget-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xE4, IGET_WIDE_QUICK, "iget-wide-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \
+ V(0xE5, IGET_OBJECT_QUICK, "iget-object-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xE6, IPUT_QUICK, "iput-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \
+ V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArg) \
+ V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRange) \
V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \
V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \
V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index dd96f8dead..685ca219b2 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -470,6 +470,98 @@ static void DoInvoke(Thread* self, ShadowFrame& shadow_frame,
}
}
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
+// specialization.
+template<bool is_range>
+static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, JValue* result)
+ NO_THREAD_SAFETY_ANALYSIS;
+
+template<bool is_range>
+static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, JValue* result) {
+ uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ Object* receiver = shadow_frame.GetVRegReference(vregC);
+ if (UNLIKELY(receiver == NULL)) {
+ // We lost the reference to the method index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return;
+ }
+ uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+ AbstractMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx);
+ if (UNLIKELY(method == NULL)) {
+ CHECK(self->IsExceptionPending());
+ result->SetJ(0);
+ return;
+ }
+ MethodHelper mh(method);
+
+ const DexFile::CodeItem* code_item = mh.GetCodeItem();
+ uint16_t num_regs;
+ uint16_t num_ins;
+ if (code_item != NULL) {
+ num_regs = code_item->registers_size_;
+ num_ins = code_item->ins_size_;
+ } else if (method->IsAbstract()) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;",
+ "abstract method \"%s\"", PrettyMethod(method).c_str());
+ return;
+ } else {
+ DCHECK(method->IsNative() || method->IsProxyMethod());
+ num_regs = num_ins = AbstractMethod::NumArgRegisters(mh.GetShorty());
+ if (!method->IsStatic()) {
+ num_regs++;
+ num_ins++;
+ }
+ }
+
+ void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
+ ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame,
+ method, 0, memory));
+ size_t cur_reg = num_regs - num_ins;
+ if (receiver != NULL) {
+ new_shadow_frame->SetVRegReference(cur_reg, receiver);
+ ++cur_reg;
+ }
+
+ size_t arg_offset = (receiver == NULL) ? 0 : 1;
+ const char* shorty = mh.GetShorty();
+ uint32_t arg[5];
+ if (!is_range) {
+ inst->GetArgs(arg);
+ }
+ for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) {
+ DCHECK_LT(shorty_pos + 1, mh.GetShortyLength());
+ size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset];
+ switch (shorty[shorty_pos + 1]) {
+ case 'L': {
+ Object* o = shadow_frame.GetVRegReference(arg_pos);
+ new_shadow_frame->SetVRegReference(cur_reg, o);
+ break;
+ }
+ case 'J': case 'D': {
+ uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) |
+ static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos));
+ new_shadow_frame->SetVRegLong(cur_reg, wide_value);
+ cur_reg++;
+ arg_offset++;
+ break;
+ }
+ default:
+ new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos));
+ break;
+ }
+ }
+
+ if (LIKELY(Runtime::Current()->IsStarted())) {
+ (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
+ } else {
+ UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins);
+ }
+}
+
// We use template functions to optimize compiler inlining process. Otherwise,
// some parts of the code (like a switch statement) which depend on a constant
// parameter would not be inlined while it should be. These constant parameters
@@ -535,6 +627,41 @@ static inline void DoFieldGet(Thread* self, ShadowFrame& shadow_frame,
// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
// specialization.
+template<Primitive::Type field_type>
+static void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst)
+ NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
+
+template<Primitive::Type field_type>
+static inline void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
+ if (UNLIKELY(obj == NULL)) {
+ // We lost the reference to the field index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return;
+ }
+ MemberOffset field_offset(inst->VRegC_22c());
+ const bool is_volatile = false; // iget-x-quick only on non volatile fields.
+ const uint32_t vregA = inst->VRegA_22c();
+ switch (field_type) {
+ case Primitive::kPrimInt:
+ shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset, is_volatile)));
+ break;
+ case Primitive::kPrimLong:
+ shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset, is_volatile)));
+ break;
+ case Primitive::kPrimNot:
+ shadow_frame.SetVRegReference(vregA, obj->GetFieldObject<mirror::Object*>(field_offset, is_volatile));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+}
+
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
+// specialization.
template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
static void DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
const Instruction* inst)
@@ -591,6 +718,41 @@ static inline void DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
}
}
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
+// specialization.
+template<Primitive::Type field_type>
+static void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst)
+ NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
+
+template<Primitive::Type field_type>
+static inline void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
+ if (UNLIKELY(obj == NULL)) {
+ // We lost the reference to the field index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return;
+ }
+ MemberOffset field_offset(inst->VRegC_22c());
+ const bool is_volatile = false; // iput-x-quick only on non volatile fields.
+ const uint32_t vregA = inst->VRegA_22c();
+ switch (field_type) {
+ case Primitive::kPrimInt:
+ obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
+ break;
+ case Primitive::kPrimLong:
+ obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile);
+ break;
+ case Primitive::kPrimNot:
+ obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+}
+
static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Class* java_lang_string_class = String::GetJavaLangString();
@@ -1770,6 +1932,21 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte
DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
break;
+ case Instruction::IGET_QUICK:
+ PREAMBLE();
+ DoIGetQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
+ case Instruction::IGET_WIDE_QUICK:
+ PREAMBLE();
+ DoIGetQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
+ case Instruction::IGET_OBJECT_QUICK:
+ PREAMBLE();
+ DoIGetQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
case Instruction::SGET_BOOLEAN:
PREAMBLE();
DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
@@ -1840,6 +2017,21 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte
DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
break;
+ case Instruction::IPUT_QUICK:
+ PREAMBLE();
+ DoIPutQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
+ case Instruction::IPUT_WIDE_QUICK:
+ PREAMBLE();
+ DoIPutQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
+ case Instruction::IPUT_OBJECT_QUICK:
+ PREAMBLE();
+ DoIPutQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
case Instruction::SPUT_BOOLEAN:
PREAMBLE();
DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
@@ -1925,6 +2117,16 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte
DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx);
break;
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ PREAMBLE();
+ DoInvokeVirtualQuick<false>(self, shadow_frame, inst, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx);
+ break;
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+ PREAMBLE();
+ DoInvokeVirtualQuick<true>(self, shadow_frame, inst, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx);
+ break;
case Instruction::NEG_INT:
PREAMBLE();
shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x()));
@@ -2715,7 +2917,7 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte
inst = inst->Next_2xx();
break;
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
- case Instruction::UNUSED_E3 ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
case Instruction::UNUSED_73:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
diff --git a/src/mem_map.cc b/src/mem_map.cc
index fb19424c48..c75dffa63c 100644
--- a/src/mem_map.cc
+++ b/src/mem_map.cc
@@ -183,4 +183,26 @@ bool MemMap::Protect(int prot) {
return false;
}
+bool MemMap::ProtectRegion(uint8_t* addr, size_t length, int prot) {
+ CHECK_GE(addr, base_begin_);
+ CHECK_LT(addr + length, reinterpret_cast<const uint8_t*>(base_begin_) + base_size_);
+
+ /*
+ * Align "addr" to a page boundary and adjust "length" appropriately.
+ * (The address must be page-aligned, the length doesn't need to be,
+ * but we do need to ensure we cover the same range.)
+ */
+ uint8_t* alignAddr = (uint8_t*) ((uintptr_t) addr & ~(kPageSize-1));
+ size_t alignLength = length + (addr - alignAddr);
+
+ if (mprotect(alignAddr, alignLength, prot) == 0) {
+ prot_ = prot;
+ return true;
+ }
+
+ PLOG(ERROR) << "mprotect(" << reinterpret_cast<void*>(alignAddr) << ", " << alignLength << ", "
+ << prot << ") failed";
+ return false;
+}
+
} // namespace art
diff --git a/src/mem_map.h b/src/mem_map.h
index 7310f78ddf..2eb7772705 100644
--- a/src/mem_map.h
+++ b/src/mem_map.h
@@ -61,6 +61,8 @@ class MemMap {
bool Protect(int prot);
+ bool ProtectRegion(uint8_t* addr, size_t length, int prot);
+
int GetProtect() const {
return prot_;
}
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 021e984001..87cc3286b3 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -278,6 +278,8 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca
declaring_class_(NULL),
interesting_dex_pc_(-1),
monitor_enter_dex_pcs_(NULL),
+ accessed_field(NULL),
+ invoked_method(NULL),
have_pending_hard_failure_(false),
have_pending_runtime_throw_failure_(false),
new_instance_count_(0),
@@ -308,6 +310,54 @@ void MethodVerifier::FindLocksAtDexPc() {
Verify();
}
+mirror::Field* MethodVerifier::FindAccessedFieldAtDexPc(mirror::AbstractMethod* m,
+ uint32_t dex_pc) {
+ MethodHelper mh(m);
+ MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
+ mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(),
+ m, m->GetAccessFlags(), false, true);
+ mirror::Field* field = NULL;
+ verifier.interesting_dex_pc_ = dex_pc;
+ verifier.accessed_field = &field;
+ verifier.FindAccessedFieldAtDexPc();
+ return field;
+}
+
+void MethodVerifier::FindAccessedFieldAtDexPc() {
+ CHECK(accessed_field != NULL);
+ CHECK(code_item_ != NULL); // 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.
+ Verify();
+}
+
+mirror::AbstractMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::AbstractMethod* m,
+ uint32_t dex_pc) {
+ MethodHelper mh(m);
+ MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
+ mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(),
+ m, m->GetAccessFlags(), false, true);
+ mirror::AbstractMethod* method = NULL;
+ verifier.interesting_dex_pc_ = dex_pc;
+ verifier.invoked_method = &method;
+ verifier.FindInvokedMethodAtDexPc();
+ return method;
+}
+
+void MethodVerifier::FindInvokedMethodAtDexPc() {
+ CHECK(invoked_method != NULL);
+ CHECK(code_item_ != NULL); // 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.
+ Verify();
+}
+
bool MethodVerifier::Verify() {
// If there aren't any instructions, make sure that's expected, then exit successfully.
if (code_item_ == NULL) {
@@ -2361,10 +2411,63 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, false);
break;
+ // Special instructions.
+ //
+ // Note: the following instructions encode offsets derived from class linking.
+ // As such they use Class*/Field*/AbstractMethod* as these offsets only have
+ // meaning if the class linking and resolution were successful.
+ case Instruction::IGET_QUICK:
+ VerifyIGetQuick(inst, reg_types_.Integer(), true);
+ break;
+ case Instruction::IGET_WIDE_QUICK:
+ VerifyIGetQuick(inst, reg_types_.LongLo(), true);
+ break;
+ case Instruction::IGET_OBJECT_QUICK:
+ VerifyIGetQuick(inst, reg_types_.JavaLangObject(false), false);
+ break;
+ case Instruction::IPUT_QUICK:
+ VerifyIPutQuick(inst, reg_types_.Integer(), true);
+ break;
+ case Instruction::IPUT_WIDE_QUICK:
+ VerifyIPutQuick(inst, reg_types_.LongLo(), true);
+ break;
+ case Instruction::IPUT_OBJECT_QUICK:
+ VerifyIPutQuick(inst, reg_types_.JavaLangObject(false), false);
+ break;
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
+ bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK);
+ mirror::AbstractMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range);
+ if (called_method != NULL) {
+ const char* descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
+ const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
+ if (!return_type.IsLowHalf()) {
+ work_line_->SetResultRegisterType(return_type);
+ } else {
+ work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+ }
+ just_set_result = true;
+ }
+ break;
+ }
+
/* These should never appear during verification. */
+ case Instruction::UNUSED_3E:
+ case Instruction::UNUSED_3F:
+ case Instruction::UNUSED_40:
+ case Instruction::UNUSED_41:
+ case Instruction::UNUSED_42:
+ case Instruction::UNUSED_43:
+ case Instruction::UNUSED_73:
+ case Instruction::UNUSED_79:
+ case Instruction::UNUSED_7A:
+ case Instruction::UNUSED_EB:
+ case Instruction::UNUSED_EC:
case Instruction::UNUSED_ED:
case Instruction::UNUSED_EE:
case Instruction::UNUSED_EF:
+ case Instruction::UNUSED_F0:
+ case Instruction::UNUSED_F1:
case Instruction::UNUSED_F2:
case Instruction::UNUSED_F3:
case Instruction::UNUSED_F4:
@@ -2375,30 +2478,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
case Instruction::UNUSED_F9:
case Instruction::UNUSED_FA:
case Instruction::UNUSED_FB:
- case Instruction::UNUSED_F0:
- case Instruction::UNUSED_F1:
- case Instruction::UNUSED_E3:
- case Instruction::UNUSED_E8:
- case Instruction::UNUSED_E7:
- case Instruction::UNUSED_E4:
- case Instruction::UNUSED_E9:
case Instruction::UNUSED_FC:
- case Instruction::UNUSED_E5:
- case Instruction::UNUSED_EA:
case Instruction::UNUSED_FD:
- case Instruction::UNUSED_E6:
- case Instruction::UNUSED_EB:
case Instruction::UNUSED_FE:
- case Instruction::UNUSED_3E:
- case Instruction::UNUSED_3F:
- case Instruction::UNUSED_40:
- case Instruction::UNUSED_41:
- case Instruction::UNUSED_42:
- case Instruction::UNUSED_43:
- case Instruction::UNUSED_73:
- case Instruction::UNUSED_79:
- case Instruction::UNUSED_7A:
- case Instruction::UNUSED_EC:
case Instruction::UNUSED_FF:
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
break;
@@ -2802,7 +2884,7 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const Instruction*
}
}
// We use vAA as our expected arg count, rather than res_method->insSize, because we need to
- // match the call to the signature. Also, we might might be calling through an abstract method
+ // match the call to the signature. Also, we might be calling through an abstract method
// definition (which doesn't have register count values).
const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
/* caught by static verifier */
@@ -2882,6 +2964,121 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const Instruction*
}
}
+mirror::AbstractMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst,
+ bool is_range) {
+ DCHECK(Runtime::Current()->IsStarted());
+ CHECK(invoked_method != NULL || accessed_field != NULL)
+ << "We should not be verifying " << inst->Name();
+ const RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
+ if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
+ return NULL;
+ }
+ mirror::Class* this_class = NULL;
+ if (!actual_arg_type.IsUnresolvedTypes()) {
+ this_class = actual_arg_type.GetClass();
+ } else {
+ const std::string& descriptor(actual_arg_type.GetDescriptor());
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ this_class = class_linker->FindClass(descriptor.c_str(), class_loader_);
+ if (this_class == NULL) {
+ Thread::Current()->ClearException();
+ // Look for a system class
+ this_class = class_linker->FindClass(descriptor.c_str(), NULL);
+ }
+ }
+ CHECK(this_class != NULL) << "Cannot get Class* for type " << actual_arg_type;
+ mirror::ObjectArray<mirror::AbstractMethod>* vtable = this_class->GetVTable();
+ CHECK(vtable != NULL);
+ uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ CHECK(vtable_index < vtable->GetLength());
+ mirror::AbstractMethod* res_method = vtable->Get(vtable_index);
+ CHECK(!Thread::Current()->IsExceptionPending());
+ // TODO: we should move the code below to FindInvokedMethodAtDexPc. Once the
+ // method is verified, we could access the information we need from register
+ // lines for the dex pc we are looking for.
+ if (invoked_method != NULL && work_insn_idx_ == interesting_dex_pc_) {
+ // We've been requested to tell which method is invoked at this dex pc.
+ *invoked_method = res_method;
+ }
+ if (res_method == NULL) {
+ return NULL;
+ }
+ CHECK(!res_method->IsDirect() && !res_method->IsStatic());
+
+ // We use vAA as our expected arg count, rather than res_method->insSize, because we need to
+ // match the call to the signature. Also, we might be calling through an abstract method
+ // definition (which doesn't have register count values).
+ const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
+ /* caught by static verifier */
+ DCHECK(is_range || expected_args <= 5);
+ if (expected_args > code_item_->outs_size_) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args
+ << ") exceeds outsSize (" << code_item_->outs_size_ << ")";
+ return NULL;
+ }
+
+ /*
+ * Check the "this" argument, which must be an instance of the class that declared the method.
+ * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a
+ * rigorous check here (which is okay since we have to do it at runtime).
+ */
+ if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized";
+ return NULL;
+ }
+ if (!actual_arg_type.IsZero()) {
+ mirror::Class* klass = res_method->GetDeclaringClass();
+ const RegType& res_method_class =
+ reg_types_.FromClass(ClassHelper(klass).GetDescriptor(), klass,
+ klass->CannotBeAssignedFromOtherTypes());
+ if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
+ << "' not instance of '" << res_method_class << "'";
+ return NULL;
+ }
+ }
+ /*
+ * Process the target method's signature. This signature may or may not
+ * have been verified, so we can't assume it's properly formed.
+ */
+ MethodHelper mh(res_method);
+ const DexFile::TypeList* params = mh.GetParameterTypeList();
+ size_t params_size = params == NULL ? 0 : params->Size();
+ uint32_t arg[5];
+ if (!is_range) {
+ inst->GetArgs(arg);
+ }
+ size_t actual_args = 1;
+ for (size_t param_index = 0; param_index < params_size; param_index++) {
+ if (actual_args >= expected_args) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" << PrettyMethod(res_method)
+ << "'. Expected " << expected_args << " arguments, processing argument " << actual_args
+ << " (where longs/doubles count twice).";
+ return NULL;
+ }
+ const char* descriptor =
+ mh.GetTypeDescriptorFromTypeIdx(params->GetTypeItem(param_index).type_idx_);
+ if (descriptor == NULL) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method)
+ << " missing signature component";
+ return NULL;
+ }
+ const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
+ uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args];
+ if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
+ return res_method;
+ }
+ actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1;
+ }
+ if (actual_args != expected_args) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method)
+ << " expected " << expected_args << " arguments, found " << actual_args;
+ return NULL;
+ } else {
+ return res_method;
+ }
+}
+
void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range) {
uint32_t type_idx;
if (!is_filled) {
@@ -3244,6 +3441,185 @@ void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_ty
}
}
+// Look for an instance field with this offset.
+// TODO: we may speed up the search if offsets are sorted by doing a quick search.
+static mirror::Field* FindInstanceFieldWithOffset(mirror::Class* klass,
+ uint32_t field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ObjectArray<mirror::Field>* instance_fields = klass->GetIFields();
+ if (instance_fields != NULL) {
+ for (int32_t i = 0, e = instance_fields->GetLength(); i < e; ++i) {
+ mirror::Field* field = instance_fields->Get(i);
+ if (field->GetOffset().Uint32Value() == field_offset) {
+ return field;
+ }
+ }
+ }
+ if (klass->GetSuperClass() != NULL) {
+ return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset);
+ } else {
+ return NULL;
+ }
+}
+
+void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
+ bool is_primitive) {
+ DCHECK(Runtime::Current()->IsStarted());
+ CHECK(accessed_field != NULL || invoked_method != NULL)
+ << "We should not be verifying " << inst->Name();
+ const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
+ mirror::Class* object_class = NULL;
+ if (!object_type.IsUnresolvedTypes()) {
+ object_class = object_type.GetClass();
+ } else {
+ // We need to resolve the class from its descriptor.
+ const std::string& descriptor(object_type.GetDescriptor());
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ object_class = class_linker->FindClass(descriptor.c_str(), class_loader_);
+ if (object_class == NULL) {
+ Thread::Current()->ClearException();
+ // Look for a system class
+ object_class = class_linker->FindClass(descriptor.c_str(), NULL);
+ }
+ }
+ CHECK(object_class != NULL) << "Cannot get Class* for type " << object_type;
+ uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c());
+ mirror::Field* field = FindInstanceFieldWithOffset(object_class, field_offset);
+ CHECK(field != NULL);
+ // TODO: we should move the code below to FindAccessedFieldAtDexPc. Once the
+ // method is verified, we could access the information we need from register
+ // lines for the dex pc we are looking for.
+ if (accessed_field != NULL && work_insn_idx_ == interesting_dex_pc_) {
+ // We've been requested to tell which field is accessed at this dex pc.
+ *accessed_field = field;
+ }
+ const char* descriptor = FieldHelper(field).GetTypeDescriptor();
+ mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader();
+ const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+ const uint32_t vregA = inst->VRegA_22c();
+ if (is_primitive) {
+ if (field_type.Equals(insn_type) ||
+ (field_type.IsFloat() && insn_type.IsIntegralTypes()) ||
+ (field_type.IsDouble() && insn_type.IsLongTypes())) {
+ // expected that read is of the correct primitive type or that int reads are reading
+ // floats or long reads are reading doubles
+ } else {
+ // This is a global failure rather than a class change failure as the instructions and
+ // the descriptors for the type should have been consistent within the same file at
+ // compile time
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+ << " to be of type '" << insn_type
+ << "' but found type '" << field_type << "' in get";
+ return;
+ }
+ } else {
+ if (!insn_type.IsAssignableFrom(field_type)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+ << " to be compatible with type '" << insn_type
+ << "' but found type '" << field_type
+ << "' in get-object";
+ work_line_->SetRegisterType(vregA, reg_types_.Conflict());
+ return;
+ }
+ }
+ if (!field_type.IsLowHalf()) {
+ work_line_->SetRegisterType(vregA, field_type);
+ } else {
+ work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(&reg_types_));
+ }
+}
+
+void MethodVerifier::VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
+ bool is_primitive) {
+ DCHECK(Runtime::Current()->IsStarted());
+ CHECK(accessed_field != NULL || invoked_method != NULL)
+ << "We should not be verifying " << inst->Name();
+ const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
+ mirror::Class* object_class = NULL;
+ if (!object_type.IsUnresolvedTypes()) {
+ object_class = object_type.GetClass();
+ } else {
+ // We need to resolve the class from its descriptor.
+ const std::string& descriptor(object_type.GetDescriptor());
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ object_class = class_linker->FindClass(descriptor.c_str(), class_loader_);
+ if (object_class == NULL) {
+ Thread::Current()->ClearException();
+ // Look for a system class
+ object_class = class_linker->FindClass(descriptor.c_str(), NULL);
+ }
+ }
+ CHECK(object_class != NULL) << "Cannot get Class* for type " << object_type;
+ uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c());
+ mirror::Field* field = FindInstanceFieldWithOffset(object_class, field_offset);
+ CHECK(field != NULL);
+ // TODO: like VerifyIGetQuick, we should move the code below to
+ // FindAccessedFieldAtDexPc.
+ if (accessed_field != NULL && work_insn_idx_ == interesting_dex_pc_) {
+ // We've been requested to tell which field is accessed at this dex pc.
+ *accessed_field = field;
+ }
+ const char* descriptor = FieldHelper(field).GetTypeDescriptor();
+ mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader();
+ const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+ if (field != NULL) {
+ if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
+ Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
+ << " from other class " << GetDeclaringClass();
+ return;
+ }
+ }
+ const uint32_t vregA = inst->VRegA_22c();
+ if (is_primitive) {
+ // Primitive field assignability rules are weaker than regular assignability rules
+ bool instruction_compatible;
+ bool value_compatible;
+ const RegType& value_type = work_line_->GetRegisterType(vregA);
+ if (field_type.IsIntegralTypes()) {
+ instruction_compatible = insn_type.IsIntegralTypes();
+ value_compatible = value_type.IsIntegralTypes();
+ } else if (field_type.IsFloat()) {
+ instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int
+ value_compatible = value_type.IsFloatTypes();
+ } else if (field_type.IsLong()) {
+ instruction_compatible = insn_type.IsLong();
+ value_compatible = value_type.IsLongTypes();
+ } else if (field_type.IsDouble()) {
+ instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long
+ value_compatible = value_type.IsDoubleTypes();
+ } else {
+ instruction_compatible = false; // reference field with primitive store
+ value_compatible = false; // unused
+ }
+ if (!instruction_compatible) {
+ // This is a global failure rather than a class change failure as the instructions and
+ // the descriptors for the type should have been consistent within the same file at
+ // compile time
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+ << " to be of type '" << insn_type
+ << "' but found type '" << field_type
+ << "' in put";
+ return;
+ }
+ if (!value_compatible) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA
+ << " of type " << value_type
+ << " but expected " << field_type
+ << " for store to " << PrettyField(field) << " in put";
+ return;
+ }
+ } else {
+ if (!insn_type.IsAssignableFrom(field_type)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+ << " to be compatible with type '" << insn_type
+ << "' but found type '" << field_type
+ << "' in put-object";
+ return;
+ }
+ work_line_->VerifyRegisterType(vregA, field_type);
+ }
+}
+
bool MethodVerifier::CheckNotMoveException(const uint16_t* insns, int insn_idx) {
if ((insns[insn_idx] & 0xff) == Instruction::MOVE_EXCEPTION) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid use of move-exception";
diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h
index 198d8cb17b..da6497926d 100644
--- a/src/verifier/method_verifier.h
+++ b/src/verifier/method_verifier.h
@@ -197,11 +197,23 @@ class MethodVerifier {
LOCKS_EXCLUDED(safecast_map_lock_);
// Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding
- // to the locks held at 'dex_pc' in 'm'.
+ // to the locks held at 'dex_pc' in method 'm'.
static void FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc,
std::vector<uint32_t>& monitor_enter_dex_pcs)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Returns the accessed field corresponding to the quick instruction's field
+ // offset at 'dex_pc' in method 'm'.
+ static mirror::Field* FindAccessedFieldAtDexPc(mirror::AbstractMethod* m,
+ uint32_t dex_pc)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Returns the invoked method corresponding to the quick instruction's vtable
+ // index at 'dex_pc' in method 'm'.
+ static mirror::AbstractMethod* FindInvokedMethodAtDexPc(mirror::AbstractMethod* m,
+ uint32_t dex_pc)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void Shutdown();
@@ -254,6 +266,10 @@ class MethodVerifier {
void FindLocksAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void FindAccessedFieldAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void FindInvokedMethodAtDexPc() SHARED_LOCKS_REQUIRED(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
@@ -484,6 +500,16 @@ class MethodVerifier {
bool is_primitive, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Perform verification of an iget-quick instruction.
+ void VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
+ bool is_primitive)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Perform verification of an iput-quick instruction.
+ void VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
+ bool is_primitive)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Resolves a class based on an index and performs access checks to ensure the referrer can
// access the resolved class.
const RegType& ResolveClassAndCheckAccess(uint32_t class_idx)
@@ -532,6 +558,10 @@ class MethodVerifier {
bool is_range, bool is_super)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::AbstractMethod* VerifyInvokeVirtualQuickArgs(const Instruction* inst,
+ bool is_range)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
/*
* Verify that the target instruction is not "move-exception". It's important that the only way
* to execute a move-exception is as the first instruction of an exception handler.
@@ -646,11 +676,18 @@ class MethodVerifier {
const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class.
// Instruction widths and flags, one entry per code unit.
UniquePtr<InstructionFlags[]> insn_flags_;
- // The dex PC of a FindLocksAtDexPc request, -1 otherwise.
+ // The dex PC of a FindLocksAtDexPc, FindAccessedFieldAtDexPc or
+ // FindInvokedMethodAtDexPc request, -1 otherwise.
uint32_t interesting_dex_pc_;
// The container into which FindLocksAtDexPc should write the registers containing held locks,
// NULL if we're not doing FindLocksAtDexPc.
std::vector<uint32_t>* monitor_enter_dex_pcs_;
+ // The pointer into which FindAccessedFieldAtDexPc should write the accessed field,
+ // NULL if we're not doing FindAccessedFieldAtDexPc.
+ mirror::Field** accessed_field;
+ // The pointer into which FindInvokedMethodAtDexPc should write the invoked method,
+ // NULL if we're not doing FindInvokedMethodAtDexPc.
+ mirror::AbstractMethod** invoked_method;
// The types of any error that occurs.
std::vector<VerifyError> failures_;