| /* | |
| * 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 HIDDEN { | |
| 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 bool TryConversion(Handle<mirror::Class> from, Handle<mirror::Class> to, JValue* value) | |
| REQUIRES_SHARED(Locks::mutator_lock_) { | |
| class ThrowWrongMethodTypeFunctionImpl final : public ThrowWrongMethodTypeFunction { | |
| void operator()() const override REQUIRES_SHARED(Locks::mutator_lock_) { | |
| ThrowWrongMethodTypeException("<callee-descriptor>", "<callsite-descriptor>"); | |
| } | |
| }; | |
| ThrowWrongMethodTypeFunctionImpl throw_wmt; | |
| return ConvertJValueCommon(throw_wmt, 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(from, to, &value)); | |
| ASSERT_TRUE(soa.Self()->IsExceptionPending()); | |
| ASSERT_TRUE(IsClassCastException(soa.Self()->GetException())); | |
| soa.Self()->ClearException(); | |
| } | |
| } // namespace art |