Handler-ize InstrumentationListener and related code

We are going to be making instrumentation listeners that can cause
suspensions during their running. We explicitly handler-ize all the
instrumentation listener functions in order to ensure this is safe.

Bug: 34414073
Test: ./test.py --host --ntrace --trace -j40
Change-Id: Ic719080d0991b104d41b7757df8d1f332c72cd04
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index d1afcb8..07ca35f 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -36,7 +36,9 @@
 #include "mirror/string.h"
 #include "oat.h"
 #include "obj_ptr-inl.h"
+#include "primitive.h"
 #include "quick/quick_method_frame_info.h"
+#include "read_barrier-inl.h"
 #include "runtime-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
@@ -340,6 +342,10 @@
   return dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_));
 }
 
+inline Primitive::Type ArtMethod::GetReturnTypePrimitive() {
+  return Primitive::GetType(GetReturnTypeDescriptor()[0]);
+}
+
 inline const char* ArtMethod::GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx) {
   DCHECK(!IsProxyMethod());
   const DexFile* dex_file = GetDexFile();
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d8dfdd7..3a8d279 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -27,6 +27,7 @@
 #include "modifiers.h"
 #include "obj_ptr.h"
 #include "offsets.h"
+#include "primitive.h"
 #include "read_barrier_option.h"
 
 namespace art {
@@ -569,6 +570,8 @@
 
   const char* GetReturnTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_);
 
+  ALWAYS_INLINE Primitive::Type GetReturnTypePrimitive() REQUIRES_SHARED(Locks::mutator_lock_);
+
   const char* GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index cfa56a5..7e70b75 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -149,7 +149,9 @@
   DebugInstrumentationListener() {}
   virtual ~DebugInstrumentationListener() {}
 
-  void MethodEntered(Thread* thread, mirror::Object* this_object, ArtMethod* method,
+  void MethodEntered(Thread* thread,
+                     Handle<mirror::Object> this_object,
+                     ArtMethod* method,
                      uint32_t dex_pc)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     if (method->IsNative()) {
@@ -171,12 +173,15 @@
       // also group it with other events for this location like BREAKPOINT or SINGLE_STEP.
       thread->SetDebugMethodEntry();
     } else {
-      Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr);
+      Dbg::UpdateDebugger(thread, this_object.Get(), method, 0, Dbg::kMethodEntry, nullptr);
     }
   }
 
-  void MethodExited(Thread* thread, mirror::Object* this_object, ArtMethod* method,
-                    uint32_t dex_pc, const JValue& return_value)
+  void MethodExited(Thread* thread,
+                    Handle<mirror::Object> this_object,
+                    ArtMethod* method,
+                    uint32_t dex_pc,
+                    const JValue& return_value)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     if (method->IsNative()) {
       // TODO: post location events is a suspension point and native method entry stubs aren't.
@@ -189,18 +194,22 @@
       events |= Dbg::kMethodEntry;
       thread->ClearDebugMethodEntry();
     }
-    Dbg::UpdateDebugger(thread, this_object, method, dex_pc, events, &return_value);
+    Dbg::UpdateDebugger(thread, this_object.Get(), method, dex_pc, events, &return_value);
   }
 
-  void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object ATTRIBUTE_UNUSED,
-                    ArtMethod* method, uint32_t dex_pc)
+  void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED,
+                    Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+                    ArtMethod* method,
+                    uint32_t dex_pc)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     // We're not recorded to listen to this kind of event, so complain.
     LOG(ERROR) << "Unexpected method unwind event in debugger " << ArtMethod::PrettyMethod(method)
                << " " << dex_pc;
   }
 
-  void DexPcMoved(Thread* thread, mirror::Object* this_object, ArtMethod* method,
+  void DexPcMoved(Thread* thread,
+                  Handle<mirror::Object> this_object,
+                  ArtMethod* method,
                   uint32_t new_dex_pc)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     if (IsListeningToMethodExit() && IsReturn(method, new_dex_pc)) {
@@ -217,26 +226,33 @@
         events = Dbg::kMethodEntry;
         thread->ClearDebugMethodEntry();
       }
-      Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, events, nullptr);
+      Dbg::UpdateDebugger(thread, this_object.Get(), method, new_dex_pc, events, nullptr);
     }
   }
 
-  void FieldRead(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object,
-                 ArtMethod* method, uint32_t dex_pc, ArtField* field)
+  void FieldRead(Thread* thread ATTRIBUTE_UNUSED,
+                 Handle<mirror::Object> this_object,
+                 ArtMethod* method,
+                 uint32_t dex_pc,
+                 ArtField* field)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
-    Dbg::PostFieldAccessEvent(method, dex_pc, this_object, field);
+    Dbg::PostFieldAccessEvent(method, dex_pc, this_object.Get(), field);
   }
 
-  void FieldWritten(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object,
-                    ArtMethod* method, uint32_t dex_pc, ArtField* field,
+  void FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
+                    Handle<mirror::Object> this_object,
+                    ArtMethod* method,
+                    uint32_t dex_pc,
+                    ArtField* field,
                     const JValue& field_value)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
