Merge "Fix annotations test 004." into dalvik-dev
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 16b303e..ad140ec 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -83,6 +83,7 @@
   std::string host_prefix;
   const char* Xms = NULL;
   const char* Xmx = NULL;
+  const char* verbose = NULL;
 
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
@@ -116,6 +117,8 @@
       Xms = option.data();
     } else if (option.starts_with("-Xmx")) {
       Xmx = option.data();
+    } else if (option.starts_with("-verbose:")) {
+      verbose = option.data();
     } else {
       fprintf(stderr, "unknown argument %s\n", option.data());
       usage();
@@ -164,6 +167,9 @@
   if (Xmx != NULL) {
     options.push_back(std::make_pair(Xmx, reinterpret_cast<void*>(NULL)));
   }
+  if (verbose != NULL) {
+    options.push_back(std::make_pair(verbose, reinterpret_cast<void*>(NULL)));
+  }
   if (!host_prefix.empty()) {
     options.push_back(std::make_pair("host-prefix", host_prefix.c_str()));
   }
diff --git a/src/indirect_reference_table.cc b/src/indirect_reference_table.cc
index 716c214..5f5541e 100644
--- a/src/indirect_reference_table.cc
+++ b/src/indirect_reference_table.cc
@@ -204,17 +204,17 @@
   return true;
 }
 
