Argument conversions for setter/getter MethodHandles.

Test: m test-art-host-run-test-959-invoke-polymorphic-accessors
Bug: 30550796

Change-Id: I9898605fc9f9f8a80f47f9559f3ccb99b02b07c8
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 57413dc..c6f479f 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -120,6 +120,7 @@
         "linear_alloc.cc",
         "mem_map.cc",
         "memory_region.cc",
+        "method_handles.cc",
         "mirror/array.cc",
         "mirror/class.cc",
         "mirror/class_ext.cc",
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 981cc1b..4843c4d 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -48,7 +48,8 @@
                                            const ShadowFrame& shadow_frame,
                                            ObjPtr<mirror::Object>& obj,
                                            ArtField* field,
-                                           JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+                                           JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
 
   // Report this field access to instrumentation if needed.
@@ -299,6 +300,42 @@
 EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot);      // iget-object-quick.
 #undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL
 
+static JValue GetFieldValue(const ShadowFrame& shadow_frame,
+                            Primitive::Type field_type,
+                            uint32_t vreg)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  JValue field_value;
+  switch (field_type) {
+    case Primitive::kPrimBoolean:
+      field_value.SetZ(static_cast<uint8_t>(shadow_frame.GetVReg(vreg)));
+      break;
+    case Primitive::kPrimByte:
+      field_value.SetB(static_cast<int8_t>(shadow_frame.GetVReg(vreg)));
+      break;
+    case Primitive::kPrimChar:
+      field_value.SetC(static_cast<uint16_t>(shadow_frame.GetVReg(vreg)));
+      break;
+    case Primitive::kPrimShort:
+      field_value.SetS(static_cast<int16_t>(shadow_frame.GetVReg(vreg)));
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      field_value.SetI(shadow_frame.GetVReg(vreg));
+      break;
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
+      field_value.SetJ(shadow_frame.GetVRegLong(vreg));
+      break;
+    case Primitive::kPrimNot:
+      field_value.SetL(shadow_frame.GetVRegReference(vreg));
+      break;
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable: " << field_type;
+      UNREACHABLE();
+  }
+  return field_value;
+}
+
 template<Primitive::Type field_type>
 static JValue GetFieldValue(const ShadowFrame& shadow_frame, uint32_t vreg)
     REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -337,7 +374,8 @@
                                     const ShadowFrame& shadow_frame,
                                     ObjPtr<mirror::Object>& obj,
                                     ArtField* f,
-                                    size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) {
+                                    const JValue& value)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
 
   // Report this field access to instrumentation if needed. Since we only have the offset of
@@ -347,36 +385,35 @@
     StackHandleScope<1> hs(self);
     // Wrap in handle wrapper in case the listener does thread suspension.
     HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj));
-    JValue field_value = GetFieldValue<field_type>(shadow_frame, vregA);
     ObjPtr<mirror::Object> this_object = f->IsStatic() ? nullptr : obj;
     instrumentation->FieldWriteEvent(self, this_object.Ptr(),
                                      shadow_frame.GetMethod(),
                                      shadow_frame.GetDexPC(),
                                      f,
-                                     field_value);
+                                     value);
   }
 
   switch (field_type) {
     case Primitive::kPrimBoolean:
-      f->SetBoolean<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+      f->SetBoolean<transaction_active>(obj, value.GetZ());
       break;
     case Primitive::kPrimByte:
-      f->SetByte<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+      f->SetByte<transaction_active>(obj, value.GetB());
       break;
     case Primitive::kPrimChar:
-      f->SetChar<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+      f->SetChar<transaction_active>(obj, value.GetC());
       break;
     case Primitive::kPrimShort:
-      f->SetShort<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+      f->SetShort<transaction_active>(obj, value.GetS());
       break;
     case Primitive::kPrimInt:
-      f->SetInt<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+      f->SetInt<transaction_active>(obj, value.GetI());
       break;
     case Primitive::kPrimLong:
-      f->SetLong<transaction_active>(obj, shadow_frame.GetVRegLong(vregA));
+      f->SetLong<transaction_active>(obj, value.GetJ());
       break;
     case Primitive::kPrimNot: {
-      ObjPtr<mirror::Object> reg = shadow_frame.GetVRegReference(vregA);
+      ObjPtr<mirror::Object> reg = value.GetL();
       if (do_assignability_check && reg != nullptr) {
         // FieldHelper::GetType can resolve classes, use a handle wrapper which will restore the
         // object in the destructor.
@@ -434,11 +471,12 @@
   }
 
   uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+  JValue value = GetFieldValue<field_type>(shadow_frame, vregA);
   return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self,
                                                                                   shadow_frame,
                                                                                   obj,
                                                                                   f,
-                                                                                  vregA);
+                                                                                  value);
 }
 
 // Explicitly instantiate all DoFieldPut functions.
@@ -479,37 +517,34 @@
                                     ObjPtr<mirror::Object>& obj,
                                     ArtField* field,
                                     Primitive::Type field_type,
-                                    size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) {
+                                    const JValue& value)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   static const bool kDoCheckAssignability = false;
   static const bool kTransaction = false;
   switch (field_type) {
     case Primitive::kPrimBoolean:
       return DoFieldPutCommon<Primitive::kPrimBoolean, kDoCheckAssignability, kTransaction>(
-          self, shadow_frame, obj, field, vregA);
+          self, shadow_frame, obj, field, value);
     case Primitive::kPrimByte:
       return DoFieldPutCommon<Primitive::kPrimByte, kDoCheckAssignability, kTransaction>(
-          self, shadow_frame, obj, field, vregA);
+          self, shadow_frame, obj, field, value);
     case Primitive::kPrimChar:
       return DoFieldPutCommon<Primitive::kPrimChar, kDoCheckAssignability, kTransaction>(
-          self, shadow_frame, obj, field, vregA);
+          self, shadow_frame, obj, field, value);
     case Primitive::kPrimShort:
       return DoFieldPutCommon<Primitive::kPrimShort, kDoCheckAssignability, kTransaction>(
-          self, shadow_frame, obj, field, vregA);
+          self, shadow_frame, obj, field, value);
     case Primitive::kPrimInt:
