Enable AllocWithGrowth and OutOfMemoryError throwing.

Also fix a bug where we weren't correcting the pc for the first stack frame.

Change-Id: Ic4196987eac85eff2f6d14171b19b4f5890b6c4d
diff --git a/src/heap.cc b/src/heap.cc
index eae772f..65bc4c0 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -150,18 +150,22 @@
   live_bitmap_ = NULL;
 }
 
-Object* Heap::AllocObject(Class* klass, size_t num_bytes) {
-  ScopedHeapLock lock;
-  DCHECK(klass == NULL
-         || klass->GetDescriptor() == NULL
-         || (klass->IsClassClass() && num_bytes >= sizeof(Class))
-         || (klass->IsVariableSize() || klass->GetObjectSize() == num_bytes));
-  DCHECK(num_bytes >= sizeof(Object));
-  Object* obj = AllocateLocked(num_bytes);
-  if (obj != NULL) {
-    obj->SetClass(klass);
+Object* Heap::AllocObject(Class* klass, size_t byte_count) {
+  {
+    ScopedHeapLock lock;
+    DCHECK(klass == NULL || klass->GetDescriptor() == NULL ||
+        (klass->IsClassClass() && byte_count >= sizeof(Class)) ||
+        (klass->IsVariableSize() || klass->GetObjectSize() == byte_count));
+    DCHECK_GE(byte_count, sizeof(Object));
+    Object* obj = AllocateLocked(byte_count);
+    if (obj != NULL) {
+      obj->SetClass(klass);
+      return obj;
+    }
   }
-  return obj;
+
+  Thread::Current()->ThrowOutOfMemoryError(klass, byte_count);
+  return NULL;
 }
 
 bool Heap::IsHeapAddress(const Object* obj) {
@@ -340,21 +344,21 @@
     ++Runtime::Current()->GetStats()->gc_for_alloc_count;
     ++Thread::Current()->GetStats()->gc_for_alloc_count;
   }
-  LOG(INFO) << "GC_FOR_ALLOC: TODO: test";
+  LOG(INFO) << "GC_FOR_ALLOC: AllocWithoutGrowth: TODO: test";
   CollectGarbageInternal();
   ptr = space->AllocWithoutGrowth(size);
   if (ptr != NULL) {
     return ptr;
   }
-  UNIMPLEMENTED(FATAL) << "No AllocWithGrowth, use larger -Xms -Xmx";
 
+  LOG(INFO) << "GC_FOR_ALLOC: AllocWithGrowth: TODO: test";
   // Even that didn't work;  this is an exceptional state.
   // Try harder, growing the heap if necessary.
   ptr = space->AllocWithGrowth(size);
   if (ptr != NULL) {
     //size_t new_footprint = dvmHeapSourceGetIdealFootprint();
     size_t new_footprint = space->GetMaxAllowedFootprint();
-    // TODO: may want to grow a little bit more so that the amount of
+    // OLD-TODO: may want to grow a little bit more so that the amount of
     //       free space is equal to the old free space + the
     //       utilization slop for the new allocation.
     LOG(INFO) << "Grow heap (frag case) to " << new_footprint / MB
@@ -368,7 +372,7 @@
   // spec requires that all SoftReferences have been collected and
   // cleared before throwing an OOME.
 
-  // TODO: wait for the finalizers from the previous GC to finish
+  // OLD-TODO: wait for the finalizers from the previous GC to finish
   LOG(INFO) << "Forcing collection of SoftReferences for "
             << size << "-byte allocation";
   CollectGarbageInternal();
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index bc1de52..f9d536b 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -1803,10 +1803,7 @@
     String* s = Decode<String*>(ts, java_string);
     size_t byte_count = s->GetUtfLength();
     char* bytes = new char[byte_count + 1];
-    if (bytes == NULL) {
-      ts.Self()->ThrowOutOfMemoryError();
-      return NULL;
-    }
+    CHECK(bytes != NULL); // bionic aborts anyway.
     const uint16_t* chars = s->GetCharArray()->GetData() + s->GetOffset();
     ConvertUtf16ToModifiedUtf8(bytes, chars, s->GetLength());
     bytes[byte_count] = '\0';
diff --git a/src/object.cc b/src/object.cc
index 342599e..1f64faa 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -1239,11 +1239,6 @@
     DCHECK(array->IsArrayInstance());
     array->SetLength(component_count);
   }
-
-  // TODO: throw OutOfMemoryError. (here or in Heap::AllocObject?)
-  CHECK(array != NULL) << PrettyClass(array_class)
-                       << " component_count=" << component_count
-                       << " component_size=" << component_size;
   return array;
 }
 
diff --git a/src/thread.cc b/src/thread.cc
index a481aa6..16b8b67 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -696,7 +696,8 @@
       exception_(NULL),
       suspend_count_(0),
       class_loader_override_(NULL),
-      long_jump_context_(NULL) {
+      long_jump_context_(NULL),
+      throwing_OOME_(false) {
   CHECK((sizeof(Thread) % 4) == 0) << sizeof(Thread);
 }
 
@@ -967,14 +968,14 @@
 
   while (frame.GetSP() != 0) {
     for ( ; frame.GetMethod() != 0; frame.Next()) {
-      DCHECK(frame.GetMethod()->IsWithinCode(pc));
-      visitor->VisitFrame(frame, pc);
-      pc = frame.GetReturnPC();
       // Move the PC back 2 bytes as a call will frequently terminate the
       // decoding of a particular instruction and we want to make sure we
       // get the Dex PC of the instruction with the call and not the
       // instruction following.
-      pc -= 2;
+      if (pc > 0) { pc -= 2; }
+      DCHECK(frame.GetMethod()->IsWithinCode(pc));
+      visitor->VisitFrame(frame, pc);
+      pc = frame.GetReturnPC();
     }
     if (record == NULL) {
       break;
@@ -992,14 +993,14 @@
 
   if (frame.GetSP() != 0) {
     for ( ; frame.GetMethod() != 0; frame.Next()) {
-      DCHECK(frame.GetMethod()->IsWithinCode(pc));
-      visitor->VisitFrame(frame, pc);
-      pc = frame.GetReturnPC();
       // Move the PC back 2 bytes as a call will frequently terminate the
       // decoding of a particular instruction and we want to make sure we
       // get the Dex PC of the instruction with the call and not the
       // instruction following.
-      pc -= 2;
+      if (pc > 0) { pc -= 2; }
+      DCHECK(frame.GetMethod()->IsWithinCode(pc));
+      visitor->VisitFrame(frame, pc);
+      pc = frame.GetReturnPC();
     }
     if (include_upcall) {
       visitor->VisitFrame(frame, pc);
@@ -1110,8 +1111,15 @@
   env->DeleteLocalRef(exception_class);
 }
 
-void Thread::ThrowOutOfMemoryError() {
-  UNIMPLEMENTED(FATAL);
+void Thread::ThrowOutOfMemoryError(Class* c, size_t byte_count) {
+  if (!throwing_OOME_) {
+    throwing_OOME_ = true;
+    ThrowNewException("Ljava/lang/OutOfMemoryError;", NULL);
+    LOG(ERROR) << "Failed to allocate a " << PrettyDescriptor(c->GetDescriptor()) << " (" << byte_count << " bytes)";
+  } else {
+    UNIMPLEMENTED(FATAL) << "throw one i prepared earlier...";
+  }
+  throwing_OOME_ = false;
 }
 
 class CatchBlockStackVisitor : public Thread::StackVisitor {
diff --git a/src/thread.h b/src/thread.h
index 7bd6855..c898a76 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -285,7 +285,7 @@
   void ThrowNewExceptionV(const char* exception_class_descriptor, const char* fmt, va_list ap);
 
   // This exception is special, because we need to pre-allocate an instance.
-  void ThrowOutOfMemoryError();
+  void ThrowOutOfMemoryError(Class* c, size_t byte_count);
 
   Frame FindExceptionHandler(void* throw_pc, void** handler_pc);
 
@@ -573,6 +573,9 @@
   // Thread local, lazily allocated, long jump context. Used to deliver exceptions.
   Context* long_jump_context_;
 
+  // A boolean telling us whether we're recursively throwing OOME.
+  uint32_t throwing_OOME_;
+
   // TLS key used to retrieve the VM thread object.
   static pthread_key_t pthread_key_self_;