You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
382 lines
17 KiB
382 lines
17 KiB
/*
|
|
* 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 {};
|
|
|
|
//
|
|
// 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(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));
|
|
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
|