Run time illegal access checks on static and direct methods

Fixes test 075.

Change-Id: I28b20451dcae8000dc0e2cb9068dfa5166659d43
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 670049a..f158680 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -45,13 +45,15 @@
 static void ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread* self, Class* referrer,
                                                              Class* accessed, const Method* caller,
                                                              const Method* called,
-                                                             bool is_interface, bool is_super) {
+                                                             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(),
-                           (is_interface ? "interface" : (is_super ? "super class" : "virtual")),
+                           type_stream.str().c_str(),
                            PrettyMethod(called).c_str(),
                            PrettyMethod(caller).c_str());
 }
@@ -89,13 +91,14 @@
 }
 
 static void ThrowNullPointerExceptionForMethodAccess(Thread* self, Method* caller,
-                                                     uint32_t method_idx, bool is_interface,
-                                                     bool is_super) {
+                                                     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",
-                           (is_interface ? "interface" : (is_super ? "super class" : "virtual")),
+                           type_stream.str().c_str(),
                            PrettyMethod(method_idx, dex_file, true).c_str(),
                            PrettyMethod(caller).c_str());
 }
@@ -540,40 +543,46 @@
                          bool is_static, bool is_primitive, size_t expected_size) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Field* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
-  if (LIKELY(resolved_field != NULL)) {
+  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
-    }
-    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());
-    }
-    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()) {
+      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 (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true)) {
-        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
+        }
       }
     }
   }
-  DCHECK(self->IsExceptionPending());  // Throw exception and unwind
-  return NULL;
 }
 
 extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx, const Method* referrer,
@@ -1118,8 +1127,9 @@
 
 // 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, bool is_interface, bool is_super) {
-  if (UNLIKELY(this_object == NULL)) {
+                              bool access_check, InvokeType type) {
+  bool is_direct = type == kStatic || type == kDirect;
+  if (UNLIKELY(this_object == NULL && !is_direct)) {
     return NULL;
   }
   Method* resolved_method =
@@ -1137,38 +1147,46 @@
       return NULL;
     }
   }
-  if (is_interface) {
+  if (type == kInterface) {  // Most common form of slow path dispatch.
     return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
-  } else if (is_super) {
+  } 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, bool is_interface,
-                                  bool is_super) {
+                                  Thread* self, bool access_check, InvokeType type) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Method* resolved_method = class_linker->ResolveMethod(method_idx, referrer, false);
-  if (LIKELY(resolved_method != NULL)) {
+  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_interface) {
+      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;
+          return NULL;  // failure
         } else {
           return interface_method;
         }
       } else {
         ObjectArray<Method>* vtable;
         uint16_t vtable_index = resolved_method->GetMethodIndex();
-        if (is_super) {
+        if (type == kSuper) {
           vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable();
         } else {
           vtable = this_object->GetClass()->GetVTable();
@@ -1191,8 +1209,7 @@
                                                   referring_class);
         if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
           ThrowNewIllegalAccessErrorClassForMethodDispatch(self, referring_class, methods_class,
-                                                           referrer, resolved_method, is_interface,
-                                                           is_super);
+                                                           referrer, resolved_method, type);
           return NULL;  // failure
         } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
                                                               resolved_method->GetAccessFlags()))) {
@@ -1200,21 +1217,23 @@
           return NULL;  // failure
         }
       }
-      if (is_interface) {
+      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;
+          return NULL;  // failure
         } else {
           return interface_method;
         }
       } else {
         ObjectArray<Method>* vtable;
         uint16_t vtable_index = resolved_method->GetMethodIndex();
-        if (is_super) {
+        if (type == kSuper) {
           Class* super_class = referring_class->GetSuperClass();
           if (LIKELY(super_class != NULL)) {
             vtable = referring_class->GetSuperClass()->GetVTable();
@@ -1232,32 +1251,26 @@
           self->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
                                    "attempt to invoke %s method '%s' from '%s'"
                                    " using incorrect form of method dispatch",
-                                   (is_super ? "super class" : "virtual"),
+                                   (type == kSuper ? "super class" : "virtual"),
                                    PrettyMethod(resolved_method).c_str(),
                                    PrettyMethod(referrer).c_str());
-          return NULL;
+          return NULL;  // failure
         }
       }
     }
   }
-  DCHECK(self->IsExceptionPending());  // Throw exception and unwind
-  return NULL;
 }
 
 static uint64_t artInvokeCommon(uint32_t method_idx, Object* this_object, Method* caller_method,
-                                Thread* self, Method** sp, bool access_check, bool is_interface,
-                                bool is_super){
-  Method* method = FindMethodFast(method_idx, this_object, caller_method, access_check,
-                                  is_interface, is_super);
+                                Thread* self, Method** sp, bool access_check, InvokeType type){
+  Method* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type);
   if (UNLIKELY(method == NULL)) {
     FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
-    if (UNLIKELY(this_object == NULL)) {
-      ThrowNullPointerExceptionForMethodAccess(self, caller_method, method_idx, is_interface,
-                                               is_super);
+    if (UNLIKELY(this_object == NULL && type != kDirect && type != kStatic)) {
+      ThrowNullPointerExceptionForMethodAccess(self, caller_method, method_idx, type);
       return 0;  // failure
     }
-    method = FindMethodFromCode(method_idx, this_object, caller_method, self, access_check,
-                                is_interface, is_super);
+    method = FindMethodFromCode(method_idx, this_object, caller_method, self, access_check, type);
     if (UNLIKELY(method == NULL)) {
       CHECK(self->IsExceptionPending());
       return 0;  // failure
@@ -1277,28 +1290,43 @@
 extern "C" uint64_t artInvokeInterfaceTrampoline(uint32_t method_idx, Object* this_object,
                                                  Method* caller_method, Thread* self,
                                                  Method** sp) {
-  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, false, true, false);
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, false, kInterface);
 }
 
 extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx,
                                                                 Object* this_object,
                                                                 Method* caller_method, Thread* self,
                                                                 Method** sp) {
-  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, true, false);
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kInterface);
+}
+
+
+extern "C" uint64_t artInvokeDirectTrampolineWithAccessCheck(uint32_t method_idx,
+                                                             Object* this_object,
+                                                             Method* caller_method, Thread* self,
+                                                             Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kDirect);
+}
+
+extern "C" uint64_t artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx,
+                                                            Object* this_object,
+                                                            Method* caller_method, Thread* self,
+                                                            Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kStatic);
 }
 
 extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
                                                             Object* this_object,
                                                             Method* caller_method, Thread* self,
                                                             Method** sp) {
-  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, false, true);
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kSuper);
 }
 
 extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx,
                                                               Object* this_object,
                                                               Method* caller_method, Thread* self,
                                                               Method** sp) {
-  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, false, false);
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kVirtual);
 }
 
 static void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception) {