Revert^2 "Add verifier fallback for JVMTI Get/SetLocalVariable"

This reverts commit 99cbfb55fc7ac0f65b1ccdc7076219fcee383b92.

This unreverts commit e48fd0b4780efadc6b3433fe7a56aa5be2a84325.

We were incorrectly bounds-checking the register number. We were
treating it as unsigned when it was actual signed. Previously this
wouldn't matter since normally the debug-info won't have any
information for negative slots but by falling back to the verifier
we hit some check failures.

Reason for revert: Fixed underlying issue with bad bounds check.
Bug: 131711256

Change-Id: I0b859ce322f3b23f937b72d735db8f6870c40602
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index e34a856..d8ee981 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -31,22 +31,33 @@
 
 #include "ti_method.h"
 
+#include <initializer_list>
 #include <type_traits>
+#include <variant>
 
+#include "android-base/macros.h"
 #include "arch/context.h"
 #include "art_jvmti.h"
 #include "art_method-inl.h"
 #include "base/enums.h"
+#include "base/globals.h"
+#include "base/macros.h"
 #include "base/mutex-inl.h"
 #include "deopt_manager.h"
 #include "dex/code_item_accessors-inl.h"
+#include "dex/code_item_accessors.h"
 #include "dex/dex_file_annotations.h"
 #include "dex/dex_file_types.h"
+#include "dex/dex_instruction.h"
+#include "dex/dex_instruction_iterator.h"
 #include "dex/modifiers.h"
+#include "dex/primitive.h"
 #include "events-inl.h"
 #include "gc_root-inl.h"
+#include "handle.h"
 #include "jit/jit.h"
 #include "jni/jni_internal.h"
+#include "jvmti.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
@@ -56,13 +67,18 @@
 #include "obj_ptr.h"
 #include "runtime_callbacks.h"
 #include "scoped_thread_state_change-inl.h"
+#include "scoped_thread_state_change.h"
 #include "stack.h"
 #include "thread-current-inl.h"
 #include "thread.h"
 #include "thread_list.h"
+#include "ti_logging.h"
 #include "ti_stack.h"
 #include "ti_thread.h"
 #include "ti_phase.h"
