| /* | |
| * Copyright (C) 2025 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 "SwiftCxxInteropTestbed.h" | |
| #include <optional> | |
| #include <wtf/Assertions.h> | |
| namespace SwiftCxxInteropTestbed { | |
| // Handed to a stored handler that resetStoredIntCompletionHandler() has to call itself. | |
| static constexpr int teardownValue = -2; | |
| // Left behind in a probe that has been moved from, so a test sees an obviously wrong value rather than a | |
| // plausible one if the bridge hands Swift the source of a move instead of the destination. | |
| static constexpr int movedFromValue = -1; | |
| static int& liveMoveOnlyProbes() | |
| { | |
| static int count = 0; | |
| return count; | |
| } | |
| MoveOnlyProbe::MoveOnlyProbe(int value) | |
| : m_value(value) | |
| { | |
| ++liveMoveOnlyProbes(); | |
| } | |
| MoveOnlyProbe::MoveOnlyProbe(MoveOnlyProbe&& other) | |
| : m_value(other.m_value) | |
| { | |
| other.m_value = movedFromValue; | |
| ++liveMoveOnlyProbes(); | |
| } | |
| MoveOnlyProbe::~MoveOnlyProbe() | |
| { | |
| --liveMoveOnlyProbes(); | |
| } | |
| int liveMoveOnlyProbeCount() | |
| { | |
| return liveMoveOnlyProbes(); | |
| } | |
| static int& liveCopyCountingProbes() | |
| { | |
| static int count = 0; | |
| return count; | |
| } | |
| static int& copyCountingProbeCopies() | |
| { | |
| static int count = 0; | |
| return count; | |
| } | |
| CopyCountingProbe::CopyCountingProbe(int value) | |
| : m_value(value) | |
| { | |
| ++liveCopyCountingProbes(); | |
| } | |
| CopyCountingProbe::CopyCountingProbe(const CopyCountingProbe& other) | |
| : m_value(other.m_value) | |
| { | |
| ++copyCountingProbeCopies(); | |
| ++liveCopyCountingProbes(); | |
| } | |
| CopyCountingProbe::CopyCountingProbe(CopyCountingProbe&& other) | |
| : m_value(other.m_value) | |
| { | |
| other.m_value = movedFromValue; | |
| ++liveCopyCountingProbes(); | |
| } | |
| CopyCountingProbe::~CopyCountingProbe() | |
| { | |
| --liveCopyCountingProbes(); | |
| } | |
| int liveCopyCountingProbeCount() | |
| { | |
| return liveCopyCountingProbes(); | |
| } | |
| int copyCountingProbeCopyCount() | |
| { | |
| return copyCountingProbeCopies(); | |
| } | |
| void resetCopyCountingProbeCounts() | |
| { | |
| copyCountingProbeCopies() = 0; | |
| } | |
| static int& liveCountingProbeErrors() | |
| { | |
| static int count = 0; | |
| return count; | |
| } | |
| CountingProbeError::CountingProbeError(ProbeError value) | |
| : m_value(value) | |
| { | |
| ++liveCountingProbeErrors(); | |
| } | |
| CountingProbeError::CountingProbeError(const CountingProbeError& other) | |
| : m_value(other.m_value) | |
| { | |
| ++liveCountingProbeErrors(); | |
| } | |
| CountingProbeError::~CountingProbeError() | |
| { | |
| --liveCountingProbeErrors(); | |
| } | |
| int liveCountingProbeErrorCount() | |
| { | |
| return liveCountingProbeErrors(); | |
| } | |
| int callIntBoolFunction(bool argument, IntBoolFunction&& function) | |
| { | |
| return function(argument); | |
| } | |
| void callIntCompletionHandler(int argument, IntCompletionHandler&& completionHandler) | |
| { | |
| completionHandler(argument); | |
| } | |
| void callVoidCompletionHandler(VoidCompletionHandler&& completionHandler) | |
| { | |
| completionHandler(); | |
| } | |
| static std::optional<IntCompletionHandler>& storedIntCompletionHandler() | |
| { | |
| static std::optional<IntCompletionHandler> handler; | |
| return handler; | |
| } | |
| void storeIntCompletionHandler(IntCompletionHandler&& completionHandler) | |
| { | |
| // Overwriting the slot would destroy an uncalled handler: that trips | |
| // ~CompletionHandler's assertion and strands whatever the handler owned, so a test | |
| // awaiting a continuation it held would hang instead of failing. | |
| RELEASE_ASSERT(!storedIntCompletionHandler()); | |
| storedIntCompletionHandler().emplace(WTF::move(completionHandler)); | |
| } | |
| void invokeStoredIntCompletionHandler(int argument) | |
| { | |
| auto handler = WTF::move(storedIntCompletionHandler()); | |
| storedIntCompletionHandler().reset(); | |
| if (handler) | |
| (*handler)(argument); | |
| } | |
| void resetStoredIntCompletionHandler() | |
| { | |
| auto handler = WTF::move(storedIntCompletionHandler()); | |
| storedIntCompletionHandler().reset(); | |
| // Call rather than just drop it, both because ~CompletionHandler requires it and so | |
| // that a test which returned early fails on the sentinel instead of hanging. Teardown | |
| // must never assert itself, so check the handler has not already run. | |
| if (handler && *handler) | |
| (*handler)(teardownValue); | |
| } | |
| void callMoveOnlyProbeCompletionHandler(int argument, MoveOnlyProbeCompletionHandler&& completionHandler) | |
| { | |
| completionHandler(MoveOnlyProbe { argument }); | |
| } | |
| void callCopyCountingProbeCompletionHandler(int argument, CopyCountingProbeCompletionHandler&& completionHandler) | |
| { | |
| completionHandler(CopyCountingProbe { argument }); | |
| } | |
| static std::optional<MoveOnlyProbeCompletionHandler>& storedMoveOnlyProbeCompletionHandler() | |
| { | |
| static std::optional<MoveOnlyProbeCompletionHandler> handler; | |
| return handler; | |
| } | |
| void storeMoveOnlyProbeCompletionHandler(MoveOnlyProbeCompletionHandler&& completionHandler) | |
| { | |
| // See storeIntCompletionHandler(). | |
| RELEASE_ASSERT(!storedMoveOnlyProbeCompletionHandler()); | |
| storedMoveOnlyProbeCompletionHandler().emplace(WTF::move(completionHandler)); | |
| } | |
| void invokeStoredMoveOnlyProbeCompletionHandler(int argument) | |
| { | |
| auto handler = WTF::move(storedMoveOnlyProbeCompletionHandler()); | |
| storedMoveOnlyProbeCompletionHandler().reset(); | |
| if (handler) | |
| (*handler)(MoveOnlyProbe { argument }); | |
| } | |
| void resetStoredMoveOnlyProbeCompletionHandler() | |
| { | |
| auto handler = WTF::move(storedMoveOnlyProbeCompletionHandler()); | |
| storedMoveOnlyProbeCompletionHandler().reset(); | |
| // See resetStoredIntCompletionHandler(). | |
| if (handler && *handler) | |
| (*handler)(MoveOnlyProbe { teardownValue }); | |
| } | |
| static int& sharedProbeRefs() | |
| { | |
| static int count = 0; | |
| return count; | |
| } | |
| static int& sharedProbeDerefs() | |
| { | |
| static int count = 0; | |
| return count; | |
| } | |
| void SharedProbe::ref() | |
| { | |
| ++sharedProbeRefs(); | |
| ++m_refCount; | |
| } | |
| void SharedProbe::deref() | |
| { | |
| ++sharedProbeDerefs(); | |
| --m_refCount; | |
| // Deliberately never freed: see the class comment. A test that drove the count to zero has to be | |
| // able to report that rather than crash on the next access. | |
| } | |
| static SharedProbe& sharedProbe() | |
| { | |
| static SharedProbe probe; | |
| return probe; | |
| } | |
| int callSharedProbeHolderCompletionHandler(SharedProbeHolderCompletionHandler&& completionHandler) | |
| { | |
| completionHandler(SharedProbeHolder { &sharedProbe() }); | |
| return sharedProbe().refCount(); | |
| } | |
| int sharedProbeRefCalls() | |
| { | |
| return sharedProbeRefs(); | |
| } | |
| int sharedProbeDerefCalls() | |
| { | |
| return sharedProbeDerefs(); | |
| } | |
| int sharedProbeRefCount() | |
| { | |
| return sharedProbe().refCount(); | |
| } | |
| void resetSharedProbe() | |
| { | |
| sharedProbeRefs() = 0; | |
| sharedProbeDerefs() = 0; | |
| sharedProbe().resetRefCount(); | |
| } | |
| SelfReferentialProbe::SelfReferentialProbe(int value) | |
| : m_value(value) | |
| , m_self(&m_value) | |
| { | |
| } | |
| SelfReferentialProbe::SelfReferentialProbe(SelfReferentialProbe&& other) | |
| : m_value(other.m_value) | |
| , m_self(&m_value) | |
| { | |
| other.m_value = movedFromValue; | |
| } | |
| SelfReferentialProbe::~SelfReferentialProbe() | |
| { | |
| // Non-trivial on purpose. See the class comment: this is what makes Swift relocate the value with | |
| // the move constructor above instead of copying its bytes. | |
| m_self = nullptr; | |
| } | |
| void callSelfReferentialProbeCompletionHandler(int argument, SelfReferentialProbeCompletionHandler&& completionHandler) | |
| { | |
| completionHandler(SelfReferentialProbe { argument }); | |
| } | |
| IntExpected makeIntExpected(int value) | |
| { | |
| return IntExpected { value }; | |
| } | |
| IntExpected makeIntUnexpected(ProbeError error) | |
| { | |
| return IntExpected { makeUnexpected(error) }; | |
| } | |
| CopyCountingProbeExpected makeCopyCountingProbeExpected(int value) | |
| { | |
| // In place, so that the probe the `Expected` holds is the only one this constructed: a test counting | |
| // copies is measuring what Swift did, not what building the `Expected` did. | |
| return CopyCountingProbeExpected { std::in_place, value }; | |
| } | |
| CopyCountingProbeExpected makeCopyCountingProbeUnexpected(ProbeError error) | |
| { | |
| return CopyCountingProbeExpected { makeUnexpected(error) }; | |
| } | |
| SharedProbeHolderExpected makeSharedProbeHolderExpected() | |
| { | |
| return SharedProbeHolderExpected { SharedProbeHolder { &sharedProbe() } }; | |
| } | |
| SharedProbeHolderExpected makeSharedProbeHolderUnexpected(ProbeError error) | |
| { | |
| return SharedProbeHolderExpected { makeUnexpected(error) }; | |
| } | |
| MoveOnlyProbeExpected makeMoveOnlyProbeExpected(int value) | |
| { | |
| return MoveOnlyProbeExpected { std::in_place, value }; | |
| } | |
| MoveOnlyProbeExpected makeMoveOnlyProbeUnexpected(ProbeError error) | |
| { | |
| return MoveOnlyProbeExpected { makeUnexpected(error) }; | |
| } | |
| SelfReferentialProbeExpected makeSelfReferentialProbeExpected(int value) | |
| { | |
| return SelfReferentialProbeExpected { std::in_place, value }; | |
| } | |
| SelfReferentialProbeExpected makeSelfReferentialProbeUnexpected(ProbeError error) | |
| { | |
| return SelfReferentialProbeExpected { makeUnexpected(error) }; | |
| } | |
| CountedErrorExpected makeCountedErrorExpected(int value) | |
| { | |
| return CountedErrorExpected { std::in_place, value }; | |
| } | |
| CountedErrorExpected makeCountedErrorUnexpected(ProbeError error) | |
| { | |
| return CountedErrorExpected { makeUnexpected(CountingProbeError { error }) }; | |
| } | |
| }; |