| /* |
| * Copyright (C) 2017 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include "MoveOnly.h" |
| #include <optional> |
| #include <wtf/Function.h> |
| |
| namespace TestWebKitAPI { |
| |
| static Function<int()> function_for_reentrancy_test; |
| static unsigned testObjectDestructorCalls = 0; |
| |
| enum class AssignmentMode { Null, Lambda, None }; |
| |
| class TestObject { |
| public: |
| TestObject(AssignmentMode); |
| TestObject(TestObject&&); |
| |
| ~TestObject(); |
| int operator()(); |
| |
| TestObject(const TestObject&) = delete; |
| TestObject& operator=(const TestObject&) = delete; |
| |
| private: |
| AssignmentMode m_assignmentMode; |
| }; |
| |
| TestObject::TestObject(AssignmentMode assignmentMode) |
| : m_assignmentMode(assignmentMode) |
| { |
| } |
| |
| TestObject::TestObject(TestObject&& other) |
| { |
| m_assignmentMode = std::exchange(other.m_assignmentMode, AssignmentMode::None); |
| } |
| |
| TestObject::~TestObject() |
| { |
| if (m_assignmentMode == AssignmentMode::None) |
| return; |
| |
| ++testObjectDestructorCalls; |
| if (m_assignmentMode == AssignmentMode::Null) |
| function_for_reentrancy_test = nullptr; |
| else |
| function_for_reentrancy_test = [] { return -1; }; |
| } |
| |
| int TestObject::operator()() |
| { |
| return 0; |
| } |
| |
| TEST(WTF_Function, assignNullReEntersAssignNull) |
| { |
| function_for_reentrancy_test = nullptr; |
| testObjectDestructorCalls = 0; |
| |
| function_for_reentrancy_test = TestObject(AssignmentMode::Null); |
| EXPECT_EQ(0, function_for_reentrancy_test()); |
| EXPECT_FALSE(!function_for_reentrancy_test); |
| EXPECT_EQ(0U, testObjectDestructorCalls); |
| function_for_reentrancy_test = nullptr; |
| EXPECT_EQ(1U, testObjectDestructorCalls); |
| EXPECT_TRUE(!function_for_reentrancy_test); |
| } |
| |
| TEST(WTF_Function, assignLamdbaReEntersAssignNull) |
| { |
| function_for_reentrancy_test = nullptr; |
| testObjectDestructorCalls = 0; |
| |
| function_for_reentrancy_test = TestObject(AssignmentMode::Null); |
| EXPECT_EQ(0, function_for_reentrancy_test()); |
| EXPECT_FALSE(!function_for_reentrancy_test); |
| EXPECT_EQ(0U, testObjectDestructorCalls); |
| function_for_reentrancy_test = [] { return 3; }; |
| EXPECT_EQ(1U, testObjectDestructorCalls); |
| EXPECT_TRUE(!function_for_reentrancy_test); |
| } |
| |
| TEST(WTF_Function, assignLamdbaReEntersAssignLamdba) |
| { |
| function_for_reentrancy_test = nullptr; |
| testObjectDestructorCalls = 0; |
| |
| function_for_reentrancy_test = TestObject(AssignmentMode::Lambda); |
| EXPECT_EQ(0, function_for_reentrancy_test()); |
| EXPECT_FALSE(!function_for_reentrancy_test); |
| EXPECT_EQ(0, function_for_reentrancy_test()); |
| EXPECT_EQ(0U, testObjectDestructorCalls); |
| function_for_reentrancy_test = [] { return 3; }; |
| EXPECT_EQ(1U, testObjectDestructorCalls); |
| EXPECT_FALSE(!function_for_reentrancy_test); |
| EXPECT_EQ(-1, function_for_reentrancy_test()); |
| } |
| |
| TEST(WTF_Function, assignNullReEntersAssignLamda) |
| { |
| function_for_reentrancy_test = nullptr; |
| testObjectDestructorCalls = 0; |
| |
| function_for_reentrancy_test = TestObject(AssignmentMode::Lambda); |
| EXPECT_EQ(0, function_for_reentrancy_test()); |
| EXPECT_FALSE(!function_for_reentrancy_test); |
| EXPECT_EQ(0, function_for_reentrancy_test()); |
| EXPECT_EQ(0U, testObjectDestructorCalls); |
| function_for_reentrancy_test = nullptr; |
| EXPECT_EQ(1U, testObjectDestructorCalls); |
| EXPECT_FALSE(!function_for_reentrancy_test); |
| EXPECT_EQ(-1, function_for_reentrancy_test()); |
| } |
| |
| TEST(WTF_Function, Basics) |
| { |
| Function<unsigned()> a; |
| EXPECT_FALSE(static_cast<bool>(a)); |
| |
| a = [] { |
| return 1U; |
| }; |
| EXPECT_TRUE(static_cast<bool>(a)); |
| EXPECT_EQ(1U, a()); |
| |
| a = nullptr; |
| EXPECT_FALSE(static_cast<bool>(a)); |
| |
| a = MoveOnly { 2 }; |
| EXPECT_TRUE(static_cast<bool>(a)); |
| EXPECT_EQ(2U, a()); |
| |
| Function<unsigned()> b = WTFMove(a); |
| EXPECT_TRUE(static_cast<bool>(b)); |
| EXPECT_EQ(2U, b()); |
| EXPECT_FALSE(static_cast<bool>(a)); |
| |
| a = MoveOnly { 3 }; |
| Function<unsigned()> c = WTFMove(a); |
| EXPECT_TRUE(static_cast<bool>(c)); |
| EXPECT_EQ(3U, c()); |
| EXPECT_FALSE(static_cast<bool>(a)); |
| |
| b = WTFMove(c); |
| EXPECT_TRUE(static_cast<bool>(b)); |
| EXPECT_EQ(3U, b()); |
| EXPECT_FALSE(static_cast<bool>(c)); |
| } |
| |
| struct FunctionDestructionChecker { |
| FunctionDestructionChecker(Function<unsigned()>& function) |
| : function { function } |
| { |
| } |
| |
| ~FunctionDestructionChecker() |
| { |
| functionAsBool = static_cast<bool>(function); |
| functionResult = function(); |
| } |
| |
| unsigned operator()() const |
| { |
| return 10; |
| } |
| |
| Function<unsigned()>& function; |
| static std::optional<bool> functionAsBool; |
| static std::optional<unsigned> functionResult; |
| }; |
| |
| std::optional<bool> FunctionDestructionChecker::functionAsBool; |
| std::optional<unsigned> FunctionDestructionChecker::functionResult; |
| |
| TEST(WTF_Function, AssignBeforeDestroy) |
| { |
| Function<unsigned()> a; |
| |
| a = FunctionDestructionChecker(a); |
| a = [] { |
| return 1U; |
| }; |
| EXPECT_TRUE(static_cast<bool>(FunctionDestructionChecker::functionAsBool)); |
| EXPECT_TRUE(static_cast<bool>(FunctionDestructionChecker::functionResult)); |
| EXPECT_TRUE(FunctionDestructionChecker::functionAsBool.value()); |
| EXPECT_EQ(1U, FunctionDestructionChecker::functionResult.value()); |
| FunctionDestructionChecker::functionAsBool = std::nullopt; |
| FunctionDestructionChecker::functionResult = std::nullopt; |
| |
| a = FunctionDestructionChecker(a); |
| a = MoveOnly { 2 }; |
| EXPECT_TRUE(static_cast<bool>(FunctionDestructionChecker::functionAsBool)); |
| EXPECT_TRUE(static_cast<bool>(FunctionDestructionChecker::functionResult)); |
| EXPECT_TRUE(FunctionDestructionChecker::functionAsBool.value()); |
| EXPECT_EQ(2U, FunctionDestructionChecker::functionResult.value()); |
| FunctionDestructionChecker::functionAsBool = std::nullopt; |
| FunctionDestructionChecker::functionResult = std::nullopt; |
| } |
| |
| static int returnThree() |
| { |
| return 3; |
| } |
| |
| static int returnFour() |
| { |
| return 4; |
| } |
| |
| static int returnPassedValue(int value) |
| { |
| return value; |
| } |
| |
| TEST(WTF_Function, AssignFunctionPointer) |
| { |
| Function<int()> f1 = returnThree; |
| EXPECT_TRUE(static_cast<bool>(f1)); |
| EXPECT_EQ(3, f1()); |
| |
| f1 = returnFour; |
| EXPECT_TRUE(static_cast<bool>(f1)); |
| EXPECT_EQ(4, f1()); |
| |
| f1 = nullptr; |
| EXPECT_FALSE(static_cast<bool>(f1)); |
| |
| Function<int(int)> f2 = returnPassedValue; |
| EXPECT_TRUE(static_cast<bool>(f2)); |
| EXPECT_EQ(3, f2(3)); |
| EXPECT_EQ(-3, f2(-3)); |
| |
| f2 = nullptr; |
| EXPECT_FALSE(static_cast<bool>(f2)); |
| } |
| |
| } // namespace TestWebKitAPI |