ART: Support for VarHandle invokers

Enables VarHandles to invoked by a MethodHandle.

Bug: b/65872996
Test: art/test/run-test --host 713

Change-Id: I4672dd50654396c2b45bd212a523698cf22879eb
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
index c953365..3155c14 100644
--- a/libdexfile/dex/dex_instruction.h
+++ b/libdexfile/dex/dex_instruction.h
@@ -741,7 +741,7 @@
 // existing InstructionOperands instance.
 class NoReceiverInstructionOperands FINAL : public InstructionOperands {
  public:
-  explicit NoReceiverInstructionOperands(InstructionOperands* inner)
+  explicit NoReceiverInstructionOperands(const InstructionOperands* const inner)
       : InstructionOperands(inner->GetNumberOfOperands() - 1), inner_(inner) {}
   ~NoReceiverInstructionOperands() {}
   uint32_t GetOperand(size_t operand_index) const OVERRIDE;
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index 2bc71f4..41c8384 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -33,10 +33,9 @@
 
 inline bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type,
                                  Handle<mirror::MethodType> callee_type,
-                                 int index,
+                                 ObjPtr<mirror::Class> from_class,
+                                 ObjPtr<mirror::Class> to_class,
                                  JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) {
-  ObjPtr<mirror::Class> from_class(callsite_type->GetPTypes()->GetWithoutChecks(index));
-  ObjPtr<mirror::Class> to_class(callee_type->GetPTypes()->GetWithoutChecks(index));
   if (from_class == to_class) {
     return true;
   }
@@ -55,6 +54,17 @@
   }
 }
 
+inline bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type,
+                                 Handle<mirror::MethodType> callee_type,
+                                 int index,
+                                 JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) {
+  return ConvertArgumentValue(callsite_type,
+                              callee_type,
+                              callsite_type->GetPTypes()->GetWithoutChecks(index),
+                              callee_type->GetPTypes()->GetWithoutChecks(index),
+                              value);
+}
+
 inline bool ConvertReturnValue(Handle<mirror::MethodType> callsite_type,
                                Handle<mirror::MethodType> callee_type,
                                JValue* value)  REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -84,16 +94,17 @@
                         Handle<mirror::MethodType> callee_type,
                         G* getter,
                         S* setter,
-                        int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_) {
+                        int32_t start_index,
+                        int32_t end_index) REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<2> hs(self);
   Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(callsite_type->GetPTypes()));
   Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes()));
 
-  for (int32_t i = 0; i < num_conversions; ++i) {
+  for (int32_t i = start_index; i < end_index; ++i) {
     ObjPtr<mirror::Class> from(from_types->GetWithoutChecks(i));
-    ObjPtr<mirror::Class> to(to_types->GetWithoutChecks(i));
-    const Primitive::Type from_type = from_types->GetWithoutChecks(i)->GetPrimitiveType();
-    const Primitive::Type to_type = to_types->GetWithoutChecks(i)->GetPrimitiveType();
+    ObjPtr<mirror::Class> to(to_types->GetWithoutChecks(i - start_index));
+    const Primitive::Type from_type = from->GetPrimitiveType();
+    const Primitive::Type to_type = to->GetPrimitiveType();
     if (from == to) {
       // Easy case - the types are identical. Nothing left to do except to pass
       // the arguments along verbatim.
@@ -106,7 +117,6 @@
       }
     } else {
       JValue value;
-
       if (Primitive::Is64BitType(from_type)) {
         value.SetJ(getter->GetLong());
       } else if (from_type == Primitive::kPrimNot) {
@@ -114,13 +124,11 @@
       } else {
         value.SetI(getter->Get());
       }
-
       // Caveat emptor - ObjPtr's not guaranteed valid after this call.
-      if (!ConvertArgumentValue(callsite_type, callee_type, i, &value)) {
+      if (!ConvertArgumentValue(callsite_type, callee_type, from, to, &value)) {
         DCHECK(self->IsExceptionPending());
         return false;
       }
-
       if (Primitive::Is64BitType(to_type)) {
         setter->SetLong(value.GetJ());
       } else if (to_type == Primitive::kPrimNot) {
@@ -130,10 +138,31 @@
       }
     }
   }
-
   return true;
 }
 
+template <typename G, typename S>
+bool PerformConversions(Thread* self,
+                        Handle<mirror::MethodType> callsite_type,
+                        Handle<mirror::MethodType> callee_type,
+                        G* getter,
+                        S* setter,
+                        int32_t num_conversions)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return PerformConversions(self, callsite_type, callee_type, getter, setter, 0, num_conversions);
+}
+
+template <typename G, typename S>
+bool PerformConversions(Thread* self,
+                        Handle<mirror::MethodType> callsite_type,
+                        Handle<mirror::MethodType> callee_type,
+                        G* getter,
+                        S* setter)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  int32_t num_conversions = callee_type->GetPTypes()->GetLength();
+  return PerformConversions(self, callsite_type, callee_type, getter, setter, 0, num_conversions);
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_METHOD_HANDLES_INL_H_
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 9b21e1d..82370c4 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -24,6 +24,7 @@
 #include "mirror/emulated_stack_frame.h"
 #include "mirror/method_handle_impl-inl.h"
 #include "mirror/method_type.h"
