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/class_linker.cc b/src/class_linker.cc
index 8ee3b1e..f6fb470 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -118,7 +118,9 @@
// a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
// failed in verification, in which case v2 5.4.1 says we need to re-throw
// the previous error.
- LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c);
+ if (!Runtime::Current()->IsCompiler()) { // Give info if this occurs at runtime.
+ LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c);
+ }
CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus();
if (c->GetVerifyErrorClass() != NULL) {
@@ -3465,31 +3467,8 @@
}
if (resolved != NULL) {
// We found a method, check for incompatible class changes.
- switch (type) {
- case kDirect:
- if (resolved->IsStatic()) {
- resolved = NULL; // Incompatible class change.
- }
- break;
- case kStatic:
- if (!resolved->IsStatic()) {
- resolved = NULL; // Incompatible class change.
- }
- break;
- case kInterface:
- if (resolved->IsConstructor() || !resolved->GetDeclaringClass()->IsInterface()) {
- resolved = NULL; // Incompatible class change.
- }
- break;
- case kSuper:
- // TODO: appropriate checks for call to super class.
- break;
- case kVirtual:
- if (resolved->IsConstructor() || (resolved->GetDeclaringClass()->IsInterface() &&
- !resolved->IsMiranda())) {
- resolved = NULL; // Incompatible class change.
- }
- break;
+ if (resolved->CheckIncompatibleClassChange(type)) {
+ resolved = NULL;
}
}
if (resolved != NULL) {
@@ -3518,12 +3497,12 @@
Class* methods_class = resolved->GetDeclaringClass();
Class* referring_class = referrer->GetDeclaringClass();
if (!referring_class->CanAccess(methods_class)) {
- ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread::Current(), referring_class, methods_class,
- referrer, resolved, type);
+ ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
+ referrer, resolved, type);
return NULL;
} else if (!referring_class->CanAccessMember(methods_class,
resolved->GetAccessFlags())) {
- ThrowNewIllegalAccessErrorMethod(Thread::Current(), referring_class, resolved);
+ ThrowIllegalAccessErrorMethod(referring_class, resolved);
return NULL;
}
}
diff --git a/src/common_throws.cc b/src/common_throws.cc
new file mode 100644
index 0000000..5bd30b4
--- /dev/null
+++ b/src/common_throws.cc
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common_throws.h"
+
+#include "dex_instruction.h"
+#include "invoke_type.h"
+#include "logging.h"
+#include "object_utils.h"
+#include "thread.h"
+
+#include <sstream>
+
+namespace art {
+
+static void AddReferrerLocation(std::ostream& os, const Method* referrer)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
+ if (referrer != NULL) {
+ ClassHelper kh(referrer->GetDeclaringClass());
+ std::string location(kh.GetLocation());
+ if (!location.empty()) {
+ os << " (accessed from " << location << ")";
+ }
+ }
+}
+
+static void AddReferrerLocationFromClass(std::ostream& os, Class* referrer)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
+ if (referrer != NULL) {
+ ClassHelper kh(referrer);
+ std::string location(kh.GetLocation());
+ if (!location.empty()) {
+ os << " (declaration of '" << PrettyDescriptor(referrer)
+ << "' appears in " << location << ")";
+ }
+ }
+}
+
+// NullPointerException
+
+void ThrowNullPointerExceptionForFieldAccess(Field* field, bool is_read) {
+ std::ostringstream msg;
+ msg << "Attempt to " << (is_read ? "read from" : "write to")
+ << " field '" << PrettyField(field, true) << "' on a null object reference";
+ Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", msg.str().c_str());
+}
+
+void ThrowNullPointerExceptionForMethodAccess(Method* caller, uint32_t method_idx,
+ InvokeType type) {
+ DexCache* dex_cache = caller->GetDeclaringClass()->GetDexCache();
+ const DexFile& dex_file = Runtime::Current()->GetClassLinker()->FindDexFile(dex_cache);
+ std::ostringstream msg;
+ msg << "Attempt to invoke " << ToStr<InvokeType>(type).str() << " method '"
+ << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference";
+ Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", msg.str().c_str());
+}
+
+void ThrowNullPointerExceptionFromDexPC(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(throw_method, dec_insn.vB, kDirect);
+ break;
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ ThrowNullPointerExceptionForMethodAccess(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(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(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:
+ Thread::Current()->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:
+ Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;",
+ "Attempt to write to null array");
+ break;
+ case Instruction::ARRAY_LENGTH:
+ Thread::Current()->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 += "'";
+ Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", message.c_str());
+ break;
+ }
+ }
+}
+
+// IllegalAccessError
+
+void ThrowIllegalAccessErrorClass(Class* referrer, Class* accessed) {
+ std::ostringstream msg;
+ msg << "Illegal class access: '" << PrettyDescriptor(referrer) << "' -> '"
+ << PrettyDescriptor(accessed) << "'";
+ AddReferrerLocationFromClass(msg, referrer);
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+}
+
+void ThrowIllegalAccessErrorClassForMethodDispatch(Class* referrer, Class* accessed,
+ const Method* caller,
+ const Method* called,
+ InvokeType type) {
+ std::ostringstream msg;
+ msg << "Illegal class access ('" << PrettyDescriptor(referrer) << "' -> '"
+ << PrettyDescriptor(accessed) << "') in attempt to invoke " << ToStr<InvokeType>(type).str()
+ << " method " << PrettyMethod(called).c_str();
+ AddReferrerLocation(msg, caller);
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+}
+
+void ThrowIllegalAccessErrorMethod(Class* referrer, Method* accessed) {
+ std::ostringstream msg;
+ msg << "Method '" << PrettyMethod(accessed) << "' is inaccessible to class '"
+ << PrettyDescriptor(referrer) << "'";
+ AddReferrerLocationFromClass(msg, referrer);
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+}
+
+void ThrowIllegalAccessErrorField(Class* referrer, Field* accessed) {
+ std::ostringstream msg;
+ msg << "Field '" << PrettyField(accessed, false) << "' is inaccessible to class '"
+ << PrettyDescriptor(referrer) << "'";
+ AddReferrerLocationFromClass(msg, referrer);
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+}
+
+void ThrowIllegalAccessErrorFinalField(const Method* referrer, Field* accessed) {
+ std::ostringstream msg;
+ msg << "Final field '" << PrettyField(accessed, false) << "' cannot be written to by method '"
+ << PrettyMethod(referrer) << "'";
+ AddReferrerLocation(msg, referrer);
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str());
+}
+
+// IncompatibleClassChangeError
+
+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;
+ AddReferrerLocation(msg, referrer);
+ Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;",
+ msg.str().c_str());
+}
+
+void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(const Method* interface_method,
+ Object* this_object,
+ const Method* referrer) {
+ // Referrer is calling interface_method on this_object, however, the interface_method isn't
+ // implemented by this_object.
+ CHECK(this_object != NULL);
+ std::ostringstream msg;
+ msg << "Class '" << PrettyDescriptor(this_object->GetClass())
+ << "' does not implement interface '"
+ << PrettyDescriptor(interface_method->GetDeclaringClass())
+ << "' in call to '" << PrettyMethod(interface_method) << "'";
+ AddReferrerLocation(msg, referrer);
+ Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;",
+ msg.str().c_str());
+}
+
+void ThrowIncompatibleClassChangeErrorField(const Field* resolved_field, bool is_static,
+ const Method* referrer) {
+ std::ostringstream msg;
+ msg << "Expected '" << PrettyField(resolved_field) << "' to be a "
+ << (is_static ? "static" : "instance") << " field";
+ AddReferrerLocation(msg, referrer);
+ Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;",
+ msg.str().c_str());
+}
+
+// NoSuchMethodError
+
+void ThrowNoSuchMethodError(InvokeType type, Class* c, const StringPiece& name,
+ const StringPiece& signature, const Method* referrer) {
+ std::ostringstream msg;
+ ClassHelper kh(c);
+ msg << "No " << type << " method " << name << signature
+ << " in class " << kh.GetDescriptor() << " or its super classes";
+ AddReferrerLocation(msg, referrer);
+ Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str());
+}
+
+void ThrowNoSuchMethodError(uint32_t method_idx, const Method* referrer) {
+ DexCache* dex_cache = referrer->GetDeclaringClass()->GetDexCache();
+ const DexFile& dex_file = Runtime::Current()->GetClassLinker()->FindDexFile(dex_cache);
+ std::ostringstream msg;
+ msg << "No method '" << PrettyMethod(method_idx, dex_file, true) << "'";
+ AddReferrerLocation(msg, referrer);
+ Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str());
+}
+
+} // namespace art
diff --git a/src/common_throws.h b/src/common_throws.h
new file mode 100644
index 0000000..47186b0
--- /dev/null
+++ b/src/common_throws.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_COMMON_THROWS__H_
+#define ART_SRC_COMMON_THROWS_H_
+
+#include "mutex.h"
+#include "object.h"
+
+namespace art {
+
+// NullPointerException
+
+void ThrowNullPointerExceptionForFieldAccess(Field* field, bool is_read)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+void ThrowNullPointerExceptionForMethodAccess(Method* caller, uint32_t method_idx, InvokeType type)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+void ThrowNullPointerExceptionFromDexPC(Method* throw_method, uint32_t dex_pc)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+// IllegalAccessError
+
+void ThrowIllegalAccessErrorClass(Class* referrer, Class* accessed)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+void ThrowIllegalAccessErrorClassForMethodDispatch(Class* referrer, Class* accessed,
+ const Method* caller, const Method* called,
+ InvokeType type)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+void ThrowIllegalAccessErrorMethod(Class* referrer, Method* accessed)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+void ThrowIllegalAccessErrorField(Class* referrer, Field* accessed)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+void ThrowIllegalAccessErrorFinalField(const Method* referrer, Field* accessed)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+// IncompatibleClassChangeError
+
+void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type,
+ Method* method, const Method* referrer)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(const Method* interface_method,
+ Object* this_object,
+ const Method* referrer)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+void ThrowIncompatibleClassChangeErrorField(const Field* resolved_field, bool is_static,
+ const Method* referrer)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+// NoSuchMethodError
+
+void ThrowNoSuchMethodError(InvokeType type, Class* c, const StringPiece& name,
+ const StringPiece& signature, const Method* referrer)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+void ThrowNoSuchMethodError(uint32_t method_idx, const Method* referrer)
+ SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
+
+} // namespace art
+
+#endif // ART_SRC_COMMON_THROWS_H_
diff --git a/src/compiler_llvm/runtime_support_llvm.cc b/src/compiler_llvm/runtime_support_llvm.cc
index a6fa80d..dd56ac8 100644
--- a/src/compiler_llvm/runtime_support_llvm.cc
+++ b/src/compiler_llvm/runtime_support_llvm.cc
@@ -133,11 +133,7 @@
Thread* thread = art_get_current_thread_from_code();
// We need the calling method as context for the method_idx
Method* method = thread->GetCurrentMethod();
- thread->ThrowNewException("Ljava/lang/NoSuchMethodError;",
- MethodNameFromIndex(method,
- method_idx,
- verifier::VERIFY_ERROR_REF_METHOD,
- false).c_str());
+ ThrowNoSuchMethodError(method_idx, method);
}
void art_throw_null_pointer_exception_from_code(uint32_t dex_pc)
@@ -146,7 +142,7 @@
NthCallerVisitor visitor(thread->GetManagedStack(), thread->GetTraceStack(), 0);
visitor.WalkStack();
Method* throw_method = visitor.caller;
- ThrowNullPointerExceptionFromDexPC(thread, throw_method, dex_pc);
+ ThrowNullPointerExceptionFromDexPC(throw_method, dex_pc);
}
void art_throw_stack_overflow_from_code()
@@ -226,17 +222,17 @@
Object* art_alloc_array_from_code(uint32_t type_idx,
Method* referrer,
uint32_t length,
- Thread* thread)
+ Thread* /*thread*/)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- return AllocArrayFromCode(type_idx, referrer, length, thread, false);
+ return AllocArrayFromCode(type_idx, referrer, length, false);
}
Object* art_alloc_array_from_code_with_access_check(uint32_t type_idx,
Method* referrer,
uint32_t length,
- Thread* thread)
+ Thread* /*thread*/)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- return AllocArrayFromCode(type_idx, referrer, length, thread, true);
+ return AllocArrayFromCode(type_idx, referrer, length, true);
}
Object* art_check_and_alloc_array_from_code(uint32_t type_idx,
diff --git a/src/oat/runtime/support_alloc.cc b/src/oat/runtime/support_alloc.cc
index 4a03f98..528198d 100644
--- a/src/oat/runtime/support_alloc.cc
+++ b/src/oat/runtime/support_alloc.cc
@@ -37,7 +37,7 @@
Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- return AllocArrayFromCode(type_idx, method, component_count, self, false);
+ return AllocArrayFromCode(type_idx, method, component_count, false);
}
extern "C" Array* artAllocArrayFromCodeWithAccessCheck(uint32_t type_idx, Method* method,
@@ -45,7 +45,7 @@
Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- return AllocArrayFromCode(type_idx, method, component_count, self, true);
+ return AllocArrayFromCode(type_idx, method, component_count, true);
}
extern "C" Array* artCheckAndAllocArrayFromCode(uint32_t type_idx, Method* method,
diff --git a/src/oat/runtime/support_field.cc b/src/oat/runtime/support_field.cc
index 289553e..fe8974b 100644
--- a/src/oat/runtime/support_field.cc
+++ b/src/oat/runtime/support_field.cc
@@ -77,7 +77,7 @@
field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int32_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(self, field, true);
+ ThrowNullPointerExceptionForFieldAccess(field, true);
} else {
return field->Get32(obj);
}
@@ -96,7 +96,7 @@
field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int64_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(self, field, true);
+ ThrowNullPointerExceptionForFieldAccess(field, true);
} else {
return field->Get64(obj);
}
@@ -115,7 +115,7 @@
field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectRead, sizeof(Object*));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(self, field, true);
+ ThrowNullPointerExceptionForFieldAccess(field, true);
} else {
return field->GetObj(obj);
}
@@ -188,7 +188,7 @@
field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int32_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(self, field, false);
+ ThrowNullPointerExceptionForFieldAccess(field, false);
} else {
field->Set32(obj, new_value);
return 0; // success
@@ -212,7 +212,7 @@
field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int64_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(self, field, false);
+ ThrowNullPointerExceptionForFieldAccess(field, false);
} else {
field->Set64(obj, new_value);
return 0; // success
@@ -233,7 +233,7 @@
field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectWrite, sizeof(Object*));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(self, field, false);
+ ThrowNullPointerExceptionForFieldAccess(field, false);
} else {
field->SetObj(obj, new_value);
return 0; // success
diff --git a/src/oat/runtime/support_invoke.cc b/src/oat/runtime/support_invoke.cc
index 9c7b3a2..b2867ef 100644
--- a/src/oat/runtime/support_invoke.cc
+++ b/src/oat/runtime/support_invoke.cc
@@ -26,7 +26,7 @@
if (UNLIKELY(method == NULL)) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
if (UNLIKELY(this_object == NULL && type != kDirect && type != kStatic)) {
- ThrowNullPointerExceptionForMethodAccess(self, caller_method, method_idx, type);
+ ThrowNullPointerExceptionForMethodAccess(caller_method, method_idx, type);
return 0; // failure
}
method = FindMethodFromCode(method_idx, this_object, caller_method, self, access_check, type);
diff --git a/src/oat/runtime/support_throw.cc b/src/oat/runtime/support_throw.cc
index 9776290..0abdb04 100644
--- a/src/oat/runtime/support_throw.cc
+++ b/src/oat/runtime/support_throw.cc
@@ -54,7 +54,7 @@
FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
uint32_t dex_pc;
Method* throw_method = self->GetCurrentMethod(&dex_pc);
- ThrowNullPointerExceptionFromDexPC(self, throw_method, dex_pc);
+ ThrowNullPointerExceptionFromDexPC(throw_method, dex_pc);
self->DeliverException();
}
@@ -93,8 +93,7 @@
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
Method* method = self->GetCurrentMethod();
- self->ThrowNewException("Ljava/lang/NoSuchMethodError;",
- MethodNameFromIndex(method, method_idx, verifier::VERIFY_ERROR_REF_METHOD, false).c_str());
+ ThrowNoSuchMethodError(method_idx, method);
self->DeliverException();
}
diff --git a/src/object.h b/src/object.h
index 85b1721..6d686f4 100644
--- a/src/object.h
+++ b/src/object.h
@@ -2452,27 +2452,25 @@
}
inline bool Method::CheckIncompatibleClassChange(InvokeType type) {
- bool icce = true;
switch (type) {
case kStatic:
- icce = !IsStatic();
- break;
+ return !IsStatic();
case kDirect:
- icce = !IsDirect();
- break;
- case kVirtual:
- icce = IsDirect();
- break;
+ return !IsDirect() || IsStatic();
+ case kVirtual: {
+ Class* methods_class = GetDeclaringClass();
+ return IsDirect() || (methods_class->IsInterface() && !IsMiranda());
+ }
case kSuper:
- icce = false;
- break;
+ return false; // TODO: appropriate checks for call to super class.
case kInterface: {
Class* methods_class = GetDeclaringClass();
- icce = IsDirect() || !(methods_class->IsInterface() || methods_class->IsObjectClass());
- break;
+ return IsDirect() || !(methods_class->IsInterface() || methods_class->IsObjectClass());
}
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ return true;
}
- return icce;
}
inline void Method::AssertPcIsWithinCode(uintptr_t pc) const {
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>.
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 2956ea7..8f6b655 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -18,6 +18,7 @@
#define ART_SRC_RUNTIME_SUPPORT_H_
#include "class_linker.h"
+#include "common_throws.h"
#include "dex_file.h"
#include "invoke_type.h"
#include "object.h"
@@ -43,47 +44,6 @@
class Method;
class Object;
-// Helpers to give consistent descriptive exception messages
-void ThrowNewIllegalAccessErrorClass(Thread* self, Class* referrer, Class* accessed)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread* self, Class* referrer,
- Class* accessed,
- const Method* caller,
- const Method* called,
- InvokeType type)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(Thread* self,
- const Method* referrer,
- const Method* interface_method,
- Object* this_object)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type,
- Method* method, const Method* referrer)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowNoSuchMethodError(InvokeType type, Class* c, const StringPiece& name,
- const StringPiece& signature, const Method* referrer)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowNewIllegalAccessErrorField(Thread* self, Class* referrer, Field* accessed)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowNewIllegalAccessErrorFinalField(Thread* self, const Method* referrer, Field* accessed)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowNewIllegalAccessErrorMethod(Thread* self, Class* referrer, Method* accessed)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowNullPointerExceptionForFieldAccess(Thread* self, Field* field, bool is_read)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowNullPointerExceptionForMethodAccess(Thread* self, Method* caller, uint32_t method_idx,
- InvokeType type)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-void ThrowNullPointerExceptionFromDexPC(Thread* self, Method* caller, uint32_t dex_pc)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-
-std::string FieldNameFromIndex(const Method* method, uint32_t ref,
- verifier::VerifyErrorRefType ref_type, bool access)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-std::string MethodNameFromIndex(const Method* method, uint32_t ref,
- verifier::VerifyErrorRefType ref_type, bool access)
- SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_);
-
// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
// cannot be resolved, throw an error. If it can, use it to create an instance.
// When verification/compiler hasn't been able to verify access, optionally perform an access
@@ -108,7 +68,7 @@
}
Class* referrer = method->GetDeclaringClass();
if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowNewIllegalAccessErrorClass(self, referrer, klass);
+ ThrowIllegalAccessErrorClass(referrer, klass);
return NULL; // Failure
}
}
@@ -124,7 +84,7 @@
// When verification/compiler hasn't been able to verify access, optionally perform an access
// check.
static inline Array* AllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
- Thread* self, bool access_check)
+ bool access_check)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
if (UNLIKELY(component_count < 0)) {
Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d",
@@ -143,7 +103,7 @@
if (access_check) {
Class* referrer = method->GetDeclaringClass();
if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowNewIllegalAccessErrorClass(self, referrer, klass);
+ ThrowIllegalAccessErrorClass(referrer, klass);
return NULL; // Failure
}
}