Merge "ART: Add mirror classes for VarHandles"
diff --git a/runtime/Android.bp b/runtime/Android.bp
index afc7d27..ff5ae9f 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -153,6 +153,7 @@
         "mirror/stack_trace_element.cc",
         "mirror/string.cc",
         "mirror/throwable.cc",
+        "mirror/var_handle.cc",
         "monitor.cc",
         "native_bridge_art_interface.cc",
         "native_stack_dump.cc",
@@ -599,6 +600,7 @@
         "mirror/dex_cache_test.cc",
         "mirror/method_type_test.cc",
         "mirror/object_test.cc",
+        "mirror/var_handle_test.cc",
         "monitor_pool_test.cc",
         "monitor_test.cc",
         "oat_file_test.cc",
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b199933..5435c11 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -98,6 +98,7 @@
 #include "mirror/reference-inl.h"
 #include "mirror/stack_trace_element.h"
 #include "mirror/string-inl.h"
+#include "mirror/var_handle.h"
 #include "native/dalvik_system_DexFile.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "oat.h"
@@ -698,6 +699,12 @@
   SetClassRoot(kJavaLangReflectMethodArrayClass, class_root);
   mirror::Method::SetArrayClass(class_root);
 
+  // Create java.lang.invoke.CallSite.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeCallSite, class_root);
+  mirror::CallSite::SetClass(class_root);
+
   // Create java.lang.invoke.MethodType.class root
   class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodType;");
   CHECK(class_root != nullptr);
@@ -716,11 +723,35 @@
   SetClassRoot(kJavaLangInvokeMethodHandlesLookup, class_root);
   mirror::MethodHandlesLookup::SetClass(class_root);
 
-  // Create java.lang.invoke.CallSite.class root
-  class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;");
+  // Create java.lang.invoke.VarHandle.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/VarHandle;");
   CHECK(class_root != nullptr);
-  SetClassRoot(kJavaLangInvokeCallSite, class_root);
-  mirror::CallSite::SetClass(class_root);
+  SetClassRoot(kJavaLangInvokeVarHandle, class_root);
+  mirror::VarHandle::SetClass(class_root);
+
+  // Create java.lang.invoke.FieldVarHandle.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/FieldVarHandle;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeFieldVarHandle, class_root);
+  mirror::FieldVarHandle::SetClass(class_root);
+
+  // Create java.lang.invoke.ArrayElementVarHandle.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/ArrayElementVarHandle;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeArrayElementVarHandle, class_root);
+  mirror::ArrayElementVarHandle::SetClass(class_root);
+
+  // Create java.lang.invoke.ByteArrayViewVarHandle.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteArrayViewVarHandle;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeByteArrayViewVarHandle, class_root);
+  mirror::ByteArrayViewVarHandle::SetClass(class_root);
+
+  // Create java.lang.invoke.ByteBufferViewVarHandle.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteBufferViewVarHandle;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeByteBufferViewVarHandle, class_root);
+  mirror::ByteBufferViewVarHandle::SetClass(class_root);
 
   class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;");
   CHECK(class_root != nullptr);
@@ -988,10 +1019,15 @@
   mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass));
   mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod));
   mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass));
-  mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType));
+  mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite));
   mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl));
   mirror::MethodHandlesLookup::SetClass(GetClassRoot(kJavaLangInvokeMethodHandlesLookup));
-  mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite));
+  mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType));
+  mirror::VarHandle::SetClass(GetClassRoot(kJavaLangInvokeVarHandle));
+  mirror::FieldVarHandle::SetClass(GetClassRoot(kJavaLangInvokeFieldVarHandle));
+  mirror::ArrayElementVarHandle::SetClass(GetClassRoot(kJavaLangInvokeArrayElementVarHandle));
+  mirror::ByteArrayViewVarHandle::SetClass(GetClassRoot(kJavaLangInvokeByteArrayViewVarHandle));
+  mirror::ByteBufferViewVarHandle::SetClass(GetClassRoot(kJavaLangInvokeByteBufferViewVarHandle));
   mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
   mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
   mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
@@ -2083,10 +2119,15 @@
   mirror::IntArray::ResetArrayClass();
   mirror::LongArray::ResetArrayClass();
   mirror::ShortArray::ResetArrayClass();
+  mirror::CallSite::ResetClass();
   mirror::MethodType::ResetClass();
   mirror::MethodHandleImpl::ResetClass();
   mirror::MethodHandlesLookup::ResetClass();
-  mirror::CallSite::ResetClass();
+  mirror::VarHandle::ResetClass();
+  mirror::FieldVarHandle::ResetClass();
+  mirror::ArrayElementVarHandle::ResetClass();
+  mirror::ByteArrayViewVarHandle::ResetClass();
+  mirror::ByteBufferViewVarHandle::ResetClass();
   mirror::EmulatedStackFrame::ResetClass();
   Thread* const self = Thread::Current();
   for (const ClassLoaderData& data : class_loaders_) {
@@ -8484,6 +8525,11 @@
     "Ljava/lang/invoke/MethodHandleImpl;",
     "Ljava/lang/invoke/MethodHandles$Lookup;",
     "Ljava/lang/invoke/MethodType;",
+    "Ljava/lang/invoke/VarHandle;",
+    "Ljava/lang/invoke/FieldVarHandle;",
+    "Ljava/lang/invoke/ArrayElementVarHandle;",
+    "Ljava/lang/invoke/ByteArrayViewVarHandle;",
+    "Ljava/lang/invoke/ByteBufferViewVarHandle;",
     "Ljava/lang/ClassLoader;",
     "Ljava/lang/Throwable;",
     "Ljava/lang/ClassNotFoundException;",
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index eba2022..2d9ec5a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -120,6 +120,11 @@
     kJavaLangInvokeMethodHandleImpl,
     kJavaLangInvokeMethodHandlesLookup,
     kJavaLangInvokeMethodType,
+    kJavaLangInvokeVarHandle,
+    kJavaLangInvokeFieldVarHandle,
+    kJavaLangInvokeArrayElementVarHandle,
+    kJavaLangInvokeByteArrayViewVarHandle,
+    kJavaLangInvokeByteBufferViewVarHandle,
     kJavaLangClassLoader,
     kJavaLangThrowable,
     kJavaLangClassNotFoundException,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 3e92317..4d92826 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -48,6 +48,7 @@
 #include "mirror/reference.h"
 #include "mirror/stack_trace_element.h"
 #include "mirror/string-inl.h"
+#include "mirror/var_handle.h"
 #include "scoped_thread_state_change-inl.h"
 #include "standard_dex_file.h"
 #include "thread-current-inl.h"
@@ -777,6 +778,39 @@
   }
 };
 
