Implement NPE, div by 0 and AIOBE.

Implement runtime support for throwing NPE, divide by 0 and
ArrayIndexOutOfBounds excptions. Add simple unit tests.

Change-Id: I69453c10b6692aa79512bcab1f68c5ab16c8c256
diff --git a/src/asm_support.h b/src/asm_support.h
new file mode 100644
index 0000000..6eda4bf
--- /dev/null
+++ b/src/asm_support.h
@@ -0,0 +1,11 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_ASM_SUPPORT_H_
+#define ART_SRC_ASM_SUPPORT_H_
+
+#if defined(__i386__)
+// Offset of field Thread::self_ verified in InitCpu
+#define THREAD_SELF_OFFSET 0x161
+#endif
+
+#endif  // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/runtime_support.S b/src/runtime_support.S
index 5372742..24883fc 100644
--- a/src/runtime_support.S
+++ b/src/runtime_support.S
@@ -1,3 +1,5 @@
+#include "asm_support.h"
+
 #if defined(__arm__)
 
     .balign 4
@@ -12,9 +14,45 @@
 art_deliver_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 r1, r9
-    mov r2, sp
-    b artDeliverExceptionHelper  @ artThrowExceptionHelper(Throwable*, SP)
+    mov r1, r9                   @ pass Thread::Current
+    mov r2, sp                   @ pass SP
+    b artDeliverExceptionHelper  @ artDeliverExceptionHelper(Throwable*, Thread*, SP)
+
+    .global art_throw_null_pointer_exception_from_code
+    .extern artThrowNullPointerExceptionFromCodeHelper
+    /*
+     * Create NPE and deliver
+     */
+art_throw_null_pointer_exception_from_code:
+    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 artThrowNullPointerExceptionFromCodeHelper @ artThrowNullPointerExceptionFromCodeHelper(Thread*, SP)
+
+    .global art_throw_div_zero_from_code
+    .extern artThrowDivZeroFromCodeHelper
+    /*
+     * Create ArithmeticException and deliver
+     */
+art_throw_div_zero_from_code:
+    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 artThrowDivZeroFromCodeHelper @ artThrowDivZeroFromCodeHelper(Thread*, SP)
+
+    .global art_throw_array_bounds_from_code
+    .extern artThrowArrayBoundsFromCodeHelper
+    /*
+     * Create ArrayIndexOutOfBoundsException and deliver
+     */
+art_throw_array_bounds_from_code:
+    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 r2, r9                   @ pass Thread::Current
+    mov r3, sp                   @ pass SP
+    b artThrowArrayBoundsFromCodeHelper @ artThrowArrayBoundsFromCodeHelper(index, limit, Thread*, SP)
 
     .global art_invoke_interface_trampoline
     .extern artFindInterfaceMethodInCache
@@ -144,10 +182,10 @@
     mov %esp, %ecx
     // Outgoing argument set up
     pushl $0    // Alignment padding
-    pushl %ecx
-    pushl $0    // TODO: pass fs:offsetof(Thread,self_) - for now this is computed in the helper
-    pushl %eax
-    call artDeliverExceptionHelper  // artThrowExceptionHelper(Throwable*, Thread*, SP)
+    pushl %ecx                      // pass SP
+    pushl %fs:THREAD_SELF_OFFSET    // pass fs:offsetof(Thread,self_)
+    pushl %eax                      // pass Throwable*
+    call artDeliverExceptionHelper  // artDeliverExceptionHelper(Throwable*, Thread*, SP)
     int3
 
 #endif
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 1f8da91..d421fcc 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -11,6 +11,9 @@
   extern "C" uint64_t art_shl_long(uint64_t, uint32_t);
   extern "C" uint64_t art_shr_long(uint64_t, uint32_t);
   extern "C" uint64_t art_ushr_long(uint64_t, uint32_t);
+  extern "C" void art_throw_null_pointer_exception_from_code();
+  extern "C" void art_throw_div_zero_from_code();
+  extern "C" void art_throw_array_bounds_from_code(int32_t index, int32_t limit);
   extern "C" void art_invoke_interface_trampoline(void*, void*, void*, void*);
 
   /* Conversions */
diff --git a/src/thread.cc b/src/thread.cc
index 3d68802..6eac92b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -75,9 +75,6 @@
    * and threw a NPE if NULL.  This routine responsible for setting
    * exception_ in thread and delivering the exception.
    */
-#if defined(__i386__)
-  thread = art::Thread::Current();  // TODO: fix passing this in as an argument
-#endif
   // Place a special frame at the TOS that will save all callee saves
   *sp = thread->CalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
@@ -88,6 +85,41 @@
   thread->DeliverException(exception);
 }
 
