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:
Zaggy1024
2026-02-11 17:20:04 -06:00
committed by Gregory Bertilson
parent a0052316e5
commit d5bf929699
Notes: github-actions[bot] 2026-02-18 19:15:57 +00:00
3 changed files with 79 additions and 0 deletions

View File

@@ -357,6 +357,7 @@ private:
case FunctionKind::Inline:
case FunctionKind::Block:
other_wrapper->init_and_swap(m_storage, inline_capacity);
other_wrapper->~CallableWrapperBase();
m_kind = other.m_kind;
break;
case FunctionKind::Outline:

View File

@@ -27,6 +27,7 @@ set(AK_TEST_SOURCES
TestFind.cpp
TestFixedArray.cpp
TestFixedPoint.cpp
TestFunction.cpp
TestFlyString.cpp
TestFormat.cpp
TestGenericLexer.cpp

77
Tests/AK/TestFunction.cpp Normal file
View 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);
}