Clean up access checks.

Change-Id: Ia62ba6c8f1d0a9bfbbfde2d7be4c52c0f982b9d2
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b1117a2..b5d9fdf 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4186,7 +4186,7 @@
       mirror::Class* referring_class = referrer->GetDeclaringClass();
       if (!referring_class->CanAccess(methods_class)) {
         ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
-                                                      referrer, resolved, type);
+                                                      resolved, type);
         return NULL;
       } else if (!referring_class->CanAccessMember(methods_class,
                                                    resolved->GetAccessFlags())) {
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 0419dab..dd832df 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -136,7 +136,6 @@
 }
 
 void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed,
-                                                   const mirror::ArtMethod* caller,
                                                    const mirror::ArtMethod* called,
                                                    InvokeType type) {
   std::ostringstream msg;
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 3164f30..7f13891 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -76,7 +76,6 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
 void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed,
-                                                   const mirror::ArtMethod* caller,
                                                    const mirror::ArtMethod* called,
                                                    InvokeType type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8304229..09be56e 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -226,28 +226,14 @@
       return nullptr;
     }
     mirror::Class* referring_class = referrer->GetDeclaringClass();
-    if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
-                 !referring_class->CanAccessMember(fields_class,
-                                                   resolved_field->GetAccessFlags()))) {
-      // The referring class can't access the resolved field, this may occur as a result of a
-      // protected field being made public by a sub-class. Resort to the dex file to determine
-      // the correct class for the access check.
-      const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
-      fields_class = class_linker->ResolveType(dex_file,
-                                               dex_file.GetFieldId(field_idx).class_idx_,
-                                               referring_class);
-      if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
-        ThrowIllegalAccessErrorClass(referring_class, fields_class);
-        return nullptr;  // failure
-      } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
-                                                            resolved_field->GetAccessFlags()))) {
-        ThrowIllegalAccessErrorField(referring_class, resolved_field);
-        return nullptr;  // failure
-      }
+    if (UNLIKELY(!referring_class->CanAccessResolvedField<true>(fields_class, resolved_field,
+                                                                field_idx))) {
+      DCHECK(self->IsExceptionPending());  // Throw exception and unwind.
+      return nullptr;  // Failure.
     }
     if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
       ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
-      return nullptr;  // failure
+      return nullptr;  // Failure.
     } else {
       FieldHelper fh(resolved_field);
       if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
@@ -259,7 +245,7 @@
                                  expected_size * (32 / sizeof(int32_t)),
                                  is_primitive ? "primitive" : "non-primitive",
                                  PrettyField(resolved_field, true).c_str());
-        return nullptr;  // failure
+        return nullptr;  // Failure.
       }
     }
   }
@@ -277,7 +263,7 @@
         return resolved_field;
       } else {
         DCHECK(self->IsExceptionPending());  // Throw exception and unwind
-        return nullptr;  // failure
+        return nullptr;  // Failure.
       }
     }
   }
@@ -330,26 +316,10 @@
     }
     mirror::Class* methods_class = resolved_method->GetDeclaringClass();
     mirror::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 = *referring_class->GetDexCache()->GetDexFile();
-      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      methods_class = class_linker->ResolveType(dex_file,
-                                                dex_file.GetMethodId(method_idx).class_idx_,
-                                                referring_class);
-      if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
-        ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
-                                                      referrer, resolved_method, type);
-        return nullptr;  // Failure.
-      } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
-                                                            resolved_method->GetAccessFlags()))) {
-        ThrowIllegalAccessErrorMethod(referring_class, resolved_method);
-        return nullptr;  // Failure.
-      }
+    if (!referring_class->CanAccessResolvedMethod<true, type>(methods_class, resolved_method,
+                                                              method_idx)) {
+      DCHECK(self->IsExceptionPending());  // Throw exception and unwind.
+      return nullptr;  // Failure.
     }
   }
   switch (type) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index e0fab8c..cd44ebc 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -22,6 +22,7 @@
 #include "art_field.h"
 #include "art_method.h"
 #include "class_loader.h"
+#include "common_throws.h"
 #include "dex_cache.h"
 #include "gc/heap-inl.h"
 #include "iftable.h"
@@ -202,6 +203,68 @@
   return IsArrayAssignableFromArray(src);
 }
 