-      return DoFieldPutCommon<Primitive::kPrimInt, kDoCheckAssignability, kTransaction>(
-          self, shadow_frame, obj, field, vregA);
-    case Primitive::kPrimLong:
-      return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>(
-          self, shadow_frame, obj, field, vregA);
     case Primitive::kPrimFloat:
       return DoFieldPutCommon<Primitive::kPrimInt, kDoCheckAssignability, kTransaction>(
-          self, shadow_frame, obj, field, vregA);
+          self, shadow_frame, obj, field, value);
+    case Primitive::kPrimLong:
     case Primitive::kPrimDouble:
       return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>(
-          self, shadow_frame, obj, field, vregA);
+          self, shadow_frame, obj, field, value);
     case Primitive::kPrimNot:
       return DoFieldPutCommon<Primitive::kPrimNot, kDoCheckAssignability, kTransaction>(
-          self, shadow_frame, obj, field, vregA);
+          self, shadow_frame, obj, field, value);
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable: " << field_type;
       UNREACHABLE();
@@ -797,7 +832,8 @@
                                 ShadowFrame& shadow_frame,
                                 const Instruction* inst,
                                 uint16_t inst_data,
-                                JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+                                JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   // Invoke-polymorphic instructions always take a receiver. i.e, they are never static.
   const uint32_t vRegC = (is_range) ? inst->VRegC_4rcc() : inst->VRegC_45cc();
   const int invoke_method_idx = (is_range) ? inst->VRegB_4rcc() : inst->VRegB_45cc();
@@ -960,20 +996,23 @@
     Primitive::Type field_type = field->GetTypeAsPrimitiveType();;
 
     if (!is_invoke_exact) {
-      // TODO(oth): conversion plumbing for invoke().
-      UNIMPLEMENTED(FATAL);
+      if (handle_type->GetPTypes()->GetLength() != callsite_type->GetPTypes()->GetLength()) {
+        // Too many arguments to setter or getter.
+        ThrowWrongMethodTypeException(callsite_type.Get(), handle_type.Get());
+        return false;
+      }
     }
 
     switch (handle_kind) {
       case kInstanceGet: {
         ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg);
         DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result);
+        if (!ConvertReturnValue(callsite_type, handle_type, result)) {
+          DCHECK(self->IsExceptionPending());
+          return false;
+        }
         return true;
       }
-      case kInstancePut: {
-        ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg);
-        return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[1]);
-      }
       case kStaticGet: {
         ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field);
         if (obj == nullptr) {
@@ -981,15 +1020,31 @@
           return false;
         }
         DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result);
