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