ART: Verifier support for VarHandles

Extends checking of signature polymorphic methods to support VarHandle
accessor methods.

Bug: 65872996
Test: run-test --host 954
Change-Id: I696bfdfbf1cd99f7cd6720cda5911dd4f5e66ada
diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc
index 42b8473..0b4dde1 100644
--- a/runtime/mirror/method_handle_impl.cc
+++ b/runtime/mirror/method_handle_impl.cc
@@ -22,6 +22,14 @@
 namespace art {
 namespace mirror {
 
+const char* MethodHandle::GetReturnTypeDescriptor(const char* invoke_method_name) {
+  if (strcmp(invoke_method_name, "invoke") == 0 || strcmp(invoke_method_name, "invokeExact") == 0) {
+    return "Ljava/lang/Object;";
+  } else {
+    return nullptr;
+  }
+}
+
 mirror::Class* MethodHandle::StaticClass() {
   mirror::Class* klass = MethodHandleImpl::StaticClass()->GetSuperClass();
   DCHECK(klass->DescriptorEquals("Ljava/lang/invoke/MethodHandle;"));
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index f362d43..a1bc976 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -81,6 +81,10 @@
 
   ALWAYS_INLINE ObjPtr<mirror::Class> GetTargetClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Gets the return type for a named invoke method, or nullptr if the invoke method is not
+  // supported.
+  static const char* GetReturnTypeDescriptor(const char* invoke_method_name);
+
   static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
  protected:
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index e7eac1a..3f4a28c 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -26,6 +26,59 @@
 
 namespace {
 
+struct VarHandleAccessorToAccessModeEntry {
+  const char* method_name;
+  VarHandle::AccessMode access_mode;
+
+  // Binary predicate function for finding access_mode by
+  // method_name. The access_mode field is ignored.
+  static bool CompareName(const VarHandleAccessorToAccessModeEntry& lhs,
+                          const VarHandleAccessorToAccessModeEntry& rhs) {
+    return strcmp(lhs.method_name, rhs.method_name) < 0;
+  }
+};
+
+// Map of VarHandle accessor method names to access mode values. The list is alpha-sorted to support
+// binary search. For the usage scenario - lookups in the verifier - a linear scan would likely
+// suffice since we expect VarHandles to be a lesser encountered class. We could use a std::hashmap
+// here and this would be easier to maintain if new values are added here. However, this entails
+// CPU cycles initializing the structure on every execution and uses O(N) more memory for
+// intermediate nodes and makes that memory dirty. Compile-time magic using constexpr is possible
+// here, but that's a tax when this code is recompiled.
+const VarHandleAccessorToAccessModeEntry kAccessorToAccessMode[VarHandle::kNumberOfAccessModes] = {
+  { "compareAndExchange", VarHandle::AccessMode::kCompareAndExchange },
+  { "compareAndExchangeAcquire", VarHandle::AccessMode::kCompareAndExchangeAcquire },
+  { "compareAndExchangeRelease", VarHandle::AccessMode::kCompareAndExchangeRelease },
+  { "compareAndSet", VarHandle::AccessMode::kCompareAndSet },
+  { "get", VarHandle::AccessMode::kGet },
+  { "getAcquire", VarHandle::AccessMode::kGetAcquire },
+  { "getAndAdd", VarHandle::AccessMode::kGetAndAdd },
+  { "getAndAddAcquire", VarHandle::AccessMode::kGetAndAddAcquire },
+  { "getAndAddRelease", VarHandle::AccessMode::kGetAndAddRelease },
+  { "getAndBitwiseAnd", VarHandle::AccessMode::kGetAndBitwiseAnd },
+  { "getAndBitwiseAndAcquire", VarHandle::AccessMode::kGetAndBitwiseAndAcquire },
+  { "getAndBitwiseAndRelease", VarHandle::AccessMode::kGetAndBitwiseAndRelease },
+  { "getAndBitwiseOr", VarHandle::AccessMode::kGetAndBitwiseOr },
+  { "getAndBitwiseOrAcquire", VarHandle::AccessMode::kGetAndBitwiseOrAcquire },
+  { "getAndBitwiseOrRelease", VarHandle::AccessMode::kGetAndBitwiseOrRelease },
+  { "getAndBitwiseXor", VarHandle::AccessMode::kGetAndBitwiseXor },
+  { "getAndBitwiseXorAcquire", VarHandle::AccessMode::kGetAndBitwiseXorAcquire },
+  { "getAndBitwiseXorRelease", VarHandle::AccessMode::kGetAndBitwiseXorRelease },
+  { "getAndSet", VarHandle::AccessMode::kGetAndSet },
+  { "getAndSetAcquire", VarHandle::AccessMode::kGetAndSetAcquire },
+  { "getAndSetRelease", VarHandle::AccessMode::kGetAndSetRelease },
+  { "getOpaque", VarHandle::AccessMode::kGetOpaque },
+  { "getVolatile", VarHandle::AccessMode::kGetVolatile },
+  { "set", VarHandle::AccessMode::kSet },
+  { "setOpaque", VarHandle::AccessMode::kSetOpaque },
+  { "setRelease", VarHandle::AccessMode::kSetRelease },
+  { "setVolatile", VarHandle::AccessMode::kSetVolatile },
+  { "weakCompareAndSet", VarHandle::AccessMode::kWeakCompareAndSet },
+  { "weakCompareAndSetAcquire", VarHandle::AccessMode::kWeakCompareAndSetAcquire },
+  { "weakCompareAndSetPlain", VarHandle::AccessMode::kWeakCompareAndSetPlain },
+  { "weakCompareAndSetRelease", VarHandle::AccessMode::kWeakCompareAndSetRelease },
+};
+
 // Enumeration for describing the parameter and return types of an AccessMode.
 enum class AccessModeTemplate : uint32_t {
   kGet,                 // T Op(C0..CN)
@@ -281,6 +334,41 @@
   return GetMethodTypeForAccessMode(self, this, access_mode);
 }
 
+const char* VarHandle::GetReturnTypeDescriptor(const char* accessor_name) {
+  AccessMode access_mode;
+  if (!GetAccessModeByMethodName(accessor_name, &access_mode)) {
+    return nullptr;
+  }
+  AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
+  switch (access_mode_template) {
+    case AccessModeTemplate::kGet:
+    case AccessModeTemplate::kCompareAndExchange:
+    case AccessModeTemplate::kGetAndUpdate:
+      return "Ljava/lang/Object;";
+    case AccessModeTemplate::kCompareAndSet:
+      return "Z";
+    case AccessModeTemplate::kSet:
+      return "V";
+  }
+}
+
+bool VarHandle::GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode) {
+  if (method_name == nullptr) {
+    return false;
+  }
+  VarHandleAccessorToAccessModeEntry target = { method_name, /*dummy*/VarHandle::AccessMode::kGet };
+  auto last = std::cend(kAccessorToAccessMode);
+  auto it = std::lower_bound(std::cbegin(kAccessorToAccessMode),
+                             last,
+                             target,
+                             VarHandleAccessorToAccessModeEntry::CompareName);
+  if (it == last || strcmp(it->method_name, method_name) != 0) {
+    return false;
+  }
+  *access_mode = it->access_mode;
+  return true;
+}
+
 void VarHandle::SetClass(Class* klass) {
   CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
   CHECK(klass != nullptr);
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
index 54a6d27..7b48669 100644
--- a/runtime/mirror/var_handle.h
+++ b/runtime/mirror/var_handle.h
@@ -78,7 +78,9 @@
     kGetAndBitwiseXor,
     kGetAndBitwiseXorRelease,
     kGetAndBitwiseXorAcquire,
+    kLast = kGetAndBitwiseXorAcquire,
   };
+  constexpr static size_t kNumberOfAccessModes = static_cast<size_t>(AccessMode::kLast) + 1u;
 
   // Returns true if the AccessMode specified is a supported operation.
   bool IsAccessModeSupported(AccessMode accessMode) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -105,6 +107,14 @@
     return static_class_.Read();
   }
 
+  // Gets the return type descriptor for a named accessor method,
+  // nullptr if accessor_method is not supported.
+  static const char* GetReturnTypeDescriptor(const char* accessor_method);
+
+  // Returns true and sets access_mode if method_name corresponds to a
+  // VarHandle access method, such as "setOpaque". Returns false otherwise.
+  static bool GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode);
+
   static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
   static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
   static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index 159f80c..e844fd4 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -987,5 +987,79 @@
   }
 }
 