-    Dbg::PostFieldModificationEvent(method, dex_pc, this_object, field, &field_value);
+    Dbg::PostFieldModificationEvent(method, dex_pc, this_object.Get(), field, &field_value);
   }
 
-  void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED, mirror::Throwable* exception_object)
+  void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED,
+                       Handle<mirror::Throwable> exception_object)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
-    Dbg::PostException(exception_object);
+    Dbg::PostException(exception_object.Get());
   }
 
   // We only care about branches in the Jit.
@@ -248,10 +264,10 @@
 
   // We only care about invokes in the Jit.
   void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED,
-                                mirror::Object*,
+                                Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                                 ArtMethod* method,
                                 uint32_t dex_pc,
-                                ArtMethod*)
+                                ArtMethod* target ATTRIBUTE_UNUSED)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     LOG(ERROR) << "Unexpected invoke event in debugger " << ArtMethod::PrettyMethod(method)
                << " " << dex_pc;
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index d862ff2..540b2e7 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -20,6 +20,7 @@
 
 #include "arch/context.h"
 #include "art_method-inl.h"
+#include "art_field-inl.h"
 #include "atomic.h"
 #include "class_linker.h"
 #include "debugger.h"
@@ -31,6 +32,7 @@
 #include "interpreter/interpreter.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
+#include "jvalue-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
 #include "mirror/object_array-inl.h"
@@ -45,6 +47,30 @@
 
 constexpr bool kVerboseInstrumentation = false;
 
+void InstrumentationListener::MethodExited(Thread* thread,
+                                           Handle<mirror::Object> this_object,
+                                           ArtMethod* method,
+                                           uint32_t dex_pc,
+                                           Handle<mirror::Object> return_value) {
+  DCHECK_EQ(method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetReturnTypePrimitive(),
+            Primitive::kPrimNot);
+  JValue v;
+  v.SetL(return_value.Get());
+  MethodExited(thread, this_object, method, dex_pc, v);
+}
+
+void InstrumentationListener::FieldWritten(Thread* thread,
+                                           Handle<mirror::Object> this_object,
+                                           ArtMethod* method,
+                                           uint32_t dex_pc,
+                                           ArtField* field,
+                                           Handle<mirror::Object> field_value) {
+  DCHECK(!field->IsPrimitiveType());
+  JValue v;
+  v.SetL(field_value.Get());
+  FieldWritten(thread, this_object, method, dex_pc, field, v);
+}
+
 // Instrumentation works on non-inlined frames by updating returned PCs
 // of compiled frames.
 static constexpr StackVisitor::StackWalkKind kInstrumentationStackWalk =
@@ -916,48 +942,75 @@
   return class_linker->GetQuickOatCodeFor(method);
 }
 
-void Instrumentation::MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
+void Instrumentation::MethodEnterEventImpl(Thread* thread,
+                                           ObjPtr<mirror::Object> this_object,
                                            ArtMethod* method,
                                            uint32_t dex_pc) const {
   if (HasMethodEntryListeners()) {
+    Thread* self = Thread::Current();
+    StackHandleScope<1> hs(self);
+    Handle<mirror::Object> thiz(hs.NewHandle(this_object));
     for (InstrumentationListener* listener : method_entry_listeners_) {
       if (listener != nullptr) {
-        listener->MethodEntered(thread, this_object, method, dex_pc);
+        listener->MethodEntered(thread, thiz, method, dex_pc);
       }
     }
   }
 }
 
