ART: Type conversion fixes for MethodHandles

Remove illegal conversion path from byte to short.

Throw WrongMethodTypeException on later error paths of reference to
primitive conversion.

Bug: 72489224
Test: art/test.py --host -g
Change-Id: Iddca81ee7185bb90cc5e8ab19cfa03ddfb1652ec
diff --git a/runtime/Android.bp b/runtime/Android.bp
index f2f7c3e..a759cf7 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -691,6 +691,7 @@
         "leb128_test.cc",
         "mem_map_test.cc",
         "memory_region_test.cc",
+        "method_handles_test.cc",
         "mirror/dex_cache_test.cc",
         "mirror/method_type_test.cc",
         "mirror/object_test.cc",
@@ -701,6 +702,7 @@
         "oat_file_assistant_test.cc",
         "parsed_options_test.cc",
         "prebuilt_tools_test.cc",
+        "primitive_test.cc",
         "reference_table_test.cc",
         "runtime_callbacks_test.cc",
         "subtype_check_info_test.cc",
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 88f30a8..8bb3bec 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -231,7 +231,7 @@
     StackHandleScope<2> hs(Thread::Current());
     Handle<mirror::Class> h_to(hs.NewHandle(to));
     Handle<mirror::Object> h_obj(hs.NewHandle(src_value.GetL()));