+TEST_F(VarHandleTest, GetMethodTypeForAccessMode) {
+  VarHandle::AccessMode access_mode;
+
+  // Invalid access mode names
+  EXPECT_FALSE(VarHandle::GetAccessModeByMethodName(nullptr, &access_mode));
+  EXPECT_FALSE(VarHandle::GetAccessModeByMethodName("", &access_mode));
+  EXPECT_FALSE(VarHandle::GetAccessModeByMethodName("CompareAndExchange", &access_mode));
+  EXPECT_FALSE(VarHandle::GetAccessModeByMethodName("compareAndExchangX", &access_mode));
+
+  // Valid access mode names
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("compareAndExchange", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kCompareAndExchange, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("compareAndExchangeAcquire", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kCompareAndExchangeAcquire, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("compareAndExchangeRelease", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kCompareAndExchangeRelease, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("compareAndSet", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kCompareAndSet, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("get", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGet, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAcquire", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAcquire, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndAdd", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndAdd, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndAddAcquire", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndAddAcquire, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndAddRelease", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndAddRelease, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseAnd", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseAnd, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseAndAcquire", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseAndAcquire, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseAndRelease", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseAndRelease, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseOr", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseOr, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseOrAcquire", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseOrAcquire, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseOrRelease", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseOrRelease, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseXor", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseXor, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseXorAcquire", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseXorAcquire, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseXorRelease", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseXorRelease, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndSet", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndSet, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndSetAcquire", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndSetAcquire, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndSetRelease", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetAndSetRelease, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getOpaque", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetOpaque, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getVolatile", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kGetVolatile, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("set", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kSet, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("setOpaque", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kSetOpaque, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("setRelease", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kSetRelease, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("setVolatile", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kSetVolatile, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("weakCompareAndSet", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kWeakCompareAndSet, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("weakCompareAndSetAcquire", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kWeakCompareAndSetAcquire, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("weakCompareAndSetPlain", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kWeakCompareAndSetPlain, access_mode);
+  EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("weakCompareAndSetRelease", &access_mode));
+  EXPECT_EQ(VarHandle::AccessMode::kWeakCompareAndSetRelease, access_mode);
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5e5e96b..8d9b82e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -48,6 +48,7 @@
 #include "mirror/method_type.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
+#include "mirror/var_handle.h"
 #include "reg_type-inl.h"
 #include "register_line-inl.h"
 #include "runtime.h"
@@ -3141,6 +3142,7 @@
       }
       if (!CheckSignaturePolymorphicMethod(called_method) ||
           !CheckSignaturePolymorphicReceiver(inst)) {
+        DCHECK(HasFailures());
         break;
       }
       const uint32_t proto_idx = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
@@ -4418,14 +4420,20 @@
 
 bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) {
   mirror::Class* klass = method->GetDeclaringClass();
-  if (klass != mirror::MethodHandle::StaticClass()) {
+  const char* method_name = method->GetName();
+
+  const char* expected_return_descriptor;
+  if (klass == mirror::MethodHandle::StaticClass()) {
+    expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name);
+  } else if (klass == mirror::VarHandle::StaticClass()) {
+    expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name);
+  } else {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD)
-        << "Signature polymorphic method must be declared in java.lang.invoke.MethodClass";
+        << "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor();
     return false;
   }
 
-  const char* method_name = method->GetName();
-  if (strcmp(method_name, "invoke") != 0 && strcmp(method_name, "invokeExact") != 0) {
+  if (expected_return_descriptor == nullptr) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD)
         << "Signature polymorphic method name invalid: " << method_name;
     return false;
@@ -4447,9 +4455,10 @@
   }
 
   const char* return_descriptor = method->GetReturnTypeDescriptor();
-  if (strcmp(return_descriptor, "Ljava/lang/Object;") != 0) {
+  if (strcmp(return_descriptor, expected_return_descriptor) != 0) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD)
-        << "Signature polymorphic method has unexpected return type: " << return_descriptor;
+        << "Signature polymorphic method has unexpected return type: " << return_descriptor
+        << " != " << expected_return_descriptor;
     return false;
   }
 
@@ -4476,9 +4485,10 @@
         << "invoke-polymorphic receiver has no class: "
         << this_type;
     return false;
-  } else if (!this_type.GetClass()->IsSubClass(mirror::MethodHandle::StaticClass())) {
+  } else if (!this_type.GetClass()->IsSubClass(mirror::MethodHandle::StaticClass()) &&
+             !this_type.GetClass()->IsSubClass(mirror::VarHandle::StaticClass())) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD)
-        << "invoke-polymorphic receiver is not a subclass of MethodHandle: "
+        << "invoke-polymorphic receiver is not a subclass of MethodHandle or VarHandle: "
         << this_type;
     return false;
   }
diff --git a/test/954-invoke-polymorphic-verifier/expected.txt b/test/954-invoke-polymorphic-verifier/expected.txt
index 5df393a..d49af96 100644
--- a/test/954-invoke-polymorphic-verifier/expected.txt
+++ b/test/954-invoke-polymorphic-verifier/expected.txt
@@ -5,6 +5,12 @@
 java.lang.VerifyError: Verifier rejected class TooManyArguments: void TooManyArguments.<init>() failed to verify: void TooManyArguments.<init>(): void TooManyArguments.<init>(): Rejecting invocation, expected 4 argument registers, method signature has 3
 java.lang.VerifyError: Verifier rejected class BadThis: void BadThis.<init>() failed to verify: void BadThis.<init>(): void BadThis.<init>(): 'this' argument 'Precise Reference: java.lang.String' not instance of 'Reference: java.lang.invoke.MethodHandle'
 java.lang.VerifyError: Verifier rejected class FakeSignaturePolymorphic: void FakeSignaturePolymorphic.<init>() failed to verify: void FakeSignaturePolymorphic.<init>(): void FakeSignaturePolymorphic.<init>(): invoke type (METHOD_POLYMORPHIC) does not match method type of java.lang.Object Main.invoke(java.lang.Object[])
-java.lang.VerifyError: Verifier rejected class BetterFakeSignaturePolymorphic: void BetterFakeSignaturePolymorphic.<init>() failed to verify: void BetterFakeSignaturePolymorphic.<init>(): Signature polymorphic method must be declared in java.lang.invoke.MethodClass
+java.lang.VerifyError: Verifier rejected class BetterFakeSignaturePolymorphic: void BetterFakeSignaturePolymorphic.<init>() failed to verify: void BetterFakeSignaturePolymorphic.<init>(): Signature polymorphic method in unsuppported class: Main
 Passed Subclass test
 java.lang.VerifyError: Verifier rejected class Unresolved: void Unresolved.<init>() failed to verify: void Unresolved.<init>(): invoke-polymorphic receiver has no class: Unresolved Reference: other.thing.Foo
+Passed VarHandleHappyAccessors test
+java.lang.VerifyError: Verifier rejected class VarHandleUnhappyAccessors: void VarHandleUnhappyAccessors.compareAndExchange(java.lang.invoke.VarHandle, java.lang.Object[]) failed to verify: void VarHandleUnhappyAccessors.compareAndExchange(java.lang.invoke.VarHandle, java.lang.Object[]): void VarHandleUnhappyAccessors.compareAndExchange(java.lang.invoke.VarHandle, java.lang.Object[]): couldn't find method java.lang.invoke.VarHandle.compareAndExchange ([Ljava/lang/Object;)Ljava/lang/Integer;
+ void VarHandleUnhappyAccessors.compareAndExchangeAcquire(java.lang.invoke.VarHandle) failed to verify: void VarHandleUnhappyAccessors.compareAndExchangeAcquire(java.lang.invoke.VarHandle): void VarHandleUnhappyAccessors.compareAndExchangeAcquire(java.lang.invoke.VarHandle): couldn't find method java.lang.invoke.VarHandle.compareAndExchangeAcquire (I)Ljava/lang/Object;
+ void VarHandleUnhappyAccessors.compareAndExchangeRelease(java.lang.invoke.VarHandle) failed to verify: void VarHandleUnhappyAccessors.compareAndExchangeRelease(java.lang.invoke.VarHandle): void VarHandleUnhappyAccessors.compareAndExchangeRelease(java.lang.invoke.VarHandle): couldn't find method java.lang.invoke.VarHandle.compareAndExchangeRelease ()V
+ void VarHandleUnhappyAccessors.compareAndSet(java.lang.invoke.VarHandle) failed to verify: void VarHandleUnhappyAccessors.compareAndSet(java.lang.invoke.VarHandle): void VarHandleUnhappyAccessors.compareAndSet(java.lang.invoke.VarHandle): couldn't find method java.lang.invoke.VarHandle.compareAndSet (I)Z
+java.lang.VerifyError: Verifier rejected class VarHandleUnknownAccessor: void VarHandleUnknownAccessor.<init>() failed to verify: void VarHandleUnknownAccessor.<init>(): void VarHandleUnknownAccessor.<init>(): couldn't find method java.lang.invoke.VarHandle.unknownAccessor ([Ljava/lang/Object;)Ljava/lang/Object;
diff --git a/test/954-invoke-polymorphic-verifier/smali/Main.smali b/test/954-invoke-polymorphic-verifier/smali/Main.smali
index 5b5e555..e35aae7 100644
--- a/test/954-invoke-polymorphic-verifier/smali/Main.smali
+++ b/test/954-invoke-polymorphic-verifier/smali/Main.smali
@@ -50,6 +50,12 @@
   invoke-static {v0}, LMain;->test(Ljava/lang/String;)V
   const-string v0, "Unresolved"
   invoke-static {v0}, LMain;->test(Ljava/lang/String;)V
+  const-string v0, "VarHandleHappyAccessors"
+  invoke-static {v0}, LMain;->test(Ljava/lang/String;)V
+  const-string v0, "VarHandleUnhappyAccessors"
+  invoke-static {v0}, LMain;->test(Ljava/lang/String;)V
+const-string v0, "VarHandleUnknownAccessor"
+  invoke-static {v0}, LMain;->test(Ljava/lang/String;)V
   return-void
 .end method
 
diff --git a/test/954-invoke-polymorphic-verifier/smali/VarHandleHappyAccessors.smali b/test/954-invoke-polymorphic-verifier/smali/VarHandleHappyAccessors.smali
new file mode 100644
index 0000000..ec6aa5b
--- /dev/null
+++ b/test/954-invoke-polymorphic-verifier/smali/VarHandleHappyAccessors.smali
@@ -0,0 +1,72 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.source "VarHandleHappyAccessors.smali"
+
+.class public LVarHandleHappyAccessors;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+.registers 4
+  invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+  invoke-static {}, LVarHandleHappyAccessors;->getVarHandle()Ljava/lang/invoke/VarHandle;
+  move-result-object v0
+  if-eqz v0, :done
+  const/4 v1, 0
+  move-object v1, v1
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->compareAndSet([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->get([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndAdd([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndAddAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndAddRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseAnd([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseAndAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseAndRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseOr([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseOrAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseOrRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseXor([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseXorAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseXorRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndSet([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndSetAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndSetRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getOpaque([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getVolatile([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->set([Ljava/lang/Object;)V, ([Ljava/lang/Object;)V
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->setOpaque([Ljava/lang/Object;)V, ([Ljava/lang/Object;)V
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->setRelease([Ljava/lang/Object;)V, ([Ljava/lang/Object;)V
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->setVolatile([Ljava/lang/Object;)V, ([Ljava/lang/Object;)V
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->weakCompareAndSet([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->weakCompareAndSetAcquire([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->weakCompareAndSetPlain([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object;
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->weakCompareAndSetRelease([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object;
+  return-void
+  :done
+  sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+  const-string v1, "Passed VarHandleHappyAccessors test"
+  invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+  return-void
+.end method
+
+.method public static getVarHandle()Ljava/lang/invoke/VarHandle;
+.registers 1
+  const/4 v0, 0
+  return-object v0
+.end method
diff --git a/test/954-invoke-polymorphic-verifier/smali/VarHandleUnhappyAccessors.smali b/test/954-invoke-polymorphic-verifier/smali/VarHandleUnhappyAccessors.smali
new file mode 100644
index 0000000..0832c04
--- /dev/null
+++ b/test/954-invoke-polymorphic-verifier/smali/VarHandleUnhappyAccessors.smali
@@ -0,0 +1,70 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.source "VarHandleUnhappyAccessors.smali"
+
+.class public LVarHandleUnhappyAccessors;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+.registers 4
+  invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+  invoke-static {}, LVarHandleUnhappyAccessors;->getVarHandle()Ljava/lang/invoke/VarHandle;
+  move-result-object v0
+  invoke-static {}, LVarHandleUnhappyAccessors;->getObjectArray()[Ljava/lang/Object;
+  move-result-object v1
+  invoke-static {v0, v1}, LVarHandleUnhappyAccessors;->compareAndExchange(Ljava/lang/invoke/VarHandle;[Ljava/lang/Object;)V
+  invoke-static {v0}, LVarHandleUnhappyAccessors;->compareAndExchangeAcquire(Ljava/lang/invoke/VarHandle;)V
+  invoke-static {v0}, LVarHandleUnhappyAccessors;->compareAndExchangeRelease(Ljava/lang/invoke/VarHandle;)V
+  invoke-static {v0}, LVarHandleUnhappyAccessors;->compareAndSet(Ljava/lang/invoke/VarHandle;)V
+  return-void
+.end method
+
+# The following methods all invoke VarHandle accessors but the targetted
+# accessor methods have the wrong signature.
+
+.method public static compareAndExchange(Ljava/lang/invoke/VarHandle;[Ljava/lang/Object;)V
+.registers 2
+  invoke-polymorphic {p0, p1}, Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Integer;, ([Ljava/lang/Object;)Ljava/lang/Object;
+.end method
+
+.method public static compareAndExchangeAcquire(Ljava/lang/invoke/VarHandle;)V
+.registers 2
+  const/4 v0, 1
+  invoke-polymorphic {p0, v0}, Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire(I)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+.end method
+
+.method public static compareAndExchangeRelease(Ljava/lang/invoke/VarHandle;)V
+.registers 1
+  invoke-polymorphic {p0}, Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease()V, ([Ljava/lang/Object;)Ljava/lang/Object;
+.end method
+
+.method public static compareAndSet(Ljava/lang/invoke/VarHandle;)V
+.registers 2
+  const/4 v0, 1
+  invoke-polymorphic {p0, v0}, Ljava/lang/invoke/VarHandle;->compareAndSet(I)Z, ([Ljava/lang/Object;)Ljava/lang/Object;
+.end method
+
+.method public static getVarHandle()Ljava/lang/invoke/VarHandle;
+.registers 1
+  const/4 v0, 0
+  return-object v0
+.end method
+
+.method public static getObjectArray()[Ljava/lang/Object;
+.registers 1
+  const/4 v0, 0
+  return-object v0
+.end method
diff --git a/test/954-invoke-polymorphic-verifier/smali/VarHandleUnknownAccessor.smali b/test/954-invoke-polymorphic-verifier/smali/VarHandleUnknownAccessor.smali
new file mode 100644
index 0000000..35084f5
--- /dev/null
+++ b/test/954-invoke-polymorphic-verifier/smali/VarHandleUnknownAccessor.smali
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.source "VarHandleUnknownAccessor.smali"
+
+.class public LVarHandleUnknownAccessor;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+.registers 4
+  invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+  invoke-static {}, LVarHandleUnknownAccessor;->getVarHandle()Ljava/lang/invoke/VarHandle;
+  move-result-object v0
+  const/4 v1, 0
+  move-object v1, v1
+  # Attempt invoke-polymorphic on VarHandle.unknownAccessor().
+  invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->unknownAccessor([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object;
+  return-void
+.end method
+
+.method public static getVarHandle()Ljava/lang/invoke/VarHandle;
+.registers 1
+  const/4 v0, 0
+  return-object v0
+.end method