1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-08 05:27:14 +09:00
ladybird/Tests/AK/TestFunction.mm
Andrew Kaster 01ac48b36f AK: Support storing blocks in AK::Function
This has two slightly different implementations for ARC and non-ARC
compiler modes. The main idea is to store a block pointer as our
closure and use either ARC magic or BlockRuntime methods to manage
the memory for the block. Things are complicated by the fact that
we don't yet force-enable swift, so we can't count on the swift.org
llvm fork being our compiler toolchain. The patch adds some CMake
checks and ifdefs to still support environments without support
for blocks or ARC.
2025-03-18 17:15:08 -06:00

145 lines
2.6 KiB
Text

/*
* Copyright (c) 2025, Andrew Kaster <andrew@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibTest/TestCase.h>
#include <AK/ByteReader.h>
#include <AK/Function.h>
#include <AK/Platform.h>
TEST_CASE(SimpleBlock)
{
auto b = ^{ };
static_assert(IsBlockClosure<decltype(b)>);
auto f = Function<void()>(b);
f();
}
TEST_CASE(BlockCaptureInt)
{
__block int x = 0;
auto b = ^{
x = 2;
};
auto f = Function<void()>(b);
f();
EXPECT_EQ(x, 2);
}
TEST_CASE(BlockCaptureString)
{
__block String s = "hello"_string;
auto b = ^{
s = "world"_string;
};
auto f = Function<void()>(b);
f();
EXPECT_EQ(s, "world"_string);
}
TEST_CASE(BlockCaptureLongStringAndInt)
{
__block String s = "hello, world, this is a long string to avoid small string optimization"_string;
__block int x = 0;
auto b = ^{
s = "world, hello, this is a long string to avoid small string optimization"_string;
x = 2;
};
auto f = Function<void()>(b);
f();
EXPECT_EQ(s, "world, hello, this is a long string to avoid small string optimization"_string);
EXPECT_EQ(x, 2);
}
// Struct definitions from llvm-project/compiler-rt/lib/BlocksRuntime/Block_private.h @ d0177670a0e59e9d9719386f85bb78de0929407c
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void* dst, void* src);
void (*dispose)(void*);
};
struct Block_layout {
void* isa;
int flags;
int reserved;
void (*invoke)(void*, ...);
struct Block_descriptor* descriptor;
/* Imported variables. */
};
// This check is super important for proper tracking of block closure captures
static_assert(sizeof(Block_layout) == AK::Detail::block_layout_size);
TEST_CASE(BlockPointerCaptures)
{
int x = 0;
int* p = &x;
auto b = ^{
*p = 2;
};
auto f = Function<void()>(b);
auto span = f.raw_capture_range();
int* captured_p = ByteReader::load_pointer<int>(span.data());
EXPECT_EQ(captured_p, p);
f();
EXPECT_EQ(x, 2);
}
TEST_CASE(AssignBlock)
{
auto b = ^{ };
auto f = Function<void()>(b);
auto b2 = ^{ };
f = b2;
f();
f = b;
f();
}
#ifdef AK_HAS_OBJC_ARC
TEST_CASE(AssignWeakBlock)
{
__block int count = 0;
Function<void()> f;
{
auto b = ^{ ++count; };
f = b;
}
f();
EXPECT_EQ(count, 1);
{
auto b = ^{ ++count; };
auto const __weak weak_b = b;
f = weak_b;
f();
EXPECT_EQ(count, 2);
}
f();
EXPECT_EQ(count, 3);
}
#endif