+struct VarHandleOffsets : public CheckOffsets<mirror::VarHandle> {
+  VarHandleOffsets() : CheckOffsets<mirror::VarHandle>(
+      false, "Ljava/lang/invoke/VarHandle;") {
+    addOffset(OFFSETOF_MEMBER(mirror::VarHandle, access_modes_bit_mask_), "accessModesBitMask");
+    addOffset(OFFSETOF_MEMBER(mirror::VarHandle, coordinate_type0_), "coordinateType0");
+    addOffset(OFFSETOF_MEMBER(mirror::VarHandle, coordinate_type1_), "coordinateType1");
+    addOffset(OFFSETOF_MEMBER(mirror::VarHandle, var_type_), "varType");
+  }
+};
+
+struct FieldVarHandleOffsets : public CheckOffsets<mirror::FieldVarHandle> {
+  FieldVarHandleOffsets() : CheckOffsets<mirror::FieldVarHandle>(
+      false, "Ljava/lang/invoke/FieldVarHandle;") {
+    addOffset(OFFSETOF_MEMBER(mirror::FieldVarHandle, art_field_), "artField");
+  }
+};
+
+struct ByteArrayViewVarHandleOffsets : public CheckOffsets<mirror::ByteArrayViewVarHandle> {
+  ByteArrayViewVarHandleOffsets() : CheckOffsets<mirror::ByteArrayViewVarHandle>(
+      false, "Ljava/lang/invoke/ByteArrayViewVarHandle;") {
+    addOffset(OFFSETOF_MEMBER(mirror::ByteArrayViewVarHandle, native_byte_order_),
+              "nativeByteOrder");
+  }
+};
+
+struct ByteBufferViewVarHandleOffsets : public CheckOffsets<mirror::ByteBufferViewVarHandle> {
+  ByteBufferViewVarHandleOffsets() : CheckOffsets<mirror::ByteBufferViewVarHandle>(
+      false, "Ljava/lang/invoke/ByteBufferViewVarHandle;") {
+    addOffset(OFFSETOF_MEMBER(mirror::ByteBufferViewVarHandle, native_byte_order_),
+              "nativeByteOrder");
+  }
+};
+
 // C++ fields must exactly match the fields in the Java classes. If this fails,
 // reorder the fields in the C++ class. Managed class fields are ordered by
 // ClassLinker::LinkFields.
@@ -802,6 +836,10 @@
   EXPECT_TRUE(MethodHandlesLookupOffsets().Check());
   EXPECT_TRUE(EmulatedStackFrameOffsets().Check());
   EXPECT_TRUE(CallSiteOffsets().Check());