+#include "mirror/var_handle.h"
 #include "reflection-inl.h"
 #include "reflection.h"
 #include "well_known_classes.h"
@@ -365,6 +366,11 @@
           || handle_kind == mirror::MethodHandle::Kind::kInvokeCallSiteTransform);
 }
 
+inline bool IsInvokeVarHandle(const mirror::MethodHandle::Kind handle_kind) {
+  return (handle_kind == mirror::MethodHandle::Kind::kInvokeVarHandle ||
+          handle_kind == mirror::MethodHandle::Kind::kInvokeVarHandleExact);
+}
+
 inline bool IsFieldAccess(mirror::MethodHandle::Kind handle_kind) {
   return (handle_kind >= mirror::MethodHandle::Kind::kFirstAccessorKind
           && handle_kind <= mirror::MethodHandle::Kind::kLastAccessorKind);
@@ -957,6 +963,118 @@
   }
 }
 
+bool DoVarHandleInvokeTranslationUnchecked(Thread* self,
+                                           ShadowFrame& shadow_frame,
+                                           mirror::VarHandle::AccessMode access_mode,
+                                           Handle<mirror::VarHandle> vh,
+                                           Handle<mirror::MethodType> vh_type,
+                                           Handle<mirror::MethodType> callsite_type,
+                                           const InstructionOperands* const operands,
+                                           JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK_EQ(operands->GetNumberOfOperands(), static_cast<uint32_t>(vh_type->GetNumberOfPTypes()));
+  DCHECK_EQ(operands->GetNumberOfOperands(),
+            static_cast<uint32_t>(callsite_type->GetNumberOfPTypes()));
+  const size_t vreg_count = vh_type->NumberOfVRegs();
+  ShadowFrameAllocaUniquePtr accessor_frame =
+      CREATE_SHADOW_FRAME(vreg_count, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
+  ShadowFrameGetter getter(shadow_frame, operands);
+  static const uint32_t kFirstAccessorReg = 0;
+  ShadowFrameSetter setter(accessor_frame.get(), kFirstAccessorReg);
+  if (!PerformConversions(self, callsite_type, vh_type, &getter, &setter)) {
+    return false;
+  }
+  RangeInstructionOperands accessor_operands(kFirstAccessorReg, kFirstAccessorReg + vreg_count);
+  if (!vh->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) {
+    return false;
+  }
+  return ConvertReturnValue(callsite_type, vh_type, result);
+}
+
+bool DoVarHandleInvokeTranslation(Thread* self,
+                                  ShadowFrame& shadow_frame,
+                                  bool invokeExact,
+                                  Handle<mirror::MethodHandle> method_handle,
+                                  Handle<mirror::MethodType> callsite_type,
+                                  const InstructionOperands* const operands,
+                                  JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (!invokeExact) {
+    // Exact invokes are checked for compatability higher up. The
+    // non-exact invoke path doesn't have a similar check due to
+    // transformers which have EmulatedStack frame arguments with the
+    // actual method type associated with the frame.
+    if (UNLIKELY(!callsite_type->IsConvertible(method_handle->GetMethodType()))) {
+      ThrowWrongMethodTypeException(method_handle->GetMethodType(), callsite_type.Get());
+      return false;
+    }
+  }
+
+  //
+  // Basic checks that apply in all cases.
+  //
+  StackHandleScope<6> hs(self);
+  Handle<mirror::ObjectArray<mirror::Class>>
+      callsite_ptypes(hs.NewHandle(callsite_type->GetPTypes()));
+  Handle<mirror::ObjectArray<mirror::Class>>
+         mh_ptypes(hs.NewHandle(method_handle->GetMethodType()->GetPTypes()));
+
+  // Check that the first parameter is a VarHandle
+  if (callsite_ptypes->GetLength() < 1 ||
+      !mh_ptypes->Get(0)->IsAssignableFrom(callsite_ptypes->Get(0)) ||
+      mh_ptypes->Get(0) != mirror::VarHandle::StaticClass()) {
+    ThrowWrongMethodTypeException(method_handle->GetMethodType(), callsite_type.Get());
+    return false;
+  }
+
+  // Get the receiver
+  mirror::Object* receiver = shadow_frame.GetVRegReference(operands->GetOperand(0));
+  if (receiver == nullptr) {
+    ThrowNullPointerException("Expected argument 1 to be a non-null VarHandle");
+    return false;
+  }
+
+  // Cast to VarHandle instance
+  Handle<mirror::VarHandle> vh(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver)));
+  DCHECK(mirror::VarHandle::StaticClass()->IsAssignableFrom(vh->GetClass()));
+
+  // Determine the accessor kind to dispatch
+  ArtMethod* target_method = method_handle->GetTargetMethod();
+  int intrinsic_index = target_method->GetIntrinsic();
+  mirror::VarHandle::AccessMode access_mode =
+      mirror::VarHandle::GetAccessModeByIntrinsic(static_cast<Intrinsics>(intrinsic_index));
+  Handle<mirror::MethodType> vh_type =
+      hs.NewHandle(vh->GetMethodTypeForAccessMode(self, access_mode));
+  Handle<mirror::MethodType> mh_invoke_type = hs.NewHandle(
+      mirror::MethodType::CloneWithoutLeadingParameter(self, method_handle->GetMethodType()));
+  if (method_handle->GetHandleKind() == mirror::MethodHandle::Kind::kInvokeVarHandleExact) {
+    if (!mh_invoke_type->IsExactMatch(vh_type.Get())) {
+      ThrowWrongMethodTypeException(vh_type.Get(), mh_invoke_type.Get());
+      return false;
+    }
+  } else {
+    DCHECK_EQ(method_handle->GetHandleKind(), mirror::MethodHandle::Kind::kInvokeVarHandle);
+    if (!mh_invoke_type->IsConvertible(vh_type.Get())) {
+      ThrowWrongMethodTypeException(vh_type.Get(), mh_invoke_type.Get());
+      return false;
+    }
+  }
+
+  Handle<mirror::MethodType> callsite_type_without_varhandle =
+      hs.NewHandle(mirror::MethodType::CloneWithoutLeadingParameter(self, callsite_type.Get()));
+  NoReceiverInstructionOperands varhandle_operands(operands);
+  DCHECK_EQ(static_cast<int32_t>(varhandle_operands.GetNumberOfOperands()),
+            callsite_type_without_varhandle->GetPTypes()->GetLength());
+  return DoVarHandleInvokeTranslationUnchecked(self,
+                                               shadow_frame,
+                                               access_mode,
+                                               vh,
+                                               vh_type,
+                                               callsite_type_without_varhandle,
+                                               &varhandle_operands,
+                                               result);
+}
+
 static inline bool MethodHandleInvokeInternal(Thread* self,
                                               ShadowFrame& shadow_frame,
                                               Handle<mirror::MethodHandle> method_handle,
@@ -981,6 +1099,15 @@
         operands,
         result);
   }
