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/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.