-        return true;
-      }
-      case kStaticPut: {
-        ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field);
-        if (obj == nullptr) {
+        if (!ConvertReturnValue(callsite_type, handle_type, result)) {
           DCHECK(self->IsExceptionPending());
           return false;
         }
-        return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[0]);
+        return true;
+      }
+      case kInstancePut: {
+        JValue value = GetFieldValue(shadow_frame, field_type, arg[1]);
+        if (!ConvertArgumentValue(callsite_type, handle_type, 1, &value)) {
+          DCHECK(self->IsExceptionPending());
+          return false;
+        }
+        ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg);
+        result->SetL(0);
+        return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
+      }
+      case kStaticPut: {
+        JValue value = GetFieldValue(shadow_frame, field_type, arg[0]);
+        if (!ConvertArgumentValue(callsite_type, handle_type, 0, &value)) {
+          DCHECK(self->IsExceptionPending());
+          return false;
+        }
+        ObjPtr<mirror::Object> obj = field->GetDeclaringClass();
+        result->SetL(0);
+        return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
       }
       default:
         LOG(FATAL) << "Unreachable: " << handle_kind;
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index b279a37..1240792 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -31,127 +31,70 @@
 
 namespace art {
 
-// Assigns |type| to the primitive type associated with |dst_class|. Returns
-// true iff. |dst_class| was a boxed type (Integer, Long etc.), false otherwise.
-REQUIRES_SHARED(Locks::mutator_lock_)
-static inline bool GetPrimitiveType(ObjPtr<mirror::Class> dst_class, Primitive::Type* type) {
-  if (dst_class->DescriptorEquals("Ljava/lang/Boolean;")) {
-    (*type) = Primitive::kPrimBoolean;
+inline bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type,
+                                 Handle<mirror::MethodType> callee_type,
+                                 int index,
+                                 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;
-  } else if (dst_class->DescriptorEquals("Ljava/lang/Byte;")) {
-    (*type) = Primitive::kPrimByte;
-    return true;
-  } else if (dst_class->DescriptorEquals("Ljava/lang/Character;")) {
-    (*type) = Primitive::kPrimChar;
-    return true;
-  } else if (dst_class->DescriptorEquals("Ljava/lang/Float;")) {
-    (*type) = Primitive::kPrimFloat;
-    return true;
-  } else if (dst_class->DescriptorEquals("Ljava/lang/Double;")) {
-    (*type) = Primitive::kPrimDouble;
-    return true;
-  } else if (dst_class->DescriptorEquals("Ljava/lang/Integer;")) {
-    (*type) = Primitive::kPrimInt;
-    return true;
-  } else if (dst_class->DescriptorEquals("Ljava/lang/Long;")) {
-    (*type) = Primitive::kPrimLong;
-    return true;
-  } else if (dst_class->DescriptorEquals("Ljava/lang/Short;")) {
-    (*type) = Primitive::kPrimShort;
+  }
+
+  // |value| may contain a bare heap pointer which is generally
+  // |unsafe. ConvertJValueCommon() saves |value|, |from_class|, and
+  // |to_class| to Handles where necessary to avoid issues if the heap
+  // changes.
+  if (ConvertJValueCommon(callsite_type, callee_type, from_class, to_class, value)) {
+    DCHECK(!Thread::Current()->IsExceptionPending());
     return true;
   } else {
+    DCHECK(Thread::Current()->IsExceptionPending());
+    value->SetJ(0);
     return false;
   }
 }
 
-inline bool ConvertJValue(Handle<mirror::Class> from,
-                          Handle<mirror::Class> to,
-                          const JValue& from_value,
-                          JValue* to_value) {
-  const Primitive::Type from_type = from->GetPrimitiveType();
-  const Primitive::Type to_type = to->GetPrimitiveType();
-
-  // This method must be called only when the types don't match.
-  DCHECK(from.Get() != to.Get());
-
-  if ((from_type != Primitive::kPrimNot) && (to_type != Primitive::kPrimNot)) {
-    // Throws a ClassCastException if we're unable to convert a primitive value.
-    return ConvertPrimitiveValue(false, from_type, to_type, from_value, to_value);
-  } else if ((from_type == Primitive::kPrimNot) && (to_type == Primitive::kPrimNot)) {
-    // They're both reference types. If "from" is null, we can pass it
-    // through unchanged. If not, we must generate a cast exception if
-    // |to| is not assignable from the dynamic type of |ref|.
-    mirror::Object* const ref = from_value.GetL();
-    if (ref == nullptr || to->IsAssignableFrom(ref->GetClass())) {
-      to_value->SetL(ref);
-      return true;
-    } else {
-      ThrowClassCastException(to.Get(), ref->GetClass());
-      return false;
-    }
-  } else {
-    // Precisely one of the source or the destination are reference types.
-    // We must box or unbox.
-    if (to_type == Primitive::kPrimNot) {
-      // The target type is a reference, we must box.
-      Primitive::Type type;
-      // TODO(narayan): This is a CHECK for now. There might be a few corner cases
-      // here that we might not have handled yet. For exmple, if |to| is java/lang/Number;,
-      // we will need to box this "naturally".
-      CHECK(GetPrimitiveType(to.Get(), &type));
-      // First perform a primitive conversion to the unboxed equivalent of the target,
-      // if necessary. This should be for the rarer cases like (int->Long) etc.
-      if (UNLIKELY(from_type != type)) {
-        if (!ConvertPrimitiveValue(false, from_type, type, from_value, to_value)) {
-          return false;
-        }
-      } else {
-        *to_value = from_value;
-      }
-
-      // Then perform the actual boxing, and then set the reference. Note that
-      // BoxPrimitive can return null if an OOM occurs.
-      ObjPtr<mirror::Object> boxed = BoxPrimitive(type, from_value);
-      if (boxed.Ptr() == nullptr) {
-        DCHECK(Thread::Current()->IsExceptionPending());
-        return false;
-      }
-
-      to_value->SetL(boxed.Ptr());
-      return true;
-    } else {
-      // The target type is a primitive, we must unbox.
-      ObjPtr<mirror::Object> ref(from_value.GetL());
-
-      // Note that UnboxPrimitiveForResult already performs all of the type
-      // conversions that we want, based on |to|.
-      JValue unboxed_value;
-      return UnboxPrimitiveForResult(ref, to.Get(), to_value);
-    }
+inline bool ConvertReturnValue(Handle<mirror::MethodType> callsite_type,
+                               Handle<mirror::MethodType> callee_type,
+                               JValue* value)  REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::Class> from_class(callee_type->GetRType());
+  ObjPtr<mirror::Class> to_class(callsite_type->GetRType());
+  if (to_class->GetPrimitiveType() == Primitive::kPrimVoid || from_class == to_class) {
+    return true;
   }
 
-  return true;
+  // |value| may contain a bare heap pointer which is generally
+  // unsafe. ConvertJValueCommon() saves |value|, |from_class|, and
+  // |to_class| to Handles where necessary to avoid issues if the heap
+  // changes.
+  if (ConvertJValueCommon(callsite_type, callee_type, from_class, to_class, value)) {
+    DCHECK(!Thread::Current()->IsExceptionPending());
+    return true;
+  } else {
+    DCHECK(Thread::Current()->IsExceptionPending());
+    value->SetJ(0);
+    return false;
+  }
 }
 
 template <typename G, typename S>
 bool PerformConversions(Thread* self,
-                        Handle<mirror::ObjectArray<mirror::Class>> from_types,
-                        Handle<mirror::ObjectArray<mirror::Class>> to_types,
+                        Handle<mirror::MethodType> callsite_type,
+                        Handle<mirror::MethodType> callee_type,
                         G* getter,
                         S* setter,
-                        int32_t num_conversions) {
+                        int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<2> hs(self);
-  MutableHandle<mirror::Class> from(hs.NewHandle<mirror::Class>(nullptr));
-  MutableHandle<mirror::Class> to(hs.NewHandle<mirror::Class>(nullptr));
+  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) {
-    from.Assign(from_types->GetWithoutChecks(i));
-    to.Assign(to_types->GetWithoutChecks(i));
-
-    const Primitive::Type from_type = from->GetPrimitiveType();
-    const Primitive::Type to_type = to->GetPrimitiveType();
-
-    if (from.Get() == to.Get()) {
+    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();
+    if (from == to) {
       // Easy case - the types are identical. Nothing left to do except to pass
       // the arguments along verbatim.
       if (Primitive::Is64BitType(from_type)) {
@@ -162,28 +105,28 @@
         setter->Set(getter->Get());
       }
     } else {
-      JValue from_value;
-      JValue to_value;
+      JValue value;
 
       if (Primitive::Is64BitType(from_type)) {
-        from_value.SetJ(getter->GetLong());
+        value.SetJ(getter->GetLong());
       } else if (from_type == Primitive::kPrimNot) {
-        from_value.SetL(getter->GetReference());
+        value.SetL(getter->GetReference());
       } else {
-        from_value.SetI(getter->Get());
+        value.SetI(getter->Get());
       }
 
-      if (!ConvertJValue(from, to, from_value, &to_value)) {
+      // Caveat emptor - ObjPtr's not guaranteed valid after this call.
+      if (!ConvertArgumentValue(callsite_type, callee_type, i, &value)) {
         DCHECK(self->IsExceptionPending());
         return false;
       }
 
       if (Primitive::Is64BitType(to_type)) {
-        setter->SetLong(to_value.GetJ());
+        setter->SetLong(value.GetJ());
       } else if (to_type == Primitive::kPrimNot) {
-        setter->SetReference(to_value.GetL());
+        setter->SetReference(value.GetL());
       } else {
-        setter->Set(to_value.GetI());
+        setter->Set(value.GetI());
       }
     }
   }
@@ -199,10 +142,10 @@
                                             uint32_t first_src_reg,
                                             uint32_t first_dest_reg,
                                             const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
-                                            ShadowFrame* callee_frame) {
-  StackHandleScope<4> 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()));
+                                            ShadowFrame* callee_frame)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::ObjectArray<mirror::Class>> from_types(callsite_type->GetPTypes());
+  ObjPtr<mirror::ObjectArray<mirror::Class>> to_types(callee_type->GetPTypes());
 
   const int32_t num_method_params = from_types->GetLength();
   if (to_types->GetLength() != num_method_params) {
@@ -214,8 +157,8 @@
   ShadowFrameSetter setter(callee_frame, first_dest_reg);
 
   return PerformConversions<ShadowFrameGetter<is_range>, ShadowFrameSetter>(self,
-                                                                            from_types,
-                                                                            to_types,
+                                                                            callsite_type,
+                                                                            callee_type,
                                                                             &getter,
                                                                             &setter,
                                                                             num_method_params);
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
new file mode 100644
index 0000000..491d139
--- /dev/null
+++ b/runtime/method_handles.cc
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2016 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 "method_handles.h"
+
+#include "method_handles-inl.h"
+#include "jvalue.h"
+#include "jvalue-inl.h"
+#include "reflection.h"
+#include "reflection-inl.h"
+
+namespace art {
+
+namespace {
+
+static const char* kBoxedBooleanClass = "Ljava/lang/Boolean;";
+static const char* kBoxedByteClass = "Ljava/lang/Byte;";
+static const char* kBoxedCharacterClass = "Ljava/lang/Character;";
+static const char* kBoxedDoubleClass = "Ljava/lang/Double;";
+static const char* kBoxedFloatClass = "Ljava/lang/Float;";
+static const char* kBoxedIntegerClass = "Ljava/lang/Integer;";
+static const char* kBoxedLongClass = "Ljava/lang/Long;";
+static const char* kBoxedShortClass = "Ljava/lang/Short;";
+
+// Assigns |type| to the primitive type associated with |klass|. Returns
+// true iff. |klass| was a boxed type (Integer, Long etc.), false otherwise.
+bool GetUnboxedPrimitiveType(ObjPtr<mirror::Class> klass, Primitive::Type* type)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+  if (klass->DescriptorEquals(kBoxedBooleanClass)) {
+    (*type) = Primitive::kPrimBoolean;
+    return true;
+  } else if (klass->DescriptorEquals(kBoxedByteClass)) {
+    (*type) = Primitive::kPrimByte;
+    return true;
+  } else if (klass->DescriptorEquals(kBoxedCharacterClass)) {
+    (*type) = Primitive::kPrimChar;
+    return true;
+  } else if (klass->DescriptorEquals(kBoxedFloatClass)) {
+    (*type) = Primitive::kPrimFloat;
+    return true;
+  } else if (klass->DescriptorEquals(kBoxedDoubleClass)) {
+    (*type) = Primitive::kPrimDouble;
+    return true;
+  } else if (klass->DescriptorEquals(kBoxedIntegerClass)) {
+    (*type) = Primitive::kPrimInt;
+    return true;
+  } else if (klass->DescriptorEquals(kBoxedLongClass)) {
+    (*type) = Primitive::kPrimLong;
+    return true;
+  } else if (klass->DescriptorEquals(kBoxedShortClass)) {
+    (*type) = Primitive::kPrimShort;
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// Returns the class corresponding to the boxed type for the primitive |type|.
+ObjPtr<mirror::Class> GetBoxedPrimitiveClass(Primitive::Type type)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  switch (type) {
+    case Primitive::kPrimBoolean:
+      return class_linker->FindSystemClass(Thread::Current(), kBoxedBooleanClass);
+    case Primitive::kPrimByte:
+      return class_linker->FindSystemClass(Thread::Current(), kBoxedByteClass);
+    case Primitive::kPrimChar:
+      return class_linker->FindSystemClass(Thread::Current(), kBoxedCharacterClass);
+    case Primitive::kPrimShort:
+      return class_linker->FindSystemClass(Thread::Current(), kBoxedShortClass);
+    case Primitive::kPrimInt:
+      return class_linker->FindSystemClass(Thread::Current(), kBoxedIntegerClass);
+    case Primitive::kPrimLong:
+      return class_linker->FindSystemClass(Thread::Current(), kBoxedLongClass);
+    case Primitive::kPrimFloat:
+      return class_linker->FindSystemClass(Thread::Current(), kBoxedFloatClass);
+    case Primitive::kPrimDouble:
+      return class_linker->FindSystemClass(Thread::Current(), kBoxedDoubleClass);
+    case Primitive::kPrimNot:
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable";
+      return nullptr;
+  }
+}
+
+// Returns true if |klass| is a boxed primitive type or a sub-class of a boxed primitive type.
+bool IsSubClassOfBoxedPrimitive(const Handle<mirror::Class>& klass)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  StackHandleScope<1> hs(Thread::Current());
+  MutableHandle<mirror::Class> h_klass(hs.NewHandle(klass.Get()));
+  do {
+    Primitive::Type type;
+    if (GetUnboxedPrimitiveType(h_klass.Get(), &type)) {
+      return true;
+    }
+    h_klass.Assign(h_klass->GetSuperClass());
+  } while (h_klass.Get() != nullptr);
+  return false;
+}
+
+// Unboxed the value |o| to |unboxed_value| of type |dst_class|.
+// |unboxed_value| must be zero on entry to avoid dangling pointers.
+// Returns true on success, false if an exception is raised.
+bool UnboxPrimitiveForMethodHandles(ObjPtr<mirror::Object> o,
+                                    ObjPtr<mirror::Class> dst_class,
+                                    JValue* unboxed_value)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Check unboxed_value does not contain a dangling pointer.
+  DCHECK_EQ(unboxed_value->GetJ(), 0);
+  DCHECK(dst_class->IsPrimitive());
+
+  // This is derived from UnboxPrimitive() in reflection.cc, but with
+  // exceptions appropriate to method handles.
+  if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) {
+    ThrowClassCastException(o->GetClass(), dst_class);
+    return false;
+  }
+  if (UNLIKELY(o == nullptr)) {
+    ThrowNullPointerException(
+        StringPrintf("Expected to unbox a '%s' primitive type but was returned null",
+                     dst_class->PrettyDescriptor().c_str()).c_str());
+    return false;
+  }
+
+  JValue boxed_value;
+  ObjPtr<mirror::Class> klass = o->GetClass();
+  ObjPtr<mirror::Class> src_class = nullptr;
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0);
+  if (klass->DescriptorEquals(kBoxedBooleanClass)) {
+    src_class = class_linker->FindPrimitiveClass('Z');
+    boxed_value.SetZ(primitive_field->GetBoolean(o));
+  } else if (klass->DescriptorEquals(kBoxedByteClass)) {
+    src_class = class_linker->FindPrimitiveClass('B');
+    boxed_value.SetB(primitive_field->GetByte(o));
+  } else if (klass->DescriptorEquals(kBoxedCharacterClass)) {
+    src_class = class_linker->FindPrimitiveClass('C');
+    boxed_value.SetC(primitive_field->GetChar(o));
+  } else if (klass->DescriptorEquals(kBoxedFloatClass)) {
+    src_class = class_linker->FindPrimitiveClass('F');
+    boxed_value.SetF(primitive_field->GetFloat(o));
+  } else if (klass->DescriptorEquals(kBoxedDoubleClass)) {
+    src_class = class_linker->FindPrimitiveClass('D');
+    boxed_value.SetD(primitive_field->GetDouble(o));
+  } else if (klass->DescriptorEquals(kBoxedIntegerClass)) {
+    src_class = class_linker->FindPrimitiveClass('I');
+    boxed_value.SetI(primitive_field->GetInt(o));
+  } else if (klass->DescriptorEquals(kBoxedLongClass)) {
+    src_class = class_linker->FindPrimitiveClass('J');
+    boxed_value.SetJ(primitive_field->GetLong(o));
+  } else if (klass->DescriptorEquals(kBoxedShortClass)) {
+    src_class = class_linker->FindPrimitiveClass('S');
+    boxed_value.SetS(primitive_field->GetShort(o));
+  } else {
+    std::string temp;
+    ThrowIllegalArgumentException(
+        StringPrintf("result has type %s, got %s",
+                     dst_class->PrettyDescriptor().c_str(),
+                     PrettyDescriptor(o->GetClass()->GetDescriptor(&temp)).c_str()).c_str());
+    return false;
+  }
+
+  if (!ConvertPrimitiveValueNoThrow(src_class->GetPrimitiveType(),
+                                    dst_class->GetPrimitiveType(),
+                                    boxed_value,
+                                    unboxed_value)) {
+    ThrowClassCastException(src_class, dst_class);
+    return false;
+  }
+  return true;
+}
+
+inline bool IsReferenceType(Primitive::Type type) {
+  return type == Primitive::kPrimNot;
+}
+
+inline bool IsPrimitiveType(Primitive::Type type) {
+  return !IsReferenceType(type);
+}
+
+}  // namespace
+
+bool ConvertJValueCommon(
+    Handle<mirror::MethodType> callsite_type,
+    Handle<mirror::MethodType> callee_type,
+    ObjPtr<mirror::Class> from,
+    ObjPtr<mirror::Class> to,
+    JValue* value) {
+  // The reader maybe concerned about the safety of the heap object
+  // that may be in |value|. There is only one case where allocation
+  // is obviously needed and that's for boxing. However, in the case
+  // of boxing |value| contains a non-reference type.
+
+  const Primitive::Type from_type = from->GetPrimitiveType();
+  const Primitive::Type to_type = to->GetPrimitiveType();
+
+  // This method must be called only when the types don't match.
+  DCHECK(from != to);
+
+  if (IsPrimitiveType(from_type) && IsPrimitiveType(to_type)) {
+    // The source and target types are both primitives.
+    if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, to_type, *value, value))) {
+      ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+      value->SetJ(0);
+      return false;
+    }
+    return true;
+  } else if (IsReferenceType(from_type) && IsReferenceType(to_type)) {
+    // They're both reference types. If "from" is null, we can pass it
+    // through unchanged. If not, we must generate a cast exception if
+    // |to| is not assignable from the dynamic type of |ref|.
+    //
+    // Playing it safe with StackHandleScope here, not expecting any allocation
+    // in mirror::Class::IsAssignable().
+    StackHandleScope<2> hs(Thread::Current());
+    Handle<mirror::Class> h_to(hs.NewHandle(to));
+    Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL()));
+
+    // |value| will now be the result value, invalidate its existing value
+    // as |h_obj| now owns it.
+    value->SetJ(0);
+
+    if (h_obj.Get() != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
+      ThrowClassCastException(h_to.Get(), h_obj->GetClass());
+      return false;
+    }
+    value->SetL(h_obj.Get());
+    return true;
+  } else if (IsReferenceType(to_type)) {
+    DCHECK(IsPrimitiveType(from_type));
+    // Playing it safe with StackHandleScope here with regards to
+    // GetUnboxedPrimitiveType() and GetBoxedPrimitiveClass().
+    StackHandleScope<1> hs(Thread::Current());
+    Handle<mirror::Class> h_to(hs.NewHandle(to));
+    // The source type is a primitive and the target type is a reference, so we must box.
+    // The target type maybe a super class of the boxed source type, for example,
+    // if the source type is int, it's boxed type is java.lang.Integer, and the target
+    // type could be java.lang.Number.
+    Primitive::Type type;
+    if (!GetUnboxedPrimitiveType(to, &type)) {
+      ObjPtr<mirror::Class> boxed_from_class = GetBoxedPrimitiveClass(from_type);
+      if (boxed_from_class->IsSubClass(h_to.Get())) {
+        type = from_type;
+      } else {
+        value->SetJ(0);
+        ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+        return false;
+      }
+    }
+
+    if (UNLIKELY(from_type != type)) {
+      value->SetJ(0);
+      ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+      return false;
+    }
+
+    if (!ConvertPrimitiveValueNoThrow(from_type, type, *value, value)) {
+      value->SetJ(0);
+      ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+      return false;
+    }
+
+    // Then perform the actual boxing, and then set the reference.
+    ObjPtr<mirror::Object> boxed = BoxPrimitive(type, *value);
+    value->SetL(boxed.Ptr());
+    return true;
+  } else {
+    // The source type is a reference and the target type is a primitive, so we must unbox.
+    DCHECK(IsReferenceType(from_type));
+    DCHECK(IsPrimitiveType(to_type));
+
+    // Use StackHandleScope to protect |from|, |to|, and the reference
+    // in |value| from heap re-arrangements that could be triggered
+    // ahead of unboxing step.
+    StackHandleScope<3> hs(Thread::Current());
+    Handle<mirror::Class> h_to(hs.NewHandle(to));
+    Handle<mirror::Class> h_from(hs.NewHandle(from));
+    Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL()));
+
+    // |value| will now be the result value, invalidate its existing value
+    // as |h_obj| now owns it.
+    value->SetJ(0);
+
+    // Check source type is a boxed primitive or has a boxed primitive super-class.
+    ObjPtr<mirror::Class> boxed_to_class = GetBoxedPrimitiveClass(to_type);
+    if (!IsSubClassOfBoxedPrimitive(h_from) && !boxed_to_class->IsSubClass(h_from.Get())) {
+      ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+      return false;
+    }
+
+    if (h_obj.Get() == nullptr) {
+      ThrowNullPointerException(
+        StringPrintf("Expected to unbox a '%s' but instance was null",
+                     h_from->PrettyDescriptor().c_str()).c_str());
+      return false;
+    }
+
+    return UnboxPrimitiveForMethodHandles(h_obj.Get(), h_to.Get(), value);
+  }
+}
+
+}  // namespace art
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 06509bf..0cc69f2 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -20,7 +20,10 @@
 #include <ostream>
 
 #include "dex_instruction.h"
