Title: [218465] trunk
Revision
218465
Author
[email protected]
Date
2017-06-18 13:25:19 -0700 (Sun, 18 Jun 2017)

Log Message

Crash when re-entering MediaDevicesEnumerationRequest::cancel()
https://bugs.webkit.org/show_bug.cgi?id=173522
<rdar://problem/31185739>

Reviewed by Darin Adler.

Source/WebCore:

When a MediaDevicesRequest is started, it creates a MediaDevicesEnumerationRequest
object and passes a completion handler to that MediaDevicesEnumerationRequest
object. The completion handler holds a reference to the MediaDevicesRequest object
so that its stays alive until the MediaDevicesEnumerationRequest either completes
or is canceled. MediaDevicesRequest also holds a reference to the
MediaDevicesEnumerationRequest object via its m_enumerationRequest data member.

When the document is destroyed, both MediaDevicesRequest::contextDestroyed() and
MediaDevicesEnumerationRequest::contextDestroyed() gets called and the other is not
pre-determined. If MediaDevicesEnumerationRequest::contextDestroyed() gets called
first then it calls MediaDevicesEnumerationRequest::cancel(). Calling cancel() ends
up destroying the completion handler. Destroying the completion handler ends up
dereferencing and destroying the MediaDevicesRequest object. The MediaDevicesRequest
destructor would call MediaDevicesEnumerationRequest::cancel() again, causing us to
re-enter it and assign nullptr to the completion callback again. Re-entering
std::function's operator=(nullptr_t) is not safe because of the way it is implemented
as we end up trying to destroy the lambda twice and crashing. Using a WTF::Function
instead fixes this particular issue because re-entering WTF::Function's operator=(nullptr_t)
is safe.

However, this fix is not sufficient. Calling the MediaDevicesRequest destructor also
dereferencing and destroys the MediaDevicesEnumerationRequest object. As a result,
when MediaDevicesEnumerationRequest::contextDestroyed() returns from its call to cancel
|this| is already dead when we call ContextDestructionObserver::contextDestroyed().
To address this issue, we now protect |this| in MediaDevicesEnumerationRequest::contextDestroyed().

Test: fast/mediastream/destroy-document-while-enumerating-devices.html

* Modules/mediastream/MediaDevicesEnumerationRequest.cpp:
(WebCore::MediaDevicesEnumerationRequest::contextDestroyed):
Protect |this| as the call to cancel() may destroy |this| before calling
ContextDestructionObserver::contextDestroyed() otherwise.

* Modules/mediastream/MediaDevicesEnumerationRequest.h:
Use WTF::Function instead of std::function for the completion handler as
it is safer (in terms of re-entrency) and avoids unnecessary copying.

* Modules/mediastream/MediaDevicesRequest.cpp:
(WebCore::MediaDevicesRequest::~MediaDevicesRequest):
Stop calling MediaDevicesEnumerationRequest::cancel(). When the destructor
is called, the MediaDevicesEnumerationRequest has either completed or been
canceled so there is no need to cancel again. I added an assertion to
make sure it is the case. This avoids re-entering
MediaDevicesEnumerationRequest::cancel() is some cases, which was risky.

(WebCore::MediaDevicesRequest::start):
Add comment for clarity and capture a Ref<> instead of a RefPtr<> now that
we can since we use WTF::Function.

Tools:

Add API test for re-entering Function's assignment operators.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WTF/Function.cpp: Added.
(TestWebKitAPI::TestObject::TestObject):
(TestWebKitAPI::TestObject::~TestObject):
(TestWebKitAPI::TestObject::operator()):
(TestWebKitAPI::TEST):

LayoutTests:

Add layout test coverage. This test would flakily crash before the fix because it
relies on the order in which contextDestroyed() is called for MediaDevicesRequest
and MediaDevicesEnumerationRequest.