-void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_object,
+void Instrumentation::MethodExitEventImpl(Thread* thread,
+                                          ObjPtr<mirror::Object> this_object,
                                           ArtMethod* method,
-                                          uint32_t dex_pc, const JValue& return_value) const {
+                                          uint32_t dex_pc,
+                                          const JValue& return_value) const {
   if (HasMethodExitListeners()) {
-    for (InstrumentationListener* listener : method_exit_listeners_) {
-      if (listener != nullptr) {
-        listener->MethodExited(thread, this_object, method, dex_pc, return_value);
+    Thread* self = Thread::Current();
+    StackHandleScope<2> hs(self);
+    Handle<mirror::Object> thiz(hs.NewHandle(this_object));
+    if (method->GetInterfaceMethodIfProxy(kRuntimePointerSize)
+              ->GetReturnTypePrimitive() != Primitive::kPrimNot) {
+      for (InstrumentationListener* listener : method_exit_listeners_) {
+        if (listener != nullptr) {
+          listener->MethodExited(thread, thiz, method, dex_pc, return_value);
+        }
+      }
+    } else {
+      Handle<mirror::Object> ret(hs.NewHandle(return_value.GetL()));
+      for (InstrumentationListener* listener : method_exit_listeners_) {
+        if (listener != nullptr) {
+          listener->MethodExited(thread, thiz, method, dex_pc, ret);
+        }
       }
     }
   }
 }
 
-void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_object,
+void Instrumentation::MethodUnwindEvent(Thread* thread,
+                                        mirror::Object* this_object,
                                         ArtMethod* method,
                                         uint32_t dex_pc) const {
   if (HasMethodUnwindListeners()) {
+    Thread* self = Thread::Current();
+    StackHandleScope<1> hs(self);
+    Handle<mirror::Object> thiz(hs.NewHandle(this_object));
     for (InstrumentationListener* listener : method_unwind_listeners_) {
       if (listener != nullptr) {
-        listener->MethodUnwind(thread, this_object, method, dex_pc);
+        listener->MethodUnwind(thread, thiz, method, dex_pc);
       }
     }
   }
 }
 
-void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
+void Instrumentation::DexPcMovedEventImpl(Thread* thread,
+                                          ObjPtr<mirror::Object> this_object,
                                           ArtMethod* method,
                                           uint32_t dex_pc) const {
+  Thread* self = Thread::Current();
+  StackHandleScope<1> hs(self);
+  Handle<mirror::Object> thiz(hs.NewHandle(this_object));
   for (InstrumentationListener* listener : dex_pc_listeners_) {
     if (listener != nullptr) {
-      listener->DexPcMoved(thread, this_object, method, dex_pc);
+      listener->DexPcMoved(thread, thiz, method, dex_pc);
     }
   }
 }
@@ -974,36 +1027,56 @@
 }
 
 void Instrumentation::InvokeVirtualOrInterfaceImpl(Thread* thread,
-                                                   mirror::Object* this_object,
+                                                   ObjPtr<mirror::Object> this_object,
                                                    ArtMethod* caller,
                                                    uint32_t dex_pc,
                                                    ArtMethod* callee) const {
-  // We cannot have thread suspension since that would cause the this_object parameter to
-  // potentially become a dangling pointer. An alternative could be to put it in a handle instead.
-  ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+  Thread* self = Thread::Current();
+  StackHandleScope<1> hs(self);
+  Handle<mirror::Object> thiz(hs.NewHandle(this_object));
   for (InstrumentationListener* listener : invoke_virtual_or_interface_listeners_) {
     if (listener != nullptr) {
-      listener->InvokeVirtualOrInterface(thread, this_object, caller, dex_pc, callee);
+      listener->InvokeVirtualOrInterface(thread, thiz, caller, dex_pc, callee);
     }
   }
 }
 
-void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
-                                         ArtMethod* method, uint32_t dex_pc,
+void Instrumentation::FieldReadEventImpl(Thread* thread,
+                                         ObjPtr<mirror::Object> this_object,
+                                         ArtMethod* method,
+                                         uint32_t dex_pc,
                                          ArtField* field) const {
+  Thread* self = Thread::Current();
+  StackHandleScope<1> hs(self);
+  Handle<mirror::Object> thiz(hs.NewHandle(this_object));
   for (InstrumentationListener* listener : field_read_listeners_) {
     if (listener != nullptr) {
-      listener->FieldRead(thread, this_object, method, dex_pc, field);
+      listener->FieldRead(thread, thiz, method, dex_pc, field);
     }
   }
 }
 
-void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_object,
-                                         ArtMethod* method, uint32_t dex_pc,
-                                         ArtField* field, const JValue& field_value) const {
-  for (InstrumentationListener* listener : field_write_listeners_) {
-    if (listener != nullptr) {
-      listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value);
+void Instrumentation::FieldWriteEventImpl(Thread* thread,
+                                          ObjPtr<mirror::Object> this_object,
+                                          ArtMethod* method,
+                                          uint32_t dex_pc,
+                                          ArtField* field,
+                                          const JValue& field_value) const {
+  Thread* self = Thread::Current();
+  StackHandleScope<2> hs(self);
+  Handle<mirror::Object> thiz(hs.NewHandle(this_object));
+  if (field->IsPrimitiveType()) {
+    for (InstrumentationListener* listener : field_write_listeners_) {
+      if (listener != nullptr) {
+        listener->FieldWritten(thread, thiz, method, dex_pc, field, field_value);
+      }
+    }
+  } else {
+    Handle<mirror::Object> val(hs.NewHandle(field_value.GetL()));
+    for (InstrumentationListener* listener : field_write_listeners_) {
+      if (listener != nullptr) {
+        listener->FieldWritten(thread, thiz, method, dex_pc, field, val);
+      }
     }
   }
 }
@@ -1018,7 +1091,7 @@
     thread->ClearException();
     for (InstrumentationListener* listener : exception_caught_listeners_) {
       if (listener != nullptr) {
-        listener->ExceptionCaught(thread, h_exception.Get());
+        listener->ExceptionCaught(thread, h_exception);
       }
     }
     thread->SetException(h_exception.Get());
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 01071a5..363985f 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -36,6 +36,7 @@
 }  // namespace mirror
 class ArtField;
 class ArtMethod;
+template <typename T> class Handle;
 union JValue;
 class Thread;
 
@@ -62,37 +63,70 @@
   virtual ~InstrumentationListener() {}
 
   // Call-back for when a method is entered.
-  virtual void MethodEntered(Thread* thread, mirror::Object* this_object,
+  virtual void MethodEntered(Thread* thread,
+                             Handle<mirror::Object> this_object,
                              ArtMethod* method,
                              uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 
-  // Call-back for when a method is exited.
-  virtual void MethodExited(Thread* thread, mirror::Object* this_object,
-                            ArtMethod* method, uint32_t dex_pc,
+  virtual void MethodExited(Thread* thread,
+                            Handle<mirror::Object> this_object,
+                            ArtMethod* method,
+                            uint32_t dex_pc,
+                            Handle<mirror::Object> return_value)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Call-back for when a method is exited. The implementor should either handler-ize the return
+  // value (if appropriate) or use the alternate MethodExited callback instead if they need to
+  // go through a suspend point.
+  virtual void MethodExited(Thread* thread,
+                            Handle<mirror::Object> this_object,
+                            ArtMethod* method,
+                            uint32_t dex_pc,
                             const JValue& return_value)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 
   // Call-back for when a method is popped due to an exception throw. A method will either cause a
   // MethodExited call-back or a MethodUnwind call-back when its activation is removed.
-  virtual void MethodUnwind(Thread* thread, mirror::Object* this_object,
-                            ArtMethod* method, uint32_t dex_pc)
+  virtual void MethodUnwind(Thread* thread,
+                            Handle<mirror::Object> this_object,
+                            ArtMethod* method,
+                            uint32_t dex_pc)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 
   // Call-back for when the dex pc moves in a method.
-  virtual void DexPcMoved(Thread* thread, mirror::Object* this_object,
-                          ArtMethod* method, uint32_t new_dex_pc)
+  virtual void DexPcMoved(Thread* thread,
+                          Handle<mirror::Object> this_object,
+                          ArtMethod* method,
+                          uint32_t new_dex_pc)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 
   // Call-back for when we read from a field.
-  virtual void FieldRead(Thread* thread, mirror::Object* this_object, ArtMethod* method,
-                         uint32_t dex_pc, ArtField* field) = 0;
+  virtual void FieldRead(Thread* thread,
+                         Handle<mirror::Object> this_object,
+                         ArtMethod* method,
+                         uint32_t dex_pc,
+                         ArtField* field) = 0;
+
+  virtual void FieldWritten(Thread* thread,
+                            Handle<mirror::Object> this_object,
+                            ArtMethod* method,
+                            uint32_t dex_pc,
+                            ArtField* field,
+                            Handle<mirror::Object> field_value)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Call-back for when we write into a field.
-  virtual void FieldWritten(Thread* thread, mirror::Object* this_object, ArtMethod* method,
-                            uint32_t dex_pc, ArtField* field, const JValue& field_value) = 0;
+  virtual void FieldWritten(Thread* thread,
+                            Handle<mirror::Object> this_object,
+                            ArtMethod* method,
+                            uint32_t dex_pc,
+                            ArtField* field,
+                            const JValue& field_value)
+      REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 
   // Call-back when an exception is caught.
-  virtual void ExceptionCaught(Thread* thread, mirror::Throwable* exception_object)
+  virtual void ExceptionCaught(Thread* thread,
+                               Handle<mirror::Throwable> exception_object)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 
   // Call-back for when we execute a branch.
@@ -104,11 +138,10 @@
 
   // Call-back for when we get an invokevirtual or an invokeinterface.
   virtual void InvokeVirtualOrInterface(Thread* thread,
-                                        mirror::Object* this_object,
+                                        Handle<mirror::Object> this_object,
                                         ArtMethod* caller,
                                         uint32_t dex_pc,
                                         ArtMethod* callee)
-      REQUIRES(Roles::uninterruptible_)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 };
 
@@ -323,8 +356,10 @@
   }
 
   // Inform listeners that a method has been exited.
-  void MethodExitEvent(Thread* thread, mirror::Object* this_object,
-                       ArtMethod* method, uint32_t dex_pc,
+  void MethodExitEvent(Thread* thread,
+                       mirror::Object* this_object,
+                       ArtMethod* method,
+                       uint32_t dex_pc,
                        const JValue& return_value) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
     if (UNLIKELY(HasMethodExitListeners())) {
@@ -465,31 +500,42 @@
   // exclusive access to mutator lock which you can't get if the runtime isn't started.
   void SetEntrypointsInstrumented(bool instrumented) NO_THREAD_SAFETY_ANALYSIS;
 
-  void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
-                            ArtMethod* method, uint32_t dex_pc) const
+  void MethodEnterEventImpl(Thread* thread,
+                            ObjPtr<mirror::Object> this_object,
+                            ArtMethod* method,
+                            uint32_t dex_pc) const
       REQUIRES_SHARED(Locks::mutator_lock_);
-  void MethodExitEventImpl(Thread* thread, mirror::Object* this_object,
+  void MethodExitEventImpl(Thread* thread,
+                           ObjPtr<mirror::Object> this_object,
                            ArtMethod* method,
-                           uint32_t dex_pc, const JValue& return_value) const
+                           uint32_t dex_pc,
+                           const JValue& return_value) const
       REQUIRES_SHARED(Locks::mutator_lock_);
-  void DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
-                           ArtMethod* method, uint32_t dex_pc) const
+  void DexPcMovedEventImpl(Thread* thread,
+                           ObjPtr<mirror::Object> this_object,
+                           ArtMethod* method,
+                           uint32_t dex_pc) const
       REQUIRES_SHARED(Locks::mutator_lock_);
   void BranchImpl(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t offset) const
       REQUIRES_SHARED(Locks::mutator_lock_);
   void InvokeVirtualOrInterfaceImpl(Thread* thread,
-                                    mirror::Object* this_object,
+                                    ObjPtr<mirror::Object> this_object,
                                     ArtMethod* caller,
                                     uint32_t dex_pc,
                                     ArtMethod* callee) const
       REQUIRES_SHARED(Locks::mutator_lock_);
-  void FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
-                           ArtMethod* method, uint32_t dex_pc,
-                           ArtField* field) const
+  void FieldReadEventImpl(Thread* thread,
+                          ObjPtr<mirror::Object> this_object,
+                          ArtMethod* method,
+                          uint32_t dex_pc,
+                          ArtField* field) const
       REQUIRES_SHARED(Locks::mutator_lock_);
-  void FieldWriteEventImpl(Thread* thread, mirror::Object* this_object,
-                           ArtMethod* method, uint32_t dex_pc,
-                           ArtField* field, const JValue& field_value) const
+  void FieldWriteEventImpl(Thread* thread,
+                           ObjPtr<mirror::Object> this_object,
+                           ArtMethod* method,
+                           uint32_t dex_pc,
+                           ArtField* field,
+                           const JValue& field_value) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Read barrier-aware utility functions for accessing deoptimized_methods_
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 9926ee7..2a601c9 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -23,11 +23,13 @@
 #include "dex_file.h"
 #include "gc/scoped_gc_critical_section.h"
 #include "handle_scope-inl.h"
+#include "jni_internal.h"
 #include "jvalue.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread_list.h"
-#include "thread-current-inl.h"
+#include "thread-inl.h"
+#include "well_known_classes.h"
 
 namespace art {
 namespace instrumentation {
@@ -35,16 +37,22 @@
 class TestInstrumentationListener FINAL : public instrumentation::InstrumentationListener {
  public:
   TestInstrumentationListener()
-    : received_method_enter_event(false), received_method_exit_event(false),
-      received_method_unwind_event(false), received_dex_pc_moved_event(false),
-      received_field_read_event(false), received_field_written_event(false),
-      received_exception_caught_event(false), received_branch_event(false),
+    : received_method_enter_event(false),
+      received_method_exit_event(false),
+      received_method_exit_object_event(false),
+      received_method_unwind_event(false),
+      received_dex_pc_moved_event(false),
+      received_field_read_event(false),
+      received_field_written_event(false),
+      received_field_written_object_event(false),
+      received_exception_caught_event(false),
+      received_branch_event(false),
       received_invoke_virtual_or_interface_event(false) {}
 
   virtual ~TestInstrumentationListener() {}
 
   void MethodEntered(Thread* thread ATTRIBUTE_UNUSED,
-                     mirror::Object* this_object ATTRIBUTE_UNUSED,
+                     Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                      ArtMethod* method ATTRIBUTE_UNUSED,
                      uint32_t dex_pc ATTRIBUTE_UNUSED)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -52,7 +60,16 @@
   }
 
   void MethodExited(Thread* thread ATTRIBUTE_UNUSED,
-                    mirror::Object* this_object ATTRIBUTE_UNUSED,
+                    Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+                    ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED,
+                    Handle<mirror::Object> return_value ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+    received_method_exit_object_event = true;
+  }
+
+  void MethodExited(Thread* thread ATTRIBUTE_UNUSED,
+                    Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                     ArtMethod* method ATTRIBUTE_UNUSED,
                     uint32_t dex_pc ATTRIBUTE_UNUSED,
                     const JValue& return_value ATTRIBUTE_UNUSED)
@@ -61,7 +78,7 @@
   }
 
   void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED,
-                    mirror::Object* this_object ATTRIBUTE_UNUSED,
+                    Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                     ArtMethod* method ATTRIBUTE_UNUSED,
                     uint32_t dex_pc ATTRIBUTE_UNUSED)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -69,7 +86,7 @@
   }
 
   void DexPcMoved(Thread* thread ATTRIBUTE_UNUSED,
-                  mirror::Object* this_object ATTRIBUTE_UNUSED,
+                  Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                   ArtMethod* method ATTRIBUTE_UNUSED,
                   uint32_t new_dex_pc ATTRIBUTE_UNUSED)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -77,7 +94,7 @@
   }
 
   void FieldRead(Thread* thread ATTRIBUTE_UNUSED,
-                 mirror::Object* this_object ATTRIBUTE_UNUSED,
+                 Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                  ArtMethod* method ATTRIBUTE_UNUSED,
                  uint32_t dex_pc ATTRIBUTE_UNUSED,
                  ArtField* field ATTRIBUTE_UNUSED)
