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