-static int LinearScan(IndirectRef iref, int bottomIndex, int topIndex, const Object** table) {
+static int Find(Object* direct_pointer, int bottomIndex, int topIndex, const Object** table) {
   for (int i = bottomIndex; i < topIndex; ++i) {
-    if (table[i] == reinterpret_cast<const Object*>(iref)) {
+    if (table[i] == direct_pointer) {
       return i;
     }
   }
   return -1;
 }
 
-bool IndirectReferenceTable::Contains(IndirectRef iref) const {
-  return LinearScan(iref, 0, segment_state_.parts.topIndex, table_) != -1;
+bool IndirectReferenceTable::ContainsDirectPointer(Object* direct_pointer) const {
+  return Find(direct_pointer, 0, segment_state_.parts.topIndex, table_) != -1;
 }
 
 /*
@@ -249,7 +249,8 @@
     return true;
   }
   if (GetIndirectRefKind(iref) == kSirtOrInvalid || vm->work_around_app_jni_bugs) {
-    idx = LinearScan(iref, bottomIndex, topIndex, table_);
+    Object* direct_pointer = reinterpret_cast<Object*>(iref);
+    idx = Find(direct_pointer, bottomIndex, topIndex, table_);
     if (idx == -1) {
       LOG(WARNING) << "trying to work around app JNI bugs, but didn't find " << iref << " in table!";
       return false;
diff --git a/src/indirect_reference_table.h b/src/indirect_reference_table.h
index ba20d4d..f6cab95 100644
--- a/src/indirect_reference_table.h
+++ b/src/indirect_reference_table.h
@@ -281,7 +281,7 @@
   }
 
   // TODO: remove when we remove work_around_app_jni_bugs support.
-  bool Contains(IndirectRef iref) const;
+  bool ContainsDirectPointer(Object* direct_pointer) const;
 
   /*
    * Remove an existing entry.
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 1d46526..9480644 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -24,6 +24,21 @@
 #include "stringpiece.h"
 #include "thread.h"
 
+static const size_t kMonitorsInitial = 32; // Arbitrary.
+static const size_t kMonitorsMax = 4096; // Arbitrary sanity check.
+
+static const size_t kLocalsInitial = 64; // Arbitrary.
+static const size_t kLocalsMax = 512; // Arbitrary sanity check.
+
+static const size_t kPinTableInitial = 16; // Arbitrary.
+static const size_t kPinTableMax = 1024; // Arbitrary sanity check.
+
+static const size_t kGlobalsInitial = 512; // Arbitrary.
+static const size_t kGlobalsMax = 51200; // Arbitrary sanity check.
+
+static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
+static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check.
+
 namespace art {
 
 /*
@@ -780,22 +795,40 @@
     LOG(FATAL) << "JNI FatalError called: " << msg;
   }
 
-  static jint PushLocalFrame(JNIEnv* env, jint cap) {
+  static jint PushLocalFrame(JNIEnv* env, jint capacity) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(WARNING) << "ignoring PushLocalFrame(" << cap << ")";
+    if (EnsureLocalCapacity(ts, capacity, "PushLocalFrame") != JNI_OK) {
+      return JNI_ERR;
+    }
+    ts.Env()->PushFrame(capacity);
     return JNI_OK;
   }
 
-  static jobject PopLocalFrame(JNIEnv* env, jobject res) {
+  static jobject PopLocalFrame(JNIEnv* env, jobject java_survivor) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(WARNING) << "ignoring PopLocalFrame " << res;
-    return res;
+    Object* survivor = Decode<Object*>(ts, java_survivor);
+    ts.Env()->PopFrame();
+    return AddLocalReference<jobject>(env, survivor);
   }
 
-  static jint EnsureLocalCapacity(JNIEnv* env, jint cap) {
+  static jint EnsureLocalCapacity(JNIEnv* env, jint desired_capacity) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(WARNING) << "ignoring EnsureLocalCapacity(" << cap << ")";
-    return 0;
+    return EnsureLocalCapacity(ts, desired_capacity, "EnsureLocalCapacity");
+  }
+
+  static jint EnsureLocalCapacity(ScopedJniThreadState& ts, jint desired_capacity, const char* caller) {
+    // TODO: we should try to expand the table if necessary.
+    if (desired_capacity < 1 || desired_capacity > static_cast<jint>(kLocalsMax)) {
+      LOG(ERROR) << "Invalid capacity given to " << caller << ": " << desired_capacity;
+      return JNI_ERR;
+    }
+    // TODO: this isn't quite right, since "capacity" includes holes.
+    size_t capacity = ts.Env()->locals.Capacity();
+    bool okay = (static_cast<jint>(kLocalsMax - capacity) >= desired_capacity);
+    if (!okay) {
+      ts.Self()->ThrowOutOfMemoryError(caller);
+    }
+    return okay ? JNI_OK : JNI_ERR;
   }
 
   static jobject NewGlobalRef(JNIEnv* env, jobject obj) {
@@ -2213,7 +2246,10 @@
     IndirectRefKind kind = GetIndirectRefKind(ref);
     switch (kind) {
     case kLocal:
-      return JNILocalRefType;
+      if (ts.Env()->locals.Get(ref) != kInvalidIndirectRefObject) {
+        return JNILocalRefType;
+      }
+      return JNIInvalidRefType;
     case kGlobal:
       return JNIGlobalRefType;
     case kWeakGlobal:
@@ -2231,7 +2267,7 @@
       // If we're handing out direct pointers, check whether it's a direct pointer
       // to a local reference.
       if (Decode<Object*>(ts, java_object) == reinterpret_cast<Object*>(java_object)) {
-        if (ts.Env()->locals.Contains(java_object)) {
+        if (ts.Env()->locals.ContainsDirectPointer(reinterpret_cast<Object*>(java_object))) {
           return JNILocalRefType;
         }
       }
@@ -2477,12 +2513,6 @@
   JNI::GetObjectRefType,
 };
 
-static const size_t kMonitorsInitial = 32; // Arbitrary.
-static const size_t kMonitorsMax = 4096; // Arbitrary sanity check.
-
-static const size_t kLocalsInitial = 64; // Arbitrary.
-static const size_t kLocalsMax = 512; // Arbitrary sanity check.
-
 JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm)
     : self(self),
       vm(vm),
@@ -2510,6 +2540,17 @@
   monitors.Dump();
 }
 
+void JNIEnvExt::PushFrame(int capacity) {
+  stacked_local_ref_cookies.push_back(local_ref_cookie);
+  local_ref_cookie = locals.GetSegmentState();
+}
+
+void JNIEnvExt::PopFrame() {
+  locals.SetSegmentState(local_ref_cookie);
+  local_ref_cookie = stacked_local_ref_cookies.back();
+  stacked_local_ref_cookies.pop_back();
+}
+
 // JNI Invocation interface.
 
 extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, void** p_env, void* vm_args) {
@@ -2609,15 +2650,6 @@
   JII::AttachCurrentThreadAsDaemon
 };
 
-static const size_t kPinTableInitialSize = 16;
-static const size_t kPinTableMaxSize = 1024;
-
-static const size_t kGlobalsInitial = 512; // Arbitrary.
-static const size_t kGlobalsMax = 51200; // Arbitrary sanity check.
-
-static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
-static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check.
-
 JavaVMExt::JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options)
     : runtime(runtime),
       check_jni_abort_hook(NULL),
@@ -2628,7 +2660,7 @@
       trace(options->jni_trace_),
       work_around_app_jni_bugs(false), // TODO: add a way to enable this
       pins_lock("JNI pin table lock"),
-      pin_table("pin table", kPinTableInitialSize, kPinTableMaxSize),
+      pin_table("pin table", kPinTableInitial, kPinTableMax),
       globals_lock("JNI global reference table lock"),
       globals(kGlobalsInitial, kGlobalsMax, kGlobal),
       weak_globals_lock("JNI weak global reference table lock"),
diff --git a/src/jni_internal.h b/src/jni_internal.h
index e715bdf..6f9b755 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -125,6 +125,9 @@
 
   void DumpReferenceTables();
 
+  void PushFrame(int capacity);
+  void PopFrame();
+
   static Offset SegmentStateOffset() {
     return Offset(OFFSETOF_MEMBER(JNIEnvExt, locals) +
                   IndirectReferenceTable::SegmentStateOffset().Int32Value());
@@ -137,12 +140,17 @@
   Thread* const self;
   JavaVMExt* vm;
 
-  // Cookie used when using the local indirect reference table
+  // Cookie used when using the local indirect reference table.
   uint32_t local_ref_cookie;
 
   // JNI local references.
   IndirectReferenceTable locals;
 
+  // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
+  // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
+  // to a native method.
+  std::vector<uint32_t> stacked_local_ref_cookies;
+
   // Frequently-accessed fields cached from JavaVM.
   bool check_jni;
   bool work_around_app_jni_bugs;
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index ddd39b5..59dca3a 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -720,7 +720,7 @@
   EXPECT_TRUE(o != NULL);
   EXPECT_TRUE(o != s);
 
-  // TODO: check that o is a local reference.
+  EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(o));
 }
 
 TEST_F(JniInternalTest, DeleteLocalRef_NULL) {
@@ -746,6 +746,43 @@
   env_->DeleteLocalRef(o);
 }
 
+TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) {
+  jobject original = env_->NewStringUTF("");
+  ASSERT_TRUE(original != NULL);
+
+  jobject outer;
+  jobject inner1, inner2;
+  Object* inner2_direct_pointer;
+  {
+    env_->PushLocalFrame(4);
+    outer = env_->NewLocalRef(original);
+
+    {
+      env_->PushLocalFrame(4);
+      inner1 = env_->NewLocalRef(outer);
+      inner2 = env_->NewStringUTF("survivor");
+      inner2_direct_pointer = Decode<Object*>(env_, inner2);
+      env_->PopLocalFrame(inner2);
+    }
+
+    EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
+    EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(outer));
+    EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+
+    // Our local reference for the survivor is invalid because the survivor
+    // gets a new local reference...
+    EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+    // ...but the survivor should be in the local reference table.
+    EXPECT_TRUE(env_->locals.ContainsDirectPointer(inner2_direct_pointer));
+
+    env_->PopLocalFrame(NULL);
+  }
+  EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
+  EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(outer));
+  EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+  EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+}
+
 TEST_F(JniInternalTest, NewGlobalRef_NULL) {
   EXPECT_TRUE(env_->NewGlobalRef(NULL) == NULL);
 }
diff --git a/src/monitor.cc b/src/monitor.cc
index bc577a4..d428564 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -605,12 +605,13 @@
   DCHECK(self != NULL);
   DCHECK(obj != NULL);
   DCHECK_EQ(LW_SHAPE(*obj->GetRawLockWordAddress()), LW_SHAPE_THIN);
-  DCHECK_EQ(LW_LOCK_OWNER(*obj->GetRawLockWordAddress()), static_cast<int32_t>(self->thin_lock_id_));
+  DCHECK_EQ(LW_LOCK_OWNER(*obj->GetRawLockWordAddress()), static_cast<int32_t>(self->GetThinLockId()));
 
   // Allocate and acquire a new monitor.
   Monitor* m = new Monitor(obj);
   if (is_verbose_) {
-    LOG(INFO) << "monitor: created monitor " << m << " for object " << obj;
+    LOG(INFO) << "monitor: thread " << self->GetThinLockId()
+              << " created monitor " << m << " for object " << obj;
   }
   Runtime::Current()->GetMonitorList()->Add(m);
   m->Lock(self);
@@ -629,11 +630,11 @@
   long sleepDelayNs;
   long minSleepDelayNs = 1000000;  /* 1 millisecond */
   long maxSleepDelayNs = 1000000000;  /* 1 second */
-  uint32_t thin, newThin, threadId;
+  uint32_t thin, newThin;
 
   DCHECK(self != NULL);
   DCHECK(obj != NULL);
-  threadId = self->thin_lock_id_;
+  uint32_t threadId = self->GetThinLockId();
 retry:
   thin = *thinp;
   if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
@@ -669,7 +670,8 @@
       }
     } else {
       if (is_verbose_) {
-        LOG(INFO) << StringPrintf("monitor: (%d) spin on lock %p: %#x (%#x) %#x", threadId, thinp, 0, *thinp, thin);
+        LOG(INFO) << StringPrintf("monitor: thread %d spin on lock %p (a %s) owned by %d",
+            threadId, thinp, PrettyTypeOf(obj).c_str(), LW_LOCK_OWNER(thin));
       }
       // The lock is owned by another thread. Notify the VM that we are about to wait.
       self->monitor_enter_object_ = obj;
@@ -710,7 +712,8 @@
           // The thin lock was inflated by another thread. Let the VM know we are no longer
           // waiting and try again.
           if (is_verbose_) {
-            LOG(INFO) << "monitor: (" << threadId << ") lock " << (void*) thinp << " surprise-fattened";
+            LOG(INFO) << "monitor: thread " << threadId
+                      << " found lock " << (void*) thinp << " surprise-fattened by another thread";
           }
           self->monitor_enter_object_ = NULL;
           self->SetState(oldStatus);
@@ -718,7 +721,7 @@
         }
       }
       if (is_verbose_) {
-        LOG(INFO) << StringPrintf("monitor: (%d) spin on lock done %p: %#x (%#x) %#x", threadId, thinp, 0, *thinp, thin);
+        LOG(INFO) << StringPrintf("monitor: thread %d spin on lock %p done", threadId, thinp);
       }
       // We have acquired the thin lock. Let the VM know that we are no longer waiting.
       self->monitor_enter_object_ = NULL;