@@ -86,7 +103,17 @@
   }
 
   void FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
-                    mirror::Object* this_object ATTRIBUTE_UNUSED,
+                    Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+                    ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED,
+                    ArtField* field ATTRIBUTE_UNUSED,
+                    Handle<mirror::Object> field_value ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+    received_field_written_object_event = true;
+  }
+
+  void FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
+                    Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                     ArtMethod* method ATTRIBUTE_UNUSED,
                     uint32_t dex_pc ATTRIBUTE_UNUSED,
                     ArtField* field ATTRIBUTE_UNUSED,
@@ -96,7 +123,7 @@
   }
 
   void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED,
-                       mirror::Throwable* exception_object ATTRIBUTE_UNUSED)
+                       Handle<mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     received_exception_caught_event = true;
   }
@@ -110,7 +137,7 @@
   }
 
   void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED,
-                                mirror::Object* this_object ATTRIBUTE_UNUSED,
+                                Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                                 ArtMethod* caller ATTRIBUTE_UNUSED,
                                 uint32_t dex_pc ATTRIBUTE_UNUSED,
                                 ArtMethod* callee ATTRIBUTE_UNUSED)
@@ -121,10 +148,12 @@
   void Reset() {
     received_method_enter_event = false;
     received_method_exit_event = false;
+    received_method_exit_object_event = false;
     received_method_unwind_event = false;
     received_dex_pc_moved_event = false;
     received_field_read_event = false;
     received_field_written_event = false;
+    received_field_written_object_event = false;
     received_exception_caught_event = false;
     received_branch_event = false;
     received_invoke_virtual_or_interface_event = false;
@@ -132,10 +161,12 @@
 
   bool received_method_enter_event;
   bool received_method_exit_event;
+  bool received_method_exit_object_event;
   bool received_method_unwind_event;
   bool received_dex_pc_moved_event;
   bool received_field_read_event;
   bool received_field_written_event;
+  bool received_field_written_object_event;
   bool received_exception_caught_event;
   bool received_branch_event;
   bool received_invoke_virtual_or_interface_event;
@@ -171,6 +202,13 @@
   }
 
   void TestEvent(uint32_t instrumentation_event) {
+    TestEvent(instrumentation_event, nullptr, nullptr, false);
+  }
+
+  void TestEvent(uint32_t instrumentation_event,
+                 ArtMethod* event_method,
+                 ArtField* event_field,
+                 bool with_object) {
     ScopedObjectAccess soa(Thread::Current());
     instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
     TestInstrumentationListener listener;
@@ -180,15 +218,20 @@
       instr->AddListener(&listener, instrumentation_event);
     }
 