* fast/mediastream/destroy-document-while-enumerating-devices-expected.txt: Added.
* fast/mediastream/destroy-document-while-enumerating-devices.html: Added.
* fast/mediastream/resources/enumerate-devices-frame.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (218464 => 218465)


--- trunk/LayoutTests/ChangeLog	2017-06-18 19:49:12 UTC (rev 218464)
+++ trunk/LayoutTests/ChangeLog	2017-06-18 20:25:19 UTC (rev 218465)
@@ -1,3 +1,19 @@
+2017-06-18  Chris Dumez  <[email protected]>
+
+        Crash when re-entering MediaDevicesEnumerationRequest::cancel()
+        https://bugs.webkit.org/show_bug.cgi?id=173522
+        <rdar://problem/31185739>
+
+        Reviewed by Darin Adler.
+
+        Add layout test coverage. This test would flakily crash before the fix because it
+        relies on the order in which contextDestroyed() is called for MediaDevicesRequest
+        and MediaDevicesEnumerationRequest.
+
+        * fast/mediastream/destroy-document-while-enumerating-devices-expected.txt: Added.
+        * fast/mediastream/destroy-document-while-enumerating-devices.html: Added.
+        * fast/mediastream/resources/enumerate-devices-frame.html: Added.
+
 2017-06-17  Simon Fraser  <[email protected]>
 
         Implement DOMQuad

Added: trunk/LayoutTests/fast/mediastream/destroy-document-while-enumerating-devices-expected.txt (0 => 218465)


--- trunk/LayoutTests/fast/mediastream/destroy-document-while-enumerating-devices-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/mediastream/destroy-document-while-enumerating-devices-expected.txt	2017-06-18 20:25:19 UTC (rev 218465)
@@ -0,0 +1,9 @@
+Tests that we do not crash when destroying the document while a request to enumerate devices is ongoing.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/mediastream/destroy-document-while-enumerating-devices.html (0 => 218465)


--- trunk/LayoutTests/fast/mediastream/destroy-document-while-enumerating-devices.html	                        (rev 0)
+++ trunk/LayoutTests/fast/mediastream/destroy-document-while-enumerating-devices.html	2017-06-18 20:25:19 UTC (rev 218465)
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests that we do not crash when destroying the document while a request to enumerate devices is ongoing.");
+jsTestIsAsync = true;
+
+function destroyFrame()
+{
+    frame.remove();
+    frame = null;
+    gc();
+    setTimeout(function() {
+        gc();
+        finishJSTest();
+    }, 0);
+}
+
+frame = document.createElement("iframe");
+frame.src = ""
+document.body.appendChild(frame);
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/mediastream/resources/enumerate-devices-frame.html (0 => 218465)


--- trunk/LayoutTests/fast/mediastream/resources/enumerate-devices-frame.html	                        (rev 0)
+++ trunk/LayoutTests/fast/mediastream/resources/enumerate-devices-frame.html	2017-06-18 20:25:19 UTC (rev 218465)
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+_onload_ = function() {
+    parent.destroyFrame();
+    enumerationPromise = navigator.mediaDevices.enumerateDevices();
+};
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (218464 => 218465)


--- trunk/Source/WebCore/ChangeLog	2017-06-18 19:49:12 UTC (rev 218464)
+++ trunk/Source/WebCore/ChangeLog	2017-06-18 20:25:19 UTC (rev 218465)
@@ -1,5 +1,62 @@
 2017-06-18  Chris Dumez  <[email protected]>
 
