Exception support for CanPutArrayElementFromCode.

Also a unit test.

Change-Id: I6fb4b4180d36ea2d8447a9b4d5cf28cf410960cd
diff --git a/src/object.cc b/src/object.cc
index 64607e8..dac16a8 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -927,18 +927,6 @@
   return false;
 }
 
-void Class::CanPutArrayElementFromCode(const Object* element, const Class* array_class) {
-  DCHECK(array_class != NULL);
-  if (element == NULL) {
-    return;
-  }
-  if (!array_class->GetComponentType()->IsAssignableFrom(element->GetClass())) {
-    LOG(ERROR) << "Can't put a " << PrettyClass(element->GetClass())
-               << " into a " << PrettyClass(array_class);
-    UNIMPLEMENTED(FATAL) << "need to throw ArrayStoreException and unwind stack";
-  }
-}
-
 // Determine whether "this" is assignable from "klazz", where both of these
 // are array classes.
 //
diff --git a/src/object.h b/src/object.h
index 63a2b30..5b4c7ba 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1427,10 +1427,6 @@
     return !IsPrimitive() && GetSuperClass() == NULL;
   }
 
-  // Tests whether a possibly null 'element' can be
-  // assigned into an array of type 'array_class'.
-  static void CanPutArrayElementFromCode(const Object* element, const Class* array_class);
-
   // 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.
diff --git a/src/runtime_support.S b/src/runtime_support.S
index 185cdc2..963e1aa 100644
--- a/src/runtime_support.S
+++ b/src/runtime_support.S
@@ -169,6 +169,31 @@
     mov    r1, sp                             @ pass SP
     b      artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*, SP)
 
+    .global art_can_put_array_element_from_code
+    .extern artCanPutArrayElementFromCode
+    /*
+     * Entry from managed code that calls artCanPutArrayElementFromCode and delivers exception on
+     * failure.
+     */
+art_can_put_array_element_from_code:
+    cmp    r0, #0                             @ return if element == NULL
+    moveq  pc, lr
+    str    sp, [R9, #THREAD_TOP_OF_MANAGED_STACK_OFFSET]    @ record top of stack and pc in case of
+    str    lr, [R9, #THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET] @ walking stack
+    stmdb  sp!, {lr}                          @ Save LR
+    sub    sp, #12                            @ Align stack
+    bl     artCanPutArrayElementFromCode      @ (Object* element, Class* array_class)
+    add    sp, #12
+    ldmia  sp!, {lr}                          @ restore LR
+    cmp    r0, #0                             @ success?
+    moveq  pc, lr                             @ return on success
+                                              @ set up for throwing exception
+    stmdb  sp!, {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr}
+    sub    sp, #16                            @ 4 words of space, bottom word will hold Method*
+    mov    r0, r9                             @ pass Thread::Current
+    mov    r1, sp                             @ pass SP
+    b      artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*, SP)
+
     .global art_initialize_static_storage_from_code
     .extern _ZN3art11ClassLinker31InitializeStaticStorageFromCodeEjPKNS_6MethodE
     /*
diff --git a/src/runtime_support.h b/src/runtime_support.h
index de2a975..9da06c3 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -8,6 +8,7 @@
 
 #if defined(__arm__)
   /* Compiler helpers */
+  extern "C" void art_can_put_array_element_from_code(void*, void*);
   extern "C" void art_check_cast_from_code(void*, void*);
   extern "C" void art_handle_fill_data_from_code(void*, void*);
   extern "C" void* art_initialize_static_storage_from_code(uint32_t, void*);
diff --git a/src/thread.cc b/src/thread.cc
index 232c115..271b67b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -201,7 +201,7 @@
     return Array::AllocFromCode(type_index, method, component_count);
 }
 
-// TODO: placeholder (throw on failure)
+// Check whether it is safe to cast one class to the other, throw exception and return -1 on failure
 extern "C" int artCheckCastFromCode(const Class* a, const Class* b) {
   DCHECK(a->IsClass());
   DCHECK(b->IsClass());
@@ -216,6 +216,24 @@
   }
 }
 