+#include "verifier/register_line-inl.h"
+#include "verifier/reg_type-inl.h"
+#include "verifier/method_verifier-inl.h"
 
 namespace openjdkjvmti {
 
@@ -526,10 +542,21 @@
 
 class CommonLocalVariableClosure : public art::Closure {
  public:
-  CommonLocalVariableClosure(jint depth, jint slot)
-      : result_(ERR(INTERNAL)), depth_(depth), slot_(slot) {}
+  // The verifier isn't always able to be as specific as the local-variable-table. We can only get
+  // 32-bit, 64-bit or reference.
+  enum class VerifierPrimitiveType {
+    k32BitValue,  // float, int, short, char, boolean, byte
+    k64BitValue,  // double, long
+    kReferenceValue,  // Object
+    kZeroValue,  // null or zero constant. Might be either k32BitValue or kReferenceValue
+  };
 
-  void Run(art::Thread* self) override REQUIRES(art::Locks::mutator_lock_) {
+  using SlotType = std::variant<art::Primitive::Type, VerifierPrimitiveType>;
+
+  CommonLocalVariableClosure(jvmtiEnv* jvmti, jint depth, jint slot)
+      : jvmti_(jvmti), result_(ERR(INTERNAL)), depth_(depth), slot_(slot) {}
+
+  void Run(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
     art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
     bool needs_instrument;
     {
@@ -560,7 +587,7 @@
         return;
       }
       std::string descriptor;
-      art::Primitive::Type slot_type = art::Primitive::kPrimVoid;
+      SlotType slot_type{ art::Primitive::kPrimVoid };
       jvmtiError err = GetSlotType(method, pc, &descriptor, &slot_type);
       if (err != OK) {
         result_ = err;
@@ -587,56 +614,190 @@
   virtual jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
       REQUIRES_SHARED(art::Locks::mutator_lock_) = 0;
   virtual jvmtiError GetTypeError(art::ArtMethod* method,
-                                  art::Primitive::Type type,
+                                  SlotType type,
                                   const std::string& descriptor)
       REQUIRES_SHARED(art::Locks::mutator_lock_)  = 0;
 
   jvmtiError GetSlotType(art::ArtMethod* method,
                          uint32_t dex_pc,
                          /*out*/std::string* descriptor,
-                         /*out*/art::Primitive::Type* type)
-      REQUIRES(art::Locks::mutator_lock_) {
-    const art::DexFile* dex_file = method->GetDexFile();
-    if (dex_file == nullptr) {
+                         /*out*/SlotType* type)
+      REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+  jvmtiError InferSlotTypeFromVerifier(art::ArtMethod* method,
+                                       uint32_t dex_pc,
+                                       /*out*/ std::string* descriptor,
+                                       /*out*/ SlotType* type)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    art::Thread* self = art::Thread::Current();
+    art::StackHandleScope<2> hs(self);
+    std::unique_ptr<art::verifier::MethodVerifier> verifier(
+        art::verifier::MethodVerifier::CalculateVerificationInfo(
+            self,
+            method,
+            hs.NewHandle(method->GetDexCache()),
+            hs.NewHandle(method->GetDeclaringClass()->GetClassLoader())));
+    if (verifier == nullptr) {
+      JVMTI_LOG(WARNING, jvmti_) << "Unable to extract verification information from "
+                                 << method->PrettyMethod() << " due to hard verification failures! "
+                                 << "How did this method even get loaded!";
+      return ERR(INTERNAL);
+    }
+    art::verifier::RegisterLine* line = verifier->GetRegLine(dex_pc);
+    if (line == nullptr) {
+      JVMTI_LOG(WARNING, jvmti_) << "Unable to determine register line at dex-pc " << dex_pc
+                                 << " for method " << method->PrettyMethod();
       return ERR(OPAQUE_FRAME);
     }
-    art::CodeItemDebugInfoAccessor accessor(method->DexInstructionDebugInfo());
-    if (!accessor.HasCodeItem()) {
-      return ERR(OPAQUE_FRAME);
-    }
-    bool found = false;
-    *type = art::Primitive::kPrimVoid;
-    descriptor->clear();
-    auto visitor = [&](const art::DexFile::LocalInfo& entry) {
-      if (!found &&
-          entry.start_address_ <= dex_pc &&
-          entry.end_address_ > dex_pc &&
-          entry.reg_ == slot_) {
-        found = true;
-        *type = art::Primitive::GetType(entry.descriptor_[0]);
-        *descriptor = entry.descriptor_;
-      }
-    };
-    if (!accessor.DecodeDebugLocalInfo(method->IsStatic(), method->GetDexMethodIndex(), visitor) ||
-        !found) {
-      // Something went wrong with decoding the debug information. It might as well not be there.
+    const art::verifier::RegType& rt = line->GetRegisterType(verifier.get(), slot_);
+    if (rt.IsUndefined()) {
+      return ERR(INVALID_SLOT);
+    } else if (rt.IsNonZeroReferenceTypes() || rt.IsNull()) {
+      *descriptor = (rt.HasClass() ? rt.GetDescriptor() : "Ljava/lang/Object;");
+      *type = VerifierPrimitiveType::kReferenceValue;
+      return OK;
+    } else if (rt.IsZero()) {
+      *descriptor = "I";
+      *type = VerifierPrimitiveType::kZeroValue;
+      return OK;
+    } else if (rt.IsCategory1Types()) {
+      *descriptor = "I";
+      *type = VerifierPrimitiveType::k32BitValue;
+      return OK;
+    } else if (rt.IsCategory2Types() && rt.IsLowHalf()) {
+      *descriptor = "J";
+      *type = VerifierPrimitiveType::k64BitValue;
+      return OK;
+    } else {
+      // The slot doesn't have a type. Must not be valid here.
       return ERR(INVALID_SLOT);
     }
-    return OK;
   }
 
+  constexpr VerifierPrimitiveType SquashType(SlotType t) {
+    if (std::holds_alternative<art::Primitive::Type>(t)) {
+      switch (std::get<art::Primitive::Type>(t)) {
+        // 32-bit primitives
+        case art::Primitive::kPrimByte:
+        case art::Primitive::kPrimChar:
+        case art::Primitive::kPrimInt:
+        case art::Primitive::kPrimShort:
+        case art::Primitive::kPrimBoolean:
+        case art::Primitive::kPrimFloat:
+          return VerifierPrimitiveType::k32BitValue;
+        // 64-bit primitives
+        case art::Primitive::kPrimLong:
+        case art::Primitive::kPrimDouble:
+          return VerifierPrimitiveType::k64BitValue;
+        case art::Primitive::kPrimNot:
+          return VerifierPrimitiveType::kReferenceValue;
+        case art::Primitive::kPrimVoid:
+          LOG(FATAL) << "Got kPrimVoid";
+          UNREACHABLE();
+      }
+    } else {
+      return std::get<VerifierPrimitiveType>(t);
+    }
+  }
+
+  jvmtiEnv* jvmti_;
   jvmtiError result_;
   jint depth_;
   jint slot_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CommonLocalVariableClosure);
 };
 
+std::ostream& operator<<(std::ostream& os,
+                         CommonLocalVariableClosure::VerifierPrimitiveType state) {
+  switch (state) {
+    case CommonLocalVariableClosure::VerifierPrimitiveType::k32BitValue:
+      return os << "32BitValue";
+    case CommonLocalVariableClosure::VerifierPrimitiveType::k64BitValue:
+      return os << "64BitValue";
+    case CommonLocalVariableClosure::VerifierPrimitiveType::kReferenceValue:
+      return os << "ReferenceValue";
+    case CommonLocalVariableClosure::VerifierPrimitiveType::kZeroValue:
+      return os << "ZeroValue";
+  }
+}
+
+std::ostream& operator<<(std::ostream& os, CommonLocalVariableClosure::SlotType state) {
+  if (std::holds_alternative<art::Primitive::Type>(state)) {
+    return os << "Primitive::Type[" << std::get<art::Primitive::Type>(state) << "]";
+  } else {
+    return os << "VerifierPrimitiveType["
+              << std::get<CommonLocalVariableClosure::VerifierPrimitiveType>(state) << "]";
+  }
+}
+
+jvmtiError CommonLocalVariableClosure::GetSlotType(art::ArtMethod* method,
+                                                   uint32_t dex_pc,
+                                                   /*out*/ std::string* descriptor,
+                                                   /*out*/ SlotType* type) {
+  const art::DexFile* dex_file = method->GetDexFile();
+  if (dex_file == nullptr) {
+    return ERR(OPAQUE_FRAME);
+  }
+  art::CodeItemDebugInfoAccessor accessor(method->DexInstructionDebugInfo());
+  if (!accessor.HasCodeItem()) {
+    return ERR(OPAQUE_FRAME);
+  }
+  bool found = false;
+  *type = art::Primitive::kPrimVoid;
+  descriptor->clear();
+  auto visitor = [&](const art::DexFile::LocalInfo& entry) {
+    if (!found && entry.start_address_ <= dex_pc && entry.end_address_ > dex_pc &&
+        entry.reg_ == slot_) {
+      found = true;
+      *type = art::Primitive::GetType(entry.descriptor_[0]);
+      *descriptor = entry.descriptor_;
+    }
+  };
+  if (!accessor.DecodeDebugLocalInfo(method->IsStatic(), method->GetDexMethodIndex(), visitor) ||
+      !found) {
+    // Something went wrong with decoding the debug information. It might as well not be there.
+    // Try to find the type with the verifier.
+    // TODO This is very slow.
+    return InferSlotTypeFromVerifier(method, dex_pc, descriptor, type);
+  } else if (art::kIsDebugBuild) {
+    std::string type_unused;
+    SlotType verifier_type{ art::Primitive::kPrimVoid };
+    DCHECK_EQ(InferSlotTypeFromVerifier(method, dex_pc, &type_unused, &verifier_type), OK)
+        << method->PrettyMethod() << " failed to verify!";
+    if (*type == SlotType{ art::Primitive::kPrimNot }) {
+      // We cannot distinguish between a constant 0 and a null reference so we return that it is a
+      // 32bit value (Due to the way references are read by the interpreter this is safe even if
+      // it's modified, the value will remain null). This is not ideal since it prevents modifying
+      // locals in some circumstances but generally is not a big deal (since one can just modify it
+      // later once it's been determined to be a reference by a later instruction).
+      DCHECK(verifier_type == SlotType { VerifierPrimitiveType::kZeroValue } ||
+             verifier_type == SlotType { VerifierPrimitiveType::kReferenceValue })
+          << "Verifier disagrees on type of slot! debug: " << *type
+          << " verifier: " << verifier_type;
+    } else if (verifier_type == SlotType { VerifierPrimitiveType::kZeroValue }) {
+      DCHECK(VerifierPrimitiveType::k32BitValue == SquashType(*type) ||
+             VerifierPrimitiveType::kReferenceValue == SquashType(*type))
+          << "Verifier disagrees on type of slot! debug: " << *type
+          << " verifier: " << verifier_type;
+    } else {
+      DCHECK_EQ(SquashType(verifier_type), SquashType(*type))
+          << "Verifier disagrees on type of slot! debug: " << *type
+          << " verifier: " << verifier_type;
+    }
+  }
+  return OK;
+}
+
 class GetLocalVariableClosure : public CommonLocalVariableClosure {
  public:
-  GetLocalVariableClosure(jint depth,
+  GetLocalVariableClosure(jvmtiEnv* jvmti,
+                          jint depth,
                           jint slot,
                           art::Primitive::Type type,
                           jvalue* val)
-      : CommonLocalVariableClosure(depth, slot),
+      : CommonLocalVariableClosure(jvmti, depth, slot),
         type_(type),
         val_(val),
         obj_val_(nullptr) {}
@@ -656,22 +817,61 @@
   }
 
  protected:
-  jvmtiError GetTypeError(art::ArtMethod* method ATTRIBUTE_UNUSED,
-                          art::Primitive::Type slot_type,
-                          const std::string& descriptor ATTRIBUTE_UNUSED)
-      override REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    switch (slot_type) {
-      case art::Primitive::kPrimByte:
-      case art::Primitive::kPrimChar:
-      case art::Primitive::kPrimInt:
-      case art::Primitive::kPrimShort:
-      case art::Primitive::kPrimBoolean:
-        return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
-      case art::Primitive::kPrimLong:
+  jvmtiError
+  GetTypeError(art::ArtMethod* method, SlotType slot_type, const std::string& descriptor) override
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    jvmtiError res = GetTypeErrorInner(method, slot_type, descriptor);
+    if (res == ERR(TYPE_MISMATCH)) {
+      JVMTI_LOG(INFO, jvmti_) << "Unable to Get local variable in slot " << slot_ << ". Expected"
+                              << " slot to be of type compatible with " << SlotType { type_ }
+                              << " but slot is " << slot_type;
+    } else if (res != OK) {
+      JVMTI_LOG(INFO, jvmti_) << "Unable to get local variable in slot " << slot_ << ".";
+    }
+    return res;
+  }
+
+  jvmtiError GetTypeErrorInner(art::ArtMethod* method ATTRIBUTE_UNUSED,
+                               SlotType slot_type,
+                               const std::string& descriptor ATTRIBUTE_UNUSED)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    switch (type_) {
       case art::Primitive::kPrimFloat:
-      case art::Primitive::kPrimDouble:
+      case art::Primitive::kPrimInt: {
+        if (std::holds_alternative<VerifierPrimitiveType>(slot_type)) {
+          return (slot_type == SlotType { VerifierPrimitiveType::k32BitValue } ||
+                  slot_type == SlotType { VerifierPrimitiveType::kZeroValue })
+                     ? OK
+                     : ERR(TYPE_MISMATCH);
+        } else if (type_ == art::Primitive::kPrimFloat ||
+                   slot_type == SlotType { art::Primitive::kPrimFloat }) {
+          // Check that we are actually a float.
+          return (SlotType { type_ } == slot_type) ? OK : ERR(TYPE_MISMATCH);
+        } else {
+          // Some smaller int type.
+          return SquashType(slot_type) == SquashType(SlotType { type_ }) ? OK : ERR(TYPE_MISMATCH);
+        }
+      }
+      case art::Primitive::kPrimLong:
+      case art::Primitive::kPrimDouble: {
+        // todo
+        if (std::holds_alternative<VerifierPrimitiveType>(slot_type)) {
+          return (slot_type == SlotType { VerifierPrimitiveType::k64BitValue })
+                     ? OK
+                     : ERR(TYPE_MISMATCH);
+        } else {
+          return slot_type == SlotType { type_ } ? OK : ERR(TYPE_MISMATCH);
+        }
+      }
       case art::Primitive::kPrimNot:
-        return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
+        return (SquashType(slot_type) == VerifierPrimitiveType::kReferenceValue ||
+                SquashType(slot_type) == VerifierPrimitiveType::kZeroValue)
+                   ? OK
+                   : ERR(TYPE_MISMATCH);
+      case art::Primitive::kPrimShort:
+      case art::Primitive::kPrimChar:
+      case art::Primitive::kPrimByte:
+      case art::Primitive::kPrimBoolean:
       case art::Primitive::kPrimVoid:
         LOG(FATAL) << "Unexpected primitive type " << slot_type;
         UNREACHABLE();
@@ -735,7 +935,7 @@
   jobject obj_val_;
 };
 
-jvmtiError MethodUtil::GetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
+jvmtiError MethodUtil::GetLocalVariableGeneric(jvmtiEnv* env,
                                                jthread thread,
                                                jint depth,
                                                jint slot,
@@ -753,7 +953,7 @@
     art::Locks::thread_list_lock_->ExclusiveUnlock(self);
     return err;
   }
-  GetLocalVariableClosure c(depth, slot, type, val);
+  GetLocalVariableClosure c(env, depth, slot, type, val);
   // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
   if (!target->RequestSynchronousCheckpoint(&c)) {
     return ERR(THREAD_NOT_ALIVE);
@@ -764,49 +964,100 @@
 
 class SetLocalVariableClosure : public CommonLocalVariableClosure {
  public:
-  SetLocalVariableClosure(art::Thread* caller,
+  SetLocalVariableClosure(jvmtiEnv* jvmti,
+                          art::Thread* caller,
                           jint depth,
                           jint slot,
                           art::Primitive::Type type,
                           jvalue val)
-      : CommonLocalVariableClosure(depth, slot), caller_(caller), type_(type), val_(val) {}
+      : CommonLocalVariableClosure(jvmti, depth, slot), caller_(caller), type_(type), val_(val) {}
 
  protected:
-  jvmtiError GetTypeError(art::ArtMethod* method,
-                          art::Primitive::Type slot_type,
-                          const std::string& descriptor)
-      override REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    switch (slot_type) {
-      case art::Primitive::kPrimNot: {
-        if (type_ != art::Primitive::kPrimNot) {
+  jvmtiError
+  GetTypeError(art::ArtMethod* method, SlotType slot_type, const std::string& descriptor) override
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    jvmtiError res = GetTypeErrorInner(method, slot_type, descriptor);
+    if (res != OK) {
+      if (res == ERR(TYPE_MISMATCH)) {
+        std::ostringstream desc_exp;
+        std::ostringstream desc_set;
+        if (type_ == art::Primitive::kPrimNot) {
+          desc_exp << " (type: " << descriptor << ")";
+          art::ObjPtr<art::mirror::Object> new_val(art::Thread::Current()->DecodeJObject(val_.l));
+          desc_set << " (type: "
+                  << (new_val.IsNull() ? "NULL" : new_val->GetClass()->PrettyDescriptor()) << ")";
+        }
+        JVMTI_LOG(INFO, jvmti_) << "Unable to Set local variable in slot " << slot_ << ". Expected"
+                                << " slot to be of type compatible with " << SlotType{ type_ }
+                                << desc_set.str() << " but slot is " << slot_type << desc_exp.str();
+      } else {
+        JVMTI_LOG(INFO, jvmti_) << "Unable to set local variable in slot " << slot_ << ". "
+                                << err_.str();
+      }
+    }
+    return res;
+  }
+
+  jvmtiError
+  GetTypeErrorInner(art::ArtMethod* method, SlotType slot_type, const std::string& descriptor)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    switch (SquashType(SlotType{ type_ })) {
+      case VerifierPrimitiveType::k32BitValue: {
+        if (slot_type == SlotType{ VerifierPrimitiveType::kZeroValue }) {
+          if (val_.i == 0) {
+            return OK;
+          } else {
+            err_ << "Cannot determine if slot " << slot_ << " is a null reference or 32bit "
+                 << "constant. Cannot allow writing to slot.";
+            return ERR(INTERNAL);
+          }
+        } else if (SquashType(slot_type) != VerifierPrimitiveType::k32BitValue) {
+          return ERR(TYPE_MISMATCH);
+        } else if (slot_type == SlotType { VerifierPrimitiveType::k32BitValue } ||
+                   slot_type == SlotType { type_ }) {
+          return OK;
+        } else if (type_ == art::Primitive::kPrimFloat ||
+                   slot_type == SlotType { art::Primitive::kPrimFloat }) {
+          // we should have hit the get == type_ above
+          return ERR(TYPE_MISMATCH);
+        } else {
+          // Some smaller type then int.
+          return OK;
+        }
+      }
+      case VerifierPrimitiveType::k64BitValue: {
+        if (slot_type == SlotType { VerifierPrimitiveType::k64BitValue } ||
+            slot_type == SlotType { type_ }) {
+          return OK;
+        } else {
+          return ERR(TYPE_MISMATCH);
+        }
+      }
+      case VerifierPrimitiveType::kReferenceValue: {
+        if (SquashType(slot_type) != VerifierPrimitiveType::kReferenceValue &&
+            SquashType(slot_type) != VerifierPrimitiveType::kZeroValue) {
           return ERR(TYPE_MISMATCH);
         } else if (val_.l == nullptr) {
           return OK;
+        } else if (slot_type == SlotType { VerifierPrimitiveType::kZeroValue }) {
+          err_ << "Cannot determine if slot " << slot_ << " is a null "
+               << "reference or 32bit constant. Cannot allow writing to slot.";
+          return ERR(INTERNAL);
         } else {
           art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
-          art::ObjPtr<art::mirror::Class> set_class =
-              caller_->DecodeJObject(val_.l)->GetClass();
+          art::ObjPtr<art::mirror::Class> set_class = caller_->DecodeJObject(val_.l)->GetClass();
           art::ObjPtr<art::mirror::ClassLoader> loader =
               method->GetDeclaringClass()->GetClassLoader();
           art::ObjPtr<art::mirror::Class> slot_class =
               cl->LookupClass(caller_, descriptor.c_str(), loader);
-          DCHECK(!slot_class.IsNull());
+          DCHECK(!slot_class.IsNull()) << descriptor << " slot: " << slot_type;
           return slot_class->IsAssignableFrom(set_class) ? OK : ERR(TYPE_MISMATCH);
         }
       }
-      case art::Primitive::kPrimByte:
-      case art::Primitive::kPrimChar:
-      case art::Primitive::kPrimInt:
-      case art::Primitive::kPrimShort:
-      case art::Primitive::kPrimBoolean:
-        return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
-      case art::Primitive::kPrimLong:
-      case art::Primitive::kPrimFloat:
-      case art::Primitive::kPrimDouble:
-        return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
-      case art::Primitive::kPrimVoid:
-        LOG(FATAL) << "Unexpected primitive type " << slot_type;
+      case VerifierPrimitiveType::kZeroValue: {
+        LOG(FATAL) << "Illegal result from SquashType of art::Primitive::Type " << type_;
         UNREACHABLE();
+      }
     }
   }
 
@@ -857,9 +1108,10 @@
   art::Thread* caller_;
   art::Primitive::Type type_;
   jvalue val_;
+  std::ostringstream err_;
 };
 
-jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
+jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env,
                                                jthread thread,
                                                jint depth,
                                                jint slot,
@@ -880,7 +1132,7 @@
     art::Locks::thread_list_lock_->ExclusiveUnlock(self);
     return err;
   }
-  SetLocalVariableClosure c(self, depth, slot, type, val);
+  SetLocalVariableClosure c(env, self, depth, slot, type, val);
   // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
   if (!target->RequestSynchronousCheckpoint(&c)) {
     return ERR(THREAD_NOT_ALIVE);