+template <bool throw_on_failure>
+inline bool Class::CanAccessResolvedField(Class* access_to, ArtField* field,
+                                          uint32_t field_idx) {
+  if (UNLIKELY(!this->CanAccess(access_to))) {
+    // The referrer class can't access the field's declaring class but may still be able
+    // to access the field if the FieldId specifies an accessible subclass of the declaring
+    // class rather than the declaring class itself.
+    DexCache* referrer_dex_cache = this->GetDexCache();
+    uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
+    // The referenced class has already been resolved with the field, get it from the dex cache.
+    Class* dex_access_to = referrer_dex_cache->GetResolvedType(class_idx);
+    DCHECK(dex_access_to != nullptr);
+    if (UNLIKELY(!this->CanAccess(dex_access_to))) {
+      if (throw_on_failure) {
+        ThrowIllegalAccessErrorClass(this, dex_access_to);
+      }
+      return false;
+    }
+    DCHECK_EQ(this->CanAccessMember(access_to, field->GetAccessFlags()),
+              this->CanAccessMember(dex_access_to, field->GetAccessFlags()));
+  }
+  if (LIKELY(this->CanAccessMember(access_to, field->GetAccessFlags()))) {
+    return true;
+  }
+  if (throw_on_failure) {
+    ThrowIllegalAccessErrorField(this, field);
+  }
+  return false;
+}
+
+template <bool throw_on_failure, InvokeType throw_invoke_type>
+inline bool Class::CanAccessResolvedMethod(Class* access_to, ArtMethod* method,
+                                           uint32_t method_idx) {
+  COMPILE_ASSERT(throw_on_failure || throw_invoke_type == kStatic, non_default_throw_invoke_type);
+  if (UNLIKELY(!this->CanAccess(access_to))) {
+    // The referrer class can't access the method's declaring class but may still be able
+    // to access the method if the MethodId specifies an accessible subclass of the declaring
+    // class rather than the declaring class itself.
+    DexCache* referrer_dex_cache = this->GetDexCache();
+    uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
+    // The referenced class has already been resolved with the method, get it from the dex cache.
+    Class* dex_access_to = referrer_dex_cache->GetResolvedType(class_idx);
+    DCHECK(dex_access_to != nullptr);
+    if (UNLIKELY(!this->CanAccess(dex_access_to))) {
+      if (throw_on_failure) {
+        ThrowIllegalAccessErrorClassForMethodDispatch(this, dex_access_to,
+                                                      method, throw_invoke_type);
+      }
+      return false;
+    }
+    DCHECK_EQ(this->CanAccessMember(access_to, method->GetAccessFlags()),
+              this->CanAccessMember(dex_access_to, method->GetAccessFlags()));
+  }
+  if (LIKELY(this->CanAccessMember(access_to, method->GetAccessFlags()))) {
+    return true;
+  }
+  if (throw_on_failure) {
+    ThrowIllegalAccessErrorMethod(this, method);
+  }
+  return false;
+}
+
 inline bool Class::IsSubClass(const Class* klass) const {
   DCHECK(!IsInterface()) << PrettyClass(this);
   DCHECK(!IsArrayClass()) << PrettyClass(this);
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 9aa23d9..d751363 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_MIRROR_CLASS_H_
 
 #include "gc/heap.h"
+#include "invoke_type.h"
 #include "modifiers.h"
 #include "object.h"
 #include "primitive.h"
@@ -449,6 +450,20 @@
     return this->IsInSamePackage(access_to);
   }
 
+  // Can this class access a resolved field?
+  // Note that access to field's class is checked and this may require looking up the class
+  // referenced by the FieldId in the DexFile in case the declaring class is inaccessible.
+  template <bool throw_on_failure>
+  bool CanAccessResolvedField(Class* access_to, ArtField* field,
+                              uint32_t field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Can this class access a resolved method?
+  // Note that access to methods's class is checked and this may require looking up the class
+  // referenced by the MethodId in the DexFile in case the declaring class is inaccessible.
+  template <bool throw_on_failure, InvokeType throw_invoke_type = kStatic>
+  bool CanAccessResolvedMethod(Class* access_to, ArtMethod* resolved_method,
+                               uint32_t method_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   bool IsSubClass(const Class* klass) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);