+// Tests whether 'element' can be assigned into an array of type 'array_class'.
+// Returns 0 on success and -1 if an exception is pending.
+extern "C" int artCanPutArrayElementFromCode(const Object* element, const Class* array_class) {
+  DCHECK(array_class != NULL);
+  // element can't be NULL as we catch this is screened in runtime_support
+  Class* element_class = element->GetClass();
+  Class* component_type = array_class->GetComponentType();
+  if (component_type->IsAssignableFrom(element_class)) {
+    return 0;  // Success
+  } else {
+    Thread::Current()->ThrowNewException("Ljava/lang/ArrayStoreException;",
+                                      "Cannot store an object of type %s in to an array of type %s",
+                                         PrettyDescriptor(element_class->GetDescriptor()).c_str(),
+                                         PrettyDescriptor(array_class->GetDescriptor()).c_str());
+    return -1;  // Failure
+  }
+}
+
 extern "C" int artUnlockObjectFromCode(Thread* thread, Object* obj) {
   DCHECK(obj != NULL);  // Assumed to have been checked before entry
   return obj->MonitorExit(thread) ? 0 /* Success */ : -1 /* Failure */;
@@ -357,6 +375,7 @@
   pFmod = fmod;
   pLdivmod = __aeabi_ldivmod;
   pLmul = __aeabi_lmul;
+  pCanPutArrayElementFromCode = art_can_put_array_element_from_code;
   pCheckCastFromCode = art_check_cast_from_code;
   pHandleFillArrayDataFromCode = art_handle_fill_data_from_code;
   pInitializeStaticStorage = art_initialize_static_storage_from_code;
@@ -381,7 +400,6 @@
   pSet64Static = Field::Set64StaticFromCode;
   pGetObjStatic = Field::GetObjStaticFromCode;
   pSetObjStatic = Field::SetObjStaticFromCode;
-  pCanPutArrayElementFromCode = Class::CanPutArrayElementFromCode;
   pInitializeTypeFromCode = InitializeTypeFromCode;
   pResolveMethodFromCode = ResolveMethodFromCode;
   pInstanceofNonTrivialFromCode = Object::InstanceOf;
diff --git a/src/thread.h b/src/thread.h
index 39d984b..53eff33 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -211,7 +211,7 @@
   void (*pSet64Static)(uint32_t, const Method*, uint64_t);
   Object* (*pGetObjStatic)(uint32_t, const Method*);
   void (*pSetObjStatic)(uint32_t, const Method*, Object*);
-  void (*pCanPutArrayElementFromCode)(const Object*, const Class*);
+  void (*pCanPutArrayElementFromCode)(void*, void*);
   bool (*pInstanceofNonTrivialFromCode) (const Object*, const Class*);
   void (*pCheckCastFromCode) (void*, void*);
   Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
diff --git a/test/IntMath/IntMath.java b/test/IntMath/IntMath.java
index e5fa806..b1b7312 100644
--- a/test/IntMath/IntMath.java
+++ b/test/IntMath/IntMath.java
@@ -786,6 +786,34 @@
       return res;
     }
 
+    static void throwArrayStoreException(Object[] array, Object element) {
+      array[0] = element;
+    }
+
+    static int testArrayStoreException() {
+      int res=0;
+      Object[] array = new Number[2];
+      try {
+        throwArrayStoreException(array, null);
+        res += 1;
+      } catch(ArrayStoreException e) {
+        res += 2;
+      }
+      try {
+        throwArrayStoreException(array, Integer.valueOf(1));
+        res += 10;
+      } catch(ArrayStoreException e) {
+        res += 20;
+      }
+      try {
+        throwArrayStoreException(array, "hello MTV-44");
+        res += 100;
+      } catch(ArrayStoreException e) {
+        res += 200;
+      }
+      return res;
+    }
+
     static long recursion_count_;
     static void throwStackOverflow(long l) {
       recursion_count_++;
@@ -966,6 +994,14 @@
             failure = true;
         }
 
+        res = testArrayStoreException();
+        if (res == 211) {
+          System.out.println("testArrayStore PASSED");
+        } else {
+          System.out.println("testArrayStore FAILED: " + res);
+          failure = true;
+        }
+
         lres= testStackOverflow();
         if (lres == 0) {
             System.out.println("testStackOverflow PASSED");