+#include "handle.h"
 #include "jvalue.h"
+#include "mirror/class.h"
+#include "mirror/method_type.h"
 
 namespace art {
 
@@ -56,12 +59,36 @@
   return handle_kind <= kLastInvokeKind;
 }
 
-// Performs a single argument conversion from type |from| to a distinct
-// type |to|. Returns true on success, false otherwise.
-ALWAYS_INLINE bool ConvertJValue(Handle<mirror::Class> from,
-                          Handle<mirror::Class> to,
-                          const JValue& from_value,
-                          JValue* to_value) REQUIRES_SHARED(Locks::mutator_lock_);
+// Performs a conversion from type |from| to a distinct type |to| as
+// part of conversion of |caller_type| to |callee_type|. The value to
+// be converted is in |value|. Returns true on success and updates
+// |value| with the converted value, false otherwise.
+bool ConvertJValueCommon(Handle<mirror::MethodType> callsite_type,
+                         Handle<mirror::MethodType> callee_type,
+                         ObjPtr<mirror::Class> from,
+                         ObjPtr<mirror::Class> to,
+                         JValue* value)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+// Converts the value of the argument at position |index| from type
+// expected by |callee_type| to type used by |callsite_type|. |value|
+// represents the value to be converted. Returns true on success and
+// updates |value|, false otherwise.
+ALWAYS_INLINE bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type,
+                                        Handle<mirror::MethodType> callee_type,
+                                        int index,
+                                        JValue* value)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+// Converts the return value from return type yielded by
+// |callee_type| to the return type yielded by
+// |callsite_type|. |value| represents the value to be
+// converted. Returns true on success and updates |value|, false
+// otherwise.
+ALWAYS_INLINE bool ConvertReturnValue(Handle<mirror::MethodType> callsite_type,
+                                      Handle<mirror::MethodType> callee_type,
+                                      JValue* value)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Perform argument conversions between |callsite_type| (the type of the
 // incoming arguments) and |callee_type| (the type of the method being
@@ -109,8 +136,8 @@
 // overridden by concrete classes.
 template <typename G, typename S>
 bool PerformConversions(Thread* self,
-                        Handle<mirror::ObjectArray<mirror::Class>> from_types,
-                        Handle<mirror::ObjectArray<mirror::Class>> to_types,
+                        Handle<mirror::MethodType> callsite_type,
+                        Handle<mirror::MethodType> callee_type,
                         G* getter,
                         S* setter,
                         int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
index 861d416..d607040 100644
--- a/runtime/mirror/emulated_stack_frame.cc
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -188,7 +188,7 @@
   ShadowFrameGetter<is_range> getter(first_src_reg, arg, caller_frame);
   EmulatedStackFrameAccessor setter(references, stack_frame, stack_frame->GetLength());
   if (!PerformConversions<ShadowFrameGetter<is_range>, EmulatedStackFrameAccessor>(
-          self, from_types, to_types, &getter, &setter, num_method_params)) {
+          self, caller_type, callee_type, &getter, &setter, num_method_params)) {
     return nullptr;
   }
 
@@ -206,9 +206,8 @@
                                             Handle<mirror::MethodType> callee_type,
                                             const uint32_t first_dest_reg,
                                             ShadowFrame* callee_frame) {
-  StackHandleScope<4> hs(self);
-  Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(GetType()->GetPTypes()));
-  Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes()));
+  ObjPtr<mirror::ObjectArray<mirror::Class>> from_types(GetType()->GetPTypes());
+  ObjPtr<mirror::ObjectArray<mirror::Class>> to_types(callee_type->GetPTypes());
 
   const int32_t num_method_params = from_types->GetLength();
   if (to_types->GetLength() != num_method_params) {
@@ -216,6 +215,8 @@
     return false;
   }
 
