| /* |
| * 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 }) }; |
| } |
| |
| }; |