+  if (IsInvokeVarHandle(handle_kind)) {
+    return DoVarHandleInvokeTranslation(self,
+                                        shadow_frame,
+                                        /*invokeExact*/ false,
+                                        method_handle,
+                                        callsite_type,
+                                        operands,
+                                        result);
+  }
   return DoInvokePolymorphicMethod(self,
                                    shadow_frame,
                                    method_handle,
@@ -1016,13 +1143,22 @@
   }
 
   // Slow-path check.
-  if (IsInvokeTransform(handle_kind) || IsCallerTransformer(callsite_type)) {
+  if (IsInvokeTransform(handle_kind) ||
+      IsCallerTransformer(callsite_type)) {
     return DoInvokePolymorphicMethod(self,
                                      shadow_frame,
                                      method_handle,
                                      callsite_type,
                                      operands,
                                      result);
+  } else if (IsInvokeVarHandle(handle_kind)) {
+    return DoVarHandleInvokeTranslation(self,
+                                        shadow_frame,
+                                        /*invokeExact*/ true,
+                                        method_handle,
+                                        callsite_type,
+                                        operands,
+                                        result);
   }
 
   // On the fast-path. This is equivalent to DoCallPolymoprhic without the conversion paths.
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 3b1bf2e..7e60a5c 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -123,7 +123,8 @@
                         Handle<mirror::MethodType> callee_type,
                         G* getter,
                         S* setter,
-                        int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_);
+                        int32_t start_index,
+                        int32_t end_index) REQUIRES_SHARED(Locks::mutator_lock_);
 
 // A convenience class that allows for iteration through a list of
 // input argument registers. This is used to iterate over input
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index a1bc976..3b0002c 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -48,6 +48,8 @@
     kInvokeInterface,
     kInvokeTransform,
     kInvokeCallSiteTransform,
+    kInvokeVarHandle,
+    kInvokeVarHandleExact,
     kInstanceGet,
     kInstancePut,
     kStaticGet,
@@ -55,7 +57,7 @@
     kLastValidKind = kStaticPut,
     kFirstAccessorKind = kInstanceGet,
     kLastAccessorKind = kStaticPut,
