Support for access check in checkcast and instanceof.

Change-Id: Ie15216618b35cace7d351be2b0a1c466ed6db489
diff --git a/src/asm_support.h b/src/asm_support.h
index f81966f..8d29a35 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -10,13 +10,13 @@
 #define rSELF r9
 #define rLR r14
 // Offset of field Thread::suspend_count_ verified in InitCpu
-#define THREAD_SUSPEND_COUNT_OFFSET 388
+#define THREAD_SUSPEND_COUNT_OFFSET 392
 // Offset of field Thread::suspend_count_ verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 384
+#define THREAD_EXCEPTION_OFFSET 388
 
 #elif defined(__i386__)
 // Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 376
+#define THREAD_SELF_OFFSET 380
 #endif
 
 #endif  // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index 2edd398..a32446a 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -164,6 +164,10 @@
     art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
     Field* field = class_linker->ResolveField(fieldIdx, method, true);
     if (field == NULL) {
+        Thread* thread = Thread::Current();
+        if (thread->IsExceptionPending()) {  // clear any exception left by resolve field
+            thread->ClearException();
+        }
         return NULL;
     }
     const art::DexFile& dex_file = class_linker->
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index 4e3888d..e58a982 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -729,8 +729,14 @@
     loadValueDirectFixed(cUnit, rlSrc, r0);  // r0 <= ref
     int classReg = r2;  // r2 will hold the Class*
     if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) {
-        // Check we have access to type_idx and if not throw IllegalAccessError
-        UNIMPLEMENTED(FATAL);
+        // Check we have access to type_idx and if not throw IllegalAccessError,
+        // returns Class* in r0
+        loadWordDisp(cUnit, rSELF,
+                     OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
+                     rLR);
+        loadConstant(cUnit, r0, type_idx);
+        callRuntimeHelper(cUnit, rLR);  // InitializeTypeAndVerifyAccess(idx, method)
+        genRegCopy(cUnit, classReg, r0);  // Align usage with fast path
     } else {
         // Load dex cache entry into classReg (r2)
         loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
@@ -743,7 +749,7 @@
             // Call out to helper, which will return resolved type in r0
             loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
             loadConstant(cUnit, r0, type_idx);
-            callRuntimeHelper(cUnit, rLR);  // resolveTypeFromCode(idx, method)
+            callRuntimeHelper(cUnit, rLR);  // InitializeTypeFromCode(idx, method)
             genRegCopy(cUnit, r2, r0); // Align usage with fast path
             loadValueDirectFixed(cUnit, rlSrc, r0);  /* reload Ref */
             // Rejoin code paths
@@ -784,8 +790,14 @@
     loadCurrMethodDirect(cUnit, r1);  // r1 <= current Method*
     int classReg = r2;  // r2 will hold the Class*
     if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) {
-        // Check we have access to type_idx and if not throw IllegalAccessError
-        UNIMPLEMENTED(FATAL);
+        // Check we have access to type_idx and if not throw IllegalAccessError,
+        // returns Class* in r0
+        loadWordDisp(cUnit, rSELF,
+                     OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
+                     rLR);
+        loadConstant(cUnit, r0, type_idx);
+        callRuntimeHelper(cUnit, rLR);  // InitializeTypeAndVerifyAccess(idx, method)
+        genRegCopy(cUnit, classReg, r0);  // Align usage with fast path
     } else {
         // Load dex cache entry into classReg (r2)
         loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
@@ -798,8 +810,8 @@
             // Call out to helper, which will return resolved type in r0
             loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
             loadConstant(cUnit, r0, type_idx);
-            callRuntimeHelper(cUnit, rLR);  // resolveTypeFromCode(idx, method)
-            genRegCopy(cUnit, r2, r0); // Align usage with fast path
+            callRuntimeHelper(cUnit, rLR);  // InitializeTypeFromCode(idx, method)
+            genRegCopy(cUnit, classReg, r0); // Align usage with fast path
             // Rejoin code paths
             ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
             hopTarget->defMask = ENCODE_ALL;
@@ -815,7 +827,7 @@
     loadWordDisp(cUnit, r0,  Object::ClassOffset().Int32Value(), r1);
     /* r1 now contains object->clazz */
     loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
-    opRegReg(cUnit, kOpCmp, r1, r2);
+    opRegReg(cUnit, kOpCmp, r1, classReg);
     ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
     genRegCopy(cUnit, r0, r1);
     genRegCopy(cUnit, r1, r2);
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 8df2abe..3c10fc3 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -763,6 +763,37 @@
     CHECK(self->IsExceptionPending());
     return NULL;  // Failure - Indicate to caller to deliver exception
   }