+  StackHandleScope<3> hs(self);
+  Handle<mirror::MethodType> frame_callsite_type(hs.NewHandle(GetType()));
   Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(GetReferences()));
   Handle<ByteArray> stack_frame(hs.NewHandle(GetStackFrame()));
 
@@ -223,7 +224,7 @@
   ShadowFrameSetter setter(callee_frame, first_dest_reg);
 
   return PerformConversions<EmulatedStackFrameAccessor, ShadowFrameSetter>(
-      self, from_types, to_types, &getter, &setter, num_method_params);
+      self, frame_callsite_type, callee_type, &getter, &setter, num_method_params);
 }
 
 void EmulatedStackFrame::GetReturnValue(Thread* self, JValue* value) {
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index c4d4fae..68e7a10 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -29,11 +29,10 @@
 
 namespace art {
 
-inline bool ConvertPrimitiveValue(bool unbox_for_result,
-                                  Primitive::Type srcType,
-                                  Primitive::Type dstType,
-                                  const JValue& src,
-                                  JValue* dst) {
+inline bool ConvertPrimitiveValueNoThrow(Primitive::Type srcType,
+                                         Primitive::Type dstType,
+                                         const JValue& src,
+                                         JValue* dst) {
   DCHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot);
   if (LIKELY(srcType == dstType)) {
     dst->SetJ(src.GetJ());
@@ -91,6 +90,18 @@
   default:
     break;
   }
+  return false;
+}
+
+inline bool ConvertPrimitiveValue(bool unbox_for_result,
+                                  Primitive::Type srcType,
+                                  Primitive::Type dstType,
+                                  const JValue& src,
+                                  JValue* dst) {
+  if (ConvertPrimitiveValueNoThrow(srcType, dstType, src, dst)) {
+    return true;
+  }
+
   if (!unbox_for_result) {
     ThrowIllegalArgumentException(StringPrintf("Invalid primitive conversion from %s to %s",
                                                PrettyDescriptor(srcType).c_str(),
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 6e5ef71..f2652fd 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -47,6 +47,12 @@
                              JValue* unboxed_value)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
+ALWAYS_INLINE bool ConvertPrimitiveValueNoThrow(Primitive::Type src_class,
+                                                Primitive::Type dst_class,
+                                                const JValue& src,
+                                                JValue* dst)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
 ALWAYS_INLINE bool ConvertPrimitiveValue(bool unbox_for_result,
                                          Primitive::Type src_class,
                                          Primitive::Type dst_class,
diff --git a/test/955-methodhandles-smali/expected.txt b/test/955-methodhandles-smali/expected.txt
index 047a287..5de1274 100644
--- a/test/955-methodhandles-smali/expected.txt
+++ b/test/955-methodhandles-smali/expected.txt
@@ -5,4 +5,5 @@
 40
 43
 44
-0-11
+0
+-1
diff --git a/test/955-methodhandles-smali/smali/Main.smali b/test/955-methodhandles-smali/smali/Main.smali
index 9681d56..52460a8 100644
--- a/test/955-methodhandles-smali/smali/Main.smali
+++ b/test/955-methodhandles-smali/smali/Main.smali
@@ -220,24 +220,22 @@
     invoke-polymorphic {v0, v1, v1}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;Ljava/lang/Long;)I
     move-result v3
     sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
-    invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V
+    invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(I)V
 
     # Call compareTo(long) - this is an implicit box.
     const-wide v2, 44
     invoke-polymorphic {v0, v1, v2, v3}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;J)I
     move-result v3
     sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
-    invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V
+    invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(I)V
 
     # Call compareTo(int) - this is an implicit box.
-    const v2, 40
-    invoke-polymorphic {v0, v1, v2}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;I)I
-    move-result v3
-    sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
-    invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V
-
-    # Add a newline at the end of file.
-    invoke-virtual {v4}, Ljava/io/PrintStream;->println()V
+# This throws WrongMethodTypeException as it's a two step conversion int->long->Long or int->Integer->Long.
+#    const v2, 40
+#    invoke-polymorphic {v0, v1, v2}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;I)I
+#    move-result v3
+#    sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
+#    invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V
 
     return-void
 .end method
diff --git a/test/979-invoke-polymorphic-accessors/build b/test/959-invoke-polymorphic-accessors/build
similarity index 100%
rename from test/979-invoke-polymorphic-accessors/build
rename to test/959-invoke-polymorphic-accessors/build
diff --git a/test/959-invoke-polymorphic-accessors/expected.txt b/test/959-invoke-polymorphic-accessors/expected.txt
new file mode 100644
index 0000000..de2916b
--- /dev/null
+++ b/test/959-invoke-polymorphic-accessors/expected.txt
@@ -0,0 +1,4 @@
+1515870810
+Passed MethodHandles.Lookup tests for accessors.
+Passed MethodHandle.invokeExact() tests for accessors.
+Passed MethodHandle.invoke() tests for accessors.
diff --git a/test/979-invoke-polymorphic-accessors/info.txt b/test/959-invoke-polymorphic-accessors/info.txt
similarity index 100%
rename from test/979-invoke-polymorphic-accessors/info.txt
rename to test/959-invoke-polymorphic-accessors/info.txt
diff --git a/test/979-invoke-polymorphic-accessors/run b/test/959-invoke-polymorphic-accessors/run
similarity index 100%
rename from test/979-invoke-polymorphic-accessors/run
rename to test/959-invoke-polymorphic-accessors/run
diff --git a/test/979-invoke-polymorphic-accessors/src/Main.java b/test/959-invoke-polymorphic-accessors/src/Main.java
similarity index 83%
rename from test/979-invoke-polymorphic-accessors/src/Main.java
rename to test/959-invoke-polymorphic-accessors/src/Main.java
index 8f1e361..824a436 100644
--- a/test/979-invoke-polymorphic-accessors/src/Main.java
+++ b/test/959-invoke-polymorphic-accessors/src/Main.java
@@ -44,7 +44,26 @@
         public static final int s_fi = 0x5a5a5a5a;
     }
 
-    public static class InvokeExactTester {
+    public static class Tester {
+        public static void assertActualAndExpectedMatch(boolean actual, boolean expected)
+                throws AssertionError {
+            if (actual != expected) {
+                throw new AssertionError("Actual != Expected (" + actual + " != " + expected + ")");
+            }
+        }
+
+        public static void assertTrue(boolean value) throws AssertionError {
+            if (!value) {
+                throw new AssertionError("Value is not true");
+            }
+        }
+
+        public static void unreachable() throws Throwable{
+            throw new Error("unreachable");
+        }
+    }
+
+    public static class InvokeExactTester extends Tester {
         private enum PrimitiveType {
             Boolean,
             Byte,
@@ -64,19 +83,6 @@
             SGET,
         }
 
-        private static void assertActualAndExpectedMatch(boolean actual, boolean expected)
-                throws AssertionError {
-            if (actual != expected) {
-                throw new AssertionError("Actual != Expected (" + actual + " != " + expected + ")");
-            }
-        }
-
-        private static void assertTrue(boolean value) throws AssertionError {
-            if (!value) {
-                throw new AssertionError("Value is not true");
-            }
-        }
-
         static void setByte(MethodHandle m, ValueHolder v, byte value, boolean expectFailure)
                 throws Throwable {
             boolean exceptionThrown = false;
@@ -677,11 +683,11 @@
                 assertTrue(s.equals(ValueHolder.s_l));
             }
 
-            System.out.println("Passed InvokeExact tests for accessors.");
+            System.out.println("Passed MethodHandle.invokeExact() tests for accessors.");
         }
     }
 
-    public static class FindAccessorTester {
+    public static class FindAccessorTester extends Tester {
         public static void main() throws Throwable {
             // NB having a static field test here is essential for
             // this test. MethodHandles need to ensure the class
@@ -723,10 +729,161 @@
                 lookup.findSetter(ValueHolder.class, "m_fi", int.class);
                 unreachable();
             } catch (IllegalAccessException e) {}
+
+            System.out.println("Passed MethodHandles.Lookup tests for accessors.");
+        }
+    }
+
+    public static class InvokeTester extends Tester {
+        private static void testStaticGetter() throws Throwable {
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+            MethodHandle h0 = lookup.findStaticGetter(ValueHolder.class, "s_fi", int.class);
+            h0.invoke();
+            Number t = (Number)h0.invoke();
+            int u = (int)h0.invoke();
+            Integer v = (Integer)h0.invoke();
+            long w = (long)h0.invoke();
+            try {
+                byte x = (byte)h0.invoke();
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+            try {
+                String y = (String)h0.invoke();
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+            try {
+                Long z = (Long)h0.invoke();
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
         }
 
-        public static void unreachable() throws Throwable{
-            throw new Error("unreachable");
+        private static void testMemberGetter() throws Throwable {
+            ValueHolder valueHolder = new ValueHolder();
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+            MethodHandle h0 = lookup.findGetter(ValueHolder.class, "m_fi", int.class);
+            h0.invoke(valueHolder);
+            Number t = (Number)h0.invoke(valueHolder);
+            int u = (int)h0.invoke(valueHolder);
+            Integer v = (Integer)h0.invoke(valueHolder);
+            long w = (long)h0.invoke(valueHolder);
+            try {
+                byte x = (byte)h0.invoke(valueHolder);
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+            try {
+                String y = (String)h0.invoke(valueHolder);
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+            try {
+                Long z = (Long)h0.invoke(valueHolder);
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+        }
+
+        private static void testMemberSetter() throws Throwable {
+            ValueHolder valueHolder = new ValueHolder();
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+            MethodHandle h0 = lookup.findSetter(ValueHolder.class, "m_f", float.class);
+            h0.invoke(valueHolder, 0.22f);
+            h0.invoke(valueHolder, new Float(1.11f));
+            Number floatNumber = new Float(0.88f);
+            h0.invoke(valueHolder, floatNumber);
+            assertTrue(valueHolder.m_f == floatNumber.floatValue());
+
+            try {
+              h0.invoke(valueHolder, (Float)null);
+              unreachable();
+            } catch (NullPointerException e) {}
+
+            h0.invoke(valueHolder, (byte)1);
+            h0.invoke(valueHolder, (short)2);
+            h0.invoke(valueHolder, 3);
+            h0.invoke(valueHolder, 4l);
+            try {
+                h0.invoke(valueHolder, 0.33);
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+            try {
+                Number doubleNumber = new Double(0.89);
+                h0.invoke(valueHolder, doubleNumber);
+                unreachable();
+            } catch (ClassCastException e) {}
+            try {
+                Number doubleNumber = null;
+                h0.invoke(valueHolder, doubleNumber);
+                unreachable();
+            } catch (NullPointerException e) {}
+            try {
+                // Mismatched return type - float != void
+                float tmp = (float)h0.invoke(valueHolder, 0.45f);
+                assertTrue(tmp == 0.0);
+            } catch (Exception e) { unreachable(); }
+            try {
+                h0.invoke(valueHolder, "bam");
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+            try {
+                String s = null;
+                h0.invoke(valueHolder, s);
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+        }
+
+        private static void testStaticSetter() throws Throwable {
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+            MethodHandle h0 = lookup.findStaticSetter(ValueHolder.class, "s_f", float.class);
+            h0.invoke(0.22f);
+            h0.invoke(new Float(1.11f));
+            Number floatNumber = new Float(0.88f);
+            h0.invoke(floatNumber);
+            assertTrue(ValueHolder.s_f == floatNumber.floatValue());
+
+            try {
+              h0.invoke((Float)null);
+              unreachable();
+            } catch (NullPointerException e) {}
+
+            h0.invoke((byte)1);
+            h0.invoke((short)2);
+            h0.invoke(3);
+            h0.invoke(4l);
+            try {
+                h0.invoke(0.33);
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+            try {
+                Number doubleNumber = new Double(0.89);
+                h0.invoke(doubleNumber);
+                unreachable();
+            } catch (ClassCastException e) {}
+            try {
+                Number doubleNumber = new Double(1.01);
+                doubleNumber = (doubleNumber.doubleValue() != 0.1) ? null : doubleNumber;
+                h0.invoke(doubleNumber);
+                unreachable();
+            } catch (NullPointerException e) {}
+            try {
+                // Mismatched return type - float != void
+                float tmp = (float)h0.invoke(0.45f);
+                assertTrue(tmp == 0.0);
+            } catch (Exception e) { unreachable(); }
+            try {
+                h0.invoke("bam");
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+            try {
+                String s = null;
+                h0.invoke(s);
+                unreachable();
+            } catch (WrongMethodTypeException e) {}
+        }
+
+        public static void main() throws Throwable{
+            testStaticGetter();
+            testMemberGetter();
+            testStaticSetter();
+            testMemberSetter();
+            System.out.println("Passed MethodHandle.invoke() tests for accessors.");
         }
     }
 
@@ -735,5 +892,6 @@
         // file to ensure class initialization test is run.
         FindAccessorTester.main();
         InvokeExactTester.main();
+        InvokeTester.main();
     }
 }
diff --git a/test/979-invoke-polymorphic-accessors/expected.txt b/test/979-invoke-polymorphic-accessors/expected.txt
deleted file mode 100644
index 22f9f2d..0000000
--- a/test/979-invoke-polymorphic-accessors/expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-1515870810
-Passed InvokeExact tests for accessors.