-    if (h_obj != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
+    if (UNLIKELY(!h_obj.IsNull() && !to->IsAssignableFrom(h_obj->GetClass()))) {
       ThrowClassCastException(h_to.Get(), h_obj->GetClass());
       return false;
     }
@@ -246,7 +246,7 @@
     Primitive::Type type;
     if (!GetUnboxedPrimitiveType(to, &type)) {
       ObjPtr<mirror::Class> boxed_from_class = GetBoxedPrimitiveClass(from_type);
-      if (boxed_from_class->IsSubClass(to)) {
+      if (LIKELY(boxed_from_class->IsSubClass(to))) {
         type = from_type;
       } else {
         ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
@@ -259,7 +259,7 @@
       return false;
     }
 
-    if (!ConvertPrimitiveValueNoThrow(from_type, type, src_value, value)) {
+    if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, type, src_value, value))) {
       ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
       return false;
     }
@@ -274,7 +274,7 @@
     DCHECK(IsPrimitiveType(to_type));
 
     ObjPtr<mirror::Object> from_obj(src_value.GetL());
-    if (UNLIKELY(from_obj == nullptr)) {
+    if (UNLIKELY(from_obj.IsNull())) {
       ThrowNullPointerException(
           StringPrintf("Expected to unbox a '%s' primitive type but was returned null",
                        from->PrettyDescriptor().c_str()).c_str());
@@ -289,7 +289,14 @@
     }
 
     if (UNLIKELY(!ConvertPrimitiveValueNoThrow(unboxed_type, to_type, unboxed_value, value))) {
-      ThrowClassCastException(from, to);
+      if (from->IsAssignableFrom(GetBoxedPrimitiveClass(to_type))) {
+        // CallSite may be Number, but the Number object is
+        // incompatible, e.g. Number (Integer) for a short.
+        ThrowClassCastException(from, to);
+      } else {
+        // CallSite is incompatible, e.g. Integer for a short.
+        ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+      }
       return false;
     }
 
diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc
new file mode 100644
index 0000000..a947342
--- /dev/null
+++ b/runtime/method_handles_test.cc
@@ -0,0 +1,382 @@
+/*
+ * 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.
+ */
+
+#include "method_handles.h"
+
+#include "class_linker-inl.h"
+#include "common_runtime_test.h"
+#include "handle_scope-inl.h"
+#include "jvalue-inl.h"
+#include "mirror/method_type.h"
+#include "mirror/object_array-inl.h"
+#include "reflection.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+
+namespace art {
+
+namespace {
+  bool IsClassCastException(ObjPtr<mirror::Throwable> throwable)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return throwable->GetClass()->DescriptorEquals("Ljava/lang/ClassCastException;");
+  }
+
+  bool IsNullPointerException(ObjPtr<mirror::Throwable> throwable)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return throwable->GetClass()->DescriptorEquals("Ljava/lang/NullPointerException;");
+  }
+
+  bool IsWrongMethodTypeException(ObjPtr<mirror::Throwable> throwable)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return throwable->GetClass()->DescriptorEquals("Ljava/lang/invoke/WrongMethodTypeException;");
+  }
+
+  static mirror::MethodType* CreateVoidMethodType(Thread* self,
+                                                  Handle<mirror::Class> parameter_type)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+    ClassLinker* cl = Runtime::Current()->GetClassLinker();
+    StackHandleScope<2> hs(self);
+    ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+    ObjPtr<mirror::Class> class_array_type = cl->FindArrayClass(self, &class_type);
+    auto parameter_types = hs.NewHandle(
+        mirror::ObjectArray<mirror::Class>::Alloc(self, class_array_type, 1));
+    parameter_types->Set(0, parameter_type.Get());
+    Handle<mirror::Class> void_class = hs.NewHandle(cl->FindPrimitiveClass('V'));
+    return mirror::MethodType::Create(self, void_class, parameter_types);
+  }
+
+  static bool TryConversion(Thread* self,
+                            Handle<mirror::Class> from,
+                            Handle<mirror::Class> to,
+                            JValue* value)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    StackHandleScope<2> hs(self);
+    Handle<mirror::MethodType> from_mt = hs.NewHandle(CreateVoidMethodType(self, from));
+    Handle<mirror::MethodType> to_mt = hs.NewHandle(CreateVoidMethodType(self, to));
+    return ConvertJValueCommon(from_mt, to_mt, from.Get(), to.Get(), value);
+  }
+}  // namespace
+
+class MethodHandlesTest : public CommonRuntimeTest {};
+
+//
+// Primitive -> Primitive Conversions
+//
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveWideningBI) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('B'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  JValue value = JValue::FromPrimitive(static_cast<int8_t>(3));
+  ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_EQ(3, value.GetI());
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+}
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveWideningCJ) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('C'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J'));
+  uint16_t raw_value = 0x8000;
+  JValue value = JValue::FromPrimitive(raw_value);
+  ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+  ASSERT_EQ(static_cast<int64_t>(raw_value), value.GetJ());
+}
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveWideningIF) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('F'));
+  JValue value = JValue::FromPrimitive(-16);
+  ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+  ASSERT_FLOAT_EQ(-16.0f, value.GetF());
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningBC) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('B'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('C'));
+  JValue value;
+  value.SetB(0);
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningSC) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('S'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('C'));
+  JValue value;
+  value.SetS(0x1234);
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningDJ) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('D'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J'));
+  JValue value;
+  value.SetD(1e72);
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningZI) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('Z'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  JValue value;
+  value.SetZ(true);
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+//
+// Reference -> Reference Conversions
+//
+
+TEST_F(MethodHandlesTest, SupportedReferenceCast) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<3> hs(soa.Self());
+  static const int32_t kInitialValue = 101;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr());
+  Handle<mirror::Class> from = hs.NewHandle(boxed_value->GetClass());
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;"));
+  value.SetL(boxed_value.Get());
+  ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+  JValue unboxed_value;
+  ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), cl->FindPrimitiveClass('I'), &unboxed_value));
+  ASSERT_EQ(kInitialValue, unboxed_value.GetI());
+}
+
+TEST_F(MethodHandlesTest, UnsupportedReferenceCast) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<3> hs(soa.Self());
+  JValue value = JValue::FromPrimitive(3.733e2);
+  Handle<mirror::Object> boxed_value =
+      hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr());
+  Handle<mirror::Class> from = hs.NewHandle(boxed_value->GetClass());
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+  value.SetL(boxed_value.Get());
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsClassCastException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+//
+// Primitive -> Reference Conversions
+//
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveConversionPrimitiveToBoxed) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  const int32_t kInitialValue = 1;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+  ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+  JValue unboxed_to_value;
+  ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), from.Get(), &unboxed_to_value));
+  ASSERT_EQ(kInitialValue, unboxed_to_value.GetI());
+}
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveConversionPrimitiveToBoxedSuper) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  const int32_t kInitialValue = 1;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;"));
+  ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+  JValue unboxed_to_value;
+  ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), from.Get(), &unboxed_to_value));
+  ASSERT_EQ(kInitialValue, unboxed_to_value.GetI());
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionNotBoxable) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  const int32_t kInitialValue = 1;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Runtime;"));
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionPrimitiveToBoxedWider) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  const int32_t kInitialValue = 1;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Long;"));
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionPrimitiveToBoxedNarrower) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  const int32_t kInitialValue = 1;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Byte;"));
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+//
+// Reference -> Primitive Conversions
+//
+
+TEST_F(MethodHandlesTest, SupportedBoxedToPrimitiveConversion) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<3> hs(soa.Self());
+  const int32_t kInitialValue = 101;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  value.SetL(boxed_value.Get());
+  ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+  ASSERT_EQ(kInitialValue, value.GetI());
+}
+
+TEST_F(MethodHandlesTest, SupportedBoxedToWiderPrimitiveConversion) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<3> hs(soa.Self());
+  static const int32_t kInitialValue = 101;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J'));
+  value.SetL(boxed_value.Get());
+  ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_EQ(kInitialValue, value.GetJ());
+}
+
+TEST_F(MethodHandlesTest, UnsupportedNullBoxedToPrimitiveConversion) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<3> hs(soa.Self());
+  JValue value = JValue::FromPrimitive(101);
+  ScopedNullHandle<mirror::Object> boxed_value;
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  value.SetL(boxed_value.Get());
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsNullPointerException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedNotBoxReferenceToPrimitiveConversion) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Class;"));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+  // Set value to be converted as some non-primitive type.
+  JValue value;
+  value.SetL(cl->FindPrimitiveClass('V'));
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionNoCast) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<3> hs(soa.Self());
+  static const int32_t kInitialValue = 101;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('S'));
+  value.SetL(boxed_value.Get());
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionWithCast) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  StackHandleScope<3> hs(soa.Self());
+  static const double kInitialValue = 1e77;
+  JValue value = JValue::FromPrimitive(kInitialValue);
+  Handle<mirror::Object> boxed_value =
+      hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr());
+  Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;"));
+  Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('F'));
+  value.SetL(boxed_value.Get());
+  ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+  ASSERT_TRUE(IsClassCastException(soa.Self()->GetException()));
+  soa.Self()->ClearException();
+}
+
+}  // namespace art
diff --git a/runtime/primitive.h b/runtime/primitive.h
index 5b163d8..38ad68d 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -140,12 +140,30 @@
   // Returns the descriptor corresponding to the boxed type of |type|.
   static const char* BoxedDescriptor(Type type);
 