-    ArtMethod* const event_method = nullptr;
     mirror::Object* const event_obj = nullptr;
     const uint32_t event_dex_pc = 0;
 
     // Check the listener is registered and is notified of the event.
     EXPECT_TRUE(HasEventListener(instr, instrumentation_event));
-    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event));
-    ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc);
-    EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event));
+    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
+    ReportEvent(instr,
+                instrumentation_event,
+                soa.Self(),
+                event_method,
+                event_obj,
+                event_field,
+                event_dex_pc);
+    EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
 
     listener.Reset();
     {
@@ -199,9 +242,15 @@
 
     // Check the listener is not registered and is not notified of the event.
     EXPECT_FALSE(HasEventListener(instr, instrumentation_event));
-    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event));
-    ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc);
-    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event));
+    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
+    ReportEvent(instr,
+                instrumentation_event,
+                soa.Self(),
+                event_method,
+                event_obj,
+                event_field,
+                event_dex_pc);
+    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
   }
 
   void DeoptimizeMethod(Thread* self, ArtMethod* method, bool enable_deoptimization)
@@ -317,8 +366,12 @@
     }
   }
 
-  static void ReportEvent(const instrumentation::Instrumentation* instr, uint32_t event_type,
-                          Thread* self, ArtMethod* method, mirror::Object* obj,
+  static void ReportEvent(const instrumentation::Instrumentation* instr,
+                          uint32_t event_type,
+                          Thread* self,
+                          ArtMethod* method,
+                          mirror::Object* obj,
+                          ArtField* field,
                           uint32_t dex_pc)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     switch (event_type) {
@@ -337,11 +390,11 @@
         instr->DexPcMovedEvent(self, obj, method, dex_pc);
         break;
       case instrumentation::Instrumentation::kFieldRead:
-        instr->FieldReadEvent(self, obj, method, dex_pc, nullptr);
+        instr->FieldReadEvent(self, obj, method, dex_pc, field);
         break;
       case instrumentation::Instrumentation::kFieldWritten: {
         JValue value;
-        instr->FieldWriteEvent(self, obj, method, dex_pc, nullptr, value);
+        instr->FieldWriteEvent(self, obj, method, dex_pc, field, value);
         break;
       }
       case instrumentation::Instrumentation::kExceptionCaught: {
@@ -364,12 +417,14 @@
   }
 
   static bool DidListenerReceiveEvent(const TestInstrumentationListener& listener,
-                                      uint32_t event_type) {
+                                      uint32_t event_type,
+                                      bool with_object) {
     switch (event_type) {
       case instrumentation::Instrumentation::kMethodEntered:
         return listener.received_method_enter_event;
       case instrumentation::Instrumentation::kMethodExited:
-        return listener.received_method_exit_event;
+        return (!with_object && listener.received_method_exit_event) ||
+            (with_object && listener.received_method_exit_object_event);
       case instrumentation::Instrumentation::kMethodUnwind:
         return listener.received_method_unwind_event;
       case instrumentation::Instrumentation::kDexPcMoved:
@@ -377,7 +432,8 @@
       case instrumentation::Instrumentation::kFieldRead:
         return listener.received_field_read_event;
       case instrumentation::Instrumentation::kFieldWritten:
-        return listener.received_field_written_event;
+        return (!with_object && listener.received_field_written_event) ||
+            (with_object && listener.received_field_written_object_event);
       case instrumentation::Instrumentation::kExceptionCaught:
         return listener.received_exception_caught_event;
       case instrumentation::Instrumentation::kBranch:
@@ -419,8 +475,42 @@
   TestEvent(instrumentation::Instrumentation::kMethodEntered);
 }
 
-TEST_F(InstrumentationTest, MethodExitEvent) {
-  TestEvent(instrumentation::Instrumentation::kMethodExited);
+TEST_F(InstrumentationTest, MethodExitObjectEvent) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject class_loader = LoadDex("Instrumentation");
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+  ASSERT_TRUE(klass != nullptr);
+  ArtMethod* method = klass->FindDeclaredDirectMethod("returnReference",
+                                                      "()Ljava/lang/Object;",
+                                                      kRuntimePointerSize);
+  ASSERT_TRUE(method != nullptr);
+  TestEvent(instrumentation::Instrumentation::kMethodExited,
+            /*event_method*/ method,
+            /*event_field*/ nullptr,
+            /*with_object*/ true);
+}
+
+TEST_F(InstrumentationTest, MethodExitPrimEvent) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject class_loader = LoadDex("Instrumentation");
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+  ASSERT_TRUE(klass != nullptr);
+  ArtMethod* method = klass->FindDeclaredDirectMethod("returnPrimitive",
+                                                      "()I",
+                                                      kRuntimePointerSize);
+  ASSERT_TRUE(method != nullptr);
+  TestEvent(instrumentation::Instrumentation::kMethodExited,
+            /*event_method*/ method,
+            /*event_field*/ nullptr,
+            /*with_object*/ false);
 }
 
 TEST_F(InstrumentationTest, MethodUnwindEvent) {
@@ -435,8 +525,40 @@
   TestEvent(instrumentation::Instrumentation::kFieldRead);
 }
 
-TEST_F(InstrumentationTest, FieldWriteEvent) {
-  TestEvent(instrumentation::Instrumentation::kFieldWritten);
+TEST_F(InstrumentationTest, FieldWriteObjectEvent) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject class_loader = LoadDex("Instrumentation");
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+  ASSERT_TRUE(klass != nullptr);
+  ArtField* field = klass->FindDeclaredStaticField("referenceField", "Ljava/lang/Object;");
+  ASSERT_TRUE(field != nullptr);
+
+  TestEvent(instrumentation::Instrumentation::kFieldWritten,
+            /*event_method*/ nullptr,
+            /*event_field*/ field,
+            /*with_object*/ true);
+}
+
+TEST_F(InstrumentationTest, FieldWritePrimEvent) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject class_loader = LoadDex("Instrumentation");
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+  ASSERT_TRUE(klass != nullptr);
+  ArtField* field = klass->FindDeclaredStaticField("primitiveField", "I");
+  ASSERT_TRUE(field != nullptr);
+
+  TestEvent(instrumentation::Instrumentation::kFieldWritten,
+            /*event_method*/ nullptr,
+            /*event_field*/ field,
+            /*with_object*/ false);
 }
 
 TEST_F(InstrumentationTest, ExceptionCaughtEvent) {
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index b191dd7..32a2378 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -307,6 +307,8 @@
           instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                            shadow_frame.GetMethod(), inst->GetDexPc(insns),
                                            result);
+          // Re-load since it might have moved during the MethodExitEvent.
+          result.SetL(shadow_frame.GetVRegReference(ref_idx));
         }
         if (interpret_one_instruction) {
           /* Signal mterp to return to caller */
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 3550d56..cabd162 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -740,7 +740,7 @@
 }
 
 void Trace::DexPcMoved(Thread* thread ATTRIBUTE_UNUSED,
-                       mirror::Object* this_object ATTRIBUTE_UNUSED,
+                       Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                        ArtMethod* method,
                        uint32_t new_dex_pc) {
   // We're not recorded to listen to this kind of event, so complain.
@@ -749,7 +749,7 @@
 }
 
 void Trace::FieldRead(Thread* thread ATTRIBUTE_UNUSED,
-                      mirror::Object* this_object ATTRIBUTE_UNUSED,
+                      Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                       ArtMethod* method,
                       uint32_t dex_pc,
                       ArtField* field ATTRIBUTE_UNUSED)