+// Called by generated call to throw a NPE exception
+extern "C" void artThrowNullPointerExceptionFromCodeHelper(art::Thread* thread,
+                                                           art::Method** sp) {
+  // Place a special frame at the TOS that will save all callee saves
+  *sp = thread->CalleeSaveMethod();
+  thread->SetTopOfStack(sp, 0);
+  thread->ThrowNewException("Ljava/lang/NullPointerException;", "unexpected null reference");
+  art::Throwable* exception = thread->GetException();
+  thread->DeliverException(exception);
+}
+
+// Called by generated call to throw an arithmetic divide by zero exception
+extern "C" void artThrowDivZeroFromCodeHelper(art::Thread* thread,
+                                              art::Method** sp) {
+  // Place a special frame at the TOS that will save all callee saves
+  *sp = thread->CalleeSaveMethod();
+  thread->SetTopOfStack(sp, 0);
+  thread->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero");
+  art::Throwable* exception = thread->GetException();
+  thread->DeliverException(exception);
+}
+
+// Called by generated call to throw an arithmetic divide by zero exception
+extern "C" void artThrowArrayBoundsFromCodeHelper(int index, int limit,
+                                                  art::Thread* thread,
+                                                  art::Method** sp) {
+  // Place a special frame at the TOS that will save all callee saves
+  *sp = thread->CalleeSaveMethod();
+  thread->SetTopOfStack(sp, 0);
+  thread->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+                            "length=%d; index=%d", limit, index);
+  art::Throwable* exception = thread->GetException();
+  thread->DeliverException(exception);
+}
+
 namespace art {
 
 // TODO: placeholder.  Helper function to type
@@ -330,6 +362,9 @@
   pFmod = fmod;
   pLdivmod = __aeabi_ldivmod;
   pLmul = __aeabi_lmul;
+  pThrowNullPointerFromCode = art_throw_null_pointer_exception_from_code;
+  pThrowArrayBoundsFromCode = art_throw_array_bounds_from_code;
+  pThrowDivZeroFromCode = art_throw_div_zero_from_code;
   pInvokeInterfaceTrampoline = art_invoke_interface_trampoline;
 #endif
   pDeliverException = art_deliver_exception;
@@ -357,9 +392,6 @@
   pFindInstanceFieldFromCode = Field::FindInstanceFieldFromCode;
   pCheckSuspendFromCode = CheckSuspendFromCode;
   pStackOverflowFromCode = StackOverflowFromCode;
-  pThrowNullPointerFromCode = ThrowNullPointerFromCode;
-  pThrowArrayBoundsFromCode = ThrowArrayBoundsFromCode;
-  pThrowDivZeroFromCode = ThrowDivZeroFromCode;
   pThrowVerificationErrorFromCode = ThrowVerificationErrorFromCode;
   pThrowNegArraySizeFromCode = ThrowNegArraySizeFromCode;
   pThrowRuntimeExceptionFromCode = ThrowRuntimeExceptionFromCode;
diff --git a/src/thread_x86.cc b/src/thread_x86.cc
index 57eeffd..5aa6d24 100644
--- a/src/thread_x86.cc
+++ b/src/thread_x86.cc
@@ -6,6 +6,7 @@
 #include <sys/syscall.h>
 #include <sys/types.h>
 
+#include "asm_support.h"
 #include "macros.h"
 
 namespace art {
@@ -53,9 +54,10 @@
   // Sanity check reads from FS goes to this Thread*
   Thread* self_check;
   // TODO: use our assembler to generate code
+  CHECK_EQ(THREAD_SELF_OFFSET, OFFSETOF_MEMBER(Thread, self_));
   asm volatile("movl %%fs:(%1), %0"
       : "=r"(self_check)  // output
-      : "r"(OFFSETOF_MEMBER(Thread, self_))  // input
+      : "r"(THREAD_SELF_OFFSET)  // input
       :);  // clobber
   CHECK_EQ(self_check, this);
 }
diff --git a/test/ExceptionTest/ExceptionTest.java b/test/ExceptionTest/ExceptionTest.java
index 973e9e9..edd549e 100644
--- a/test/ExceptionTest/ExceptionTest.java
+++ b/test/ExceptionTest/ExceptionTest.java
@@ -138,6 +138,49 @@
     }
 
 
+    static void throwImplicitAIOBE(int[] array, int index) {
+      array[index] = 0;
+    }
+
+    static int checkAIOBE() {
+      int[] array = new int[10];
+      int res;
+      try {
+        throwImplicitAIOBE(array, 11);
+        res = 123;
+      } catch (NullPointerException npe) {
+        res = 768;
+      } catch (ArrayIndexOutOfBoundsException e) {
+        res = 456;
+      }
+      try {
+        throwImplicitAIOBE(array, -1);
+        res += 123;
+      } catch (NullPointerException npe) {
+        res += 768;
+      } catch (ArrayIndexOutOfBoundsException e) {
+        res += 456;
+      }
+      return res;
+    }
+
+    static int throwImplicitDivZero(int x, int y) {
+      return x / y;
+    }
+
+    static int checkDivZero() {
+      try {
+        throwImplicitDivZero(100, 0);
+        return 123;
+      } catch (NullPointerException npe) {
+        return 768;
+      } catch (ArrayIndexOutOfBoundsException e) {
+        return 987;
+      } catch (ArithmeticException e) {
+        return 456;
+      }
+    }
+
     public static void main(String[] args) {
         boolean failure = false;
         int res;
@@ -150,7 +193,6 @@
             failure = true;
         }
 
-if (false) { // TODO: enable this test when passing
         res = nullCheckTestThrow(1976);
         if (res == 2057) {
             System.out.println("nullCheckTestThrow PASSED");
@@ -158,7 +200,22 @@
             System.out.println("nullCheckTestThrow FAILED: " + res);
             failure = true;
         }
-}
+
+        res = checkAIOBE();
+        if (res == 912) {
+          System.out.println("checkAIOBE PASSED");
+        } else {
+          System.out.println("checkAIOBE FAILED: " + res);
+          failure = true;
+        }
+
+        res = checkDivZero();
+        if (res == 456) {
+          System.out.println("checkDivZero PASSED");
+        } else {
+          System.out.println("checkDivZero FAILED: " + res);
+          failure = true;
+        }
         System.exit(failure ? 1 : 0);
     }
 }