+  DCHECK(referrer->GetDeclaringClass()->CanAccess(klass));
+  // If we are the <clinit> of this class, just return our storage.
+  //
+  // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
+  // running.
+  if (klass == referrer->GetDeclaringClass() && referrer->IsClassInitializer()) {
+    return klass;
+  }
+  if (!class_linker->EnsureInitialized(klass, true)) {
+    CHECK(self->IsExceptionPending());
+    return NULL;  // Failure - Indicate to caller to deliver exception
+  }
+  referrer->GetDexCacheInitializedStaticStorage()->Set(type_idx, klass);
+  return klass;
+}
+
+Class* InitializeStaticStorageAndVerifyAccess(uint32_t type_idx, const Method* referrer,
+                                              Thread* self) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Class* klass = class_linker->ResolveType(type_idx, referrer);
+  if (UNLIKELY(klass == NULL)) {
+    CHECK(self->IsExceptionPending());
+    return NULL;  // Failure - Indicate to caller to deliver exception
+  }
+  // Perform access check
+  if (UNLIKELY(!referrer->GetDeclaringClass()->CanAccess(klass))) {
+    self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+        "Class %s is inaccessible to method %s",
+        PrettyDescriptor(klass->GetDescriptor()).c_str(),
+        PrettyMethod(referrer, true).c_str());
+  }
   // If we are the <clinit> of this class, just return our storage.
   //
   // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
@@ -791,6 +822,15 @@
   return InitializeStaticStorage(type_idx, referrer, self);
 }
 
+extern "C" Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx,
+                                                           const Method* referrer, Thread* self,
+                                                           Method** sp) {
+  // Called when caller isn't guaranteed to have access to a type and the dex cache may be
+  // unpopulated
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  return InitializeStaticStorageAndVerifyAccess(type_idx, referrer, self);
+}
+
 // TODO: placeholder.  Helper function to resolve virtual method
 void ResolveMethodFromCode(Method* method, uint32_t method_idx) {
     /*
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 90fcf2c..be53b03 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -20,6 +20,8 @@
 extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method** sp);
 void* UnresolvedDirectMethodTrampolineFromCode(int32_t, Method**, Thread*, Runtime::TrampolineType);
 extern Class* InitializeStaticStorage(uint32_t type_idx, const Method* referrer, Thread* self);
+extern Class* InitializeStaticStorageAndVerifyAccess(uint32_t type_idx, const Method* referrer,
+                                                     Thread* self);
 extern Class* InitializeTypeFromCode(uint32_t type_idx, Method* method);
 uint32_t IsAssignableFromCode(const Class* klass, const Class* ref_class);
 void ObjectInitFromCode(Object* o);
@@ -66,6 +68,7 @@
   extern "C" void* art_get_obj_static_from_code(uint32_t, void*);
   extern "C" void* art_initialize_static_storage_from_code(uint32_t, void*);
   extern "C" void* art_initialize_type_from_code(uint32_t, void*);
+  extern "C" void* art_initialize_type_and_verify_access_from_code(uint32_t, void*);
   extern "C" void* art_resolve_string_from_code(void*, uint32_t);
 
   /* Conversions */
diff --git a/src/runtime_support_asm.S b/src/runtime_support_asm.S
index 112a114..ad600ce 100644
--- a/src/runtime_support_asm.S
+++ b/src/runtime_support_asm.S
@@ -296,6 +296,23 @@
     bxne   lr                                  @ return on success
     DELIVER_PENDING_EXCEPTION
 
+    .global art_initialize_type_and_verify_access_from_code
+    .extern artInitializeTypeAndVerifyAccessFromCode
+    /*
+     * Entry from managed code when type_idx needs to be checked for access and dex cache may also
+     * miss
+     */
+art_initialize_type_and_verify_access_from_code:
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME           @ save callee saves in case of GC
+    mov    r2, r9                              @ pass Thread::Current
+    mov    r3, sp                              @ pass SP
+    @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, SP)
+    bl     artInitializeTypeAndVerifyAccessFromCode
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r0, #0                              @ success if result is non-null
+    bxne   lr                                  @ return on success
+    DELIVER_PENDING_EXCEPTION
+
     .global art_find_instance_field_from_code
     .extern artFindInstanceFieldFromCode
     /*
diff --git a/src/thread.cc b/src/thread.cc
index a8c58bf..4f47d22 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -102,6 +102,7 @@
   pHandleFillArrayDataFromCode = art_handle_fill_data_from_code;
   pInitializeStaticStorage = art_initialize_static_storage_from_code;
   pInitializeTypeFromCode = art_initialize_type_from_code;
+  pInitializeTypeAndVerifyAccessFromCode = art_initialize_type_and_verify_access_from_code;
   pInvokeInterfaceTrampoline = art_invoke_interface_trampoline;
   pLockObjectFromCode = art_lock_object_from_code;
   pObjectInit = art_object_init_from_code;
diff --git a/src/thread.h b/src/thread.h
index eec9df4..547fdfa 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -138,6 +138,7 @@
   uint32_t (*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
   void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
   void* (*pInitializeTypeFromCode)(uint32_t, void*);
+  void* (*pInitializeTypeAndVerifyAccessFromCode)(uint32_t, void*);
   void (*pLockObjectFromCode)(void*);
   void (*pObjectInit)(void*);
   void (*pResolveMethodFromCode)(Method*, uint32_t);