+        Crash when re-entering MediaDevicesEnumerationRequest::cancel()
+        https://bugs.webkit.org/show_bug.cgi?id=173522
+        <rdar://problem/31185739>
+
+        Reviewed by Darin Adler.
+
+        When a MediaDevicesRequest is started, it creates a MediaDevicesEnumerationRequest
+        object and passes a completion handler to that MediaDevicesEnumerationRequest
+        object. The completion handler holds a reference to the MediaDevicesRequest object
+        so that its stays alive until the MediaDevicesEnumerationRequest either completes
+        or is canceled. MediaDevicesRequest also holds a reference to the
+        MediaDevicesEnumerationRequest object via its m_enumerationRequest data member.
+
+        When the document is destroyed, both MediaDevicesRequest::contextDestroyed() and
+        MediaDevicesEnumerationRequest::contextDestroyed() gets called and the other is not
+        pre-determined. If MediaDevicesEnumerationRequest::contextDestroyed() gets called
+        first then it calls MediaDevicesEnumerationRequest::cancel(). Calling cancel() ends
+        up destroying the completion handler. Destroying the completion handler ends up
+        dereferencing and destroying the MediaDevicesRequest object. The MediaDevicesRequest
+        destructor would call MediaDevicesEnumerationRequest::cancel() again, causing us to
+        re-enter it and assign nullptr to the completion callback again. Re-entering
+        std::function's operator=(nullptr_t) is not safe because of the way it is implemented
+        as we end up trying to destroy the lambda twice and crashing. Using a WTF::Function
+        instead fixes this particular issue because re-entering WTF::Function's operator=(nullptr_t)
+        is safe.
+
+        However, this fix is not sufficient. Calling the MediaDevicesRequest destructor also
+        dereferencing and destroys the MediaDevicesEnumerationRequest object. As a result,
+        when MediaDevicesEnumerationRequest::contextDestroyed() returns from its call to cancel
+        |this| is already dead when we call ContextDestructionObserver::contextDestroyed().
+        To address this issue, we now protect |this| in MediaDevicesEnumerationRequest::contextDestroyed().
+
+        Test: fast/mediastream/destroy-document-while-enumerating-devices.html
+
+        * Modules/mediastream/MediaDevicesEnumerationRequest.cpp:
+        (WebCore::MediaDevicesEnumerationRequest::contextDestroyed):
+        Protect |this| as the call to cancel() may destroy |this| before calling
+        ContextDestructionObserver::contextDestroyed() otherwise.
+
+        * Modules/mediastream/MediaDevicesEnumerationRequest.h:
+        Use WTF::Function instead of std::function for the completion handler as
+        it is safer (in terms of re-entrency) and avoids unnecessary copying.
+
+        * Modules/mediastream/MediaDevicesRequest.cpp:
+        (WebCore::MediaDevicesRequest::~MediaDevicesRequest):
+        Stop calling MediaDevicesEnumerationRequest::cancel(). When the destructor
+        is called, the MediaDevicesEnumerationRequest has either completed or been
+        canceled so there is no need to cancel again. I added an assertion to
+        make sure it is the case. This avoids re-entering
+        MediaDevicesEnumerationRequest::cancel() is some cases, which was risky.
+
+        (WebCore::MediaDevicesRequest::start):
+        Add comment for clarity and capture a Ref<> instead of a RefPtr<> now that
+        we can since we use WTF::Function.
+
+2017-06-18  Chris Dumez  <[email protected]>
+
         Use WTF::Function instead of std::function in WTF/
         https://bugs.webkit.org/show_bug.cgi?id=173519
 

Modified: trunk/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp (218464 => 218465)


--- trunk/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp	2017-06-18 19:49:12 UTC (rev 218464)
+++ trunk/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp	2017-06-18 20:25:19 UTC (rev 218465)
@@ -70,6 +70,9 @@
 
 void MediaDevicesEnumerationRequest::contextDestroyed()
 {
+    // Calling cancel() may destroy ourselves.
+    Ref<MediaDevicesEnumerationRequest> protectedThis(*this);
+
     cancel();
     ContextDestructionObserver::contextDestroyed();
 }

Modified: trunk/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.h (218464 => 218465)


