Add runtime_support_common.
Addressed the issue that we shouldn't maintain 2 copies of almost the
same code.
Change-Id: I212656f65e6c47e1167a193c1024100ae97f0028
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 74eee0b..e84df3e 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -206,6 +206,7 @@
src/reflection.cc \
src/runtime.cc \
src/runtime_support.cc \
+ src/runtime_support_common.cc \
src/signal_catcher.cc \
src/space.cc \
src/stack.cc \
diff --git a/src/compiler_llvm/runtime_support_llvm.cc b/src/compiler_llvm/runtime_support_llvm.cc
index 45f818f..300376b 100644
--- a/src/compiler_llvm/runtime_support_llvm.cc
+++ b/src/compiler_llvm/runtime_support_llvm.cc
@@ -4,6 +4,7 @@
#include "dex_verifier.h"
#include "object.h"
#include "object_utils.h"
+#include "runtime_support_common.h"
#include "runtime_support_llvm.h"
#include "thread.h"
#include "thread_list.h"
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 9bf2086..7e4e410 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -23,6 +23,7 @@
#include "object.h"
#include "object_utils.h"
#include "reflection.h"
+#include "runtime_support_common.h"
#include "trace.h"
#include "ScopedLocalRef.h"
@@ -79,81 +80,6 @@
self->SetTopOfStack(sp, 0);
}
-static 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());
-}
-
-static void ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread* self, Class* referrer,
- Class* accessed, const Method* caller,
- const Method* called,
- InvokeType type) {
- std::ostringstream type_stream;
- type_stream << 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(),
- type_stream.str().c_str(),
- PrettyMethod(called).c_str(),
- PrettyMethod(caller).c_str());
-}
-
-static void ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(Thread* self,
- const Method* referrer,
- const Method* interface_method,
- Object* this_object) {
- Thread::Current()->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
- "class '%s' does not implement interface '%s' in call to '%s' from '%s'",
- PrettyDescriptor(this_object->GetClass()).c_str(),
- PrettyDescriptor(interface_method->GetDeclaringClass()).c_str(),
- PrettyMethod(interface_method).c_str(), PrettyMethod(referrer).c_str());
-}
-
-static 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());
-}
-
-static 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());
-}
-
-static 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());
-}
-
-static 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());
-}
-
-static void ThrowNullPointerExceptionForMethodAccess(Thread* self, Method* caller,
- uint32_t method_idx, InvokeType type) {
- const DexFile& dex_file =
- Runtime::Current()->GetClassLinker()->FindDexFile(caller->GetDeclaringClass()->GetDexCache());
- std::ostringstream type_stream;
- type_stream << type;
- self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
- "Attempt to invoke %s method '%s' from '%s' on a null object reference",
- type_stream.str().c_str(),
- PrettyMethod(method_idx, dex_file, true).c_str(),
- PrettyMethod(caller).c_str());
-}
-
/*
* Report location to debugger. Note: dex_pc is the current offset within
* the method. However, because the offset alone cannot distinguish between
@@ -655,85 +581,6 @@
}
-// Fast path field resolution that can't throw exceptions
-static Field* FindFieldFast(uint32_t field_idx, const Method* referrer, bool is_primitive,
- size_t expected_size, bool is_set) {
- Field* resolved_field = referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx);
- if (UNLIKELY(resolved_field == NULL)) {
- return NULL;
- }
- Class* fields_class = resolved_field->GetDeclaringClass();
- // Check class is initiliazed or initializing
- if (UNLIKELY(!fields_class->IsInitializing())) {
- return NULL;
- }
- Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
- !referring_class->CanAccessMember(fields_class,
- resolved_field->GetAccessFlags()) ||
- (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) {
- // illegal access
- return NULL;
- }
- FieldHelper fh(resolved_field);
- if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
- fh.FieldSize() != expected_size)) {
- return NULL;
- }
- return resolved_field;
-}
-
-
-// Slow path field resolution and declaring class initialization
-Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
- bool is_static, bool is_primitive, bool is_set, size_t expected_size) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Field* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
- if (UNLIKELY(resolved_field == NULL)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind
- return NULL; // failure
- } else {
- Class* fields_class = resolved_field->GetDeclaringClass();
- Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
- ThrowNewIllegalAccessErrorClass(self, referring_class, fields_class);
- return NULL; // failure
- } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
- resolved_field->GetAccessFlags()))) {
- ThrowNewIllegalAccessErrorField(self, referring_class, resolved_field);
- return NULL; // failure
- } else if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
- ThrowNewIllegalAccessErrorFinalField(self, referrer, resolved_field);
- return NULL; // failure
- } else {
- FieldHelper fh(resolved_field);
- if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
- fh.FieldSize() != expected_size)) {
- self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
- "Attempted read of %zd-bit %s on field '%s'",
- expected_size * (32 / sizeof(int32_t)),
- is_primitive ? "primitive" : "non-primitive",
- PrettyField(resolved_field, true).c_str());
- return NULL; // failure
- } else if (!is_static) {
- // instance fields must be being accessed on an initialized class
- return resolved_field;
- } else {
- // If the class is already initializing, we must be inside <clinit>, or
- // we'd still be waiting for the lock.
- if (fields_class->IsInitializing()) {
- return resolved_field;
- } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true)) {
- return resolved_field;
- } else {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind
- return NULL; // failure
- }
- }
- }
- }
-}
-
extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx, const Method* referrer,
Thread* self, Method** sp) {
Field* field = FindFieldFast(field_idx, referrer, true, false, sizeof(int32_t));
@@ -943,40 +790,6 @@
return -1; // failure
}
-// 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
-// check.
-static Object* AllocObjectFromCode(uint32_t type_idx, Method* method, Thread* self,
- bool access_check) {
- Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
- Runtime* runtime = Runtime::Current();
- if (UNLIKELY(klass == NULL)) {
- klass = runtime->GetClassLinker()->ResolveType(type_idx, method);
- if (klass == NULL) {
- DCHECK(self->IsExceptionPending());
- return NULL; // Failure
- }
- }
- if (access_check) {
- if (UNLIKELY(!klass->IsInstantiable())) {
- self->ThrowNewException("Ljava/lang/InstantiationError;",
- PrettyDescriptor(klass).c_str());
- return NULL; // Failure
- }
- Class* referrer = method->GetDeclaringClass();
- if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowNewIllegalAccessErrorClass(self, referrer, klass);
- return NULL; // Failure
- }
- }
- if (!runtime->GetClassLinker()->EnsureInitialized(klass, true)) {
- DCHECK(self->IsExceptionPending());
- return NULL; // Failure
- }
- return klass->AllocObject();
-}
-
extern "C" Object* artAllocObjectFromCode(uint32_t type_idx, Method* method,
Thread* self, Method** sp) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
@@ -989,36 +802,6 @@
return AllocObjectFromCode(type_idx, method, self, true);
}
-// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If
-// it cannot be resolved, throw an error. If it can, use it to create an array.
-// When verification/compiler hasn't been able to verify access, optionally perform an access
-// check.
-static Array* AllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
- Thread* self, bool access_check) {
- if (UNLIKELY(component_count < 0)) {
- Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d",
- component_count);
- return NULL; // Failure
- }
- Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
- if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
- klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
- if (klass == NULL) { // Error
- DCHECK(Thread::Current()->IsExceptionPending());
- return NULL; // Failure
- }
- CHECK(klass->IsArrayClass()) << PrettyClass(klass);
- }
- if (access_check) {
- Class* referrer = method->GetDeclaringClass();
- if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowNewIllegalAccessErrorClass(self, referrer, klass);
- return NULL; // Failure
- }
- }
- return Array::Alloc(klass, component_count);
-}
-
extern "C" Array* artAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
Thread* self, Method** sp) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
@@ -1032,45 +815,6 @@
return AllocArrayFromCode(type_idx, method, component_count, self, true);
}
-// 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) {
- if (UNLIKELY(component_count < 0)) {
- self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", component_count);
- return NULL; // Failure
- }
- Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
- if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
- klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
- if (klass == NULL) { // Error
- DCHECK(Thread::Current()->IsExceptionPending());
- return NULL; // Failure
- }
- }
- if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
- if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
- Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
- "Bad filled array request for type %s",
- PrettyDescriptor(klass).c_str());
- } else {
- Thread::Current()->ThrowNewExceptionF("Ljava/lang/InternalError;",
- "Found type %s; filled-new-array not implemented for anything but \'int\'",
- PrettyDescriptor(klass).c_str());
- }
- return NULL; // Failure
- } else {
- if (access_check) {
- Class* referrer = method->GetDeclaringClass();
- if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowNewIllegalAccessErrorClass(self, referrer, klass);
- return NULL; // Failure
- }
- }
- DCHECK(klass->IsArrayClass()) << PrettyClass(klass);
- return Array::Alloc(klass, component_count);
- }
-}
-
extern "C" Array* artCheckAndAllocArrayFromCode(uint32_t type_idx, Method* method,
int32_t component_count, Thread* self, Method** sp) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
@@ -1257,142 +1001,6 @@
return 0; // Success
}
-// Fast path method resolution that can't throw exceptions
-static Method* FindMethodFast(uint32_t method_idx, Object* this_object, const Method* referrer,
- bool access_check, InvokeType type) {
- bool is_direct = type == kStatic || type == kDirect;
- if (UNLIKELY(this_object == NULL && !is_direct)) {
- return NULL;
- }
- Method* resolved_method =
- referrer->GetDeclaringClass()->GetDexCache()->GetResolvedMethod(method_idx);
- if (UNLIKELY(resolved_method == NULL)) {
- return NULL;
- }
- if (access_check) {
- Class* methods_class = resolved_method->GetDeclaringClass();
- Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
- !referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- // potential illegal access
- return NULL;
- }
- }
- if (type == kInterface) { // Most common form of slow path dispatch.
- return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
- } else if (is_direct) {
- return resolved_method;
- } else if (type == kSuper) {
- return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
- } else {
- DCHECK(type == kVirtual);
- return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
- }
-}
-
-// Slow path method resolution
-static Method* FindMethodFromCode(uint32_t method_idx, Object* this_object, const Method* referrer,
- Thread* self, bool access_check, InvokeType type) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- bool is_direct = type == kStatic || type == kDirect;
- Method* resolved_method = class_linker->ResolveMethod(method_idx, referrer, is_direct);
- if (UNLIKELY(resolved_method == NULL)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind
- return NULL; // failure
- } else {
- if (!access_check) {
- if (is_direct) {
- return resolved_method;
- } else if (type == kInterface) {
- Method* interface_method =
- this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
- if (UNLIKELY(interface_method == NULL)) {
- ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
- resolved_method,
- this_object);
- return NULL; // failure
- } else {
- return interface_method;
- }
- } else {
- ObjectArray<Method>* vtable;
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- if (type == kSuper) {
- vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable();
- } else {
- vtable = this_object->GetClass()->GetVTable();
- }
- // TODO: eliminate bounds check?
- return vtable->Get(vtable_index);
- }
- } else {
- Class* methods_class = resolved_method->GetDeclaringClass();
- Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
- !referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- // The referring class can't access the resolved method, this may occur as a result of a
- // protected method being made public by implementing an interface that re-declares the
- // method public. Resort to the dex file to determine the correct class for the access check
- const DexFile& dex_file = class_linker->FindDexFile(referring_class->GetDexCache());
- methods_class = class_linker->ResolveType(dex_file,
- 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);
- return NULL; // failure
- } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- ThrowNewIllegalAccessErrorMethod(self, referring_class, resolved_method);
- return NULL; // failure
- }
- }
- if (is_direct) {
- return resolved_method;
- } else if (type == kInterface) {
- Method* interface_method =
- this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
- if (UNLIKELY(interface_method == NULL)) {
- ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
- resolved_method,
- this_object);
- return NULL; // failure
- } else {
- return interface_method;
- }
- } else {
- ObjectArray<Method>* vtable;
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- if (type == kSuper) {
- Class* super_class = referring_class->GetSuperClass();
- if (LIKELY(super_class != NULL)) {
- vtable = referring_class->GetSuperClass()->GetVTable();
- } else {
- vtable = NULL;
- }
- } else {
- vtable = this_object->GetClass()->GetVTable();
- }
- if (LIKELY(vtable != NULL &&
- vtable_index < static_cast<uint32_t>(vtable->GetLength()))) {
- return vtable->GetWithoutChecks(vtable_index);
- } else {
- // Behavior to agree with that of the verifier
- self->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
- "attempt to invoke %s method '%s' from '%s'"
- " using incorrect form of method dispatch",
- (type == kSuper ? "super class" : "virtual"),
- PrettyMethod(resolved_method).c_str(),
- PrettyMethod(referrer).c_str());
- return NULL; // failure
- }
- }
- }
- }
-}
-
static uint64_t artInvokeCommon(uint32_t method_idx, Object* this_object, Method* caller_method,
Thread* self, Method** sp, bool access_check, InvokeType type){
Method* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type);
diff --git a/src/runtime_support_common.cc b/src/runtime_support_common.cc
new file mode 100644
index 0000000..7be9a6f
--- /dev/null
+++ b/src/runtime_support_common.cc
@@ -0,0 +1,199 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+#include "runtime_support_common.h"
+
+
+namespace art {
+
+// 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) {
+ if (UNLIKELY(component_count < 0)) {
+ self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", component_count);
+ return NULL; // Failure
+ }
+ Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
+ if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
+ klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+ if (klass == NULL) { // Error
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return NULL; // Failure
+ }
+ }
+ if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
+ if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
+ Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
+ "Bad filled array request for type %s",
+ PrettyDescriptor(klass).c_str());
+ } else {
+ Thread::Current()->ThrowNewExceptionF("Ljava/lang/InternalError;",
+ "Found type %s; filled-new-array not implemented for anything but \'int\'",
+ PrettyDescriptor(klass).c_str());
+ }
+ return NULL; // Failure
+ } else {
+ if (access_check) {
+ Class* referrer = method->GetDeclaringClass();
+ if (UNLIKELY(!referrer->CanAccess(klass))) {
+ ThrowNewIllegalAccessErrorClass(self, referrer, klass);
+ return NULL; // Failure
+ }
+ }
+ DCHECK(klass->IsArrayClass()) << PrettyClass(klass);
+ return Array::Alloc(klass, component_count);
+ }
+}
+
+// Slow path field resolution and declaring class initialization
+Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
+ bool is_static, bool is_primitive, bool is_set, size_t expected_size) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Field* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
+ if (UNLIKELY(resolved_field == NULL)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind
+ return NULL; // failure
+ } else {
+ Class* fields_class = resolved_field->GetDeclaringClass();
+ Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
+ ThrowNewIllegalAccessErrorClass(self, referring_class, fields_class);
+ return NULL; // failure
+ } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
+ resolved_field->GetAccessFlags()))) {
+ ThrowNewIllegalAccessErrorField(self, referring_class, resolved_field);
+ return NULL; // failure
+ } else if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
+ ThrowNewIllegalAccessErrorFinalField(self, referrer, resolved_field);
+ return NULL; // failure
+ } else {
+ FieldHelper fh(resolved_field);
+ if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
+ fh.FieldSize() != expected_size)) {
+ self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
+ "Attempted read of %zd-bit %s on field '%s'",
+ expected_size * (32 / sizeof(int32_t)),
+ is_primitive ? "primitive" : "non-primitive",
+ PrettyField(resolved_field, true).c_str());
+ return NULL; // failure
+ } else if (!is_static) {
+ // instance fields must be being accessed on an initialized class
+ return resolved_field;
+ } else {
+ // If the class is already initializing, we must be inside <clinit>, or
+ // we'd still be waiting for the lock.
+ if (fields_class->IsInitializing()) {
+ return resolved_field;
+ } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true)) {
+ return resolved_field;
+ } else {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind
+ return NULL; // failure
+ }
+ }
+ }
+ }
+}
+
+// Slow path method resolution
+Method* FindMethodFromCode(uint32_t method_idx, Object* this_object, const Method* referrer,
+ Thread* self, bool access_check, InvokeType type) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ bool is_direct = type == kStatic || type == kDirect;
+ Method* resolved_method = class_linker->ResolveMethod(method_idx, referrer, is_direct);
+ if (UNLIKELY(resolved_method == NULL)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind
+ return NULL; // failure
+ } else {
+ if (!access_check) {
+ if (is_direct) {
+ return resolved_method;
+ } else if (type == kInterface) {
+ Method* interface_method =
+ this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+ if (UNLIKELY(interface_method == NULL)) {
+ ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
+ resolved_method,
+ this_object);
+ return NULL; // failure
+ } else {
+ return interface_method;
+ }
+ } else {
+ ObjectArray<Method>* vtable;
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ if (type == kSuper) {
+ vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable();
+ } else {
+ vtable = this_object->GetClass()->GetVTable();
+ }
+ // TODO: eliminate bounds check?
+ return vtable->Get(vtable_index);
+ }
+ } else {
+ Class* methods_class = resolved_method->GetDeclaringClass();
+ Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
+ !referring_class->CanAccessMember(methods_class,
+ resolved_method->GetAccessFlags()))) {
+ // The referring class can't access the resolved method, this may occur as a result of a
+ // protected method being made public by implementing an interface that re-declares the
+ // method public. Resort to the dex file to determine the correct class for the access check
+ const DexFile& dex_file = class_linker->FindDexFile(referring_class->GetDexCache());
+ methods_class = class_linker->ResolveType(dex_file,
+ 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);
+ return NULL; // failure
+ } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
+ resolved_method->GetAccessFlags()))) {
+ ThrowNewIllegalAccessErrorMethod(self, referring_class, resolved_method);
+ return NULL; // failure
+ }
+ }
+ if (is_direct) {
+ return resolved_method;
+ } else if (type == kInterface) {
+ Method* interface_method =
+ this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+ if (UNLIKELY(interface_method == NULL)) {
+ ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
+ resolved_method,
+ this_object);
+ return NULL; // failure
+ } else {
+ return interface_method;
+ }
+ } else {
+ ObjectArray<Method>* vtable;
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ if (type == kSuper) {
+ Class* super_class = referring_class->GetSuperClass();
+ if (LIKELY(super_class != NULL)) {
+ vtable = referring_class->GetSuperClass()->GetVTable();
+ } else {
+ vtable = NULL;
+ }
+ } else {
+ vtable = this_object->GetClass()->GetVTable();
+ }
+ if (LIKELY(vtable != NULL &&
+ vtable_index < static_cast<uint32_t>(vtable->GetLength()))) {
+ return vtable->GetWithoutChecks(vtable_index);
+ } else {
+ // Behavior to agree with that of the verifier
+ self->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
+ "attempt to invoke %s method '%s' from '%s'"
+ " using incorrect form of method dispatch",
+ (type == kSuper ? "super class" : "virtual"),
+ PrettyMethod(resolved_method).c_str(),
+ PrettyMethod(referrer).c_str());
+ return NULL; // failure
+ }
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/src/runtime_support_common.h b/src/runtime_support_common.h
new file mode 100644
index 0000000..5cb059e
--- /dev/null
+++ b/src/runtime_support_common.h
@@ -0,0 +1,252 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_RUNTIME_SUPPORT_COMMON_H_
+#define ART_SRC_RUNTIME_SUPPORT_COMMON_H_
+
+#include "class_linker.h"
+#include "constants.h"
+#include "object.h"
+#include "object_utils.h"
+#include "thread.h"
+
+#include <stdint.h>
+
+namespace art {
+
+class Array;
+class Class;
+class Field;
+class Method;
+class Object;
+
+static 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());
+}
+
+static void
+ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread* self,
+ Class* referrer,
+ Class* accessed,
+ const Method* caller,
+ const Method* called,
+ InvokeType type) {
+ std::ostringstream type_stream;
+ type_stream << 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(),
+ type_stream.str().c_str(),
+ PrettyMethod(called).c_str(),
+ PrettyMethod(caller).c_str());
+}
+
+static void
+ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(Thread* self,
+ const Method* referrer,
+ const Method* interface_method,
+ Object* this_object) {
+ self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
+ "class '%s' does not implement interface '%s' in call to '%s' from '%s'",
+ PrettyDescriptor(this_object->GetClass()).c_str(),
+ PrettyDescriptor(interface_method->GetDeclaringClass()).c_str(),
+ PrettyMethod(interface_method).c_str(), PrettyMethod(referrer).c_str());
+}
+
+static 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());
+}
+
+static 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());
+}
+
+static 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());
+}
+
+static 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());
+}
+
+static void ThrowNullPointerExceptionForMethodAccess(Thread* self,
+ Method* caller,
+ uint32_t method_idx,
+ InvokeType type) {
+ const DexFile& dex_file =
+ Runtime::Current()->GetClassLinker()->FindDexFile(caller->GetDeclaringClass()->GetDexCache());
+ std::ostringstream type_stream;
+ type_stream << type;
+ self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
+ "Attempt to invoke %s method '%s' from '%s' on a null object reference",
+ type_stream.str().c_str(),
+ PrettyMethod(method_idx, dex_file, true).c_str(),
+ PrettyMethod(caller).c_str());
+}
+
+// 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
+// check.
+static Object* AllocObjectFromCode(uint32_t type_idx, Method* method, Thread* self,
+ bool access_check) {
+ Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(klass == NULL)) {
+ klass = runtime->GetClassLinker()->ResolveType(type_idx, method);
+ if (klass == NULL) {
+ DCHECK(self->IsExceptionPending());
+ return NULL; // Failure
+ }
+ }
+ if (access_check) {
+ if (UNLIKELY(!klass->IsInstantiable())) {
+ self->ThrowNewException("Ljava/lang/InstantiationError;",
+ PrettyDescriptor(klass).c_str());
+ return NULL; // Failure
+ }
+ Class* referrer = method->GetDeclaringClass();
+ if (UNLIKELY(!referrer->CanAccess(klass))) {
+ ThrowNewIllegalAccessErrorClass(self, referrer, klass);
+ return NULL; // Failure
+ }
+ }
+ if (!runtime->GetClassLinker()->EnsureInitialized(klass, true)) {
+ DCHECK(self->IsExceptionPending());
+ return NULL; // Failure
+ }
+ return klass->AllocObject();
+}
+
+// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If
+// it cannot be resolved, throw an error. If it can, use it to create an array.
+// When verification/compiler hasn't been able to verify access, optionally perform an access
+// check.
+static Array* AllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
+ Thread* self, bool access_check) {
+ if (UNLIKELY(component_count < 0)) {
+ Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d",
+ component_count);
+ return NULL; // Failure
+ }
+ Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
+ if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
+ klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+ if (klass == NULL) { // Error
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return NULL; // Failure
+ }
+ CHECK(klass->IsArrayClass()) << PrettyClass(klass);
+ }
+ if (access_check) {
+ Class* referrer = method->GetDeclaringClass();
+ if (UNLIKELY(!referrer->CanAccess(klass))) {
+ ThrowNewIllegalAccessErrorClass(self, referrer, klass);
+ return NULL; // Failure
+ }
+ }
+ return Array::Alloc(klass, component_count);
+}
+
+extern Array* CheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
+ Thread* self, bool access_check);
+
+extern Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
+ bool is_static, bool is_primitive, bool is_set,
+ size_t expected_size);
+
+// Fast path field resolution that can't throw exceptions
+static Field* FindFieldFast(uint32_t field_idx, const Method* referrer, bool is_primitive,
+ size_t expected_size, bool is_set) {
+ Field* resolved_field = referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx);
+ if (UNLIKELY(resolved_field == NULL)) {
+ return NULL;
+ }
+ Class* fields_class = resolved_field->GetDeclaringClass();
+ // Check class is initiliazed or initializing
+ if (UNLIKELY(!fields_class->IsInitializing())) {
+ return NULL;
+ }
+ Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
+ !referring_class->CanAccessMember(fields_class,
+ resolved_field->GetAccessFlags()) ||
+ (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) {
+ // illegal access
+ return NULL;
+ }
+ FieldHelper fh(resolved_field);
+ if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
+ fh.FieldSize() != expected_size)) {
+ return NULL;
+ }
+ return resolved_field;
+}
+
+// Fast path method resolution that can't throw exceptions
+static Method* FindMethodFast(uint32_t method_idx, Object* this_object, const Method* referrer,
+ bool access_check, InvokeType type) {
+ bool is_direct = type == kStatic || type == kDirect;
+ if (UNLIKELY(this_object == NULL && !is_direct)) {
+ return NULL;
+ }
+ Method* resolved_method =
+ referrer->GetDeclaringClass()->GetDexCache()->GetResolvedMethod(method_idx);
+ if (UNLIKELY(resolved_method == NULL)) {
+ return NULL;
+ }
+ if (access_check) {
+ Class* methods_class = resolved_method->GetDeclaringClass();
+ Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
+ !referring_class->CanAccessMember(methods_class,
+ resolved_method->GetAccessFlags()))) {
+ // potential illegal access
+ return NULL;
+ }
+ }
+ if (type == kInterface) { // Most common form of slow path dispatch.
+ return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+ } else if (is_direct) {
+ return resolved_method;
+ } else if (type == kSuper) {
+ return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()->
+ Get(resolved_method->GetMethodIndex());
+ } else {
+ DCHECK(type == kVirtual);
+ return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
+ }
+}
+
+extern Method* FindMethodFromCode(uint32_t method_idx, Object* this_object, const Method* referrer,
+ Thread* self, bool access_check, InvokeType type);
+
+} // namespace art
+
+#endif // ART_SRC_RUNTIME_SUPPORT_COMMON_H_