@@ -760,7 +760,7 @@
 }
 
 void Trace::FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
-                         mirror::Object* this_object ATTRIBUTE_UNUSED,
+                         Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
                          ArtMethod* method,
                          uint32_t dex_pc,
                          ArtField* field ATTRIBUTE_UNUSED,
@@ -771,8 +771,10 @@
              << " " << dex_pc;
 }
 
-void Trace::MethodEntered(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED,
-                          ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED) {
+void Trace::MethodEntered(Thread* thread,
+                          Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+                          ArtMethod* method,
+                          uint32_t dex_pc ATTRIBUTE_UNUSED) {
   uint32_t thread_clock_diff = 0;
   uint32_t wall_clock_diff = 0;
   ReadClocks(thread, &thread_clock_diff, &wall_clock_diff);
@@ -780,8 +782,10 @@
                       thread_clock_diff, wall_clock_diff);
 }
 
-void Trace::MethodExited(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED,
-                         ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED,
+void Trace::MethodExited(Thread* thread,
+                         Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+                         ArtMethod* method,
+                         uint32_t dex_pc ATTRIBUTE_UNUSED,
                          const JValue& return_value ATTRIBUTE_UNUSED) {
   uint32_t thread_clock_diff = 0;
   uint32_t wall_clock_diff = 0;
@@ -790,8 +794,10 @@
                       thread_clock_diff, wall_clock_diff);
 }
 