--- trunk/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.h	2017-06-18 19:49:12 UTC (rev 218464)
+++ trunk/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.h	2017-06-18 20:25:19 UTC (rev 218465)
@@ -29,7 +29,7 @@
 #if ENABLE(MEDIA_STREAM)
 
 #include "ActiveDOMObject.h"
-#include <functional>
+#include <wtf/Function.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
@@ -40,7 +40,7 @@
 
 class MediaDevicesEnumerationRequest final : public ContextDestructionObserver, public RefCounted<MediaDevicesEnumerationRequest> {
 public:
-    using CompletionHandler = std::function<void(const Vector<CaptureDevice>&, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess)>;
+    using CompletionHandler = WTF::Function<void(const Vector<CaptureDevice>&, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess)>;
 
     static Ref<MediaDevicesEnumerationRequest> create(Document&, CompletionHandler&&);
 
@@ -49,6 +49,8 @@
     void start();
     void cancel();
 
+    bool wasCanceled() const { return !m_completionHandler; }
+
     WEBCORE_EXPORT void setDeviceInfo(const Vector<CaptureDevice>&, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess);
 
     WEBCORE_EXPORT SecurityOrigin* userMediaDocumentOrigin() const;

Modified: trunk/Source/WebCore/Modules/mediastream/MediaDevicesRequest.cpp (218464 => 218465)


--- trunk/Source/WebCore/Modules/mediastream/MediaDevicesRequest.cpp	2017-06-18 19:49:12 UTC (rev 218464)
+++ trunk/Source/WebCore/Modules/mediastream/MediaDevicesRequest.cpp	2017-06-18 20:25:19 UTC (rev 218465)
@@ -55,8 +55,8 @@
 
 MediaDevicesRequest::~MediaDevicesRequest()
 {
-    if (m_enumerationRequest)
-        m_enumerationRequest->cancel();
+    // This should only get destroyed after the enumeration request has completed or has been canceled.
+    ASSERT(!m_enumerationRequest || m_enumerationRequest->wasCanceled());
 }
 
 SecurityOrigin* MediaDevicesRequest::securityOrigin() const
@@ -109,8 +109,8 @@
 
 void MediaDevicesRequest::start()
 {
-    RefPtr<MediaDevicesRequest> protectedThis = this;
-    auto completion = [this, protectedThis = WTFMove(protectedThis)] (const Vector<CaptureDevice>& captureDevices, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess) mutable {
+    // This lambda keeps |this| alive until the request completes or is canceled.
+    auto completion = [this, protectedThis = makeRef(*this)] (const Vector<CaptureDevice>& captureDevices, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess) mutable {
 
         m_enumerationRequest = nullptr;
 

Modified: trunk/Tools/ChangeLog (218464 => 218465)


--- trunk/Tools/ChangeLog	2017-06-18 19:49:12 UTC (rev 218464)
+++ trunk/Tools/ChangeLog	2017-06-18 20:25:19 UTC (rev 218465)
@@ -1,3 +1,20 @@
+2017-06-18  Chris Dumez  <[email protected]>
+
+        Crash when re-entering MediaDevicesEnumerationRequest::cancel()
+        https://bugs.webkit.org/show_bug.cgi?id=173522
+        <rdar://problem/31185739>
+
+        Reviewed by Darin Adler.
+
+        Add API test for re-entering Function's assignment operators.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WTF/Function.cpp: Added.
+        (TestWebKitAPI::TestObject::TestObject):
+        (TestWebKitAPI::TestObject::~TestObject):
+        (TestWebKitAPI::TestObject::operator()):
+        (TestWebKitAPI::TEST):
+
 2017-06-16  Dan Bernstein  <[email protected]>
 
         [Cocoa] Some declarations have missing or incorrect availability attributes

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (218464 => 218465)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-06-18 19:49:12 UTC (rev 218464)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-06-18 20:25:19 UTC (rev 218465)
@@ -485,6 +485,7 @@
 		837A35F11D9A1E7D00663C57 /* DownloadRequestBlobURL.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 837A35F01D9A1E6400663C57 /* DownloadRequestBlobURL.html */; };
 		83B6DE6F1EE75221001E792F /* RestoreSessionState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83B6DE6E1EE7520F001E792F /* RestoreSessionState.cpp */; };
 		83BAEE8D1EF4625500DDE894 /* PluginLoadClientPolicies.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83BAEE8C1EF4625500DDE894 /* PluginLoadClientPolicies.mm */; };
+		83DB79691EF63B3C00BFA5E5 /* Function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83DB79671EF63B3C00BFA5E5 /* Function.cpp */; };
 		83DE134D1EF1C50800C1B355 /* ResponsivenessTimer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83DE134C1EF1C4FE00C1B355 /* ResponsivenessTimer.cpp */; };
 		8E4A85371E1D1AB200F53B0F /* GridPosition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8E4A85361E1D1AA100F53B0F /* GridPosition.cpp */; };
 		930AD402150698D00067970F /* lots-of-text.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 930AD401150698B30067970F /* lots-of-text.html */; };
