Make ICCE logic common, refactor throws.
There were 2 sets of ICCE logic in ClassLinker::ResolveMethod and
Method::CheckIncompatibleClassChange, merged into just 1 piece of logic
in Method::CheckIncompatibleClassChange.
Move Throw... routines into own file and make adding the dex location to
the detail message more thorough.
Change-Id: I953dbfa3fed3767f35799b2f82b16637c437dbbe
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 16406c4..01bdf9a 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -89,259 +89,6 @@
namespace art {
-void ThrowNewIllegalAccessErrorClass(Thread* self,
- Class* referrer,
- Class* accessed) {
- self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
- "Illegal class access: '%s' -> '%s'",
- PrettyDescriptor(referrer).c_str(),
- PrettyDescriptor(accessed).c_str());
-}
-
-void ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread* self,
- Class* referrer,
- Class* accessed,
- const Method* caller,
- const Method* called,
- InvokeType type) {
- self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
- "Illegal class access ('%s' -> '%s')"
- "in attempt to invoke %s method '%s' from '%s'",
- PrettyDescriptor(referrer).c_str(),
- PrettyDescriptor(accessed).c_str(),
- ToStr<InvokeType>(type).c_str(),
- PrettyMethod(called).c_str(),
- PrettyMethod(caller).c_str());
-}
-
-static void ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(Thread* self,
- const Method* interface_method,
- Object* this_object)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- std::string interface_method_name(PrettyMethod(interface_method));
- if (this_object != NULL) {
- std::string this_class_descriptor(PrettyDescriptor(this_object->GetClass()));
- std::string interface_class_descriptor(PrettyDescriptor(interface_method->GetDeclaringClass()));
- self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
- "Class '%s' does not implement interface '%s' in call to '%s'",
- this_class_descriptor.c_str(),
- interface_class_descriptor.c_str(),
- interface_method_name.c_str());
- } else {
- self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
- "Expected '%s' to be an interface method",
- interface_method_name.c_str());
- }
-}
-
-static void ThrowNewIncompatibleClassChangeErrorField(Thread* self, const Field* resolved_field,
- bool is_static)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
- "Expected '%s' to be a %s field",
- PrettyField(resolved_field).c_str(),
- is_static ? "static" : "instance");
-}
-
-void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type,
- Method* method, const Method* referrer) {
- std::ostringstream msg;
- msg << "The method '" << PrettyMethod(method) << "' was expected to be of type "
- << expected_type << " but instead was found to be of type " << found_type;
- if (referrer != NULL) {
- ClassHelper kh(referrer->GetDeclaringClass());
- std::string location(kh.GetLocation());
- if (!location.empty()) {
- msg << " (accessed from " << location << ")";
- }
- }
- Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;",
- msg.str().c_str());
-}
-
-void ThrowNoSuchMethodError(InvokeType type, Class* c, const StringPiece& name,
- const StringPiece& signature, const Method* referrer) {
- ClassHelper kh(c);
- std::ostringstream msg;
- msg << "No " << type << " method " << name << signature
- << " in class " << kh.GetDescriptor() << " or its superclasses";
- if (referrer != NULL) {
- kh.ChangeClass(referrer->GetDeclaringClass());
- std::string location(kh.GetLocation());
- if (!location.empty()) {
- msg << " (accessed from " << location << ")";
- }
- }
- Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str());
-}
-
-void ThrowNewIllegalAccessErrorField(Thread* self,
- Class* referrer,
- Field* accessed) {
- self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
- "Field '%s' is inaccessible to class '%s'",
- PrettyField(accessed, false).c_str(),
- PrettyDescriptor(referrer).c_str());
-}
-
-void ThrowNewIllegalAccessErrorFinalField(Thread* self,
- const Method* referrer,
- Field* accessed) {
- self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
- "Final field '%s' cannot be written to by method '%s'",
- PrettyField(accessed, false).c_str(),
- PrettyMethod(referrer).c_str());
-}
-
-void ThrowNewIllegalAccessErrorMethod(Thread* self,
- Class* referrer,
- Method* accessed) {
- self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
- "Method '%s' is inaccessible to class '%s'",
- PrettyMethod(accessed).c_str(),
- PrettyDescriptor(referrer).c_str());
-}
-
-void ThrowNullPointerExceptionForFieldAccess(Thread* self,
- Field* field,
- bool is_read) {
- self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
- "Attempt to %s field '%s' on a null object reference",
- is_read ? "read from" : "write to",
- PrettyField(field, true).c_str());
-}
-
-void ThrowNullPointerExceptionForMethodAccess(Thread* self,
- Method* caller,
- uint32_t method_idx,
- InvokeType type) {
- const DexFile& dex_file =
- Runtime::Current()->GetClassLinker()->FindDexFile(caller->GetDeclaringClass()->GetDexCache());
- self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
- "Attempt to invoke %s method '%s' on a null object reference",
- ToStr<InvokeType>(type).c_str(),
- PrettyMethod(method_idx, dex_file, true).c_str());
-}
-
-void ThrowNullPointerExceptionFromDexPC(Thread* self, Method* throw_method, uint32_t dex_pc) {
- const DexFile::CodeItem* code = MethodHelper(throw_method).GetCodeItem();
- CHECK_LT(dex_pc, code->insns_size_in_code_units_);
- const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
- DecodedInstruction dec_insn(instr);
- switch (instr->Opcode()) {
- case Instruction::INVOKE_DIRECT:
- case Instruction::INVOKE_DIRECT_RANGE:
- ThrowNullPointerExceptionForMethodAccess(self, throw_method, dec_insn.vB, kDirect);
- break;
- case Instruction::INVOKE_VIRTUAL:
- case Instruction::INVOKE_VIRTUAL_RANGE:
- ThrowNullPointerExceptionForMethodAccess(self, throw_method, dec_insn.vB, kVirtual);
- break;
- case Instruction::IGET:
- case Instruction::IGET_WIDE:
- case Instruction::IGET_OBJECT:
- case Instruction::IGET_BOOLEAN:
- case Instruction::IGET_BYTE:
- case Instruction::IGET_CHAR:
- case Instruction::IGET_SHORT: {
- Field* field =
- Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false);
- ThrowNullPointerExceptionForFieldAccess(self, field, true /* read */);
- break;
- }
- case Instruction::IPUT:
- case Instruction::IPUT_WIDE:
- case Instruction::IPUT_OBJECT:
- case Instruction::IPUT_BOOLEAN:
- case Instruction::IPUT_BYTE:
- case Instruction::IPUT_CHAR:
- case Instruction::IPUT_SHORT: {
- Field* field =
- Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false);
- ThrowNullPointerExceptionForFieldAccess(self, field, false /* write */);
- break;
- }
- case Instruction::AGET:
- case Instruction::AGET_WIDE:
- case Instruction::AGET_OBJECT:
- case Instruction::AGET_BOOLEAN:
- case Instruction::AGET_BYTE:
- case Instruction::AGET_CHAR:
- case Instruction::AGET_SHORT:
- self->ThrowNewException("Ljava/lang/NullPointerException;",
- "Attempt to read from null array");
- break;
- case Instruction::APUT:
- case Instruction::APUT_WIDE:
- case Instruction::APUT_OBJECT:
- case Instruction::APUT_BOOLEAN:
- case Instruction::APUT_BYTE:
- case Instruction::APUT_CHAR:
- case Instruction::APUT_SHORT:
- self->ThrowNewException("Ljava/lang/NullPointerException;",
- "Attempt to write to null array");
- break;
- case Instruction::ARRAY_LENGTH:
- self->ThrowNewException("Ljava/lang/NullPointerException;",
- "Attempt to get length of null array");
- break;
- default: {
- const DexFile& dex_file = Runtime::Current()->GetClassLinker()
- ->FindDexFile(throw_method->GetDeclaringClass()->GetDexCache());
- std::string message("Null pointer exception during instruction '");
- message += instr->DumpString(&dex_file);
- message += "'";
- self->ThrowNewException("Ljava/lang/NullPointerException;", message.c_str());
- break;
- }
- }
-}
-
-std::string FieldNameFromIndex(const Method* method, uint32_t ref,
- verifier::VerifyErrorRefType ref_type, bool access) {
- CHECK_EQ(static_cast<int>(ref_type), static_cast<int>(verifier::VERIFY_ERROR_REF_FIELD));
-
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const DexFile& dex_file = class_linker->FindDexFile(method->GetDeclaringClass()->GetDexCache());
-
- const DexFile::FieldId& id = dex_file.GetFieldId(ref);
- std::string class_name(PrettyDescriptor(dex_file.GetFieldDeclaringClassDescriptor(id)));
- const char* field_name = dex_file.StringDataByIdx(id.name_idx_);
- if (!access) {
- return class_name + "." + field_name;
- }
-
- std::string result;
- result += "tried to access field ";
- result += class_name + "." + field_name;
- result += " from class ";
- result += PrettyDescriptor(method->GetDeclaringClass());
- return result;
-}
-
-std::string MethodNameFromIndex(const Method* method, uint32_t ref,
- verifier::VerifyErrorRefType ref_type, bool access) {
- CHECK_EQ(static_cast<int>(ref_type), static_cast<int>(verifier::VERIFY_ERROR_REF_METHOD));
-
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const DexFile& dex_file = class_linker->FindDexFile(method->GetDeclaringClass()->GetDexCache());
-
- const DexFile::MethodId& id = dex_file.GetMethodId(ref);
- std::string class_name(PrettyDescriptor(dex_file.GetMethodDeclaringClassDescriptor(id)));
- const char* method_name = dex_file.StringDataByIdx(id.name_idx_);
- if (!access) {
- return class_name + "." + method_name;
- }
-
- std::string result;
- result += "tried to access method ";
- result += class_name + "." + method_name + ":" +
- dex_file.CreateMethodSignature(id.proto_idx_, NULL);
- result += " from class ";
- result += PrettyDescriptor(method->GetDeclaringClass());
- return result;
-}
-
// Helper function to allocate array for FILLED_NEW_ARRAY.
Array* CheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
Thread* self, bool access_check) {
@@ -372,7 +119,7 @@
if (access_check) {
Class* referrer = method->GetDeclaringClass();
if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowNewIllegalAccessErrorClass(self, referrer, klass);
+ ThrowIllegalAccessErrorClass(referrer, klass);
return NULL; // Failure
}
}
@@ -404,7 +151,7 @@
return NULL; // Failure.
} else {
if (resolved_field->IsStatic() != is_static) {
- ThrowNewIncompatibleClassChangeErrorField(self, resolved_field, is_static);
+ ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
return NULL;
}
Class* fields_class = resolved_field->GetDeclaringClass();
@@ -420,16 +167,16 @@
dex_file.GetFieldId(field_idx).class_idx_,
referring_class);
if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
- ThrowNewIllegalAccessErrorClass(self, referring_class, fields_class);
+ ThrowIllegalAccessErrorClass(referring_class, fields_class);
return NULL; // failure
} else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
resolved_field->GetAccessFlags()))) {
- ThrowNewIllegalAccessErrorField(self, referring_class, resolved_field);
+ ThrowIllegalAccessErrorField(referring_class, resolved_field);
return NULL; // failure
}
}
if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
- ThrowNewIllegalAccessErrorFinalField(self, referrer, resolved_field);
+ ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
return NULL; // failure
} else {
FieldHelper fh(resolved_field);
@@ -477,9 +224,8 @@
Method* interface_method =
this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
if (UNLIKELY(interface_method == NULL)) {
- ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self,
- resolved_method,
- this_object);
+ ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object,
+ referrer);
return NULL; // Failure.
} else {
return interface_method;
@@ -515,12 +261,12 @@
dex_file.GetMethodId(method_idx).class_idx_,
referring_class);
if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
- ThrowNewIllegalAccessErrorClassForMethodDispatch(self, referring_class, methods_class,
- referrer, resolved_method, type);
+ ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
+ referrer, resolved_method, type);
return NULL; // Failure.
} else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
resolved_method->GetAccessFlags()))) {
- ThrowNewIllegalAccessErrorMethod(self, referring_class, resolved_method);
+ ThrowIllegalAccessErrorMethod(referring_class, resolved_method);
return NULL; // Failure.
}
}
@@ -530,9 +276,8 @@
Method* interface_method =
this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
if (UNLIKELY(interface_method == NULL)) {
- ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self,
- resolved_method,
- this_object);
+ ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object,
+ referrer);
return NULL; // Failure.
} else {
return interface_method;
@@ -576,7 +321,7 @@
// Perform access check if necessary.
Class* referring_class = referrer->GetDeclaringClass();
if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) {
- ThrowNewIllegalAccessErrorClass(self, referring_class, klass);
+ ThrowIllegalAccessErrorClass(referring_class, klass);
return NULL; // Failure - Indicate to caller to deliver exception
}
// If we're just implementing const-class, we shouldn't call <clinit>.