-void Trace::MethodUnwind(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED,
-                         ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED) {
+void Trace::MethodUnwind(Thread* thread,
+                         Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+                         ArtMethod* method,
+                         uint32_t dex_pc ATTRIBUTE_UNUSED) {
   uint32_t thread_clock_diff = 0;
   uint32_t wall_clock_diff = 0;
   ReadClocks(thread, &thread_clock_diff, &wall_clock_diff);
@@ -800,7 +806,7 @@
 }
 
 void Trace::ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED,
-                            mirror::Throwable* exception_object ATTRIBUTE_UNUSED)
+                            Handle<mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   LOG(ERROR) << "Unexpected exception caught event in tracing";
 }
@@ -812,7 +818,7 @@
 }
 
 void Trace::InvokeVirtualOrInterface(Thread*,
-                                     mirror::Object*,
+                                     Handle<mirror::Object>,
                                      ArtMethod* method,
                                      uint32_t dex_pc,
                                      ArtMethod*) {
diff --git a/runtime/trace.h b/runtime/trace.h
index 485e9a1..ad10250 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -140,36 +140,54 @@
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_);
 
   // InstrumentationListener implementation.
-  void MethodEntered(Thread* thread, mirror::Object* this_object,
-                     ArtMethod* method, uint32_t dex_pc)
+  void MethodEntered(Thread* thread,
+                     Handle<mirror::Object> this_object,
+                     ArtMethod* method,
+                     uint32_t dex_pc)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
       OVERRIDE;
