mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-08 05:27:14 +09:00

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.
145 lines
2.6 KiB
Text
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
|