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