-    kLastInvokeKind = kInvokeCallSiteTransform
+    kLastInvokeKind = kInvokeVarHandleExact
   };
 
   Kind GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 4b8dfac..6ac5012 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -23,13 +23,13 @@
 namespace art {
 namespace mirror {
 
-GcRoot<mirror::Class> MethodType::static_class_;
+GcRoot<Class> MethodType::static_class_;
 
-mirror::MethodType* MethodType::Create(Thread* const self,
-                                       Handle<Class> return_type,
-                                       Handle<ObjectArray<Class>> param_types) {
+MethodType* MethodType::Create(Thread* const self,
+                               Handle<Class> return_type,
+                               Handle<ObjectArray<Class>> parameter_types) {
   StackHandleScope<1> hs(self);
-  Handle<mirror::MethodType> mt(
+  Handle<MethodType> mt(
       hs.NewHandle(ObjPtr<MethodType>::DownCast(StaticClass()->AllocObject(self))));
 
   // TODO: Do we ever create a MethodType during a transaction ? There doesn't
@@ -38,21 +38,38 @@
   mt->SetFieldObject<false>(FormOffset(), nullptr);
   mt->SetFieldObject<false>(MethodDescriptorOffset(), nullptr);
   mt->SetFieldObject<false>(RTypeOffset(), return_type.Get());
-  mt->SetFieldObject<false>(PTypesOffset(), param_types.Get());
+  mt->SetFieldObject<false>(PTypesOffset(), parameter_types.Get());
   mt->SetFieldObject<false>(WrapAltOffset(), nullptr);
 
   return mt.Get();
 }
 
+MethodType* MethodType::CloneWithoutLeadingParameter(Thread* const self,
+                                                     ObjPtr<MethodType> method_type) {
+  StackHandleScope<3> hs(self);
+  Handle<Class> rtype = hs.NewHandle(method_type->GetRType());
+  Handle<ObjectArray<Class>> src_ptypes = hs.NewHandle(method_type->GetPTypes());
+  ObjPtr<Class> class_type = Class::GetJavaLangClass();
+  ObjPtr<Class> class_array_type =
+      Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type);
+  const int32_t dst_ptypes_count = src_ptypes->GetLength() - 1;
+  Handle<ObjectArray<Class>> dst_ptypes = hs.NewHandle(
+      ObjectArray<Class>::Alloc(self, class_array_type, dst_ptypes_count));
+  for (int32_t i = 0; i < dst_ptypes_count; ++i) {
+    dst_ptypes->Set(i, src_ptypes->Get(i + 1));
+  }
+  return Create(self, rtype, dst_ptypes);
+}
+
 size_t MethodType::NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::ObjectArray<Class>* const p_types = GetPTypes();
+  ObjectArray<Class>* const p_types = GetPTypes();
   const int32_t p_types_length = p_types->GetLength();
 
   // Initialize |num_vregs| with number of parameters and only increment it for
   // types requiring a second vreg.
   size_t num_vregs = static_cast<size_t>(p_types_length);
   for (int32_t i = 0; i < p_types_length; ++i) {
-    mirror::Class* klass = p_types->GetWithoutChecks(i);
+    Class* klass = p_types->GetWithoutChecks(i);
     if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
       ++num_vregs;
     }
@@ -60,11 +77,11 @@
   return num_vregs;
 }
 
-bool MethodType::IsExactMatch(mirror::MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::ObjectArray<Class>* const p_types = GetPTypes();
+bool MethodType::IsExactMatch(MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjectArray<Class>* const p_types = GetPTypes();
   const int32_t params_length = p_types->GetLength();
 
-  mirror::ObjectArray<Class>* const target_p_types = target->GetPTypes();
+  ObjectArray<Class>* const target_p_types = target->GetPTypes();
   if (params_length != target_p_types->GetLength()) {
     return false;
   }
@@ -76,11 +93,11 @@
   return GetRType() == target->GetRType();
 }
 
-bool MethodType::IsConvertible(mirror::MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::ObjectArray<Class>* const p_types = GetPTypes();
+bool MethodType::IsConvertible(MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjectArray<Class>* const p_types = GetPTypes();
   const int32_t params_length = p_types->GetLength();
 
-  mirror::ObjectArray<Class>* const target_p_types = target->GetPTypes();
+  ObjectArray<Class>* const target_p_types = target->GetPTypes();
   if (params_length != target_p_types->GetLength()) {
     return false;
   }
@@ -105,7 +122,7 @@
   std::ostringstream ss;
   ss << "(";
 
-  mirror::ObjectArray<Class>* const p_types = GetPTypes();
+  ObjectArray<Class>* const p_types = GetPTypes();
   const int32_t params_length = p_types->GetLength();
   for (int32_t i = 0; i < params_length; ++i) {
     ss << p_types->GetWithoutChecks(i)->PrettyDescriptor();
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index a9f3c9c..edd9910 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -31,12 +31,16 @@
 // C++ mirror of java.lang.invoke.MethodType
 class MANAGED MethodType : public Object {
  public:
-  static mirror::MethodType* Create(Thread* const self,
-                                    Handle<Class> return_type,
-                                    Handle<ObjectArray<Class>> param_types)
+  static MethodType* Create(Thread* const self,
+                            Handle<Class> return_type,
+                            Handle<ObjectArray<Class>> param_types)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
 
-  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+  static MethodType* CloneWithoutLeadingParameter(Thread* const self,
+                                                  ObjPtr<MethodType> method_type)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  static Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
     return static_class_.Read();
   }
 
@@ -44,6 +48,10 @@
     return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_));
   }
 
+  int GetNumberOfPTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetPTypes()->GetLength();
+  }
+
   // Number of virtual registers required to hold the parameters for
   // this method type.
   size_t NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -58,11 +66,11 @@
 
   // Returns true iff. |this| is an exact match for method type |target|, i.e
   // iff. they have the same return types and parameter types.
-  bool IsExactMatch(mirror::MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsExactMatch(MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true iff. |this| can be converted to match |target| method type, i.e
   // iff. they have convertible return types and parameter types.
-  bool IsConvertible(mirror::MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsConvertible(MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns the pretty descriptor for this method type, suitable for display in
   // exception messages and the like.
@@ -89,13 +97,13 @@
     return MemberOffset(OFFSETOF_MEMBER(MethodType, wrap_alt_));
   }
 
-  HeapReference<mirror::Object> form_;  // Unused in the runtime
-  HeapReference<mirror::String> method_descriptor_;  // Unused in the runtime
-  HeapReference<ObjectArray<mirror::Class>> p_types_;
-  HeapReference<mirror::Class> r_type_;
-  HeapReference<mirror::Object> wrap_alt_;  // Unused in the runtime
+  HeapReference<Object> form_;  // Unused in the runtime
+  HeapReference<String> method_descriptor_;  // Unused in the runtime
+  HeapReference<ObjectArray<Class>> p_types_;
+  HeapReference<Class> r_type_;
+  HeapReference<Object> wrap_alt_;  // Unused in the runtime
 
-  static GcRoot<mirror::Class> static_class_;  // java.lang.invoke.MethodType.class
+  static GcRoot<Class> static_class_;  // java.lang.invoke.MethodType.class
 
   friend struct art::MethodTypeOffsets;  // for verifying offset information
   DISALLOW_IMPLICIT_CONSTRUCTORS(MethodType);
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index 85d06f0..a79c0a2 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -21,6 +21,7 @@
 #include "class-inl.h"
 #include "class_linker.h"
 #include "gc_root-inl.h"
+#include "intrinsics_enum.h"
 #include "jni_internal.h"
 #include "jvalue-inl.h"
 #include "method_handles.h"
@@ -1463,6 +1464,47 @@
   return true;
 }
 
+bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode,
+                                              MethodType* method_type) {
+  StackHandleScope<3> hs(Thread::Current());
+  Handle<Class> mt_rtype(hs.NewHandle(method_type->GetRType()));
+  Handle<VarHandle> vh(hs.NewHandle(this));
+  Handle<Class> var_type(hs.NewHandle(vh->GetVarType()));
+  AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
+
+  // Check return type first.
+  if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) {
+    // The result of the operation will be discarded. The return type
+    // of the VarHandle is immaterial.
+  } else {
+    ObjPtr<Class> vh_rtype(GetReturnType(access_mode_template, var_type.Get()));
+    if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) {
+      return false;
+    }
+  }
+
+  // Check the number of parameters matches (ignoring the VarHandle parameter).
+  static const int32_t kVarHandleParameters = 1;
+  ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters];
+  const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes,
+                                                      access_mode_template,
+                                                      var_type.Get(),
+                                                      GetCoordinateType0(),
+                                                      GetCoordinateType1());
+  if (vh_ptypes_count != method_type->GetPTypes()->GetLength() - kVarHandleParameters) {
+    return false;
+  }
+
+  // Check the parameter types are compatible (ignoring the VarHandle parameter).
+  ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes();
+  for (int32_t i = 0; i < vh_ptypes_count; ++i) {
+    if (!IsParameterTypeConvertible(mt_ptypes->Get(i + kVarHandleParameters), vh_ptypes[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
 MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self,
                                                   ObjPtr<VarHandle> var_handle,
                                                   AccessMode access_mode) {
@@ -1537,6 +1579,53 @@
   }
 }
 
+VarHandle::AccessMode VarHandle::GetAccessModeByIntrinsic(Intrinsics intrinsic) {
+#define VAR_HANDLE_ACCESS_MODE(V)               \
+    V(CompareAndExchange)                       \
+    V(CompareAndExchangeAcquire)                \
+    V(CompareAndExchangeRelease)                \
+    V(CompareAndSet)                            \
+    V(Get)                                      \
+    V(GetAcquire)                               \
+    V(GetAndAdd)                                \
+    V(GetAndAddAcquire)                         \
+    V(GetAndAddRelease)                         \
+    V(GetAndBitwiseAnd)                         \
+    V(GetAndBitwiseAndAcquire)                  \
+    V(GetAndBitwiseAndRelease)                  \
+    V(GetAndBitwiseOr)                          \
+    V(GetAndBitwiseOrAcquire)                   \
+    V(GetAndBitwiseOrRelease)                   \
+    V(GetAndBitwiseXor)                         \
+    V(GetAndBitwiseXorAcquire)                  \
+    V(GetAndBitwiseXorRelease)                  \
+    V(GetAndSet)                                \
+    V(GetAndSetAcquire)                         \
+    V(GetAndSetRelease)                         \
+    V(GetOpaque)                                \
+    V(GetVolatile)                              \
+    V(Set)                                      \
+    V(SetOpaque)                                \
+    V(SetRelease)                               \
+    V(SetVolatile)                              \
+    V(WeakCompareAndSet)                        \
+    V(WeakCompareAndSetAcquire)                 \
+    V(WeakCompareAndSetPlain)                   \
+    V(WeakCompareAndSetRelease)
+  switch (intrinsic) {
+#define INTRINSIC_CASE(Name)                    \
+    case Intrinsics::kVarHandle ## Name:        \
+      return VarHandle::AccessMode::k ## Name;
+    VAR_HANDLE_ACCESS_MODE(INTRINSIC_CASE)
+#undef INTRINSIC_CASE
+#undef VAR_HANDLE_ACCESS_MODE
+    default:
+      break;
+  }
+  LOG(FATAL) << "Unknown VarHandle instrinsic: " << static_cast<int>(intrinsic);
+  UNREACHABLE();
+}
+
 bool VarHandle::GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode) {
   if (method_name == nullptr) {
     return false;
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
index 6565af7..d46d900 100644
--- a/runtime/mirror/var_handle.h
+++ b/runtime/mirror/var_handle.h
@@ -28,6 +28,8 @@
 template<class T> class Handle;
 class InstructionOperands;
 
+enum class Intrinsics;
+
 struct VarHandleOffsets;
 struct FieldVarHandleOffsets;
 struct ArrayElementVarHandleOffsets;
@@ -106,6 +108,13 @@
   bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Returns true if the MethodType specified is compatible with the
+  // specified access_mode if the first parameter of method_type is
+  // ignored. This is useful for comparing MethodType instances when
+  // invoking a VarHandleAccessor via a MethodHandle invoker.
+  bool IsInvokerMethodTypeCompatible(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
@@ -126,11 +135,13 @@
   // nullptr if accessor_method is not supported.
   static const char* GetReturnTypeDescriptor(const char* accessor_method);
 
+  // Returns the AccessMode corresponding to a VarHandle accessor intrinsic.
+  static AccessMode GetAccessModeByIntrinsic(Intrinsics ordinal);
+
   // Returns true and sets access_mode if method_name corresponds to a
   // VarHandle access method, such as "setOpaque". Returns false otherwise.
   static bool GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode);
 
-
   static 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_);
diff --git a/test/713-varhandle-invokers/build b/test/713-varhandle-invokers/build
new file mode 100755
index 0000000..09d376b
--- /dev/null
+++ b/test/713-varhandle-invokers/build
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Make us exit on a failure
+set -e
+
+./default-build "$@" --experimental var-handles
diff --git a/test/713-varhandle-invokers/expected.txt b/test/713-varhandle-invokers/expected.txt
new file mode 100644
index 0000000..a62d366
--- /dev/null
+++ b/test/713-varhandle-invokers/expected.txt
@@ -0,0 +1,4 @@
+fieldVarHandleExactInvokerTest
+fieldVarHandleInvokerTest
+DivergenceExactInvokerTest
+DivergenceInvokerTest
diff --git a/test/713-varhandle-invokers/info.txt b/test/713-varhandle-invokers/info.txt
new file mode 100644
index 0000000..453c9ff
--- /dev/null
+++ b/test/713-varhandle-invokers/info.txt
@@ -0,0 +1 @@
+Tests invocation of VarHandles wrapped with MethodHandles.
diff --git a/test/713-varhandle-invokers/src/Main.java b/test/713-varhandle-invokers/src/Main.java
new file mode 100644
index 0000000..6a5ed4f
--- /dev/null
+++ b/test/713-varhandle-invokers/src/Main.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.VarHandle;
+import java.lang.invoke.WrongMethodTypeException;
+
+public final class Main {
+    static class TestSetupError extends Error {
+        TestSetupError(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    private static void failAssertion(String message) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Test failure: ");
+        sb.append(message);
+        throw new AssertionError(sb.toString());
+    }
+
+    private static void assertUnreachable() throws Throwable {
+        failAssertion("Unreachable");
+    }
+
+    private static void failAssertEquals(Object expected, Object actual) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(expected);
+        sb.append(" != ");
+        sb.append(actual);
+        failAssertion(sb.toString());
+    }
+
+    private static void assertEquals(int expected, int actual) {
+        if (expected != actual) {
+            failAssertEquals(expected, actual);
+        }
+    }
+
+    private static void assertEquals(float expected, float actual) {
+        if (expected != actual) {
+            failAssertEquals(expected, actual);
+        }
+    }
+
+    private static void assertEquals(double expected, double actual) {
+        if (expected != actual) {
+            failAssertEquals(expected, actual);
+        }
+    }
+
+    static class FieldVarHandleExactInvokerTest {
+        private static final VarHandle fieldVarHandle;
+        int field;
+
+        static {
+            try {
+                fieldVarHandle =
+                        MethodHandles.lookup()
+                                .findVarHandle(
+                                        FieldVarHandleExactInvokerTest.class, "field", int.class);
+            } catch (Exception e) {
+                throw new TestSetupError("Failed to lookup of field", e);
+            }
+        }
+
+        void run() throws Throwable {
+            System.out.println("fieldVarHandleExactInvokerTest");
+
+            MethodHandle invokerMethodHandle =
+                    MethodHandles.varHandleExactInvoker(
+                            VarHandle.AccessMode.GET_AND_SET,
+                            MethodType.methodType(
+                                    int.class, FieldVarHandleExactInvokerTest.class, int.class));
+
+            field = 3;
+            assertEquals(3, (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 4));
+            assertEquals(4, field);
+
+            //
+            // Check invocations with MethodHandle.invokeExact()
+            //
+            try {
+                // Check for unboxing
+                int i =
+                        (int)
+                                invokerMethodHandle.invokeExact(
+                                        fieldVarHandle, this, Integer.valueOf(3));
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(4, field);
+            }
+            try {
+                // Check for widening conversion
+                int i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 3);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(4, field);
+            }
+            try {
+                // Check for acceptance of void return type
+                invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(4, field);
+            }
+            try {
+                // Check for wider return type
+                long l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(4, field);
+            }
+            try {
+                // Check null VarHandle instance fails
+                VarHandle vhNull = null;
+                int i = (int) invokerMethodHandle.invokeExact(vhNull, this, 777);
+                assertUnreachable();
+            } catch (NullPointerException expected) {
+                assertEquals(4, field);
+            }
+
+            //
+            // Check invocations with MethodHandle.invoke()
+            //
+
+            // Check for unboxing
+            int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3));
+            assertEquals(3, field);
+
+            // Check for unboxing
+            i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Short.valueOf((short) 4));
+            assertEquals(4, field);
+
+            // Check for widening conversion
+            i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 23);
+            assertEquals(23, field);
+
+            // Check for acceptance of void return type
+            invokerMethodHandle.invoke(fieldVarHandle, this, 77);
+            assertEquals(77, field);
+
+            // Check for wider return type
+            long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88);
+            assertEquals(88, field);
+
+            try {
+                // Check null VarHandle instance fails
+                VarHandle vhNull = null;
+                i = (int) invokerMethodHandle.invoke(vhNull, this, 888);
+                assertUnreachable();
+            } catch (NullPointerException expected) {
+                assertEquals(88, field);
+            }
+        }
+    }
+
+    static class FieldVarHandleInvokerTest {
+        private static final VarHandle fieldVarHandle;
+        int field;
+
+        static {
+            try {
+                fieldVarHandle =
+                        MethodHandles.lookup()
+                                .findVarHandle(FieldVarHandleInvokerTest.class, "field", int.class);
+            } catch (Exception e) {
+                throw new TestSetupError("Failed to lookup of field", e);
+            }
+        }
+
+        void run() throws Throwable {
+            System.out.println("fieldVarHandleInvokerTest");
+            MethodHandle invokerMethodHandle =
+                    MethodHandles.varHandleInvoker(
+                            VarHandle.AccessMode.GET_AND_SET,
+                            MethodType.methodType(
+                                    int.class, FieldVarHandleInvokerTest.class, int.class));
+
+            field = 3;
+            int oldField = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 4);
+            assertEquals(3, oldField);
+            assertEquals(4, field);
+
+            //
+            // Check invocations with MethodHandle.invoke()
+            //
+
+            // Check for unboxing
+            int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3));
+            assertEquals(3, field);
+
+            // Check for widening conversion
+            i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 33);
+            assertEquals(33, field);
+
+            // Check for widening conversion
+            i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Byte.valueOf((byte) 34));
+            assertEquals(34, field);
+
+            // Check for acceptance of void return type
+            invokerMethodHandle.invoke(fieldVarHandle, this, 77);
+            assertEquals(77, field);
+
+            // Check for wider return type
+            long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88);
+            assertEquals(88, field);
+            try {
+                // Check narrowing conversion fails
+                i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 3.0);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+            }
+            try {
+                // Check reference type fails
+                i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, "Bad");
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+            }
+            try {
+                // Check null VarHandle instance fails
+                VarHandle vhNull = null;
+                i = (int) invokerMethodHandle.invoke(vhNull, this, 888);
+                assertUnreachable();
+            } catch (NullPointerException expected) {
+                assertEquals(88, field);
+            }
+
+            //
+            // Check invocations with MethodHandle.invokeExact()
+            //
+            field = -1;
+            try {
+                // Check for unboxing
+                i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, Integer.valueOf(3));
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(-1, field);
+            }
+            try {
+                // Check for widening conversion
+                i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 33);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(-1, field);
+            }
+            try {
+                // Check for acceptance of void return type
+                invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(-1, field);
+            }
+            try {
+                // Check for wider return type
+                l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 78);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(-1, field);
+            }
+            try {
+                // Check narrowing conversion fails
+                i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 3.0);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+            }
+            try {
+                // Check reference type fails
+                i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, "Bad");
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+            }
+            try {
+                // Check null VarHandle instance fails
+                VarHandle vhNull = null;
+                i = (int) invokerMethodHandle.invokeExact(vhNull, this, 888);
+                assertUnreachable();
+            } catch (NullPointerException expected) {
+                assertEquals(-1, field);
+            }
+        }
+    }
+
+    static class DivergenceExactInvokerTest {
+        private static final VarHandle floatsArrayVarHandle;
+
+        static {
+            try {
+                floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class);
+            } catch (Exception e) {
+                throw new TestSetupError("Failed to create VarHandle", e);
+            }
+        }
+
+        void run() throws Throwable {
+            System.out.println("DivergenceExactInvokerTest");
+            float[] floatsArray = new float[4];
+            // Exact invoker of an accessor having the form:
+            //  float accessor(float[] values, int index, Float current, float replacement)
+            MethodHandle exactInvoker =
+                    MethodHandles.varHandleExactInvoker(
+                            VarHandle.AccessMode.COMPARE_AND_EXCHANGE,
+                            MethodType.methodType(
+                                    float.class,
+                                    float[].class,
+                                    int.class,
+                                    Float.class,
+                                    float.class));
+            floatsArray[2] = Float.valueOf(4.0f);
+            // Callsite that is an exact match with exactInvoker.type().
+            try {
+                // exactInvoker.type() is not compatible with floatsArrayVarHandle accessor.
+                float old =
+                        (float)
+                                exactInvoker.invoke(
+                                        floatsArrayVarHandle,
+                                        floatsArray,
+                                        2,
+                                        Float.valueOf(4.0f),
+                                        8.0f);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(4.0f, floatsArray[2]);
+            }
+
+            // Callsites that are exact matches with exactInvoker.type()
+            try {
+                // Mismatch between exactInvoker.type() and VarHandle type (Float != float)
+                float old =
+                        (float)
+                                exactInvoker.invoke(
+                                        floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(4.0f, floatsArray[2]);
+            }
+            try {
+                // short not convertible to Float
+                float old =
+                        (float)
+                                exactInvoker.invoke(
+                                        floatsArrayVarHandle, floatsArray, 2, (short) 4, 13.0f);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(4.0f, floatsArray[2]);
+            }
+            try {
+                // int not convertible to Float
+                float old =
+                        (float) exactInvoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(4.0f, floatsArray[2]);
+            }
+        }
+    }
+
+    static class DivergenceInvokerTest {
+        private static final VarHandle floatsArrayVarHandle;
+
+        static {
+            try {
+                floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class);
+            } catch (Exception e) {
+                throw new TestSetupError("Failed to create VarHandle", e);
+            }
+        }
+
+        void run() throws Throwable {
+            System.out.println("DivergenceInvokerTest");
+            float[] floatsArray = new float[4];
+            // Invoker of an accessor having the form:
+            //  float accessor(float[] values, int index, Float current, float replacement)
+            MethodHandle invoker =
+                    MethodHandles.varHandleInvoker(
+                            VarHandle.AccessMode.COMPARE_AND_EXCHANGE,
+                            MethodType.methodType(
+                                    float.class,
+                                    float[].class,
+                                    int.class,
+                                    Float.class,
+                                    float.class));
+            floatsArray[2] = Float.valueOf(4.0f);
+            // Callsite that is an exact match with invoker.type()
+            float old =
+                    (float)
+                            invoker.invoke(
+                                    floatsArrayVarHandle,
+                                    floatsArray,
+                                    2,
+                                    Float.valueOf(4.0f),
+                                    8.0f);
+            assertEquals(4.0f, old);
+            assertEquals(8.0f, floatsArray[2]);
+
+            // Callsite that is convertible match to invoker.type()
+            old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f);
+            assertEquals(8.0f, old);
+            assertEquals(16.0f, floatsArray[2]);
+
+            // Callsites that are not convertible to invoker.type().
+            try {
+                // short is not convertible to Float
+                old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, (short) 4, 8.0f);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(16.0f, floatsArray[2]);
+            }
+            try {
+                // int is not convertible to Float
+                old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f);
+                assertUnreachable();
+            } catch (WrongMethodTypeException expected) {
+                assertEquals(16.0f, floatsArray[2]);
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Throwable {
+        new FieldVarHandleExactInvokerTest().run();
+        new FieldVarHandleInvokerTest().run();
+        new DivergenceExactInvokerTest().run();
+        new DivergenceInvokerTest().run();
+    }
+}