@@ -726,13 +729,14 @@
       // Fatten the lock.
       Inflate(self, obj);
       if (is_verbose_) {
-        LOG(INFO) << StringPrintf("monitor: (%d) lock %p fattened", threadId, thinp);
+        LOG(INFO) << StringPrintf("monitor: thread %d fattened lock %p", threadId, thinp);
       }
     }
   } else {
     // The lock is a fat lock.
     if (is_verbose_) {
-      LOG(INFO) << StringPrintf("monitor: (%d) locking fat lock %p (%p) %p on a %s", threadId, thinp, LW_MONITOR(*thinp), (void*)*thinp, PrettyTypeOf(obj).c_str());
+      LOG(INFO) << StringPrintf("monitor: thread %d locking fat lock %p (%p) %p on a %s",
+          threadId, thinp, LW_MONITOR(*thinp), (void*)*thinp, PrettyTypeOf(obj).c_str());
     }
     DCHECK(LW_MONITOR(*thinp) != NULL);
     LW_MONITOR(*thinp)->Lock(self);
@@ -756,7 +760,7 @@
      * The lock is thin.  We must ensure that the lock is owned
      * by the given thread before unlocking it.
      */
-    if (LW_LOCK_OWNER(thin) == self->thin_lock_id_) {
+    if (LW_LOCK_OWNER(thin) == self->GetThinLockId()) {
       /*
        * We are the lock owner.  It is safe to update the lock
        * without CAS as lock ownership guards the lock itself.
@@ -808,7 +812,7 @@
   uint32_t thin = *thinp;
   if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
     // Make sure that 'self' holds the lock.
-    if (LW_LOCK_OWNER(thin) != self->thin_lock_id_) {
+    if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) {
       ThrowIllegalMonitorStateException("object not locked by thread before wait()");
       return;
     }
@@ -820,7 +824,7 @@
      */
     Inflate(self, obj);
     if (is_verbose_) {
-      LOG(INFO) << StringPrintf("monitor: (%d) lock %p fattened by wait()", self->thin_lock_id_, thinp);
+      LOG(INFO) << StringPrintf("monitor: thread %d fattened lock %p by wait()", self->GetThinLockId(), thinp);
     }
   }
   LW_MONITOR(*thinp)->Wait(self, ms, ns, interruptShouldThrow);
@@ -833,7 +837,7 @@
   // waiting on an object forces lock fattening.
   if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
     // Make sure that 'self' holds the lock.
-    if (LW_LOCK_OWNER(thin) != self->thin_lock_id_) {
+    if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) {
       ThrowIllegalMonitorStateException("object not locked by thread before notify()");
       return;
     }
@@ -851,7 +855,7 @@
   // waiting on an object forces lock fattening.
   if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
     // Make sure that 'self' holds the lock.
-    if (LW_LOCK_OWNER(thin) != self->thin_lock_id_) {
+    if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) {
       ThrowIllegalMonitorStateException("object not locked by thread before notifyAll()");
       return;
     }
diff --git a/src/thread.cc b/src/thread.cc
index e1a8841..d68d1db 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1176,12 +1176,17 @@
 }
 
 void Thread::ThrowOutOfMemoryError(Class* c, size_t byte_count) {
-  LOG(ERROR) << "Failed to allocate a " << byte_count << "-byte "
-             << PrettyDescriptor(c->GetDescriptor())
-             << (throwing_OutOfMemoryError_ ? " (recursive case)" : "");
+  std::string msg(StringPrintf("Failed to allocate a %zd-byte %s", byte_count,
+      PrettyDescriptor(c->GetDescriptor()).c_str()));
+  ThrowOutOfMemoryError(msg.c_str());
+}
+
+void Thread::ThrowOutOfMemoryError(const char* msg) {
+  LOG(ERROR) << StringPrintf("Throwing OutOfMemoryError \"%s\"%s",
+      msg, (throwing_OutOfMemoryError_ ? " (recursive case)" : ""));
   if (!throwing_OutOfMemoryError_) {
     throwing_OutOfMemoryError_ = true;
-    ThrowNewException("Ljava/lang/OutOfMemoryError;", NULL);
+    ThrowNewException("Ljava/lang/OutOfMemoryError;", msg);
   } else {
     SetException(pre_allocated_OutOfMemoryError_);
   }
diff --git a/src/thread.h b/src/thread.h
index 3a8b2a8..a7097ce 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -283,7 +283,8 @@
 
   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.
+  // OutOfMemoryError is special, because we need to pre-allocate an instance.
+  void ThrowOutOfMemoryError(const char* msg);
   void ThrowOutOfMemoryError(Class* c, size_t byte_count);
 
   Frame FindExceptionHandler(void* throw_pc, void** handler_pc);