@@ -1323,6 +1324,7 @@
 		83B6DE6E1EE7520F001E792F /* RestoreSessionState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RestoreSessionState.cpp; sourceTree = "<group>"; };
 		83B88A331C80056D00BB2418 /* HTMLParserIdioms.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLParserIdioms.cpp; sourceTree = "<group>"; };
 		83BAEE8C1EF4625500DDE894 /* PluginLoadClientPolicies.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PluginLoadClientPolicies.mm; sourceTree = "<group>"; };
+		83DB79671EF63B3C00BFA5E5 /* Function.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Function.cpp; sourceTree = "<group>"; };
 		83DE134C1EF1C4FE00C1B355 /* ResponsivenessTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResponsivenessTimer.cpp; sourceTree = "<group>"; };
 		86BD19971A2DB05B006DCF0A /* RefCounter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RefCounter.cpp; sourceTree = "<group>"; };
 		8A2C750D16CED9550024F352 /* ResizeWindowAfterCrash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResizeWindowAfterCrash.cpp; sourceTree = "<group>"; };
@@ -2351,6 +2353,7 @@
 				1CB9BC371A67482300FE5678 /* WeakPtr.cpp */,
 				7AA6A1511AAC0B31002B2ED3 /* WorkQueue.cpp */,
 				265AF54F15D1E48A00B0CB4A /* WTFString.cpp */,
+				83DB79671EF63B3C00BFA5E5 /* Function.cpp */,
 			);
 			path = WTF;
 			sourceTree = "<group>";
@@ -3056,6 +3059,7 @@
 				7CCE7EFF1A411AE600447C4C /* LoadCanceledNoServerRedirectCallback.cpp in Sources */,
 				A125478F1DB18B9400358564 /* LoadDataWithNilMIMEType.mm in Sources */,
 				5C838F7F1DB04F900082858F /* LoadInvalidURLRequest.mm in Sources */,
+				83DB79691EF63B3C00BFA5E5 /* Function.cpp in Sources */,
 				7C83E0C01D0A652700FEBCF3 /* LoadInvalidURLRequest.mm in Sources */,
 				7CCE7F001A411AE600447C4C /* LoadPageOnCrash.cpp in Sources */,
 				6356FB221EC4E0BA0044BF18 /* VisibleContentRect.mm in Sources */,

Added: trunk/Tools/TestWebKitAPI/Tests/WTF/Function.cpp (0 => 218465)


--- trunk/Tools/TestWebKitAPI/Tests/WTF/Function.cpp	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/Function.cpp	2017-06-18 20:25:19 UTC (rev 218465)
@@ -0,0 +1,140 @@
+/*
+ * 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 "Test.h"
+#include <wtf/Function.h>
+
+namespace TestWebKitAPI {
+
+static WTF::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());
+}
+
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to