-  // Return true if |type| is an numeric type.
+  // Returns true if |type| is an numeric type.
   static constexpr bool IsNumericType(Type type) {
     switch (type) {
       case Primitive::Type::kPrimNot: return false;
       case Primitive::Type::kPrimBoolean: return false;
       case Primitive::Type::kPrimByte: return true;
+      case Primitive::Type::kPrimChar: return true;
+      case Primitive::Type::kPrimShort: return true;
+      case Primitive::Type::kPrimInt: return true;
+      case Primitive::Type::kPrimLong: return true;
+      case Primitive::Type::kPrimFloat: return true;
+      case Primitive::Type::kPrimDouble: return true;
+      case Primitive::Type::kPrimVoid: return false;
+    }
+    LOG(FATAL) << "Invalid type " << static_cast<int>(type);
+    UNREACHABLE();
+  }
+
+  // Return trues if |type| is a signed numeric type.
+  static constexpr bool IsSignedNumericType(Type type) {
+    switch (type) {
+      case Primitive::Type::kPrimNot: return false;
+      case Primitive::Type::kPrimBoolean: return false;
+      case Primitive::Type::kPrimByte: return true;
       case Primitive::Type::kPrimChar: return false;
       case Primitive::Type::kPrimShort: return true;
       case Primitive::Type::kPrimInt: return true;
@@ -158,17 +176,39 @@
     UNREACHABLE();
   }
 
+  // Returns the number of bits required to hold the largest
+  // positive number that can be represented by |type|.
+  static constexpr size_t BitsRequiredForLargestValue(Type type) {
+    switch (type) {
+      case Primitive::Type::kPrimNot: return 0u;
+      case Primitive::Type::kPrimBoolean: return 1u;
+      case Primitive::Type::kPrimByte: return 7u;
+      case Primitive::Type::kPrimChar: return 16u;
+      case Primitive::Type::kPrimShort: return 15u;
+      case Primitive::Type::kPrimInt: return 31u;
+      case Primitive::Type::kPrimLong: return 63u;
+      case Primitive::Type::kPrimFloat: return 128u;
+      case Primitive::Type::kPrimDouble: return 1024u;
+      case Primitive::Type::kPrimVoid: return 0u;
+    }
+  }
+
   // Returns true if it is possible to widen type |from| to type |to|. Both |from| and
   // |to| should be numeric primitive types.
   static bool IsWidenable(Type from, Type to) {
-    static_assert(Primitive::Type::kPrimByte < Primitive::Type::kPrimShort, "Bad ordering");
-    static_assert(Primitive::Type::kPrimShort < Primitive::Type::kPrimInt, "Bad ordering");
-    static_assert(Primitive::Type::kPrimInt < Primitive::Type::kPrimLong, "Bad ordering");
-    static_assert(Primitive::Type::kPrimLong < Primitive::Type::kPrimFloat, "Bad ordering");
-    static_assert(Primitive::Type::kPrimFloat < Primitive::Type::kPrimDouble, "Bad ordering");
-    // Widening is only applicable between numeric types, like byte
-    // and int. Non-numeric types, such as boolean, cannot be widened.
-    return IsNumericType(from) && IsNumericType(to) && from <= to;
+    if (!IsNumericType(from) || !IsNumericType(to)) {
+      // Widening is only applicable between numeric types.
+      return false;
+    }
+    if (IsSignedNumericType(from) && !IsSignedNumericType(to)) {
+      // Nowhere to store the sign bit in |to|.
+      return false;
+    }
+    if (BitsRequiredForLargestValue(from) > BitsRequiredForLargestValue(to)) {
+      // The from,to pair corresponds to a narrowing.
+      return false;
+    }
+    return true;
   }
 
   static bool Is64BitType(Type type) {
diff --git a/runtime/primitive_test.cc b/runtime/primitive_test.cc
new file mode 100644
index 0000000..e433b15
--- /dev/null
+++ b/runtime/primitive_test.cc
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#include "primitive.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+namespace {
+
+void CheckPrimitiveTypeWidensTo(Primitive::Type from,
+                                const std::vector<Primitive::Type>& expected_to_types) {
+  std::vector<Primitive::Type> actual_to_types;
+  int last = static_cast<int>(Primitive::Type::kPrimLast);
+  for (int i = 0; i <= last; ++i) {
+    Primitive::Type to = static_cast<Primitive::Type>(i);
+    if (Primitive::IsWidenable(from, to)) {
+      actual_to_types.push_back(to);
+    }
+  }
+  EXPECT_EQ(expected_to_types, actual_to_types);
+}
+
+}  // namespace
+
+TEST(PrimitiveTest, NotWidensTo) {
+  const std::vector<Primitive::Type> to_types = {};
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimNot, to_types);
+}
+
+TEST(PrimitiveTest, BooleanWidensTo) {
+  const std::vector<Primitive::Type> to_types = {};
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimBoolean, to_types);
+}
+
+TEST(PrimitiveTest, ByteWidensTo) {
+  const std::vector<Primitive::Type> to_types = {
+    Primitive::Type::kPrimByte,
+    Primitive::Type::kPrimShort,
+    Primitive::Type::kPrimInt,
+    Primitive::Type::kPrimLong,
+    Primitive::Type::kPrimFloat,
+    Primitive::Type::kPrimDouble,
+  };
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimByte, to_types);
+}
+
+TEST(PrimitiveTest, CharWidensTo) {
+  const std::vector<Primitive::Type> to_types = {
+    Primitive::Type::kPrimChar,
+    Primitive::Type::kPrimInt,
+    Primitive::Type::kPrimLong,
+    Primitive::Type::kPrimFloat,
+    Primitive::Type::kPrimDouble,
+  };
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimChar, to_types);
+}
+
+TEST(PrimitiveTest, ShortWidensTo) {
+  const std::vector<Primitive::Type> to_types = {
+    Primitive::Type::kPrimShort,
+    Primitive::Type::kPrimInt,
+    Primitive::Type::kPrimLong,
+    Primitive::Type::kPrimFloat,
+    Primitive::Type::kPrimDouble,
+  };
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimShort, to_types);
+}
+
+TEST(PrimitiveTest, IntWidensTo) {
+  const std::vector<Primitive::Type> to_types = {
+    Primitive::Type::kPrimInt,
+    Primitive::Type::kPrimLong,
+    Primitive::Type::kPrimFloat,
+    Primitive::Type::kPrimDouble,
+  };
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimInt, to_types);
+}
+
+TEST(PrimitiveTest, LongWidensTo) {
+  const std::vector<Primitive::Type> to_types = {
+    Primitive::Type::kPrimLong,
+    Primitive::Type::kPrimFloat,
+    Primitive::Type::kPrimDouble,
+  };
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimLong, to_types);
+}
+
+TEST(PrimitiveTest, FloatWidensTo) {
+  const std::vector<Primitive::Type> to_types = {
+    Primitive::Type::kPrimFloat,
+    Primitive::Type::kPrimDouble,
+  };
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimFloat, to_types);
+}
+
+TEST(PrimitiveTest, DoubleWidensTo) {
+  const std::vector<Primitive::Type> to_types = {
+    Primitive::Type::kPrimDouble,
+  };
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimDouble, to_types);
+}
+
+TEST(PrimitiveTest, VoidWidensTo) {
+  const std::vector<Primitive::Type> to_types = {};
+  CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimVoid, to_types);
+}
+
+}  // namespace art
diff --git a/test/959-invoke-polymorphic-accessors/src/Main.java b/test/959-invoke-polymorphic-accessors/src/Main.java
index cdde1de..03fd285 100644
--- a/test/959-invoke-polymorphic-accessors/src/Main.java
+++ b/test/959-invoke-polymorphic-accessors/src/Main.java
@@ -901,6 +901,10 @@
                 fail();
             } catch (WrongMethodTypeException expected) {}
             try {
+                h0.invoke(Double.valueOf(0.33));
+                fail();
+            } catch (WrongMethodTypeException expected) {}
+            try {
                 Number doubleNumber = getDoubleAsNumber();
                 h0.invoke(doubleNumber);
                 fail();