-  void MethodExited(Thread* thread, mirror::Object* this_object,
-                    ArtMethod* method, uint32_t dex_pc,
+  void MethodExited(Thread* thread,
+                    Handle<mirror::Object> this_object,
+                    ArtMethod* method,
+                    uint32_t dex_pc,
                     const JValue& return_value)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
       OVERRIDE;
-  void MethodUnwind(Thread* thread, mirror::Object* this_object,
-                    ArtMethod* method, uint32_t dex_pc)
+  void MethodUnwind(Thread* thread,
+                    Handle<mirror::Object> this_object,
+                    ArtMethod* method,
+                    uint32_t dex_pc)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
       OVERRIDE;
-  void DexPcMoved(Thread* thread, mirror::Object* this_object,
-                  ArtMethod* method, uint32_t new_dex_pc)
+  void DexPcMoved(Thread* thread,
+                  Handle<mirror::Object> this_object,
+                  ArtMethod* method,
+                  uint32_t new_dex_pc)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
       OVERRIDE;
-  void FieldRead(Thread* thread, mirror::Object* this_object,
-                 ArtMethod* method, uint32_t dex_pc, ArtField* field)
+  void FieldRead(Thread* thread,
+                 Handle<mirror::Object> this_object,
+                 ArtMethod* method,
+                 uint32_t dex_pc,
+                 ArtField* field)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
-  void FieldWritten(Thread* thread, mirror::Object* this_object,
-                    ArtMethod* method, uint32_t dex_pc, ArtField* field,
+  void FieldWritten(Thread* thread,
+                    Handle<mirror::Object> this_object,
+                    ArtMethod* method,
+                    uint32_t dex_pc,
+                    ArtField* field,
                     const JValue& field_value)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
-  void ExceptionCaught(Thread* thread, mirror::Throwable* exception_object)
+  void ExceptionCaught(Thread* thread,
+                       Handle<mirror::Throwable> exception_object)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
-  void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset)
+  void Branch(Thread* thread,
+              ArtMethod* method,
+              uint32_t dex_pc,
+              int32_t dex_pc_offset)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
   void InvokeVirtualOrInterface(Thread* thread,
-                                mirror::Object* this_object,
+                                Handle<mirror::Object> this_object,
                                 ArtMethod* caller,
                                 uint32_t dex_pc,
                                 ArtMethod* callee)
diff --git a/test/Instrumentation/Instrumentation.java b/test/Instrumentation/Instrumentation.java
index 09d4342..b44f78f 100644
--- a/test/Instrumentation/Instrumentation.java
+++ b/test/Instrumentation/Instrumentation.java
@@ -15,8 +15,21 @@
  */
 
 public class Instrumentation {
+  private static int primitiveField;
+  private static Object referenceField;
+
   // Direct method
   private void instanceMethod() {
     System.out.println("instanceMethod");
   }
+
+  private Object returnReference() {
+    System.out.println("returnReference");
+    return null;
+  }
+
+  private int returnPrimitive() {
+    System.out.println("returnPrimitive");
+    return 0;
+  }
 }