+  EXPECT_TRUE(VarHandleOffsets().Check());
+  EXPECT_TRUE(FieldVarHandleOffsets().Check());
+  EXPECT_TRUE(ByteArrayViewVarHandleOffsets().Check());
+  EXPECT_TRUE(ByteBufferViewVarHandleOffsets().Check());
 }
 
 TEST_F(ClassLinkerTest, FindClassNonexistent) {
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
new file mode 100644
index 0000000..e7eac1a
--- /dev/null
+++ b/runtime/mirror/var_handle.cc
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "var_handle.h"
+
+#include "class-inl.h"
+#include "class_linker.h"
+#include "gc_root-inl.h"
+#include "method_type.h"
+
+namespace art {
+namespace mirror {
+
+namespace {
+
+// Enumeration for describing the parameter and return types of an AccessMode.
+enum class AccessModeTemplate : uint32_t {
+  kGet,                 // T Op(C0..CN)
+  kSet,                 // void Op(C0..CN, T)
+  kCompareAndSet,       // boolean Op(C0..CN, T, T)
+  kCompareAndExchange,  // T Op(C0..CN, T, T)
+  kGetAndUpdate,        // T Op(C0..CN, T)
+};
+
+// Look up the AccessModeTemplate for a given VarHandle
+// AccessMode. This simplifies finding the correct signature for a
+// VarHandle accessor method.
+AccessModeTemplate GetAccessModeTemplate(VarHandle::AccessMode access_mode) {
+  switch (access_mode) {
+    case VarHandle::AccessMode::kGet:
+      return AccessModeTemplate::kGet;
+    case VarHandle::AccessMode::kSet:
+      return AccessModeTemplate::kSet;
+    case VarHandle::AccessMode::kGetVolatile:
+      return AccessModeTemplate::kGet;
+    case VarHandle::AccessMode::kSetVolatile:
+      return AccessModeTemplate::kSet;
+    case VarHandle::AccessMode::kGetAcquire:
+      return AccessModeTemplate::kGet;
+    case VarHandle::AccessMode::kSetRelease:
+      return AccessModeTemplate::kSet;
+    case VarHandle::AccessMode::kGetOpaque:
+      return AccessModeTemplate::kGet;
+    case VarHandle::AccessMode::kSetOpaque:
+      return AccessModeTemplate::kSet;
+    case VarHandle::AccessMode::kCompareAndSet:
+      return AccessModeTemplate::kCompareAndSet;
+    case VarHandle::AccessMode::kCompareAndExchange:
+      return AccessModeTemplate::kCompareAndExchange;
+    case VarHandle::AccessMode::kCompareAndExchangeAcquire:
+      return AccessModeTemplate::kCompareAndExchange;
+    case VarHandle::AccessMode::kCompareAndExchangeRelease:
+      return AccessModeTemplate::kCompareAndExchange;
+    case VarHandle::AccessMode::kWeakCompareAndSetPlain:
+      return AccessModeTemplate::kCompareAndSet;
+    case VarHandle::AccessMode::kWeakCompareAndSet:
+      return AccessModeTemplate::kCompareAndSet;
+    case VarHandle::AccessMode::kWeakCompareAndSetAcquire:
+      return AccessModeTemplate::kCompareAndSet;
+    case VarHandle::AccessMode::kWeakCompareAndSetRelease:
+      return AccessModeTemplate::kCompareAndSet;
+    case VarHandle::AccessMode::kGetAndSet:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndSetAcquire:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndSetRelease:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndAdd:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndAddAcquire:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndAddRelease:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndBitwiseOr:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndBitwiseOrRelease:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndBitwiseOrAcquire:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndBitwiseAnd:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndBitwiseAndRelease:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndBitwiseAndAcquire:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndBitwiseXor:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndBitwiseXorRelease:
+      return AccessModeTemplate::kGetAndUpdate;
+    case VarHandle::AccessMode::kGetAndBitwiseXorAcquire:
+      return AccessModeTemplate::kGetAndUpdate;
+  }
+}
+
+// Returns the number of parameters associated with an
+// AccessModeTemplate and the supplied coordinate types.
+int32_t GetParameterCount(AccessModeTemplate access_mode_template,
+                          ObjPtr<Class> coordinateType0,
+                          ObjPtr<Class> coordinateType1) {
+  int32_t index = 0;
+  if (!coordinateType0.IsNull()) {
+    index++;
+    if (!coordinateType1.IsNull()) {
+      index++;
+    }
+  }
+
+  switch (access_mode_template) {
+    case AccessModeTemplate::kGet:
+      return index;
+    case AccessModeTemplate::kSet:
+    case AccessModeTemplate::kGetAndUpdate:
+      return index + 1;
+    case AccessModeTemplate::kCompareAndSet:
+    case AccessModeTemplate::kCompareAndExchange:
+      return index + 2;
+  }
+  UNREACHABLE();
+}
+
+// Writes the parameter types associated with the AccessModeTemplate
+// into an array. The parameter types are derived from the specified
+// variable type and coordinate types. Returns the number of
+// parameters written.
+int32_t BuildParameterArray(ObjPtr<Class> (&parameters)[VarHandle::kMaxAccessorParameters],
+                            AccessModeTemplate access_mode_template,
+                            ObjPtr<Class> varType,
+                            ObjPtr<Class> coordinateType0,
+                            ObjPtr<Class> coordinateType1)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(varType != nullptr);
+  int32_t index = 0;
+  if (!coordinateType0.IsNull()) {
+    parameters[index++] = coordinateType0;
+    if (!coordinateType1.IsNull()) {
+      parameters[index++] = coordinateType1;
+    }
+  } else {
+    DCHECK(coordinateType1.IsNull());
+  }
+
+  switch (access_mode_template) {
+    case AccessModeTemplate::kCompareAndExchange:
+    case AccessModeTemplate::kCompareAndSet:
+      parameters[index++] = varType;
+      parameters[index++] = varType;
+      return index;
+    case AccessModeTemplate::kGet:
+      return index;
+    case AccessModeTemplate::kGetAndUpdate:
+    case AccessModeTemplate::kSet:
+      parameters[index++] = varType;
+      return index;
+  }
+  return -1;
+}
+
+// Returns the return type associated with an AccessModeTemplate based
+// on the template and the variable type specified.
+Class* GetReturnType(AccessModeTemplate access_mode_template, ObjPtr<Class> varType)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(varType != nullptr);
+  switch (access_mode_template) {
+    case AccessModeTemplate::kCompareAndSet:
+      return Runtime::Current()->GetClassLinker()->FindPrimitiveClass('Z');
+    case AccessModeTemplate::kCompareAndExchange:
+    case AccessModeTemplate::kGet:
+    case AccessModeTemplate::kGetAndUpdate:
+      return varType.Ptr();
+    case AccessModeTemplate::kSet:
+      return Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V');
+  }
+  return nullptr;
+}
+
+ObjectArray<Class>* NewArrayOfClasses(Thread* self, int count)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* const class_linker = runtime->GetClassLinker();
+  ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+  ObjPtr<mirror::Class> array_of_class = class_linker->FindArrayClass(self, &class_type);
+  return ObjectArray<Class>::Alloc(Thread::Current(), array_of_class, count);
+}
+
+}  // namespace
+
+Class* VarHandle::GetVarType() {
+  return GetFieldObject<Class>(VarTypeOffset());
+}
+
+Class* VarHandle::GetCoordinateType0() {
+  return GetFieldObject<Class>(CoordinateType0Offset());
+}
+
+Class* VarHandle::GetCoordinateType1() {
+  return GetFieldObject<Class>(CoordinateType1Offset());
+}
+
+int32_t VarHandle::GetAccessModesBitMask() {
+  return GetField32(AccessModesBitMaskOffset());
+}
+
+bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) {
+  ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+
+  AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
+  // Check return types first.
+  ObjPtr<Class> var_type = GetVarType();
+  ObjPtr<Class> vh_rtype = GetReturnType(access_mode_template, var_type);
+  ObjPtr<Class> void_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V');
+  ObjPtr<Class> mt_rtype = method_type->GetRType();
+
+  // If the mt_rtype is void, the result of the operation will be discarded (okay).
+  if (mt_rtype != void_type && mt_rtype != vh_rtype) {
+    return false;
+  }
+
+  // Check the number of parameters matches.
+  ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters];
+  const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes,
+                                                      access_mode_template,
+                                                      var_type,
+                                                      GetCoordinateType0(),
+                                                      GetCoordinateType1());
+  if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) {
+    return false;
+  }
+
+  // Check the parameter types match.
+  ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes();
+  for (int32_t i = 0; i < vh_ptypes_count; ++i) {
+    if (mt_ptypes->Get(i) != vh_ptypes[i].Ptr()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self,
+                                                  ObjPtr<VarHandle> var_handle,
+                                                  AccessMode access_mode) {
+  // This is a static as the var_handle might be moved by the GC during it's execution.
+  AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
+
+  StackHandleScope<3> hs(self);
+  Handle<VarHandle> vh = hs.NewHandle(var_handle);
+  Handle<Class> rtype = hs.NewHandle(GetReturnType(access_mode_template, vh->GetVarType()));
+  const int32_t ptypes_count =
+      GetParameterCount(access_mode_template, vh->GetCoordinateType0(), vh->GetCoordinateType1());
+  Handle<ObjectArray<Class>> ptypes = hs.NewHandle(NewArrayOfClasses(self, ptypes_count));
+  if (ptypes == nullptr) {
+    return nullptr;
+  }
+
+  ObjPtr<Class> ptypes_array[VarHandle::kMaxAccessorParameters];
+  BuildParameterArray(ptypes_array,
+                      access_mode_template,
+                      vh->GetVarType(),
+                      vh->GetCoordinateType0(),
+                      vh->GetCoordinateType1());
+  for (int32_t i = 0; i < ptypes_count; ++i) {
+    ptypes->Set(i, ptypes_array[i].Ptr());
+  }
+  return MethodType::Create(self, rtype, ptypes);
+}
+
+MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode access_mode) {
+  return GetMethodTypeForAccessMode(self, this, access_mode);
+}
+
+void VarHandle::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void VarHandle::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void VarHandle::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> VarHandle::static_class_;
+
+ArtField* FieldVarHandle::GetField() {
+  uintptr_t opaque_field = static_cast<uintptr_t>(GetField64(ArtFieldOffset()));
+  return reinterpret_cast<ArtField*>(opaque_field);
+}
+
+Class* FieldVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+  return static_class_.Read();
+}
+
+void FieldVarHandle::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void FieldVarHandle::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void FieldVarHandle::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> FieldVarHandle::static_class_;
+
+Class* ArrayElementVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+  return static_class_.Read();
+}
+
+void ArrayElementVarHandle::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void ArrayElementVarHandle::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void ArrayElementVarHandle::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> ArrayElementVarHandle::static_class_;
+
+bool ByteArrayViewVarHandle::GetNativeByteOrder() {
+  return GetFieldBoolean(NativeByteOrderOffset());
+}
+
+Class* ByteArrayViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+  return static_class_.Read();
+}
+
+void ByteArrayViewVarHandle::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void ByteArrayViewVarHandle::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void ByteArrayViewVarHandle::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> ByteArrayViewVarHandle::static_class_;
+
+bool ByteBufferViewVarHandle::GetNativeByteOrder() {
+  return GetFieldBoolean(NativeByteOrderOffset());
+}
+
+Class* ByteBufferViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+  return static_class_.Read();
+}
+
+void ByteBufferViewVarHandle::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void ByteBufferViewVarHandle::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void ByteBufferViewVarHandle::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> ByteBufferViewVarHandle::static_class_;
+
+}  // namespace mirror
+}  // namespace art
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
new file mode 100644
index 0000000..a2a5d8c
--- /dev/null
+++ b/runtime/mirror/var_handle.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_VAR_HANDLE_H_
+#define ART_RUNTIME_MIRROR_VAR_HANDLE_H_
+
+#include "handle.h"
+#include "gc_root.h"
+#include "object.h"
+
+namespace art {
+
+template<class T> class Handle;
+struct VarHandleOffsets;
+struct FieldVarHandleOffsets;
+struct ByteArrayViewVarHandleOffsets;
+struct ByteBufferViewVarHandleOffsets;
+
+namespace mirror {
+
+class MethodType;
+class VarHandleTest;
+
+// C++ mirror of java.lang.invoke.VarHandle
+class MANAGED VarHandle : public Object {
+ public:
+  // The maximum number of parameters a VarHandle accessor method can
+  // take. The Worst case is equivalent to a compare-and-swap
+  // operation on an array element which requires four parameters
+  // (array, index, old, new).
+  static constexpr int kMaxAccessorParameters = 4;
+
+  // Enumeration of the possible access modes. This mirrors the enum
+  // in java.lang.invoke.VarHandle.
+  enum class AccessMode : uint32_t {
+    kGet,
+    kSet,
+    kGetVolatile,
+    kSetVolatile,
+    kGetAcquire,
+    kSetRelease,
+    kGetOpaque,
+    kSetOpaque,
+    kCompareAndSet,
+    kCompareAndExchange,
+    kCompareAndExchangeAcquire,
+    kCompareAndExchangeRelease,
+    kWeakCompareAndSetPlain,
+    kWeakCompareAndSet,
+    kWeakCompareAndSetAcquire,
+    kWeakCompareAndSetRelease,
+    kGetAndSet,
+    kGetAndSetAcquire,
+    kGetAndSetRelease,
+    kGetAndAdd,
+    kGetAndAddAcquire,
+    kGetAndAddRelease,
+    kGetAndBitwiseOr,
+    kGetAndBitwiseOrRelease,
+    kGetAndBitwiseOrAcquire,
+    kGetAndBitwiseAnd,
+    kGetAndBitwiseAndRelease,
+    kGetAndBitwiseAndAcquire,
+    kGetAndBitwiseXor,
+    kGetAndBitwiseXorRelease,
+    kGetAndBitwiseXorAcquire,
+  };
+
+  // Returns true if the AccessMode specified is a supported operation.
+  bool IsAccessModeSupported(AccessMode accessMode) REQUIRES_SHARED(Locks::mutator_lock_) {
+    return (GetAccessModesBitMask() & (1u << static_cast<uint32_t>(accessMode))) != 0;
+  }
+
+  // Returns true if the MethodType specified is compatible with the
+  // method type associated with the specified AccessMode. The
+  // supplied MethodType is assumed to be from the point of invocation
+  // so it is valid for the supplied MethodType to have a void return
+  // value when the return value for the AccessMode is non-void. This
+  // corresponds to the result of the accessor being discarded.
+  bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Allocates and returns the MethodType associated with the
+  // AccessMode. No check is made for whether the AccessMode is a
+  // supported operation so the MethodType can be used when raising a
+  // WrongMethodTypeException exception.
+  MethodType* GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return static_class_.Read();
+  }
+
+  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_);
+
+ private:
+  Class* GetVarType() REQUIRES_SHARED(Locks::mutator_lock_);
+  Class* GetCoordinateType0() REQUIRES_SHARED(Locks::mutator_lock_);
+  Class* GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_);
+  int32_t GetAccessModesBitMask() REQUIRES_SHARED(Locks::mutator_lock_);
+
+  static MethodType* GetMethodTypeForAccessMode(Thread* self,
+                                                ObjPtr<VarHandle> var_handle,
+                                                AccessMode access_mode)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  static MemberOffset VarTypeOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(VarHandle, var_type_));
+  }
+
+  static MemberOffset CoordinateType0Offset() {
+    return MemberOffset(OFFSETOF_MEMBER(VarHandle, coordinate_type0_));
+  }
+
+  static MemberOffset CoordinateType1Offset() {
+    return MemberOffset(OFFSETOF_MEMBER(VarHandle, coordinate_type1_));
+  }
+
+  static MemberOffset AccessModesBitMaskOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(VarHandle, access_modes_bit_mask_));
+  }
+
+  HeapReference<mirror::Class> coordinate_type0_;
+  HeapReference<mirror::Class> coordinate_type1_;
+  HeapReference<mirror::Class> var_type_;
+  int32_t access_modes_bit_mask_;
+
+  // Root representing java.lang.invoke.VarHandle.class.
+  static GcRoot<mirror::Class> static_class_;
+
+  friend class VarHandleTest;  // for testing purposes
+  friend struct art::VarHandleOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(VarHandle);
+};
+
+// Represents a VarHandle to a static or instance field.
+// The corresponding managed class in libart java.lang.invoke.FieldVarHandle.
+class MANAGED FieldVarHandle : public VarHandle {
+ public:
+  ArtField* GetField() REQUIRES_SHARED(Locks::mutator_lock_);
+
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  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_);
+
+ private:
+  static MemberOffset ArtFieldOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(FieldVarHandle, art_field_));
+  }
+
+  // ArtField instance corresponding to variable for accessors.
+  int64_t art_field_;
+
+  // Root representing java.lang.invoke.FieldVarHandle.class.
+  static GcRoot<mirror::Class> static_class_;
+
+  friend class VarHandleTest;  // for var_handle_test.
+  friend struct art::FieldVarHandleOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(FieldVarHandle);
+};
+
+// Represents a VarHandle providing accessors to an array.
+// The corresponding managed class in libart java.lang.invoke.ArrayElementVarHandle.
+class MANAGED ArrayElementVarHandle : public VarHandle {
+ public:
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  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_);
+
+ private:
+  // Root representing java.lang.invoke.ArrayElementVarHandle.class.
+  static GcRoot<mirror::Class> static_class_;
+
+  friend class VarHandleTest;
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ArrayElementVarHandle);
+};
+
+// Represents a VarHandle providing accessors to a view of a ByteArray.
+// The corresponding managed class in libart java.lang.invoke.ByteArrayViewVarHandle.
+class MANAGED ByteArrayViewVarHandle : public VarHandle {
+ public:
+  bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_);
+
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  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_);
+
+ private:
+  static MemberOffset NativeByteOrderOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(ByteArrayViewVarHandle, native_byte_order_));
+  }
+
+  // Flag indicating that accessors should use native byte-ordering.
+  uint8_t native_byte_order_;
+
+  // Root representing java.lang.invoke.ByteArrayViewVarHandle.class.
+  static GcRoot<mirror::Class> static_class_;
+
+  friend class VarHandleTest;  // for var_handle_test.
+  friend struct art::ByteArrayViewVarHandleOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArrayViewVarHandle);
+};
+
+// Represents a VarHandle providing accessors to a view of a ByteBuffer
+// The corresponding managed class in libart java.lang.invoke.ByteBufferViewVarHandle.
+class MANAGED ByteBufferViewVarHandle : public VarHandle {
+ public:
+  bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_);
+
+  static ByteBufferViewVarHandle* Create(Thread* const self, bool native_byte_order)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  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_);
+
+ private:
+  static MemberOffset NativeByteOrderOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(ByteBufferViewVarHandle, native_byte_order_));
+  }
+
+  // Flag indicating that accessors should use native byte-ordering.
+  uint8_t native_byte_order_;
+
+  // Root representing java.lang.invoke.ByteBufferViewVarHandle.class.
+  static GcRoot<mirror::Class> static_class_;
+
+  friend class VarHandleTest;  // for var_handle_test.
+  friend struct art::ByteBufferViewVarHandleOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ByteBufferViewVarHandle);
+};
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_VAR_HANDLE_H_
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
new file mode 100644
index 0000000..0e1c994
--- /dev/null
+++ b/runtime/mirror/var_handle_test.cc
@@ -0,0 +1,991 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "var_handle.h"
+
+#include <string>
+#include <vector>
+
+#include "art_field-inl.h"
+#include "class-inl.h"
+#include "class_linker-inl.h"
+#include "class_loader.h"
+#include "common_runtime_test.h"
+#include "handle_scope-inl.h"
+#include "jvalue-inl.h"
+#include "method_type.h"
+#include "object_array-inl.h"
+#include "reflection.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+namespace mirror {
+
+// Tests for mirror::VarHandle and it's descendents.
+class VarHandleTest : public CommonRuntimeTest {
+ public:
+  static FieldVarHandle* CreateFieldVarHandle(Thread* const self,
+                                              ArtField* art_field,
+                                              int32_t access_modes_bit_mask)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+    StackHandleScope<4> hs(self);
+    Handle<FieldVarHandle> fvh = hs.NewHandle(
+        ObjPtr<FieldVarHandle>::DownCast(FieldVarHandle::StaticClass()->AllocObject(self)));
+    Handle<Class> var_type = hs.NewHandle(art_field->GetType<true>().Ptr());
+
+    if (art_field->IsStatic()) {
+      InitializeVarHandle(fvh.Get(), var_type, access_modes_bit_mask);
+    } else {
+      Handle<Class> declaring_type = hs.NewHandle(art_field->GetDeclaringClass().Ptr());
+      InitializeVarHandle(fvh.Get(),
+                          var_type,
+                          declaring_type,
+                          access_modes_bit_mask);
+    }
+    uintptr_t opaque_field = reinterpret_cast<uintptr_t>(art_field);
+    fvh->SetField64<false>(FieldVarHandle::ArtFieldOffset(), opaque_field);
+    return fvh.Get();
+  }
+
+  static ArrayElementVarHandle* CreateArrayElementVarHandle(Thread* const self,
+                                                            Handle<Class> array_class,
+                                                            int32_t access_modes_bit_mask)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+    StackHandleScope<3> hs(self);
+    Handle<ArrayElementVarHandle> vh = hs.NewHandle(
+        ObjPtr<ArrayElementVarHandle>::DownCast(
+            ArrayElementVarHandle::StaticClass()->AllocObject(self)));
+
+    // Initialize super class fields
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    Handle<Class> var_type = hs.NewHandle(array_class->GetComponentType());
+    Handle<Class> index_type = hs.NewHandle(class_linker->FindPrimitiveClass('I'));
+    InitializeVarHandle(vh.Get(), var_type, array_class, index_type, access_modes_bit_mask);
+    return vh.Get();
+  }
+
+  static ByteArrayViewVarHandle* CreateByteArrayViewVarHandle(Thread* const self,
+                                                              Handle<Class> view_array_class,
+                                                              bool native_byte_order,
+                                                              int32_t access_modes_bit_mask)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+    StackHandleScope<4> hs(self);
+    Handle<ByteArrayViewVarHandle> bvh = hs.NewHandle(
+        ObjPtr<ByteArrayViewVarHandle>::DownCast(
+            ByteArrayViewVarHandle::StaticClass()->AllocObject(self)));
+
+    // Initialize super class fields
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    Handle<Class> var_type = hs.NewHandle(view_array_class->GetComponentType());
+    Handle<Class> index_type = hs.NewHandle(class_linker->FindPrimitiveClass('I'));
+    ObjPtr<mirror::Class> byte_class = class_linker->FindPrimitiveClass('B');
+    Handle<Class> byte_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &byte_class)));
+    InitializeVarHandle(bvh.Get(), var_type, byte_array_class, index_type, access_modes_bit_mask);
+    bvh->SetFieldBoolean<false>(ByteArrayViewVarHandle::NativeByteOrderOffset(), native_byte_order);
+    return bvh.Get();
+  }
+
+  static ByteBufferViewVarHandle* CreateByteBufferViewVarHandle(Thread* const self,
+                                                                Handle<Class> view_array_class,
+                                                                bool native_byte_order,
+                                                                int32_t access_modes_bit_mask)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+    StackHandleScope<5> hs(self);
+    Handle<ByteBufferViewVarHandle> bvh = hs.NewHandle(
+        ObjPtr<ByteBufferViewVarHandle>::DownCast(
+            ByteArrayViewVarHandle::StaticClass()->AllocObject(self)));
+    // Initialize super class fields
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    Handle<Class> var_type = hs.NewHandle(view_array_class->GetComponentType());
+    Handle<Class> index_type = hs.NewHandle(class_linker->FindPrimitiveClass('I'));
+    Handle<ClassLoader> boot_class_loader;
+    Handle<Class> byte_buffer_class = hs.NewHandle(
+        class_linker->FindSystemClass(self, "Ljava/nio/ByteBuffer;"));
+    InitializeVarHandle(bvh.Get(), var_type, byte_buffer_class, index_type, access_modes_bit_mask);
+    bvh->SetFieldBoolean<false>(ByteBufferViewVarHandle::NativeByteOrderOffset(),
+                                native_byte_order);
+    return bvh.Get();
+  }
+
+  static int32_t AccessModesBitMask(VarHandle::AccessMode mode) {
+    return 1 << static_cast<int32_t>(mode);
+  }
+
+  template<typename... Args>
+  static int32_t AccessModesBitMask(VarHandle::AccessMode first, Args... args) {
+    return AccessModesBitMask(first) | AccessModesBitMask(args...);
+  }
+
+  // Helper to get the VarType of a VarHandle.
+  static Class* GetVarType(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) {
+    return vh->GetVarType();
+  }
+
+  // Helper to get the CoordinateType0 of a VarHandle.
+  static Class* GetCoordinateType0(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) {
+    return vh->GetCoordinateType0();
+  }
+
+  // Helper to get the CoordinateType1 of a VarHandle.
+  static Class* GetCoordinateType1(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) {
+    return vh->GetCoordinateType1();
+  }
+
+  // Helper to get the AccessModesBitMask of a VarHandle.
+  static int32_t GetAccessModesBitMask(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) {
+    return vh->GetAccessModesBitMask();
+  }
+
+ private:
+  static void InitializeVarHandle(VarHandle* vh,
+                                  Handle<Class> var_type,
+                                  int32_t access_modes_bit_mask)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    vh->SetFieldObject<false>(VarHandle::VarTypeOffset(), var_type.Get());
+    vh->SetField32<false>(VarHandle::AccessModesBitMaskOffset(), access_modes_bit_mask);
+  }
+
+  static void InitializeVarHandle(VarHandle* vh,
+                                  Handle<Class> var_type,
+                                  Handle<Class> coordinate_type0,
+                                  int32_t access_modes_bit_mask)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    InitializeVarHandle(vh, var_type, access_modes_bit_mask);
+    vh->SetFieldObject<false>(VarHandle::CoordinateType0Offset(), coordinate_type0.Get());
+  }
+
+  static void InitializeVarHandle(VarHandle* vh,
+                                  Handle<Class> var_type,
+                                  Handle<Class> coordinate_type0,
+                                  Handle<Class> coordinate_type1,
+                                  int32_t access_modes_bit_mask)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    InitializeVarHandle(vh, var_type, access_modes_bit_mask);
+    vh->SetFieldObject<false>(VarHandle::CoordinateType0Offset(), coordinate_type0.Get());
+    vh->SetFieldObject<false>(VarHandle::CoordinateType1Offset(), coordinate_type1.Get());
+  }
+};
+
+// Convenience method for constructing MethodType instances from
+// well-formed method descriptors.
+static MethodType* MethodTypeOf(const std::string& method_descriptor) {
+  std::vector<std::string> descriptors;
+
+  auto it = method_descriptor.cbegin();
+  if (*it++ != '(') {
+    LOG(FATAL) << "Bad descriptor: " << method_descriptor;
+  }
+
+  bool returnValueSeen = false;
+  const char* prefix = "";
+  for (; it != method_descriptor.cend() && !returnValueSeen; ++it) {
+    switch (*it) {
+      case ')':
+        descriptors.push_back(std::string(++it, method_descriptor.cend()));
+        returnValueSeen = true;
+        break;
+      case '[':
+        prefix = "[";
+        break;
+      case 'Z':
+      case 'B':
+      case 'C':
+      case 'S':
+      case 'I':
+      case 'J':
+      case 'F':
+      case 'D':
+        descriptors.push_back(prefix + std::string(it, it + 1));
+        prefix = "";
+        break;
+      case 'L': {
+        auto last = it + 1;
+        while (*last != ';') {
+          ++last;
+        }
+        descriptors.push_back(prefix + std::string(it, last + 1));
+        prefix = "";
+        it = last;
+        break;
+      }
+      default:
+        LOG(FATAL) << "Bad descriptor: " << method_descriptor;
+    }
+  }
+
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* const class_linker = runtime->GetClassLinker();
+  Thread* const self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  StackHandleScope<3> hs(self);
+  int ptypes_count = static_cast<int>(descriptors.size()) - 1;
+  ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+  ObjPtr<mirror::Class> array_of_class = class_linker->FindArrayClass(self, &class_type);
+  Handle<ObjectArray<Class>> ptypes = hs.NewHandle(
+      ObjectArray<Class>::Alloc(Thread::Current(), array_of_class, ptypes_count));
+  Handle<mirror::ClassLoader> boot_class_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
+  for (int i = 0; i < ptypes_count; ++i) {
+    ptypes->Set(i, class_linker->FindClass(self, descriptors[i].c_str(), boot_class_loader));
+  }
+  Handle<Class> rtype =
+      hs.NewHandle(class_linker->FindClass(self, descriptors.back().c_str(), boot_class_loader));
+  return MethodType::Create(self, rtype, ptypes);
+}
+
+TEST_F(VarHandleTest, InstanceFieldVarHandle) {
+  Thread * const self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  ObjPtr<Object> i = BoxPrimitive(Primitive::kPrimInt, JValue::FromPrimitive<int32_t>(37));
+  ArtField* value = mirror::Class::FindField(self, i->GetClass(), "value", "I");
+  int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
+                                    VarHandle::AccessMode::kGetAndSet,
+                                    VarHandle::AccessMode::kGetAndBitwiseXor);
+  StackHandleScope<1> hs(self);
+  Handle<mirror::FieldVarHandle> fvh(hs.NewHandle(CreateFieldVarHandle(self, value, mask)));
+  EXPECT_FALSE(fvh.IsNull());
+  EXPECT_EQ(value, fvh->GetField());
+
+  // Check access modes
+  EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+  EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+  EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+  // Check compatibility - "Get" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)I")));
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+  }
+
+  // Check compatibility - "Set" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+  }
+
+  // Check compatibility - "CompareAndSet" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
+                                             MethodTypeOf("(Ljava/lang/Integer;II)I")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+  }
+
+  // Check compatibility - "CompareAndExchange" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I")));
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+  }
+
+  // Check compatibility - "GetAndUpdate" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I")));
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+  }
+
+  // Check synthesized method types match expected forms.
+  {
+    MethodType* get = MethodTypeOf("(Ljava/lang/Integer;)I");
+    MethodType* set = MethodTypeOf("(Ljava/lang/Integer;I)V");
+    MethodType* compareAndSet = MethodTypeOf("(Ljava/lang/Integer;II)Z");
+    MethodType* compareAndExchange = MethodTypeOf("(Ljava/lang/Integer;II)I");
+    MethodType* getAndUpdate = MethodTypeOf("(Ljava/lang/Integer;I)I");
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+  }
+}
+
+TEST_F(VarHandleTest, StaticFieldVarHandle) {
+  Thread * const self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  ObjPtr<Object> i = BoxPrimitive(Primitive::kPrimInt, JValue::FromPrimitive<int32_t>(37));
+  ArtField* value = mirror::Class::FindField(self, i->GetClass(), "MIN_VALUE", "I");
+  int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kSet,
+                                    VarHandle::AccessMode::kGetOpaque,
+                                    VarHandle::AccessMode::kGetAndBitwiseAndRelease);
+  StackHandleScope<1> hs(self);
+  Handle<mirror::FieldVarHandle> fvh(hs.NewHandle(CreateFieldVarHandle(self, value, mask)));
+  EXPECT_FALSE(fvh.IsNull());
+  EXPECT_EQ(value, fvh->GetField());
+
+  // Check access modes
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+  EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+  EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+  EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+  EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+  // Check compatibility - "Get" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()I")));
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+  }
+
+  // Check compatibility - "Set" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(F)V")));
+  }
+
+  // Check compatibility - "CompareAndSet" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
+                                             MethodTypeOf("(II)Ljava/lang/String;")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+  }
+
+  // Check compatibility - "CompareAndExchange" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I")));
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)D")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+  }
+
+  // Check compatibility - "GetAndUpdate" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)I")));
+    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)Z")));
+    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+  }
+
+  // Check synthesized method types match expected forms.
+  {
+    MethodType* get = MethodTypeOf("()I");
+    MethodType* set = MethodTypeOf("(I)V");
+    MethodType* compareAndSet = MethodTypeOf("(II)Z");
+    MethodType* compareAndExchange = MethodTypeOf("(II)I");
+    MethodType* getAndUpdate = MethodTypeOf("(I)I");
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+  }
+}
+
+TEST_F(VarHandleTest, ArrayElementVarHandle) {
+  Thread * const self = Thread::Current();
+  ScopedObjectAccess soa(self);
+  StackHandleScope<2> hs(self);
+
+  int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
+                                    VarHandle::AccessMode::kSet,
+                                    VarHandle::AccessMode::kGetVolatile,
+                                    VarHandle::AccessMode::kSetVolatile,
+                                    VarHandle::AccessMode::kGetAcquire,
+                                    VarHandle::AccessMode::kSetRelease,
+                                    VarHandle::AccessMode::kGetOpaque,
+                                    VarHandle::AccessMode::kSetOpaque,
+                                    VarHandle::AccessMode::kCompareAndSet,
+                                    VarHandle::AccessMode::kCompareAndExchange,
+                                    VarHandle::AccessMode::kCompareAndExchangeAcquire,
+                                    VarHandle::AccessMode::kCompareAndExchangeRelease,
+                                    VarHandle::AccessMode::kWeakCompareAndSetPlain,
+                                    VarHandle::AccessMode::kWeakCompareAndSet,
+                                    VarHandle::AccessMode::kWeakCompareAndSetAcquire,
+                                    VarHandle::AccessMode::kWeakCompareAndSetRelease,
+                                    VarHandle::AccessMode::kGetAndSet,
+                                    VarHandle::AccessMode::kGetAndSetAcquire,
+                                    VarHandle::AccessMode::kGetAndSetRelease,
+                                    VarHandle::AccessMode::kGetAndAdd,
+                                    VarHandle::AccessMode::kGetAndAddAcquire,
+                                    VarHandle::AccessMode::kGetAndAddRelease,
+                                    VarHandle::AccessMode::kGetAndBitwiseOr,
+                                    VarHandle::AccessMode::kGetAndBitwiseOrRelease,
+                                    VarHandle::AccessMode::kGetAndBitwiseOrAcquire,
+                                    VarHandle::AccessMode::kGetAndBitwiseAnd,
+                                    VarHandle::AccessMode::kGetAndBitwiseAndRelease,
+                                    VarHandle::AccessMode::kGetAndBitwiseAndAcquire,
+                                    VarHandle::AccessMode::kGetAndBitwiseXor,
+                                    VarHandle::AccessMode::kGetAndBitwiseXorRelease,
+                                    VarHandle::AccessMode::kGetAndBitwiseXorAcquire);
+
+  ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Handle<Class> string_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &string_class)));
+  Handle<mirror::ArrayElementVarHandle> vh(hs.NewHandle(CreateArrayElementVarHandle(self, string_array_class, mask)));
+  EXPECT_FALSE(vh.IsNull());
+
+  // Check access modes
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+  // Check compatibility - "Get" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;")));
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;Ljava/lang/String;)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+  }
+
+  // Check compatibility - "Set" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+  }
+
+  // Check compatibility - "CompareAndSet" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+    EXPECT_TRUE(
+        vh->IsMethodTypeCompatible(
+            access_mode,
+            MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
+                                             MethodTypeOf("([Ljava/lang/String;III)I")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+  }
+
+  // Check compatibility - "CompareAndExchange" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")));
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;II)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+  }
+
+  // Check compatibility - "GetAndUpdate" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;")));
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+  }
+
+  // Check synthesized method types match expected forms.
+  {
+    MethodType* get = MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;");
+    MethodType* set = MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V");
+    MethodType* compareAndSet = MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z");
+    MethodType* compareAndExchange = MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+    MethodType* getAndUpdate = MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;");
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+  }
+}
+
+TEST_F(VarHandleTest, ByteArrayViewVarHandle) {
+  Thread * const self = Thread::Current();
+  ScopedObjectAccess soa(self);
+  StackHandleScope<2> hs(self);
+
+  int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
+                                    VarHandle::AccessMode::kGetVolatile,
+                                    VarHandle::AccessMode::kGetAcquire,
+                                    VarHandle::AccessMode::kGetOpaque,
+                                    VarHandle::AccessMode::kCompareAndSet,
+                                    VarHandle::AccessMode::kCompareAndExchangeAcquire,
+                                    VarHandle::AccessMode::kWeakCompareAndSetPlain,
+                                    VarHandle::AccessMode::kWeakCompareAndSetAcquire,
+                                    VarHandle::AccessMode::kGetAndSet,
+                                    VarHandle::AccessMode::kGetAndSetRelease,
+                                    VarHandle::AccessMode::kGetAndAddAcquire,
+                                    VarHandle::AccessMode::kGetAndBitwiseOr,
+                                    VarHandle::AccessMode::kGetAndBitwiseOrAcquire,
+                                    VarHandle::AccessMode::kGetAndBitwiseAndRelease,
+                                    VarHandle::AccessMode::kGetAndBitwiseXor,
+                                    VarHandle::AccessMode::kGetAndBitwiseXorAcquire);
+
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  ObjPtr<mirror::Class> char_class = class_linker->FindPrimitiveClass('C');
+  Handle<Class> char_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &char_class)));
+  const bool native_byte_order = true;
+  Handle<mirror::ByteArrayViewVarHandle> vh(hs.NewHandle(CreateByteArrayViewVarHandle(self, char_array_class, native_byte_order, mask)));
+  EXPECT_FALSE(vh.IsNull());
+  EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder());
+
+  // Check access modes
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+  // Check compatibility - "Get" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)C")));
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BC)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+  }
+
+  // Check compatibility - "Set" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+  }
+
+  // Check compatibility - "CompareAndSet" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+    EXPECT_TRUE(
+        vh->IsMethodTypeCompatible(
+            access_mode,
+            MethodTypeOf("([BICC)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
+                                             MethodTypeOf("([BIII)I")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+  }
+
+  // Check compatibility - "CompareAndExchange" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)C")));
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BII)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+  }
+
+  // Check compatibility - "GetAndUpdate" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)C")));
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+  }
+
+  // Check synthesized method types match expected forms.
+  {
+    MethodType* get = MethodTypeOf("([BI)C");
+    MethodType* set = MethodTypeOf("([BIC)V");
+    MethodType* compareAndSet = MethodTypeOf("([BICC)Z");
+    MethodType* compareAndExchange = MethodTypeOf("([BICC)C");
+    MethodType* getAndUpdate = MethodTypeOf("([BIC)C");
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+  }
+}
+
+TEST_F(VarHandleTest, ByteBufferViewVarHandle) {
+  Thread * const self = Thread::Current();
+  ScopedObjectAccess soa(self);
+  StackHandleScope<2> hs(self);
+
+  int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
+                                    VarHandle::AccessMode::kGetVolatile,
+                                    VarHandle::AccessMode::kGetAcquire,
+                                    VarHandle::AccessMode::kGetOpaque,
+                                    VarHandle::AccessMode::kCompareAndSet,
+                                    VarHandle::AccessMode::kCompareAndExchangeAcquire,
+                                    VarHandle::AccessMode::kWeakCompareAndSetPlain,
+                                    VarHandle::AccessMode::kWeakCompareAndSetAcquire,
+                                    VarHandle::AccessMode::kGetAndSet,
+                                    VarHandle::AccessMode::kGetAndSetRelease,
+                                    VarHandle::AccessMode::kGetAndAddAcquire,
+                                    VarHandle::AccessMode::kGetAndBitwiseOr,
+                                    VarHandle::AccessMode::kGetAndBitwiseOrAcquire,
+                                    VarHandle::AccessMode::kGetAndBitwiseAndRelease,
+                                    VarHandle::AccessMode::kGetAndBitwiseXor,
+                                    VarHandle::AccessMode::kGetAndBitwiseXorAcquire);
+
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  ObjPtr<mirror::Class> double_class = class_linker->FindPrimitiveClass('D');
+  Handle<Class> double_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &double_class)));
+  const bool native_byte_order = false;
+  Handle<mirror::ByteBufferViewVarHandle> vh(hs.NewHandle(CreateByteBufferViewVarHandle(self, double_array_class, native_byte_order, mask)));
+  EXPECT_FALSE(vh.IsNull());
+  EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder());
+
+  // Check access modes
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+  EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+  EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+  // Check compatibility - "Get" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)D")));
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;D)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+  }
+
+  // Check compatibility - "Set" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+  }
+
+  // Check compatibility - "CompareAndSet" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+    EXPECT_TRUE(
+        vh->IsMethodTypeCompatible(
+            access_mode,
+            MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
+                                             MethodTypeOf("(Ljava/nio/ByteBuffer;IDI)D")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+  }
+
+  // Check compatibility - "CompareAndExchange" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D")));
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;II)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+  }
+
+  // Check compatibility - "GetAndUpdate" pattern
+  {
+    const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D")));
+    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)Z")));
+    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+  }
+
+  // Check synthesized method types match expected forms.
+  {
+    MethodType* get = MethodTypeOf("(Ljava/nio/ByteBuffer;I)D");
+    MethodType* set = MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V");
+    MethodType* compareAndSet = MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z");
+    MethodType* compareAndExchange = MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D");
+    MethodType* getAndUpdate = MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D");
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+    EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+  }
+}
+
+}  // namespace mirror
+}  // namespace art