| /* |
| * 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 "class_root-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-alloc-inl.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 ObjPtr<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_array_type = GetClassRoot<mirror::ObjectArray<mirror::Class>>(cl); |
| 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(GetClassRoot(ClassRoot::kPrimitiveVoid, cl)); |
| 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 { |
| protected: |
| MethodHandlesTest() { |
| use_boot_image_ = true; // Make the Runtime creation cheaper. |
| } |
| }; |
| |
| // |
| // 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)); |
| 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)); |
| 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)); |
| 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)); |
| 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(IsClassCastException(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)); |
| 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)); |
| 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 |