Merge "Tidy up some C-isms." into ics-mr1-plus-art
diff --git a/src/class_linker.h b/src/class_linker.h
index a8fa01d..1925a9d 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -194,19 +194,6 @@
const ClassLoader* class_loader,
bool is_static);
- Field* ResolveFieldJLS(uint32_t field_idx, const Method* referrer) {
- Field* resolved_field =
- referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx);
- if (UNLIKELY(resolved_field == NULL)) {
- Class* declaring_class = referrer->GetDeclaringClass();
- DexCache* dex_cache = declaring_class->GetDexCache();
- const ClassLoader* class_loader = declaring_class->GetClassLoader();
- const DexFile& dex_file = FindDexFile(dex_cache);
- resolved_field = ResolveFieldJLS(dex_file, field_idx, dex_cache, class_loader);
- }
- return resolved_field;
- }
-
// Resolve a field with a given ID from the DexFile, storing the
// result in DexCache. The ClassLinker and ClassLoader are used as
// in ResolveType. No is_static argument is provided so that Java
diff --git a/src/object_utils.h b/src/object_utils.h
index 59ef515..ceef186 100644
--- a/src/object_utils.h
+++ b/src/object_utils.h
@@ -183,7 +183,13 @@
}
std::string GetLocation() {
- return GetDexCache()->GetLocation()->ToModifiedUtf8();
+ DexCache* dex_cache = GetDexCache();
+ if (dex_cache != NULL && !klass_->IsProxyClass()) {
+ return dex_cache->GetLocation()->ToModifiedUtf8();
+ } else {
+ // Arrays and proxies are generated and have no corresponding dex file location.
+ return "generated class";
+ }
}
const DexFile& GetDexFile() {
@@ -196,6 +202,16 @@
return *result;
}
+ DexCache* GetDexCache() {
+ DexCache* result = dex_cache_;
+ if (result == NULL) {
+ DCHECK(klass_ != NULL);
+ result = klass_->GetDexCache();
+ dex_cache_ = result;
+ }
+ return result;
+ }
+
private:
const DexFile::TypeList* GetInterfaceTypeList() {
const DexFile::TypeList* result = interface_type_list_;
@@ -209,16 +225,6 @@
return result;
}
- DexCache* GetDexCache() {
- DexCache* result = dex_cache_;
- if (result == NULL) {
- DCHECK(klass_ != NULL);
- result = klass_->GetDexCache();
- dex_cache_ = result;
- }
- return result;
- }
-
ClassLinker* GetClassLinker() {
ClassLinker* result = class_linker_;
if (result == NULL) {
@@ -400,6 +406,7 @@
SetMethod(new_m);
shorty_ = NULL;
}
+
const char* GetName() {
const DexFile& dex_file = GetDexFile();
uint32_t dex_method_idx = method_->GetDexMethodIndex();
@@ -420,12 +427,14 @@
}
}
}
+
String* GetNameAsString() {
const DexFile& dex_file = GetDexFile();
uint32_t dex_method_idx = method_->GetDexMethodIndex();
const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
return GetClassLinker()->ResolveString(dex_file, method_id.name_idx_, GetDexCache());
}
+
const char* GetShorty() {
const char* result = shorty_;
if (result == NULL) {
@@ -436,12 +445,14 @@
}
return result;
}
+
uint32_t GetShortyLength() {
if (shorty_ == NULL) {
GetShorty();
}
return shorty_len_;
}
+
const std::string GetSignature() {
const DexFile& dex_file = GetDexFile();
uint32_t dex_method_idx = method_->GetDexMethodIndex();
@@ -451,14 +462,17 @@
return "<no signature>";
}
}
+
const DexFile::ProtoId& GetPrototype() {
const DexFile& dex_file = GetDexFile();
return dex_file.GetMethodPrototype(dex_file.GetMethodId(method_->GetDexMethodIndex()));
}
+
const DexFile::TypeList* GetParameterTypeList() {
const DexFile::ProtoId& proto = GetPrototype();
return GetDexFile().GetProtoParameters(proto);
}
+
ObjectArray<Class>* GetParameterTypes() {
const DexFile::TypeList* params = GetParameterTypeList();
Class* array_class = GetClassLinker()->FindSystemClass("[Ljava/lang/Class;");
@@ -474,6 +488,7 @@
}
return result;
}
+
Class* GetReturnType() {
const DexFile& dex_file = GetDexFile();
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex());
@@ -481,6 +496,7 @@
uint16_t return_type_idx = proto_id.return_type_idx_;
return GetClassFromTypeIdx(return_type_idx);
}
+
const char* GetReturnTypeDescriptor() {
const DexFile& dex_file = GetDexFile();
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex());
@@ -488,10 +504,12 @@
uint16_t return_type_idx = proto_id.return_type_idx_;
return dex_file.GetTypeDescriptor(dex_file.GetTypeId(return_type_idx));
}
+
int32_t GetLineNumFromNativePC(uintptr_t raw_pc) {
const DexFile& dex_file = GetDexFile();
return dex_file.GetLineNumFromPC(method_, method_->ToDexPC(raw_pc));
}
+
const char* GetDeclaringClassDescriptor() {
Class* klass = method_->GetDeclaringClass();
DCHECK(!klass->IsProxyClass());
@@ -499,6 +517,7 @@
const DexFile& dex_file = GetDexFile();
return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx));
}
+
const char* GetDeclaringClassSourceFile() {
const char* descriptor = GetDeclaringClassDescriptor();
const DexFile& dex_file = GetDexFile();
@@ -506,17 +525,33 @@
CHECK(dex_class_def != NULL);
return dex_file.GetSourceFile(*dex_class_def);
}
+
+ uint32_t GetClassDefIndex() {
+ const char* descriptor = GetDeclaringClassDescriptor();
+ const DexFile& dex_file = GetDexFile();
+ uint32_t index;
+ CHECK(dex_file.FindClassDefIndex(descriptor, index));
+ return index;
+ }
+
+ ClassLoader* GetClassLoader() {
+ return method_->GetDeclaringClass()->GetClassLoader();
+ }
+
bool IsStatic() {
return method_->IsStatic();
}
+
bool IsClassInitializer() {
return IsStatic() && StringPiece(GetName()) == "<clinit>";
}
+
size_t NumArgs() {
// "1 +" because the first in Args is the receiver.
// "- 1" because we don't count the return type.
return (IsStatic() ? 0 : 1) + GetShortyLength() - 1;
}
+
// Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods
bool IsParamALongOrDouble(size_t param) {
CHECK_LT(param, NumArgs());
@@ -528,6 +563,7 @@
char ch = GetShorty()[param];
return (ch == 'J' || ch == 'D');
}
+
// Is the specified parameter a reference, where parameter 0 is 'this' for instance methods
bool IsParamAReference(size_t param) {
CHECK_LT(param, NumArgs());
@@ -538,6 +574,7 @@
}
return GetShorty()[param] == 'L'; // An array also has a shorty character of 'L' (not '[')
}
+
bool HasSameNameAndSignature(MethodHelper* other) {
if (GetDexCache() == other->GetDexCache()) {
const DexFile& dex_file = GetDexFile();
@@ -550,12 +587,15 @@
StringPiece other_name(other->GetName());
return name == other_name && GetSignature() == other->GetSignature();
}
+
const DexFile::CodeItem* GetCodeItem() {
return GetDexFile().GetCodeItem(method_->GetCodeItemOffset());
}
+
bool IsResolvedTypeIdx(uint16_t type_idx) const {
return method_->GetDexCacheResolvedTypes()->Get(type_idx) != NULL;
}
+
Class* GetClassFromTypeIdx(uint16_t type_idx) {
Class* type = method_->GetDexCacheResolvedTypes()->Get(type_idx);
if (type == NULL) {
@@ -564,13 +604,16 @@
}
return type;
}
+
const char* GetTypeDescriptorFromTypeIdx(uint16_t type_idx) {
const DexFile& dex_file = GetDexFile();
return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx));
}
+
Class* GetDexCacheResolvedType(uint16_t type_idx) {
return GetDexCache()->GetResolvedType(type_idx);
}
+
const DexFile& GetDexFile() {
const DexFile* result = dex_file_;
if (result == NULL) {
@@ -580,6 +623,16 @@
}
return *result;
}
+
+ DexCache* GetDexCache() {
+ DexCache* result = dex_cache_;
+ if (result == NULL) {
+ Class* klass = method_->GetDeclaringClass();
+ result = klass->GetDexCache();
+ dex_cache_ = result;
+ }
+ return result;
+ }
private:
// Set the method_ field, for proxy methods looking up the interface method via the resolved
// methods table.
@@ -596,15 +649,7 @@
}
method_ = method;
}
- DexCache* GetDexCache() {
- DexCache* result = dex_cache_;
- if (result == NULL) {
- Class* klass = method_->GetDeclaringClass();
- result = klass->GetDexCache();
- dex_cache_ = result;
- }
- return result;
- }
+
ClassLinker* GetClassLinker() {
ClassLinker* result = class_linker_;
if (result == NULL) {
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 50f0130..1152c79 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -359,9 +359,6 @@
case verifier::VERIFY_ERROR_BAD_CLASS_HARD:
// Generic VerifyError; use default exception, no message.
break;
- case verifier::VERIFY_ERROR_NONE:
- CHECK(false);
- break;
}
self->ThrowNewException(exception_class, msg.c_str());
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 1979940..60b17d6 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -184,174 +184,145 @@
error += PrettyDescriptor(super);
return false;
}
- for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
- Method* method = klass->GetDirectMethod(i);
- if (!VerifyMethod(method)) {
- error = "Verifier rejected class ";
- error += PrettyDescriptor(klass);
- error += " due to bad method ";
- error += PrettyMethod(method, true);
- return false;
- }
+ ClassHelper kh(klass);
+ const DexFile& dex_file = kh.GetDexFile();
+ uint32_t class_def_idx;
+ if (!dex_file.FindClassDefIndex(kh.GetDescriptor(), class_def_idx)) {
+ error = "Verifier rejected class ";
+ error += PrettyDescriptor(klass);
+ error += " that isn't present in dex file ";
+ error += dex_file.GetLocation();
+ return false;
}
- for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
- Method* method = klass->GetVirtualMethod(i);
- if (!VerifyMethod(method)) {
- error = "Verifier rejected class ";
- error += PrettyDescriptor(klass);
- error += " due to bad method ";
- error += PrettyMethod(method, true);
- return false;
- }
- }
- return true;
-}
-
-bool MethodVerifier::VerifyMethod(Method* method) {
- MethodVerifier verifier(method);
- bool success = verifier.VerifyAll();
- CHECK_EQ(success, verifier.failure_ == VERIFY_ERROR_NONE);
-
- // We expect either success and no verification error, or failure and a generic failure to
- // reject the class.
- if (success) {
- if (verifier.failure_ != VERIFY_ERROR_NONE) {
- LOG(FATAL) << "Unhandled failure in verification of " << PrettyMethod(method) << std::endl
- << verifier.fail_messages_;
- }
- } else {
- LOG(INFO) << "Verification error in " << PrettyMethod(method) << " "
- << verifier.fail_messages_.str();
- if (gDebugVerify) {
- std::cout << std::endl << verifier.info_messages_.str();
- verifier.Dump(std::cout);
- }
- DCHECK((verifier.failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) ||
- (verifier.failure_ == VERIFY_ERROR_BAD_CLASS_HARD)) << verifier.failure_;
- }
- return success;
-}
-
-void MethodVerifier::VerifyMethodAndDump(Method* method) {
- MethodVerifier verifier(method);
- verifier.VerifyAll();
-
- LOG(INFO) << "Dump of method " << PrettyMethod(method) << " "
- << verifier.fail_messages_.str() << std::endl
- << verifier.info_messages_.str() << Dumpable<MethodVerifier>(verifier);
+ return VerifyClass(&dex_file, kh.GetDexCache(), klass->GetClassLoader(), class_def_idx, error);
}
bool MethodVerifier::VerifyClass(const DexFile* dex_file, DexCache* dex_cache,
const ClassLoader* class_loader, uint32_t class_def_idx, std::string& error) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
const byte* class_data = dex_file->GetClassData(class_def);
+ if (class_data == NULL) {
+ // empty class, probably a marker interface
+ return true;
+ }
ClassDataItemIterator it(*dex_file, class_data);
while (it.HasNextStaticField() || it.HasNextInstanceField()) {
it.Next();
}
+ size_t error_count = 0;
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
while (it.HasNextDirectMethod()) {
uint32_t method_idx = it.GetMemberIndex();
+ Method* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, true);
+ if (method == NULL) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ // We couldn't resolve the method, but continue regardless.
+ Thread::Current()->ClearException();
+ }
if (!VerifyMethod(method_idx, dex_file, dex_cache, class_loader, class_def_idx,
- it.GetMethodCodeItem())) {
- error = "Verifier rejected class";
+ it.GetMethodCodeItem(), method, it.GetMemberAccessFlags())) {
+ if (error_count > 0) {
+ error += "\n";
+ }
+ error = "Verifier rejected class ";
error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def));
error += " due to bad method ";
error += PrettyMethod(method_idx, *dex_file);
- return false;
+ ++error_count;
}
it.Next();
}
while (it.HasNextVirtualMethod()) {
uint32_t method_idx = it.GetMemberIndex();
+ Method* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, false);
+ if (method == NULL) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ // We couldn't resolve the method, but continue regardless.
+ Thread::Current()->ClearException();
+ }
if (!VerifyMethod(method_idx, dex_file, dex_cache, class_loader, class_def_idx,
- it.GetMethodCodeItem())) {
- error = "Verifier rejected class";
+ it.GetMethodCodeItem(), method, it.GetMemberAccessFlags())) {
+ if (error_count > 0) {
+ error += "\n";
+ }
+ error = "Verifier rejected class ";
error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def));
error += " due to bad method ";
error += PrettyMethod(method_idx, *dex_file);
- return false;
+ ++error_count;
}
it.Next();
}
- return true;
+ return error_count == 0;
}
bool MethodVerifier::VerifyMethod(uint32_t method_idx, const DexFile* dex_file, DexCache* dex_cache,
- const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item) {
- MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item);
-
- // Without a method*, we can only verify the structure.
- bool success = verifier.VerifyStructure();
- CHECK_EQ(success, verifier.failure_ == VERIFY_ERROR_NONE);
-
- // We expect either success and no verification error, or failure and a generic failure to
- // reject the class.
+ const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+ Method* method, uint32_t method_access_flags) {
+ MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item, method_idx,
+ method, method_access_flags);
+ bool success = verifier.Verify();
if (success) {
- if (verifier.failure_ != VERIFY_ERROR_NONE) {
- LOG(FATAL) << "Unhandled failure in verification of " << PrettyMethod(method_idx, *dex_file)
- << std::endl << verifier.fail_messages_;
+ // Verification completed, however failures may be pending that didn't cause the verification
+ // to hard fail.
+ if (verifier.failures_.size() != 0) {
+ verifier.DumpFailures(LOG(INFO) << "Soft verification failures in "
+ << PrettyMethod(method_idx, *dex_file) << std::endl);
+ success = false;
}
} else {
- LOG(INFO) << "Verification error in " << PrettyMethod(method_idx, *dex_file) << " "
- << verifier.fail_messages_.str();
+ // Bad method data.
+ CHECK_NE(verifier.failures_.size(), 0U);
+ CHECK(verifier.have_pending_hard_failure_);
+ verifier.DumpFailures(LOG(INFO) << "Verification error in "
+ << PrettyMethod(method_idx, *dex_file) << std::endl);
if (gDebugVerify) {
std::cout << std::endl << verifier.info_messages_.str();
verifier.Dump(std::cout);
}
- DCHECK((verifier.failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) ||
- (verifier.failure_ == VERIFY_ERROR_BAD_CLASS_HARD)) << verifier.failure_;
}
return success;
}
-MethodVerifier::MethodVerifier(Method* method)
- : work_insn_idx_(-1),
- method_(method),
- failure_(VERIFY_ERROR_NONE),
- new_instance_count_(0),
- monitor_enter_count_(0) {
+void MethodVerifier::VerifyMethodAndDump(Method* method) {
CHECK(method != NULL);
- dex_cache_ = method->GetDeclaringClass()->GetDexCache();
- class_loader_ = method->GetDeclaringClass()->GetClassLoader();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- dex_file_ = &class_linker->FindDexFile(dex_cache_);
- code_item_ = dex_file_->GetCodeItem(method->GetCodeItemOffset());
- const DexFile::ClassDef* class_def = ClassHelper(method_->GetDeclaringClass()).GetClassDef();
- class_def_idx_ = dex_file_->GetIndexForClassDef(*class_def);
+ MethodHelper mh(method);
+ MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
+ mh.GetClassDefIndex(), mh.GetCodeItem(), method->GetDexMethodIndex(),
+ method, method->GetAccessFlags());
+ verifier.Verify();
+ verifier.DumpFailures(LOG(INFO) << "Dump of method " << PrettyMethod(method) << std::endl)
+ << verifier.info_messages_.str() << Dumpable<MethodVerifier>(verifier);
}
MethodVerifier::MethodVerifier(const DexFile* dex_file, DexCache* dex_cache,
- const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item)
+ const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_idx, Method* method, uint32_t method_access_flags)
: work_insn_idx_(-1),
- method_(NULL),
+ method_idx_(method_idx),
+ foo_method_(method),
+ method_access_flags_(method_access_flags),
dex_file_(dex_file),
dex_cache_(dex_cache),
class_loader_(class_loader),
class_def_idx_(class_def_idx),
code_item_(code_item),
- failure_(VERIFY_ERROR_NONE),
+ have_pending_hard_failure_(false),
+ have_pending_rewrite_failure_(false),
new_instance_count_(0),
monitor_enter_count_(0) {
}
-bool MethodVerifier::VerifyAll() {
- CHECK(method_ != NULL);
+bool MethodVerifier::Verify() {
// If there aren't any instructions, make sure that's expected, then exit successfully.
if (code_item_ == NULL) {
- if (!method_->IsNative() && !method_->IsAbstract()) {
+ if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
return false;
} else {
return true;
}
}
- return VerifyStructure() && VerifyCodeFlow();
-}
-
-bool MethodVerifier::VerifyStructure() {
- if (code_item_ == NULL) {
- return true;
- }
// Sanity-check the register counts. ins + locals = registers, so make sure that ins <= registers.
if (code_item_->ins_size_ > code_item_->registers_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" << code_item_->ins_size_
@@ -366,39 +337,73 @@
result = result && ScanTryCatchBlocks();
// Perform static instruction verification.
result = result && VerifyInstructions();
- return result;
+ // Perform code-flow analysis and return.
+ return result && VerifyCodeFlow();
}
std::ostream& MethodVerifier::Fail(VerifyError error) {
- CHECK_EQ(failure_, VERIFY_ERROR_NONE);
- if (Runtime::Current()->IsCompiler()) {
- switch (error) {
- // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx
- // errors into soft verification errors so that we re-verify at runtime. We may fail to find
- // or to agree on access because of not yet available class loaders, or class loaders that
- // will differ at runtime.
- case VERIFY_ERROR_NO_CLASS:
- case VERIFY_ERROR_NO_FIELD:
- case VERIFY_ERROR_NO_METHOD:
- case VERIFY_ERROR_ACCESS_CLASS:
- case VERIFY_ERROR_ACCESS_FIELD:
- case VERIFY_ERROR_ACCESS_METHOD:
+ switch (error) {
+ case VERIFY_ERROR_NO_CLASS:
+ case VERIFY_ERROR_NO_FIELD:
+ case VERIFY_ERROR_NO_METHOD:
+ case VERIFY_ERROR_ACCESS_CLASS:
+ case VERIFY_ERROR_ACCESS_FIELD:
+ case VERIFY_ERROR_ACCESS_METHOD:
+ if (Runtime::Current()->IsCompiler()) {
+ // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx
+ // errors into soft verification errors so that we re-verify at runtime. We may fail to find
+ // or to agree on access because of not yet available class loaders, or class loaders that
+ // will differ at runtime.
error = VERIFY_ERROR_BAD_CLASS_SOFT;
- break;
+ } else {
+ have_pending_rewrite_failure_ = true;
+ }
+ break;
+ // Errors that are bad at both compile and runtime, but don't cause rejection of the class.
+ case VERIFY_ERROR_CLASS_CHANGE:
+ case VERIFY_ERROR_INSTANTIATION:
+ have_pending_rewrite_failure_ = true;
+ break;
+ // Indication that verification should be retried at runtime.
+ case VERIFY_ERROR_BAD_CLASS_SOFT:
+ if (!Runtime::Current()->IsCompiler()) {
+ // It is runtime so hard fail.
+ have_pending_hard_failure_ = true;
+ }
+ break;
// Hard verification failures at compile time will still fail at runtime, so the class is
// marked as rejected to prevent it from being compiled.
- case VERIFY_ERROR_BAD_CLASS_HARD: {
+ case VERIFY_ERROR_BAD_CLASS_HARD: {
+ if (Runtime::Current()->IsCompiler()) {
Compiler::ClassReference ref(dex_file_, class_def_idx_);
AddRejectedClass(ref);
- break;
}
- default:
- break;
+ have_pending_hard_failure_ = true;
+ break;
}
}
- failure_ = error;
- return fail_messages_ << "VFY: " << PrettyMethod(method_)
- << '[' << reinterpret_cast<void*>(work_insn_idx_) << "] : ";
+ failures_.push_back(error);
+ std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(method_idx_, *dex_file_).c_str(),
+ work_insn_idx_));
+ std::ostringstream* failure_message = new std::ostringstream(location);
+ failure_messages_.push_back(failure_message);
+ return *failure_message;
+}
+
+void MethodVerifier::PrependToLastFailMessage(std::string prepend) {
+ size_t failure_num = failure_messages_.size();
+ DCHECK_NE(failure_num, 0U);
+ std::ostringstream* last_fail_message = failure_messages_[failure_num - 1];
+ prepend += last_fail_message->str();
+ failure_messages_[failure_num - 1] = new std::ostringstream(prepend);
+ delete last_fail_message;
+}
+
+void MethodVerifier::AppendToLastFailMessage(std::string append) {
+ size_t failure_num = failure_messages_.size();
+ DCHECK_NE(failure_num, 0U);
+ std::ostringstream* last_fail_message = failure_messages_[failure_num - 1];
+ (*last_fail_message) << append;
}
bool MethodVerifier::ComputeWidthsAndCountOps() {
@@ -474,7 +479,7 @@
const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
if (inst->Opcode() != Instruction::MOVE_EXCEPTION) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "exception handler doesn't start with move-exception ("
- << dex_pc << ")";
+ << dex_pc << ")";
return false;
}
insn_flags_[dex_pc].SetBranchTarget();
@@ -503,8 +508,7 @@
uint32_t insns_size = code_item_->insns_size_in_code_units_;
for (uint32_t dex_pc = 0; dex_pc < insns_size;) {
if (!VerifyInstruction(inst, dex_pc)) {
- DCHECK_NE(failure_, VERIFY_ERROR_NONE);
- fail_messages_ << "Rejecting opcode " << inst->Name() << " at " << dex_pc;
+ DCHECK_NE(failures_.size(), 0U);
return false;
}
/* Flag instructions that are garbage collection points */
@@ -928,30 +932,34 @@
/* Initialize register types of method arguments. */
if (!SetTypesFromSignature()) {
- DCHECK_NE(failure_, VERIFY_ERROR_NONE);
- fail_messages_ << "Bad signature in " << PrettyMethod(method_);
+ DCHECK_NE(failures_.size(), 0U);
+ std::string prepend("Bad signature in ");
+ prepend += PrettyMethod(method_idx_, *dex_file_);
+ PrependToLastFailMessage(prepend);
return false;
}
/* Perform code flow verification. */
if (!CodeFlowVerifyMethod()) {
- DCHECK_NE(failure_, VERIFY_ERROR_NONE);
+ DCHECK_NE(failures_.size(), 0U);
return false;
}
/* Generate a register map and add it to the method. */
UniquePtr<const std::vector<uint8_t> > map(GenerateGcMap());
if (map.get() == NULL) {
- DCHECK_NE(failure_, VERIFY_ERROR_NONE);
+ DCHECK_NE(failures_.size(), 0U);
return false; // Not a real failure, but a failure to encode
}
#ifndef NDEBUG
VerifyGcMap(*map);
#endif
const std::vector<uint8_t>* gc_map = CreateLengthPrefixedGcMap(*(map.get()));
- Compiler::MethodReference ref(dex_file_, method_->GetDexMethodIndex());
+ Compiler::MethodReference ref(dex_file_, method_idx_);
verifier::MethodVerifier::SetGcMap(ref, *gc_map);
- method_->SetGcMap(&gc_map->at(0));
+ if (foo_method_ != NULL) {
+ foo_method_->SetGcMap(&gc_map->at(0));
+ }
#if defined(ART_USE_LLVM_COMPILER)
/* Generate Inferred Register Category for LLVM-based Code Generator */
@@ -962,6 +970,18 @@
return true;
}
+std::ostream& MethodVerifier::DumpFailures(std::ostream& os) {
+ DCHECK_EQ(failures_.size(), failure_messages_.size());
+ for (size_t i = 0; i < failures_.size(); ++i) {
+ os << failure_messages_[i]->str() << std::endl;
+ }
+ return os;
+}
+
+extern "C" void MethodVerifierGdbDump(MethodVerifier* v) {
+ v->Dump(std::cerr);
+}
+
void MethodVerifier::Dump(std::ostream& os) {
if (code_item_ == NULL) {
os << "Native method" << std::endl;
@@ -1005,22 +1025,22 @@
DCHECK_GE(arg_start, 0); /* should have been verified earlier */
//Include the "this" pointer.
size_t cur_arg = 0;
- if (!method_->IsStatic()) {
+ if (!IsStatic()) {
// If this is a constructor for a class other than java.lang.Object, mark the first ("this")
// argument as uninitialized. This restricts field access until the superclass constructor is
// called.
- Class* declaring_class = method_->GetDeclaringClass();
- if (method_->IsConstructor() && !declaring_class->IsObjectClass()) {
+ const RegType& declaring_class = GetDeclaringClass();
+ if (IsConstructor() && !declaring_class.IsJavaLangObject()) {
reg_line->SetRegisterType(arg_start + cur_arg,
reg_types_.UninitializedThisArgument(declaring_class));
} else {
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.FromClass(declaring_class));
+ reg_line->SetRegisterType(arg_start + cur_arg, declaring_class);
}
cur_arg++;
}
const DexFile::ProtoId& proto_id =
- dex_file_->GetMethodPrototype(dex_file_->GetMethodId(method_->GetDexMethodIndex()));
+ dex_file_->GetMethodPrototype(dex_file_->GetMethodId(method_idx_));
DexFileParameterIterator iterator(*dex_file_, proto_id);
for (; iterator.HasNext(); iterator.Next()) {
@@ -1041,8 +1061,7 @@
// it's effectively considered initialized the instant we reach here (in the sense that we
// can return without doing anything or call virtual methods).
{
- const RegType& reg_type =
- reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
+ const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor);
reg_line->SetRegisterType(arg_start + cur_arg, reg_type);
}
break;
@@ -1163,7 +1182,7 @@
if (work_line_->CompareLine(register_line) != 0) {
Dump(std::cout);
std::cout << info_messages_.str();
- LOG(FATAL) << "work_line diverged in " << PrettyMethod(method_)
+ LOG(FATAL) << "work_line diverged in " << PrettyMethod(method_idx_, *dex_file_)
<< "@" << reinterpret_cast<void*>(work_insn_idx_) << std::endl
<< " work_line=" << *work_line_ << std::endl
<< " expected=" << *register_line;
@@ -1172,7 +1191,9 @@
#endif
}
if (!CodeFlowVerifyInstruction(&start_guess)) {
- fail_messages_ << std::endl << PrettyMethod(method_) << " failed to verify";
+ std::string prepend(PrettyMethod(method_idx_, *dex_file_));
+ prepend += " failed to verify: ";
+ PrependToLastFailMessage(prepend);
return false;
}
/* Clear "changed" and mark as visited. */
@@ -1180,7 +1201,7 @@
insn_flags_[insn_idx].ClearChanged();
}
- if (DEAD_CODE_SCAN && ((method_->GetAccessFlags() & kAccWritable) == 0)) {
+ if (DEAD_CODE_SCAN && ((method_access_flags_ & kAccWritable) == 0)) {
/*
* Scan for dead code. There's nothing "evil" about dead code
* (besides the wasted space), but it indicates a flaw somewhere
@@ -1335,14 +1356,14 @@
break;
}
case Instruction::RETURN_VOID:
- if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
- if (!GetMethodReturnType().IsUnknown()) {
+ if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
+ if (!GetMethodReturnType().IsConflict()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void not expected";
}
}
break;
case Instruction::RETURN:
- if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
+ if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
/* check the method signature */
const RegType& return_type = GetMethodReturnType();
if (!return_type.IsCategory1Types()) {
@@ -1356,30 +1377,31 @@
return_type.IsShort() || return_type.IsChar()) &&
src_type.IsInteger()));
/* check the register contents */
- work_line_->VerifyRegisterType(dec_insn.vA, use_src ? src_type : return_type);
- if (failure_ != VERIFY_ERROR_NONE) {
- fail_messages_ << " return-1nr on invalid register v" << dec_insn.vA;
+ bool success =
+ work_line_->VerifyRegisterType(dec_insn.vA, use_src ? src_type : return_type);
+ if (!success) {
+ AppendToLastFailMessage(StringPrintf(" return-1nr on invalid register v%d", dec_insn.vA));
}
}
}
break;
case Instruction::RETURN_WIDE:
- if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
+ if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
/* check the method signature */
const RegType& return_type = GetMethodReturnType();
if (!return_type.IsCategory2Types()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-wide not expected";
} else {
/* check the register contents */
- work_line_->VerifyRegisterType(dec_insn.vA, return_type);
- if (failure_ != VERIFY_ERROR_NONE) {
- fail_messages_ << " return-wide on invalid register pair v" << dec_insn.vA;
+ bool success = work_line_->VerifyRegisterType(dec_insn.vA, return_type);
+ if (!success) {
+ AppendToLastFailMessage(StringPrintf(" return-wide on invalid register v%d", dec_insn.vA));
}
}
}
break;
case Instruction::RETURN_OBJECT:
- if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
+ if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
const RegType& return_type = GetMethodReturnType();
if (!return_type.IsReferenceTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected";
@@ -1426,9 +1448,9 @@
// Get type from instruction if unresolved then we need an access check
// TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB);
- // Register holds class, ie its type is class, but on error we keep it Unknown
+ // Register holds class, ie its type is class, on error it will hold Conflict.
work_line_->SetRegisterType(dec_insn.vA,
- res_type.IsUnknown() ? res_type : reg_types_.JavaLangClass());
+ res_type.IsConflict() ? res_type : reg_types_.JavaLangClass());
break;
}
case Instruction::MONITOR_ENTER:
@@ -1471,9 +1493,12 @@
bool is_checkcast = dec_insn.opcode == Instruction::CHECK_CAST;
const RegType& res_type =
ResolveClassAndCheckAccess(is_checkcast ? dec_insn.vB : dec_insn.vC);
- if (res_type.IsUnknown()) {
- CHECK_NE(failure_, VERIFY_ERROR_NONE);
- break; // couldn't resolve class
+ if (res_type.IsConflict()) {
+ DCHECK_NE(failures_.size(), 0U);
+ if (!is_checkcast) {
+ work_line_->SetRegisterType(dec_insn.vA, reg_types_.Boolean());
+ }
+ break; // bad class
}
// TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
const RegType& orig_type =
@@ -1504,9 +1529,9 @@
}
case Instruction::NEW_INSTANCE: {
const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB);
- if (res_type.IsUnknown()) {
- CHECK_NE(failure_, VERIFY_ERROR_NONE);
- break; // couldn't resolve class
+ if (res_type.IsConflict()) {
+ DCHECK_NE(failures_.size(), 0U);
+ break; // bad class
}
// TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
// can't create an instance of an interface or abstract class */
@@ -1590,9 +1615,8 @@
if (!array_type.IsArrayTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type;
} else {
- const RegType& component_type = reg_types_.GetComponentType(array_type,
- method_->GetDeclaringClass()->GetClassLoader());
- DCHECK(!component_type.IsUnknown());
+ const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
+ DCHECK(!component_type.IsConflict());
if (component_type.IsNonZeroReferenceTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type "
<< component_type;
@@ -1806,28 +1830,25 @@
bool is_super = (dec_insn.opcode == Instruction::INVOKE_SUPER ||
dec_insn.opcode == Instruction::INVOKE_SUPER_RANGE);
Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_VIRTUAL, is_range, is_super);
- if (failure_ == VERIFY_ERROR_NONE) {
- const char* descriptor;
- if (called_method == NULL) {
- uint32_t method_idx = dec_insn.vB;
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
- descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- } else {
- descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
- }
- const RegType& return_type =
- reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
- work_line_->SetResultRegisterType(return_type);
- just_set_result = true;
+ const char* descriptor;
+ if (called_method == NULL) {
+ uint32_t method_idx = dec_insn.vB;
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+ uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+ descriptor = dex_file_->StringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
}
+ const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor);
+ work_line_->SetResultRegisterType(return_type);
+ just_set_result = true;
break;
}
case Instruction::INVOKE_DIRECT:
case Instruction::INVOKE_DIRECT_RANGE: {
bool is_range = (dec_insn.opcode == Instruction::INVOKE_DIRECT_RANGE);
Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_DIRECT, is_range, false);
- if (failure_ == VERIFY_ERROR_NONE) {
+ if (called_method != NULL) {
/*
* Some additional checks when calling a constructor. We know from the invocation arg check
* that the "this" argument is an instance of called_method->klass. Now we further restrict
@@ -1835,18 +1856,9 @@
* allowing the latter only if the "this" argument is the same as the "this" argument to
* this method (which implies that we're in a constructor ourselves).
*/
- bool is_constructor;
- if (called_method != NULL) {
- is_constructor = called_method->IsConstructor();
- } else {
- uint32_t method_idx = dec_insn.vB;
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- const char* name = dex_file_->GetMethodName(method_id);
- is_constructor = strcmp(name, "<init>") == 0;
- }
- if (is_constructor) {
+ if (called_method->IsConstructor()) {
const RegType& this_type = work_line_->GetInvocationThis(dec_insn);
- if (failure_ != VERIFY_ERROR_NONE)
+ if (this_type.IsConflict()) // failure.
break;
/* no null refs allowed (?) */
@@ -1855,18 +1867,29 @@
break;
}
if (called_method != NULL) {
- Class* this_class = this_type.GetClass();
- DCHECK(this_class != NULL);
/* must be in same class or in superclass */
- if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) {
- if (this_class != method_->GetDeclaringClass()) {
+ const RegType& this_super_klass = this_type.GetSuperClass(®_types_);
+ if (this_super_klass.IsConflict()) {
+ // Unknown super class, fail so we re-check at runtime.
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "super class unknown for '" << this_type << "'";
+ break;
+ } else {
+ if (!this_super_klass.IsZero() &&
+ called_method->GetDeclaringClass() == this_super_klass.GetClass()) {
+ if (this_type.GetClass() != GetDeclaringClass().GetClass()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "invoke-direct <init> on super only allowed for 'this' in <init>"
+ << " (this class '" << this_type << "', called class '"
+ << PrettyDescriptor(called_method->GetDeclaringClass()) << "')";
+ break;
+ }
+ } else if (this_type.GetClass() != called_method->GetDeclaringClass()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD)
- << "invoke-direct <init> on super only allowed for 'this' in <init>";
+ << "invoke-direct <init> must be on current class or super"
+ << " (current class '" << this_type << "', called class '"
+ << PrettyDescriptor(called_method->GetDeclaringClass()) << "')";
break;
}
- } else if (called_method->GetDeclaringClass() != this_class) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke-direct <init> must be on current class or super";
- break;
}
}
@@ -1882,9 +1905,26 @@
* registers that have the same object instance in them, not just the "this" register.
*/
work_line_->MarkRefsAsInitialized(this_type);
- if (failure_ != VERIFY_ERROR_NONE)
- break;
}
+ }
+ const char* descriptor;
+ if (called_method == NULL) {
+ uint32_t method_idx = dec_insn.vB;
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+ uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+ descriptor = dex_file_->StringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
+ }
+ const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor);
+ work_line_->SetResultRegisterType(return_type);
+ just_set_result = true;
+ break;
+ }
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE: {
+ bool is_range = (dec_insn.opcode == Instruction::INVOKE_STATIC_RANGE);
+ Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_STATIC, is_range, false);
const char* descriptor;
if (called_method == NULL) {
uint32_t method_idx = dec_insn.vB;
@@ -1894,88 +1934,60 @@
} else {
descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
}
- const RegType& return_type =
- reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
+ const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor);
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
break;
- }
- case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_STATIC_RANGE: {
- bool is_range = (dec_insn.opcode == Instruction::INVOKE_STATIC_RANGE);
- Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_STATIC, is_range, false);
- if (failure_ == VERIFY_ERROR_NONE) {
- const char* descriptor;
- if (called_method == NULL) {
- uint32_t method_idx = dec_insn.vB;
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
- descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- } else {
- descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
- }
- const RegType& return_type =
- reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
- work_line_->SetResultRegisterType(return_type);
- just_set_result = true;
- }
- }
- break;
case Instruction::INVOKE_INTERFACE:
case Instruction::INVOKE_INTERFACE_RANGE: {
bool is_range = (dec_insn.opcode == Instruction::INVOKE_INTERFACE_RANGE);
Method* abs_method = VerifyInvocationArgs(dec_insn, METHOD_INTERFACE, is_range, false);
- if (failure_ == VERIFY_ERROR_NONE) {
- if (abs_method != NULL) {
- Class* called_interface = abs_method->GetDeclaringClass();
- if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) {
- Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '"
- << PrettyMethod(abs_method) << "'";
- break;
- }
+ if (abs_method != NULL) {
+ Class* called_interface = abs_method->GetDeclaringClass();
+ if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) {
+ Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '"
+ << PrettyMethod(abs_method) << "'";
+ break;
}
- /* Get the type of the "this" arg, which should either be a sub-interface of called
- * interface or Object (see comments in RegType::JoinClass).
- */
- const RegType& this_type = work_line_->GetInvocationThis(dec_insn);
- if (failure_ == VERIFY_ERROR_NONE) {
- if (this_type.IsZero()) {
- /* null pointer always passes (and always fails at runtime) */
- } else {
- if (this_type.IsUninitializedTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface call on uninitialized object "
- << this_type;
- break;
- }
- // In the past we have tried to assert that "called_interface" is assignable
- // from "this_type.GetClass()", however, as we do an imprecise Join
- // (RegType::JoinClass) we don't have full information on what interfaces are
- // implemented by "this_type". For example, two classes may implement the same
- // interfaces and have a common parent that doesn't implement the interface. The
- // join will set "this_type" to the parent class and a test that this implements
- // the interface will incorrectly fail.
- }
- }
- /*
- * We don't have an object instance, so we can't find the concrete method. However, all of
- * the type information is in the abstract method, so we're good.
- */
- const char* descriptor;
- if (abs_method == NULL) {
- uint32_t method_idx = dec_insn.vB;
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
- descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- } else {
- descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor();
- }
- const RegType& return_type =
- reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
- work_line_->SetResultRegisterType(return_type);
- work_line_->SetResultRegisterType(return_type);
- just_set_result = true;
}
+ /* Get the type of the "this" arg, which should either be a sub-interface of called
+ * interface or Object (see comments in RegType::JoinClass).
+ */
+ const RegType& this_type = work_line_->GetInvocationThis(dec_insn);
+ if (this_type.IsZero()) {
+ /* null pointer always passes (and always fails at runtime) */
+ } else {
+ if (this_type.IsUninitializedTypes()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface call on uninitialized object "
+ << this_type;
+ break;
+ }
+ // In the past we have tried to assert that "called_interface" is assignable
+ // from "this_type.GetClass()", however, as we do an imprecise Join
+ // (RegType::JoinClass) we don't have full information on what interfaces are
+ // implemented by "this_type". For example, two classes may implement the same
+ // interfaces and have a common parent that doesn't implement the interface. The
+ // join will set "this_type" to the parent class and a test that this implements
+ // the interface will incorrectly fail.
+ }
+ /*
+ * We don't have an object instance, so we can't find the concrete method. However, all of
+ * the type information is in the abstract method, so we're good.
+ */
+ const char* descriptor;
+ if (abs_method == NULL) {
+ uint32_t method_idx = dec_insn.vB;
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+ uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+ descriptor = dex_file_->StringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor();
+ }
+ const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor);
+ work_line_->SetResultRegisterType(return_type);
+ work_line_->SetResultRegisterType(return_type);
+ just_set_result = true;
break;
}
case Instruction::NEG_INT:
@@ -2212,21 +2224,24 @@
*/
} // end - switch (dec_insn.opcode)
- if (failure_ != VERIFY_ERROR_NONE) {
- if (failure_ == VERIFY_ERROR_BAD_CLASS_HARD || failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) {
- /* immediate failure, reject class */
- fail_messages_ << std::endl << "Rejecting opcode " << inst->DumpString(dex_file_);
- return false;
- } else {
- /* replace opcode and continue on */
- fail_messages_ << std::endl << "Replacing opcode " << inst->DumpString(dex_file_);
- ReplaceFailingInstruction();
- /* IMPORTANT: method->insns may have been changed */
- insns = code_item_->insns_ + work_insn_idx_;
- /* continue on as if we just handled a throw-verification-error */
- failure_ = VERIFY_ERROR_NONE;
- opcode_flags = Instruction::kThrow;
+ if (have_pending_hard_failure_) {
+ if (!Runtime::Current()->IsStarted()) {
+ /* When compiling, check that the first failure is a hard failure */
+ CHECK_EQ(failures_[failures_.size() - 1], VERIFY_ERROR_BAD_CLASS_HARD);
}
+ /* immediate failure, reject class */
+ info_messages_ << "Rejecting opcode " << inst->DumpString(dex_file_);
+ return false;
+ } else if (have_pending_rewrite_failure_) {
+ /* replace opcode and continue on */
+ std::string append("Replacing opcode ");
+ append += inst->DumpString(dex_file_);
+ AppendToLastFailMessage(append);
+ ReplaceFailingInstruction();
+ /* IMPORTANT: method->insns may have been changed */
+ insns = code_item_->insns_ + work_insn_idx_;
+ /* continue on as if we just handled a throw-verification-error */
+ opcode_flags = Instruction::kThrow;
}
/*
* If we didn't just set the result register, clear it out. This ensures that you can only use
@@ -2401,28 +2416,26 @@
const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
- Class* referrer = method_->GetDeclaringClass();
- Class* klass = method_->GetDexCacheResolvedTypes()->Get(class_idx);
+ const RegType& referrer = GetDeclaringClass();
+ Class* klass = dex_cache_->GetResolvedType(class_idx);
const RegType& result =
klass != NULL ? reg_types_.FromClass(klass)
- : reg_types_.FromDescriptor(referrer->GetClassLoader(), descriptor);
+ : reg_types_.FromDescriptor(class_loader_, descriptor);
+ if (result.IsConflict()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor
+ << "' in " << referrer;
+ return result;
+ }
if (klass == NULL && !result.IsUnresolvedTypes()) {
- method_->GetDexCacheResolvedTypes()->Set(class_idx, result.GetClass());
+ dex_cache_->SetResolvedType(class_idx, result.GetClass());
}
- if (result.IsUnknown()) {
- Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing unknown class in " << PrettyDescriptor(referrer);
- return result;
- }
- // Check if access is allowed. Unresolved types use AllocObjectFromCodeWithAccessCheck to
+ // Check if access is allowed. Unresolved types use xxxWithAccessCheck to
// check at runtime if access is allowed and so pass here.
- if (!result.IsUnresolvedTypes() && !referrer->CanAccess(result.GetClass())) {
+ if (!result.IsUnresolvedTypes() && !referrer.IsUnresolvedTypes() && !referrer.CanAccess(result)) {
Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '"
- << PrettyDescriptor(referrer) << "' -> '"
- << result << "'";
- return reg_types_.Unknown();
- } else {
- return result;
+ << referrer << "' -> '" << result << "'";
}
+ return result;
}
const RegType& MethodVerifier::GetCaughtExceptionType() {
@@ -2446,7 +2459,7 @@
// We don't know enough about the type and the common path merge will result in
// Conflict. Fail here knowing the correct thing can be done at runtime.
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception;
- return reg_types_.Unknown();
+ return reg_types_.Conflict();
} else if (common_super->Equals(exception)) {
// odd case, but nothing to do
} else {
@@ -2462,25 +2475,26 @@
if (common_super == NULL) {
/* no catch blocks, or no catches with classes we can find */
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unable to find exception handler";
- return reg_types_.Unknown();
+ return reg_types_.Conflict();
}
return *common_super;
}
-Method* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t method_idx, MethodType method_type) {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+Method* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx, MethodType method_type) {
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx);
const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
- if (failure_ != VERIFY_ERROR_NONE) {
- fail_messages_ << " in attempt to access method " << dex_file_->GetMethodName(method_id);
+ if (klass_type.IsConflict()) {
+ std::string append(" in attempt to access method ");
+ append += dex_file_->GetMethodName(method_id);
+ AppendToLastFailMessage(append);
return NULL;
}
if (klass_type.IsUnresolvedTypes()) {
return NULL; // Can't resolve Class so no more to do here
}
Class* klass = klass_type.GetClass();
- Class* referrer = method_->GetDeclaringClass();
- DexCache* dex_cache = referrer->GetDexCache();
- Method* res_method = dex_cache->GetResolvedMethod(method_idx);
+ const RegType& referrer = GetDeclaringClass();
+ Method* res_method = dex_cache_->GetResolvedMethod(dex_method_idx);
if (res_method == NULL) {
const char* name = dex_file_->GetMethodName(method_id);
std::string signature(dex_file_->CreateMethodSignature(method_id.proto_idx_, NULL));
@@ -2493,7 +2507,7 @@
res_method = klass->FindVirtualMethod(name, signature);
}
if (res_method != NULL) {
- dex_cache->SetResolvedMethod(method_idx, res_method);
+ dex_cache_->SetResolvedMethod(dex_method_idx, res_method);
} else {
// If a virtual or interface method wasn't found with the expected type, look in
// the direct methods. This can happen when the wrong invoke type is used or when
@@ -2523,9 +2537,9 @@
return NULL;
}
// Check if access is allowed.
- if (!referrer->CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
+ if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call " << PrettyMethod(res_method)
- << " from " << PrettyDescriptor(referrer) << ")";
+ << " from " << referrer << ")";
return NULL;
}
// Check that invoke-virtual and invoke-super are not used on private methods of the same class.
@@ -2570,15 +2584,18 @@
// has a vtable entry for the target method.
if (is_super) {
DCHECK(method_type == METHOD_VIRTUAL);
- Class* super = method_->GetDeclaringClass()->GetSuperClass();
- if (super == NULL || res_method->GetMethodIndex() >= super->GetVTable()->GetLength()) {
- if (super == NULL) { // Only Object has no super class
- Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(method_)
+ const RegType& super = GetDeclaringClass().GetSuperClass(®_types_);
+ Class* super_klass = super.GetClass();
+ if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) {
+ if (super.IsConflict()) { // Only Object has no super class
+ Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
+ << PrettyMethod(method_idx_, *dex_file_)
<< " to super " << PrettyMethod(res_method);
} else {
MethodHelper mh(res_method);
- Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(method_)
- << " to super " << PrettyDescriptor(super)
+ Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
+ << PrettyMethod(method_idx_, *dex_file_)
+ << " to super " << super
<< "." << mh.GetName()
<< mh.GetSignature();
}
@@ -2598,15 +2615,14 @@
}
/*
- * 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, so we can't do a rigorous check here (which
- * is okay since we have to do it at runtime).
+ * 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).
*/
size_t actual_args = 0;
if (!res_method->IsStatic()) {
const RegType& actual_arg_type = work_line_->GetInvocationThis(dec_insn);
- if (failure_ != VERIFY_ERROR_NONE) {
+ if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
return NULL;
}
if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) {
@@ -2644,8 +2660,7 @@
<< " missing signature component";
return NULL;
}
- const RegType& reg_type =
- reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
+ const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor);
uint32_t get_reg = is_range ? dec_insn.vC + actual_args : dec_insn.arg[actual_args];
if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
return NULL;
@@ -2661,16 +2676,11 @@
}
}
-const RegType& MethodVerifier::GetMethodReturnType() {
- return reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(),
- MethodHelper(method_).GetReturnTypeDescriptor());
-}
-
void MethodVerifier::VerifyNewArray(const DecodedInstruction& dec_insn, bool is_filled,
bool is_range) {
const RegType& res_type = ResolveClassAndCheckAccess(is_filled ? dec_insn.vB : dec_insn.vC);
- if (res_type.IsUnknown()) {
- CHECK_NE(failure_, VERIFY_ERROR_NONE);
+ if (res_type.IsConflict()) { // bad class
+ DCHECK_NE(failures_.size(), 0U);
} else {
// TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
if (!res_type.IsArrayTypes()) {
@@ -2683,13 +2693,12 @@
} else {
// Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of
// the list and fail. It's legal, if silly, for arg_count to be zero.
- const RegType& expected_type = reg_types_.GetComponentType(res_type,
- method_->GetDeclaringClass()->GetClassLoader());
+ const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_);
uint32_t arg_count = dec_insn.vA;
for (size_t ui = 0; ui < arg_count; ui++) {
uint32_t get_reg = is_range ? dec_insn.vC + ui : dec_insn.arg[ui];
if (!work_line_->VerifyRegisterType(get_reg, expected_type)) {
- work_line_->SetResultRegisterType(reg_types_.Unknown());
+ work_line_->SetResultRegisterType(reg_types_.Conflict());
return;
}
}
@@ -2720,8 +2729,7 @@
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
} else {
/* verify the class */
- const RegType& component_type = reg_types_.GetComponentType(array_type,
- method_->GetDeclaringClass()->GetClassLoader());
+ const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
if (!component_type.IsReferenceTypes() && !is_primitive) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
<< " source for aget-object";
@@ -2757,8 +2765,7 @@
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput";
} else {
/* verify the class */
- const RegType& component_type = reg_types_.GetComponentType(array_type,
- method_->GetDeclaringClass()->GetClassLoader());
+ const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
if (!component_type.IsReferenceTypes() && !is_primitive) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
<< " source for aput-object";
@@ -2784,16 +2791,17 @@
const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
// Check access to class
const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
- if (failure_ != VERIFY_ERROR_NONE) {
- fail_messages_ << " in attempt to access static field " << field_idx << " ("
- << dex_file_->GetFieldName(field_id) << ") in "
- << dex_file_->GetFieldDeclaringClassDescriptor(field_id);
+ if (klass_type.IsConflict()) { // bad class
+ AppendToLastFailMessage(StringPrintf(" in attempt to access static field %d (%s) in %s",
+ field_idx, dex_file_->GetFieldName(field_id),
+ dex_file_->GetFieldDeclaringClassDescriptor(field_id)));
return NULL;
}
if (klass_type.IsUnresolvedTypes()) {
- return NULL; // Can't resolve Class so no more to do here
+ return NULL; // Can't resolve Class so no more to do here, will do checking at runtime.
}
- Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(field_idx, method_);
+ Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx,
+ dex_cache_, class_loader_);
if (field == NULL) {
LOG(INFO) << "unable to resolve static field " << field_idx << " ("
<< dex_file_->GetFieldName(field_id) << ") in "
@@ -2801,10 +2809,10 @@
DCHECK(Thread::Current()->IsExceptionPending());
Thread::Current()->ClearException();
return NULL;
- } else if (!method_->GetDeclaringClass()->CanAccessMember(field->GetDeclaringClass(),
- field->GetAccessFlags())) {
+ } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
+ field->GetAccessFlags())) {
Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot access static field " << PrettyField(field)
- << " from " << PrettyClass(method_->GetDeclaringClass());
+ << " from " << GetDeclaringClass();
return NULL;
} else if (!field->IsStatic()) {
Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field) << " to be static";
@@ -2818,16 +2826,17 @@
const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
// Check access to class
const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
- if (failure_ != VERIFY_ERROR_NONE) {
- fail_messages_ << " in attempt to access instance field " << field_idx << " ("
- << dex_file_->GetFieldName(field_id) << ") in "
- << dex_file_->GetFieldDeclaringClassDescriptor(field_id);
+ if (klass_type.IsConflict()) {
+ AppendToLastFailMessage(StringPrintf(" in attempt to access instance field %d (%s) in %s",
+ field_idx, dex_file_->GetFieldName(field_id),
+ dex_file_->GetFieldDeclaringClassDescriptor(field_id)));
return NULL;
}
if (klass_type.IsUnresolvedTypes()) {
return NULL; // Can't resolve Class so no more to do here
}
- Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(field_idx, method_);
+ Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx,
+ dex_cache_, class_loader_);
if (field == NULL) {
LOG(INFO) << "unable to resolve instance field " << field_idx << " ("
<< dex_file_->GetFieldName(field_id) << ") in "
@@ -2835,10 +2844,10 @@
DCHECK(Thread::Current()->IsExceptionPending());
Thread::Current()->ClearException();
return NULL;
- } else if (!method_->GetDeclaringClass()->CanAccessMember(field->GetDeclaringClass(),
- field->GetAccessFlags())) {
+ } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
+ field->GetAccessFlags())) {
Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot access instance field " << PrettyField(field)
- << " from " << PrettyClass(method_->GetDeclaringClass());
+ << " from " << GetDeclaringClass();
return NULL;
} else if (field->IsStatic()) {
Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field)
@@ -2847,24 +2856,27 @@
} else if (obj_type.IsZero()) {
// Cannot infer and check type, however, access will cause null pointer exception
return field;
- } else if (obj_type.IsUninitializedTypes() &&
- (!method_->IsConstructor() || method_->GetDeclaringClass() != obj_type.GetClass() ||
- field->GetDeclaringClass() != method_->GetDeclaringClass())) {
- // Field accesses through uninitialized references are only allowable for constructors where
- // the field is declared in this class
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
- << " of a not fully initialized object within the context of "
- << PrettyMethod(method_);
- return NULL;
- } else if (!field->GetDeclaringClass()->IsAssignableFrom(obj_type.GetClass())) {
- // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
- // of C1. For resolution to occur the declared class of the field must be compatible with
- // obj_type, we've discovered this wasn't so, so report the field didn't exist.
- Fail(VERIFY_ERROR_NO_FIELD) << "cannot access instance field " << PrettyField(field)
- << " from object of type " << PrettyClass(obj_type.GetClass());
- return NULL;
} else {
- return field;
+ const RegType& field_klass = reg_types_.FromClass(field->GetDeclaringClass());
+ if (obj_type.IsUninitializedTypes() &&
+ (!IsConstructor() || GetDeclaringClass().Equals(obj_type) ||
+ !field_klass.Equals(GetDeclaringClass()))) {
+ // Field accesses through uninitialized references are only allowable for constructors where
+ // the field is declared in this class
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
+ << " of a not fully initialized object within the context of "
+ << PrettyMethod(method_idx_, *dex_file_);
+ return NULL;
+ } else if (!field_klass.IsAssignableFrom(obj_type)) {
+ // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
+ // of C1. For resolution to occur the declared class of the field must be compatible with
+ // obj_type, we've discovered this wasn't so, so report the field didn't exist.
+ Fail(VERIFY_ERROR_NO_FIELD) << "cannot access instance field " << PrettyField(field)
+ << " from object of type " << obj_type;
+ return NULL;
+ } else {
+ return field;
+ }
}
}
@@ -2878,46 +2890,44 @@
const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB);
field = GetInstanceField(object_type, field_idx);
}
- if (failure_ != VERIFY_ERROR_NONE) {
- work_line_->SetRegisterType(dec_insn.vA, reg_types_.Unknown());
+ const char* descriptor;
+ const ClassLoader* loader;
+ if (field != NULL) {
+ descriptor = FieldHelper(field).GetTypeDescriptor();
+ loader = field->GetDeclaringClass()->GetClassLoader();
} else {
- const char* descriptor;
- const ClassLoader* loader;
- if (field != NULL) {
- descriptor = FieldHelper(field).GetTypeDescriptor();
- loader = field->GetDeclaringClass()->GetClassLoader();
- } else {
- const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- loader = method_->GetDeclaringClass()->GetClassLoader();
- }
- const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
- 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";
- return;
- }
- }
- work_line_->SetRegisterType(dec_insn.vA, field_type);
+ const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
+ descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
+ loader = class_loader_;
}
+ const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
+ 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";
+ work_line_->SetRegisterType(dec_insn.vA, reg_types_.Conflict());
+ 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(dec_insn.vA, reg_types_.Conflict());
+ return;
+ }
+ }
+ work_line_->SetRegisterType(dec_insn.vA, field_type);
}
void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn,
@@ -2930,75 +2940,71 @@
const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB);
field = GetInstanceField(object_type, field_idx);
}
- if (failure_ != VERIFY_ERROR_NONE) {
- work_line_->SetRegisterType(dec_insn.vA, reg_types_.Unknown());
+ const char* descriptor;
+ const ClassLoader* loader;
+ if (field != NULL) {
+ descriptor = FieldHelper(field).GetTypeDescriptor();
+ loader = field->GetDeclaringClass()->GetClassLoader();
} else {
- const char* descriptor;
- const ClassLoader* loader;
- if (field != NULL) {
- descriptor = FieldHelper(field).GetTypeDescriptor();
- loader = field->GetDeclaringClass()->GetClassLoader();
+ const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
+ descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
+ loader = class_loader_;
+ }
+ const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
+ 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;
+ }
+ }
+ 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(dec_insn.vA);
+ 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 {
- const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- loader = method_->GetDeclaringClass()->GetClassLoader();
+ instruction_compatible = false; // reference field with primitive store
+ value_compatible = false; // unused
}
- const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
- if (field != NULL) {
- if (field->IsFinal() && field->GetDeclaringClass() != method_->GetDeclaringClass()) {
- Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
- << " from other class " << PrettyClass(method_->GetDeclaringClass());
- return;
- }
+ 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 (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(dec_insn.vA);
- 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" << dec_insn.vA
- << " 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(dec_insn.vA, field_type);
+ if (!value_compatible) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << dec_insn.vA
+ << " 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(dec_insn.vA, field_type);
}
}
@@ -3011,9 +3017,19 @@
}
void MethodVerifier::ReplaceFailingInstruction() {
+ // Pop the failure and clear the need for rewriting.
+ size_t failure_number = failures_.size();
+ CHECK_NE(failure_number, 0U);
+ DCHECK_EQ(failure_messages_.size(), failure_number);
+ std::ostringstream* failure_message = failure_messages_[failure_number - 1];
+ VerifyError failure = failures_[failure_number - 1];
+ failures_.pop_back();
+ failure_messages_.pop_back();
+ have_pending_rewrite_failure_ = false;
+
if (Runtime::Current()->IsStarted()) {
- LOG(ERROR) << "Verification attempting to replace instructions in " << PrettyMethod(method_)
- << " " << fail_messages_.str();
+ LOG(ERROR) << "Verification attempting to replace instructions at runtime in "
+ << PrettyMethod(method_idx_, *dex_file_) << " " << failure_message->str();
return;
}
const Instruction* inst = Instruction::At(code_item_->insns_ + work_insn_idx_);
@@ -3087,13 +3103,14 @@
}
// Encode the opcode, with the failure code in the high byte
uint16_t new_instruction = Instruction::THROW_VERIFICATION_ERROR |
- (failure_ << 8) | // AA - component
+ (failure << 8) | // AA - component
(ref_type << (8 + kVerifyErrorRefTypeShift));
insns[work_insn_idx_] = new_instruction;
// The 2nd code unit (higher in memory) with the reference in, comes from the instruction we
// rewrote, so nothing to do here.
- LOG(INFO) << "Verification error, replacing instructions in " << PrettyMethod(method_) << " "
- << fail_messages_.str();
+ LOG(INFO) << "Verification error, replacing instructions in "
+ << PrettyMethod(method_idx_, *dex_file_) << " "
+ << failure_message->str();
if (gDebugVerify) {
std::cout << std::endl << info_messages_.str();
Dump(std::cout);
@@ -3116,7 +3133,7 @@
copy->CopyFromLine(target_line);
}
changed = target_line->MergeRegisters(merge_line);
- if (failure_ != VERIFY_ERROR_NONE) {
+ if (have_pending_hard_failure_) {
return false;
}
if (gDebugVerify && changed) {
@@ -3137,6 +3154,24 @@
return &insn_flags_[work_insn_idx_];
}
+const RegType& MethodVerifier::GetMethodReturnType() {
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_);
+ const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
+ uint16_t return_type_idx = proto_id.return_type_idx_;
+ const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
+ return reg_types_.FromDescriptor(class_loader_, descriptor);
+}
+
+const RegType& MethodVerifier::GetDeclaringClass() {
+ if (foo_method_ != NULL) {
+ return reg_types_.FromClass(foo_method_->GetDeclaringClass());
+ } else {
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_);
+ const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
+ return reg_types_.FromDescriptor(class_loader_, descriptor);
+ }
+}
+
void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits,
size_t* log2_max_gc_pc) {
size_t local_gc_points = 0;
diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h
index fc5b7ea..512e26f 100644
--- a/src/verifier/method_verifier.h
+++ b/src/verifier/method_verifier.h
@@ -83,7 +83,6 @@
* to be rewritten to fail at runtime.
*/
enum VerifyError {
- VERIFY_ERROR_NONE = 0, // No error; must be zero.
VERIFY_ERROR_BAD_CLASS_HARD, // VerifyError; hard error that skips compilation.
VERIFY_ERROR_BAD_CLASS_SOFT, // VerifyError; soft error that verifies again at runtime.
@@ -153,11 +152,6 @@
public:
/* Verify a class. Returns "true" on success. */
static bool VerifyClass(const Class* klass, std::string& error);
-
- /*
- * Structurally verify a class. Returns "true" on success. Used at compile time
- * when the pointer for the method or declaring class can't be resolved.
- */
static bool VerifyClass(const DexFile* dex_file, DexCache* dex_cache,
const ClassLoader* class_loader, uint32_t class_def_idx, std::string& error);
@@ -171,15 +165,18 @@
return ®_types_;
}
- // Verification failed
+ // Log a verification failure.
std::ostream& Fail(VerifyError error);
- // Log for verification information
+ // Log for verification information.
std::ostream& LogVerifyInfo() {
- return info_messages_ << "VFY: " << PrettyMethod(method_)
+ return info_messages_ << "VFY: " << PrettyMethod(method_idx_, *dex_file_)
<< '[' << reinterpret_cast<void*>(work_insn_idx_) << "] : ";
}
+ // Dump the failures encountered by the verifier.
+ std::ostream& DumpFailures(std::ostream& os);
+
// Dump the state of the verifier, namely each instruction, what flags are set on it, register
// information
void Dump(std::ostream& os);
@@ -198,9 +195,15 @@
private:
- explicit MethodVerifier(Method* method);
explicit MethodVerifier(const DexFile* dex_file, DexCache* dex_cache,
- const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item);
+ const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_idx, Method* method, uint32_t access_flags);
+
+ // Adds the given string to the beginning of the last failure message.
+ void PrependToLastFailMessage(std::string);
+
+ // Adds the given string to the end of the last failure message.
+ void AppendToLastFailMessage(std::string);
/*
* Perform verification on a single method.
@@ -212,42 +215,15 @@
* operands.
* (3) Iterate through the method, checking type safety and looking
* for code flow problems.
- *
- * Some checks may be bypassed depending on the verification mode. We can't
- * turn this stuff off completely if we want to do "exact" GC.
- *
- * Confirmed here:
- * - code array must not be empty
- * Confirmed by ComputeWidthsAndCountOps():
- * - opcode of first instruction begins at index 0
- * - only documented instructions may appear
- * - each instruction follows the last
- * - last byte of last instruction is at (code_length-1)
- */
- static bool VerifyMethod(Method* method);
- static void VerifyMethodAndDump(Method* method);
-
- /*
- * Perform structural verification on a single method. Used at compile time
- * when the pointer for the method or declaring class can't be resolved.
- *
- * We do this in two passes:
- * (1) Walk through all code units, determining instruction locations,
- * widths, and other characteristics.
- * (2) Walk through all code units, performing static checks on
- * operands.
- *
- * Code flow verification is skipped since a resolved method and class are
- * necessary to perform all the checks.
*/
static bool VerifyMethod(uint32_t method_idx, const DexFile* dex_file, DexCache* dex_cache,
- const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item);
+ const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+ Method* method, uint32_t method_access_flags);
+ static void VerifyMethodAndDump(Method* method);
- /* Run both structural and code flow verification on the method. */
- bool VerifyAll();
-
- /* Perform structural verification on a single method. */
- bool VerifyStructure();
+ // Run verification on the method. Returns true if verification completes and false if the input
+ // has an irrecoverable corruption.
+ bool Verify();
/*
* Compute the width of the instruction at each address in the instruction stream, and store it in
@@ -519,13 +495,6 @@
MethodType method_type, bool is_range, bool is_super);
/*
- * Return the register type for the method. We can't just use the already-computed
- * DalvikJniReturnType, because if it's a reference type we need to do the class lookup.
- * Returned references are assumed to be initialized. Returns kRegTypeUnknown for "void".
- */
- const RegType& GetMethodReturnType();
-
- /*
* 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.
* Returns "true" if all is well, "false" if the target instruction is move-exception.
@@ -545,6 +514,22 @@
*/
bool UpdateRegisters(uint32_t next_insn, const RegisterLine* merge_line);
+ // Is the method being verified a constructor?
+ bool IsConstructor() const {
+ return (method_access_flags_ & kAccConstructor) != 0;
+ }
+
+ // Is the method verified static?
+ bool IsStatic() const {
+ return (method_access_flags_ & kAccStatic) != 0;
+ }
+
+ // Return the register type for the method.
+ const RegType& GetMethodReturnType();
+
+ // Get a type representing the declaring class of the method.
+ const RegType& GetDeclaringClass();
+
#if defined(ART_USE_LLVM_COMPILER)
/*
* Generate the inferred register category for LLVM-based code generator.
@@ -601,7 +586,9 @@
// Storage for the register status we're saving for later.
UniquePtr<RegisterLine> saved_line_;
- Method* method_; // The method we're working on.
+ uint32_t method_idx_; // The method we're working on.
+ Method* foo_method_; // Its object representation if known.
+ uint32_t method_access_flags_; // Method's access flags.
const DexFile* dex_file_; // The dex file containing the method.
DexCache* dex_cache_; // The dex_cache for the declaring class of the method.
const ClassLoader* class_loader_; // The class loader for the declaring class of the method.
@@ -609,12 +596,16 @@
const DexFile::CodeItem* code_item_; // The code item containing the code for the method.
UniquePtr<InsnFlags[]> insn_flags_; // Instruction widths and flags, one entry per code unit.
- // The type of any error that occurs
- VerifyError failure_;
+ // The types of any error that occurs.
+ std::vector<VerifyError> failures_;
+ // Error messages associated with failures.
+ std::vector<std::ostringstream*> failure_messages_;
+ // Is there a pending hard failure?
+ bool have_pending_hard_failure_;
+ // Is there a pending failure that will cause dex opcodes to be rewritten.
+ bool have_pending_rewrite_failure_;
- // Failure message log
- std::ostringstream fail_messages_;
- // Info message log
+ // Info message log use primarily for verifier diagnostics.
std::ostringstream info_messages_;
// The number of occurrences of specific opcodes.
diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc
index f72ad6a..6598458 100644
--- a/src/verifier/reg_type.cc
+++ b/src/verifier/reg_type.cc
@@ -23,7 +23,7 @@
namespace verifier {
static const char* type_strings[] = {
- "Unknown",
+ "Undefined",
"Conflict",
"Boolean",
"Byte",
@@ -42,11 +42,12 @@
"Uninitialized Reference",
"Uninitialized This Reference",
"Unresolved And Uninitialized Reference",
+ "Unresolved And Uninitialized This Reference",
"Reference",
};
std::string RegType::Dump() const {
- DCHECK(type_ >= kRegTypeUnknown && type_ <= kRegTypeReference);
+ DCHECK(type_ >= kRegTypeUndefined && type_ <= kRegTypeReference);
std::string result;
if (IsConstant()) {
uint32_t val = ConstantValue();
@@ -84,69 +85,44 @@
}
}
-/*
- * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
- * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
- * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J
- * is the deepest (lowest upper bound) parent of S and T).
- *
- * This operation applies for regular classes and arrays, however, for interface types there needn't
- * be a partial ordering on the types. We could solve the problem of a lack of a partial order by
- * introducing sets of types, however, the only operation permissible on an interface is
- * invoke-interface. In the tradition of Java verifiers we defer the verification of interface
- * types until an invoke-interface call on the interface typed reference at runtime and allow
- * the perversion of any Object being assignable to an interface type (note, however, that we don't
- * allow assignment of Object or Interface to any concrete subclass of Object and are therefore type
- * safe; further the Join on a Object cannot result in a sub-class by definition).
- */
-Class* RegType::ClassJoin(Class* s, Class* t) {
- DCHECK(!s->IsPrimitive()) << PrettyClass(s);
- DCHECK(!t->IsPrimitive()) << PrettyClass(t);
- if (s == t) {
- return s;
- } else if (s->IsAssignableFrom(t)) {
- return s;
- } else if (t->IsAssignableFrom(s)) {
- return t;
- } else if (s->IsArrayClass() && t->IsArrayClass()) {
- Class* s_ct = s->GetComponentType();
- Class* t_ct = t->GetComponentType();
- if (s_ct->IsPrimitive() || t_ct->IsPrimitive()) {
- // Given the types aren't the same, if either array is of primitive types then the only
- // common parent is java.lang.Object
- Class* result = s->GetSuperClass(); // short-cut to java.lang.Object
- DCHECK(result->IsObjectClass());
- return result;
- }
- Class* common_elem = ClassJoin(s_ct, t_ct);
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const ClassLoader* class_loader = s->GetClassLoader();
- std::string descriptor("[");
- descriptor += ClassHelper(common_elem).GetDescriptor();
- Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
- DCHECK(array_class != NULL);
- return array_class;
- } else {
- size_t s_depth = s->Depth();
- size_t t_depth = t->Depth();
- // Get s and t to the same depth in the hierarchy
- if (s_depth > t_depth) {
- while (s_depth > t_depth) {
- s = s->GetSuperClass();
- s_depth--;
- }
+const RegType& RegType::GetSuperClass(RegTypeCache* cache) const {
+ if (!IsUnresolvedTypes()) {
+ Class* super_klass = GetClass()->GetSuperClass();
+ if (super_klass != NULL) {
+ return cache->FromClass(super_klass);
} else {
- while (t_depth > s_depth) {
- t = t->GetSuperClass();
- t_depth--;
- }
+ return cache->Zero();
}
- // Go up the hierarchy until we get to the common parent
- while (s != t) {
- s = s->GetSuperClass();
- t = t->GetSuperClass();
+ } else {
+ // TODO: handle unresolved type cases better?
+ return cache->Conflict();
+ }
+}
+
+bool RegType::CanAccess(const RegType& other) const {
+ if (Equals(other)) {
+ return true; // Trivial accessibility.
+ } else {
+ bool this_unresolved = IsUnresolvedTypes();
+ bool other_unresolved = other.IsUnresolvedTypes();
+ if (!this_unresolved && !other_unresolved) {
+ return GetClass()->CanAccess(other.GetClass());
+ } else if (!other_unresolved) {
+ return other.GetClass()->IsPublic(); // Be conservative, only allow if other is public.
+ } else {
+ return false; // More complicated test not possible on unresolved types, be conservative.
}
- return s;
+ }
+}
+
+bool RegType::CanAccessMember(Class* klass, uint32_t access_flags) const {
+ if (access_flags & kAccPublic) {
+ return true;
+ }
+ if (!IsUnresolvedTypes()) {
+ return GetClass()->CanAccessMember(klass, access_flags);
+ } else {
+ return false; // More complicated test not possible on unresolved types, be conservative.
}
}
@@ -181,6 +157,11 @@
GetClass()->IsAssignableFrom(src.GetClass())) {
// We're assignable from the Class point-of-view
return true;
+ } else if (IsUnresolvedTypes() && src.IsUnresolvedTypes() &&
+ GetDescriptor() == src.GetDescriptor()) {
+ // Two unresolved types (maybe one is uninitialized), we're clearly assignable if the
+ // descriptor is the same.
+ return true;
} else {
return false;
}
@@ -194,13 +175,13 @@
const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_types) const {
DCHECK(!Equals(incoming_type)); // Trivial equality handled by caller
- if (IsUnknown() && incoming_type.IsUnknown()) {
- return *this; // Unknown MERGE Unknown => Unknown
+ if (IsUndefined() && incoming_type.IsUndefined()) {
+ return *this; // Undefined MERGE Undefined => Undefined
} else if (IsConflict()) {
return *this; // Conflict MERGE * => Conflict
} else if (incoming_type.IsConflict()) {
return incoming_type; // * MERGE Conflict => Conflict
- } else if (IsUnknown() || incoming_type.IsUnknown()) {
+ } else if (IsUndefined() || incoming_type.IsUndefined()) {
return reg_types->Conflict(); // Unknown MERGE * => Conflict
} else if (IsConstant() && incoming_type.IsConstant()) {
int32_t val1 = ConstantValue();
@@ -291,6 +272,58 @@
}
}
+// See comment in reg_type.h
+Class* RegType::ClassJoin(Class* s, Class* t) {
+ DCHECK(!s->IsPrimitive()) << PrettyClass(s);
+ DCHECK(!t->IsPrimitive()) << PrettyClass(t);
+ if (s == t) {
+ return s;
+ } else if (s->IsAssignableFrom(t)) {
+ return s;
+ } else if (t->IsAssignableFrom(s)) {
+ return t;
+ } else if (s->IsArrayClass() && t->IsArrayClass()) {
+ Class* s_ct = s->GetComponentType();
+ Class* t_ct = t->GetComponentType();
+ if (s_ct->IsPrimitive() || t_ct->IsPrimitive()) {
+ // Given the types aren't the same, if either array is of primitive types then the only
+ // common parent is java.lang.Object
+ Class* result = s->GetSuperClass(); // short-cut to java.lang.Object
+ DCHECK(result->IsObjectClass());
+ return result;
+ }
+ Class* common_elem = ClassJoin(s_ct, t_ct);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const ClassLoader* class_loader = s->GetClassLoader();
+ std::string descriptor("[");
+ descriptor += ClassHelper(common_elem).GetDescriptor();
+ Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+ DCHECK(array_class != NULL);
+ return array_class;
+ } else {
+ size_t s_depth = s->Depth();
+ size_t t_depth = t->Depth();
+ // Get s and t to the same depth in the hierarchy
+ if (s_depth > t_depth) {
+ while (s_depth > t_depth) {
+ s = s->GetSuperClass();
+ s_depth--;
+ }
+ } else {
+ while (t_depth > s_depth) {
+ t = t->GetSuperClass();
+ t_depth--;
+ }
+ }
+ // Go up the hierarchy until we get to the common parent
+ while (s != t) {
+ s = s->GetSuperClass();
+ t = t->GetSuperClass();
+ }
+ return s;
+ }
+}
+
std::ostream& operator<<(std::ostream& os, const RegType& rhs) {
os << rhs.Dump();
return os;
diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h
index 3b4e10b..9f89d07 100644
--- a/src/verifier/reg_type.h
+++ b/src/verifier/reg_type.h
@@ -33,8 +33,11 @@
class RegType {
public:
enum Type {
- kRegTypeUnknown = 0, // Initial state.
- kRegTypeConflict, // Merge clash makes this reg's type unknowable.
+ // A special state that identifies a register as undefined.
+ kRegTypeUndefined = 0,
+ // The bottom type, used to denote the type of operations such as returning a void, throwing
+ // an exception or merging incompatible types, such as an int and a long.
+ kRegTypeConflict,
kRegTypeBoolean, // Z.
kRegType1nrSTART = kRegTypeBoolean,
kRegTypeIntegralSTART = kRegTypeBoolean,
@@ -57,6 +60,8 @@
kRegTypeUninitializedReference, // Freshly allocated reference type.
kRegTypeUninitializedThisReference, // Freshly allocated reference passed as "this".
kRegTypeUnresolvedAndUninitializedReference, // Freshly allocated unresolved reference type.
+ // Freshly allocated unresolved reference passed as "this".
+ kRegTypeUnresolvedAndUninitializedThisReference,
kRegTypeReference, // Reference type.
};
@@ -64,7 +69,7 @@
return type_;
}
- bool IsUnknown() const { return type_ == kRegTypeUnknown; }
+ bool IsUndefined() const { return type_ == kRegTypeUndefined; }
bool IsConflict() const { return type_ == kRegTypeConflict; }
bool IsBoolean() const { return type_ == kRegTypeBoolean; }
bool IsByte() const { return type_ == kRegTypeByte; }
@@ -80,13 +85,17 @@
bool IsUnresolvedAndUninitializedReference() const {
return type_ == kRegTypeUnresolvedAndUninitializedReference;
}
+ bool IsUnresolvedAndUninitializedThisReference() const {
+ return type_ == kRegTypeUnresolvedAndUninitializedThisReference;
+ }
bool IsReference() const { return type_ == kRegTypeReference; }
bool IsUninitializedTypes() const {
return IsUninitializedReference() || IsUninitializedThisReference() ||
- IsUnresolvedAndUninitializedReference();
+ IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference();
}
bool IsUnresolvedTypes() const {
- return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference();
+ return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference() ||
+ IsUnresolvedAndUninitializedThisReference();
}
bool IsLowHalf() const { return type_ == kRegTypeLongLo ||
type_ == kRegTypeDoubleLo ||
@@ -135,12 +144,14 @@
}
bool IsReferenceTypes() const {
- return IsReference() || IsUnresolvedReference() || IsUninitializedReference() ||
- IsUninitializedThisReference() || IsUnresolvedAndUninitializedReference() || IsZero();
+ return IsReference() || IsUnresolvedReference() || IsZero() ||
+ IsUninitializedReference() || IsUninitializedThisReference() ||
+ IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference();
}
bool IsNonZeroReferenceTypes() const {
- return IsReference() || IsUnresolvedReference() || IsUninitializedReference() ||
- IsUninitializedThisReference();
+ return IsReference() || IsUnresolvedReference() || IsZero() ||
+ IsUninitializedReference() || IsUninitializedThisReference() ||
+ IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference();
}
bool IsCategory1Types() const {
return (type_ >= kRegType1nrSTART && type_ <= kRegType1nrEND) || IsConstant();
@@ -259,14 +270,23 @@
return cache_id_;
}
+ const RegType& GetSuperClass(RegTypeCache* cache) const;
+
std::string Dump() const;
+ // Can this type access other?
+ bool CanAccess(const RegType& other) const;
+ // Can this type access a member with the given properties?
+ bool CanAccessMember(Class* klass, uint32_t access_flags) const;
+
+ // Can this type be assigned by src?
bool IsAssignableFrom(const RegType& src) const;
- const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const;
-
bool Equals(const RegType& other) const { return GetId() == other.GetId(); }
+ // Compute the merge of this register from one edge (path) with incoming_type from another.
+ const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const;
+
/*
* A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
* S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
@@ -292,7 +312,7 @@
type_(type), klass_or_descriptor_(klass_or_descriptor), allocation_pc_or_constant_(allocation_pc_or_constant),
cache_id_(cache_id) {
DCHECK(IsConstant() || IsUninitializedTypes() || allocation_pc_or_constant == 0);
- if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUnknown() &&
+ if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUndefined() &&
!IsConflict()) {
DCHECK(klass_or_descriptor != NULL);
DCHECK(IsUnresolvedTypes() || klass_or_descriptor_->IsClass());
diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc
index d06377a..53b7a76 100644
--- a/src/verifier/reg_type_cache.cc
+++ b/src/verifier/reg_type_cache.cc
@@ -32,7 +32,7 @@
case Primitive::kPrimFloat: return RegType::kRegTypeFloat;
case Primitive::kPrimDouble: return RegType::kRegTypeDoubleLo;
case Primitive::kPrimVoid:
- default: return RegType::kRegTypeUnknown;
+ default: return RegType::kRegTypeConflict;
}
}
@@ -48,12 +48,12 @@
case 'F': return RegType::kRegTypeFloat;
case 'D': return RegType::kRegTypeDoubleLo;
case 'V':
- default: return RegType::kRegTypeUnknown;
+ default: return RegType::kRegTypeConflict;
}
} else if (descriptor[0] == 'L' || descriptor[0] == '[') {
return RegType::kRegTypeReference;
} else {
- return RegType::kRegTypeUnknown;
+ return RegType::kRegTypeConflict;
}
}
@@ -115,7 +115,7 @@
} else {
// The descriptor is broken return the unknown type as there's nothing sensible that
// could be done at runtime
- return Unknown();
+ return Conflict();
}
}
}
@@ -201,15 +201,30 @@
return *entry;
}
-const RegType& RegTypeCache::UninitializedThisArgument(Class* klass) {
- for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
- if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
- return *cur_entry;
+const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
+ // TODO: implement descriptor version.
+ RegType* entry;
+ if (type.IsUnresolvedTypes()) {
+ String* descriptor = type.GetDescriptor();
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->IsUnresolvedAndUninitializedThisReference() &&
+ cur_entry->GetDescriptor() == descriptor) {
+ return *cur_entry;
+ }
}
+ entry = new RegType(RegType::kRegTypeUnresolvedAndUninitializedThisReference, descriptor, 0,
+ entries_.size());
+ } else {
+ Class* klass = type.GetClass();
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
+ return *cur_entry;
+ }
+ }
+ entry = new RegType(RegType::kRegTypeUninitializedThisReference, klass, 0, entries_.size());
}
- RegType* entry = new RegType(RegType::kRegTypeUninitializedThisReference, klass, 0,
- entries_.size());
entries_.push_back(entry);
return *entry;
}
diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h
index 9c5e227..51eccd5 100644
--- a/src/verifier/reg_type_cache.h
+++ b/src/verifier/reg_type_cache.h
@@ -27,7 +27,7 @@
class RegTypeCache {
public:
explicit RegTypeCache() : entries_(RegType::kRegTypeLastFixedLocation + 1) {
- Unknown(); // ensure Unknown is initialized
+ Undefined(); // ensure Undefined is initialized
}
~RegTypeCache() {
STLDeleteElements(&entries_);
@@ -60,13 +60,14 @@
const RegType& JavaLangString() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/String;"); }
const RegType& JavaLangThrowable() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Throwable;"); }
- const RegType& Unknown() { return FromType(RegType::kRegTypeUnknown); }
+ const RegType& Undefined(){ return FromType(RegType::kRegTypeUndefined); }
const RegType& Conflict() { return FromType(RegType::kRegTypeConflict); }
const RegType& ConstLo() { return FromType(RegType::kRegTypeConstLo); }
const RegType& Zero() { return FromCat1Const(0); }
const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc);
- const RegType& UninitializedThisArgument(Class* klass);
+ // Create an uninitialized 'this' argument for the given type.
+ const RegType& UninitializedThisArgument(const RegType& type);
const RegType& FromUninitialized(const RegType& uninit_type);
// Representatives of various constant types. When merging constants we can't infer a type,
diff --git a/src/verifier/reg_type_test.cc b/src/verifier/reg_type_test.cc
index 52e4228..18c1655 100644
--- a/src/verifier/reg_type_test.cc
+++ b/src/verifier/reg_type_test.cc
@@ -29,7 +29,7 @@
RegTypeCache cache;
const RegType& bool_reg_type = cache.Boolean();
- EXPECT_FALSE(bool_reg_type.IsUnknown());
+ EXPECT_FALSE(bool_reg_type.IsUndefined());
EXPECT_FALSE(bool_reg_type.IsConflict());
EXPECT_FALSE(bool_reg_type.IsZero());
EXPECT_FALSE(bool_reg_type.IsOne());
@@ -60,7 +60,7 @@
EXPECT_TRUE(bool_reg_type.IsArrayIndexTypes());
const RegType& byte_reg_type = cache.Byte();
- EXPECT_FALSE(byte_reg_type.IsUnknown());
+ EXPECT_FALSE(byte_reg_type.IsUndefined());
EXPECT_FALSE(byte_reg_type.IsConflict());
EXPECT_FALSE(byte_reg_type.IsZero());
EXPECT_FALSE(byte_reg_type.IsOne());
@@ -91,7 +91,7 @@
EXPECT_TRUE(byte_reg_type.IsArrayIndexTypes());
const RegType& char_reg_type = cache.Char();
- EXPECT_FALSE(char_reg_type.IsUnknown());
+ EXPECT_FALSE(char_reg_type.IsUndefined());
EXPECT_FALSE(char_reg_type.IsConflict());
EXPECT_FALSE(char_reg_type.IsZero());
EXPECT_FALSE(char_reg_type.IsOne());
@@ -122,7 +122,7 @@
EXPECT_TRUE(char_reg_type.IsArrayIndexTypes());
const RegType& short_reg_type = cache.Short();
- EXPECT_FALSE(short_reg_type.IsUnknown());
+ EXPECT_FALSE(short_reg_type.IsUndefined());
EXPECT_FALSE(short_reg_type.IsConflict());
EXPECT_FALSE(short_reg_type.IsZero());
EXPECT_FALSE(short_reg_type.IsOne());
@@ -153,7 +153,7 @@
EXPECT_TRUE(short_reg_type.IsArrayIndexTypes());
const RegType& int_reg_type = cache.Integer();
- EXPECT_FALSE(int_reg_type.IsUnknown());
+ EXPECT_FALSE(int_reg_type.IsUndefined());
EXPECT_FALSE(int_reg_type.IsConflict());
EXPECT_FALSE(int_reg_type.IsZero());
EXPECT_FALSE(int_reg_type.IsOne());
@@ -184,7 +184,7 @@
EXPECT_TRUE(int_reg_type.IsArrayIndexTypes());
const RegType& long_reg_type = cache.Long();
- EXPECT_FALSE(long_reg_type.IsUnknown());
+ EXPECT_FALSE(long_reg_type.IsUndefined());
EXPECT_FALSE(long_reg_type.IsConflict());
EXPECT_FALSE(long_reg_type.IsZero());
EXPECT_FALSE(long_reg_type.IsOne());
@@ -215,7 +215,7 @@
EXPECT_FALSE(long_reg_type.IsArrayIndexTypes());
const RegType& float_reg_type = cache.Float();
- EXPECT_FALSE(float_reg_type.IsUnknown());
+ EXPECT_FALSE(float_reg_type.IsUndefined());
EXPECT_FALSE(float_reg_type.IsConflict());
EXPECT_FALSE(float_reg_type.IsZero());
EXPECT_FALSE(float_reg_type.IsOne());
@@ -246,7 +246,7 @@
EXPECT_FALSE(float_reg_type.IsArrayIndexTypes());
const RegType& double_reg_type = cache.Double();
- EXPECT_FALSE(double_reg_type.IsUnknown());
+ EXPECT_FALSE(double_reg_type.IsUndefined());
EXPECT_FALSE(double_reg_type.IsConflict());
EXPECT_FALSE(double_reg_type.IsZero());
EXPECT_FALSE(double_reg_type.IsOne());
diff --git a/src/verifier/register_line.cc b/src/verifier/register_line.cc
index 6a86411..085a101 100644
--- a/src/verifier/register_line.cc
+++ b/src/verifier/register_line.cc
@@ -23,7 +23,8 @@
bool RegisterLine::CheckConstructorReturn() const {
for (size_t i = 0; i < num_regs_; i++) {
- if (GetRegisterType(i).IsUninitializedThisReference()) {
+ if (GetRegisterType(i).IsUninitializedThisReference() ||
+ GetRegisterType(i).IsUnresolvedAndUninitializedThisReference()) {
verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
<< "Constructor returning without calling superclass constructor";
return false;
@@ -33,7 +34,7 @@
}
bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) {
- DCHECK(vdst < num_regs_);
+ DCHECK_LT(vdst, num_regs_);
if (new_type.IsLowHalf()) {
line_[vdst] = new_type.GetId();
line_[vdst + 1] = new_type.HighHalf(verifier_->GetRegTypeCache()).GetId();
@@ -53,9 +54,8 @@
}
void RegisterLine::SetResultTypeToUnknown() {
- uint16_t unknown_id = verifier_->GetRegTypeCache()->Unknown().GetId();
- result_[0] = unknown_id;
- result_[1] = unknown_id;
+ result_[0] = RegType::kRegTypeUndefined;
+ result_[1] = RegType::kRegTypeUndefined;
}
void RegisterLine::SetResultRegisterType(const RegType& new_type) {
@@ -64,7 +64,7 @@
DCHECK_EQ(new_type.HighHalf(verifier_->GetRegTypeCache()).GetId(), new_type.GetId() + 1);
result_[1] = new_type.GetId() + 1;
} else {
- result_[1] = verifier_->GetRegTypeCache()->Unknown().GetId();
+ result_[1] = RegType::kRegTypeUndefined;
}
}
@@ -77,14 +77,14 @@
const RegType& RegisterLine::GetInvocationThis(const DecodedInstruction& dec_insn) {
if (dec_insn.vA < 1) {
verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke lacks 'this'";
- return verifier_->GetRegTypeCache()->Unknown();
+ return verifier_->GetRegTypeCache()->Conflict();
}
/* get the element type of the array held in vsrc */
const RegType& this_type = GetRegisterType(dec_insn.vC);
if (!this_type.IsReferenceTypes()) {
verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "tried to get class from non-reference register v"
<< dec_insn.vC << " (type=" << this_type << ")";
- return verifier_->GetRegTypeCache()->Unknown();
+ return verifier_->GetRegTypeCache()->Conflict();
}
return this_type;
}
@@ -168,9 +168,9 @@
verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
<< "copyRes1 v" << vdst << "<- result0" << " type=" << type;
} else {
- DCHECK(verifier_->GetRegTypeCache()->GetFromId(result_[1]).IsUnknown());
+ DCHECK(verifier_->GetRegTypeCache()->GetFromId(result_[1]).IsUndefined());
SetRegisterType(vdst, type);
- result_[0] = verifier_->GetRegTypeCache()->Unknown().GetId();
+ result_[0] = RegType::kRegTypeUndefined;
}
}
@@ -187,8 +187,8 @@
} else {
DCHECK(type_l.CheckWidePair(type_h)); // Set should never allow this case
SetRegisterType(vdst, type_l); // also sets the high
- result_[0] = verifier_->GetRegTypeCache()->Unknown().GetId();
- result_[1] = verifier_->GetRegTypeCache()->Unknown().GetId();
+ result_[0] = RegType::kRegTypeUndefined;
+ result_[1] = RegType::kRegTypeUndefined;
}
}
diff --git a/src/verifier/register_line.h b/src/verifier/register_line.h
index 6b921cc..f214b81 100644
--- a/src/verifier/register_line.h
+++ b/src/verifier/register_line.h
@@ -56,8 +56,8 @@
RegisterLine(size_t num_regs, MethodVerifier* verifier) :
line_(new uint16_t[num_regs]), verifier_(verifier), num_regs_(num_regs) {
memset(line_.get(), 0, num_regs_ * sizeof(uint16_t));
- result_[0] = RegType::kRegTypeUnknown;
- result_[1] = RegType::kRegTypeUnknown;
+ result_[0] = RegType::kRegTypeUndefined;
+ result_[1] = RegType::kRegTypeUndefined;
}
// Implement category-1 "move" instructions. Copy a 32-bit value from "vsrc" to "vdst".
@@ -285,7 +285,7 @@
MethodVerifier* verifier_;
// Length of reg_types_
- const size_t num_regs_;
+ const uint32_t num_regs_;
// A stack of monitor enter locations
std::deque<uint32_t> monitors_;
// A map from register to a bit vector of indices into the monitors_ stack. As we pop the monitor