AK+Tests: Destroy moved-from CallableWrappers
We won't attempt to destroy this wrapper later because m_kind gets set to NullPointer. If any fields of the CallableType are not movable, this results in a leak. A test replicating the issue before the fix is added to a new TestFunction.cpp file.
This commit is contained in:
parent
a0052316e5
commit
d5bf929699
3 changed files with 79 additions and 0 deletions
|
|
@ -357,6 +357,7 @@ private:
|
||||||
case FunctionKind::Inline:
|
case FunctionKind::Inline:
|
||||||
case FunctionKind::Block:
|
case FunctionKind::Block:
|
||||||
other_wrapper->init_and_swap(m_storage, inline_capacity);
|
other_wrapper->init_and_swap(m_storage, inline_capacity);
|
||||||
|
other_wrapper->~CallableWrapperBase();
|
||||||
m_kind = other.m_kind;
|
m_kind = other.m_kind;
|
||||||
break;
|
break;
|
||||||
case FunctionKind::Outline:
|
case FunctionKind::Outline:
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ set(AK_TEST_SOURCES
|
||||||
TestFind.cpp
|
TestFind.cpp
|
||||||
TestFixedArray.cpp
|
TestFixedArray.cpp
|
||||||
TestFixedPoint.cpp
|
TestFixedPoint.cpp
|
||||||
|
TestFunction.cpp
|
||||||
TestFlyString.cpp
|
TestFlyString.cpp
|
||||||
TestFormat.cpp
|
TestFormat.cpp
|
||||||
TestGenericLexer.cpp
|
TestGenericLexer.cpp
|
||||||
|
|
|
||||||
77
Tests/AK/TestFunction.cpp
Normal file
77
Tests/AK/TestFunction.cpp
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2026, Gregory Bertilson <gregory@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibTest/TestCase.h>
|
||||||
|
|
||||||
|
#include <AK/Function.h>
|
||||||
|
#include <AK/NonnullRefPtr.h>
|
||||||
|
#include <AK/RefCounted.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct CopyOnly {
|
||||||
|
int& instance_count;
|
||||||
|
|
||||||
|
CopyOnly(int& count)
|
||||||
|
: instance_count(count)
|
||||||
|
{
|
||||||
|
instance_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyOnly(CopyOnly const& other)
|
||||||
|
: instance_count(other.instance_count)
|
||||||
|
{
|
||||||
|
instance_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
~CopyOnly()
|
||||||
|
{
|
||||||
|
instance_count--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(move_construction_destroys_old_inline_wrapper)
|
||||||
|
{
|
||||||
|
int instance_count = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
Function<void()> source = [captured = CopyOnly(instance_count)]() {
|
||||||
|
(void)captured;
|
||||||
|
};
|
||||||
|
EXPECT_EQ(instance_count, 1);
|
||||||
|
|
||||||
|
Function<void()> destination = move(source);
|
||||||
|
EXPECT_EQ(instance_count, 1);
|
||||||
|
|
||||||
|
source = nullptr;
|
||||||
|
EXPECT_EQ(instance_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(instance_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(move_assignment_destroys_old_inline_wrapper)
|
||||||
|
{
|
||||||
|
int instance_count = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
Function<void()> source = [captured = CopyOnly(instance_count)]() {
|
||||||
|
(void)captured;
|
||||||
|
};
|
||||||
|
EXPECT_EQ(instance_count, 1);
|
||||||
|
|
||||||
|
Function<void()> destination;
|
||||||
|
destination = move(source);
|
||||||
|
EXPECT_EQ(instance_count, 1);
|
||||||
|
|
||||||
|
source = nullptr;
|
||||||
|
EXPECT_EQ(instance_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(instance_count, 0);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue