Files
bun-src/test/internal/source-lints/dead-symbols-ffi-sys-test-runner-cpp.test.ts
2026-08-27 21:09:14 +00:00

241 lines
11 KiB
TypeScript

// Guards against reintroduction of symbols removed as dead code from the
// Windows libuv FFI crate, bun_cares_sys, bun_brotli_sys, the simdutf FFI
// (Rust declarations plus their C++ wrappers), the test-runner JSValue
// extension trait, and the C++ bindings (event emitter, cookies, node:module,
// streams, webcore headers). Every entry was verified to have zero references
// across src/, scripts/, test/ and the regenerated build/debug/codegen/ output
// before deletion; the removal was validated by `cargo check --workspace` on
// all 10 CI target triples, `cargo check --workspace --tests`, and a full
// `bun bd` build.
//
// This is a source-tree lint: it reads files from src/ and does not touch the
// built binary, so it belongs in test/internal/source-lints/ per the README.
// All checks read the working tree; nothing in this sweep deleted a whole file.
import { expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import path from "node:path";
const repoRoot = path.resolve(import.meta.dir, "..", "..", "..");
function src(p: string): string {
return readFileSync(path.join(repoRoot, p), "utf8");
}
function resurrected(checks: Array<[string, RegExp]>): string[] {
return checks.filter(([file, re]) => re.test(src(file))).map(([file, re]) => `${file}: ${re.source}`);
}
test("dead libuv FFI declarations (windows-only crate) do not reappear", () => {
expect(
resurrected([
// extern "C" declarations no Rust code called: tcp/udp/tty/poll/prepare/
// check/fs_poll/threading/os-info surface of uv.h that bun never uses.
["src/libuv_sys/libuv.rs", /\bfn uv_tcp_connect\b/],
["src/libuv_sys/libuv.rs", /\bfn uv_udp_recv_start\b/],
["src/libuv_sys/libuv.rs", /\bfn uv_tty_get_winsize\b/],
["src/libuv_sys/libuv.rs", /\bfn uv_prepare_init\b/],
["src/libuv_sys/libuv.rs", /\bfn uv_fs_poll_start\b/],
["src/libuv_sys/libuv.rs", /\bfn uv_thread_create\b/],
["src/libuv_sys/libuv.rs", /\bfn uv_os_get_passwd\b/],
["src/libuv_sys/libuv.rs", /\bfn uv_queue_work\b/],
["src/libuv_sys/libuv.rs", /\bfn uv_async_init\b/],
["src/libuv_sys/libuv.rs", /\bfn uv_dlopen\b/],
// structs / callback aliases that only those declarations referenced.
["src/libuv_sys/libuv.rs", /\bstruct uv_passwd_t\b/],
["src/libuv_sys/libuv.rs", /\bstruct uv_getnameinfo_t\b/],
["src/libuv_sys/libuv.rs", /\bstruct uv_thread_options_t\b/],
["src/libuv_sys/libuv.rs", /\bstruct uv_lib_t\b/],
["src/libuv_sys/libuv.rs", /\bstruct uv_dirent_t\b/],
["src/libuv_sys/libuv.rs", /\btype uv_random_cb\b/],
// never-used wrappers: the UvReq marker trait, Pipe/Loop/async helpers.
["src/libuv_sys/libuv.rs", /\btrait UvReq\b/],
["src/libuv_sys/libuv.rs", /\bfn dump_active_handles\b/],
["src/libuv_sys/libuv.rs", /\bfn set_pending_instances_count\b/],
["src/libuv_sys/libuv.rs", /\bfn as_stream_ptr\b/],
// alias tables nothing read: StdioFlags (bun_spawn uses UV_* directly),
// UV_FS_O_* (duplicates of the `O` module), priorities, clock ids.
["src/libuv_sys/libuv.rs", /\bmod StdioFlags\b/],
["src/libuv_sys/libuv.rs", /\bUV_FS_O_APPEND\b/],
["src/libuv_sys/libuv.rs", /\bUV_PRIORITY_LOW\b/],
["src/libuv_sys/libuv.rs", /\bUV_CLOCK_MONOTONIC\b/],
["src/spawn/process.rs", /\bStdioFlags\b/],
]),
).toEqual([]);
});
test("dead c-ares and brotli FFI declarations do not reappear", () => {
expect(
resurrected([
// bun drives c-ares through ares_init_options / ares_query /
// ares_getaddrinfo; these entry points were declared but never called.
["src/cares_sys/c_ares.rs", /\bfn ares_init\b/],
["src/cares_sys/c_ares.rs", /\bfn ares_gethostbyname\b/],
["src/cares_sys/c_ares.rs", /\bfn ares_expand_name\b/],
["src/cares_sys/c_ares.rs", /\bfn ares_timeout\b/],
["src/cares_sys/c_ares.rs", /\bfn ares_parse_uri_reply\b/],
["src/cares_sys/c_ares.rs", /\bstruct ares_socket_functions\b/],
["src/cares_sys/c_ares.rs", /\bstruct_ares_uri_reply\b/],
// Windows-only struct definitions that existed solely for the above.
["src/cares_sys/lib.rs", /\bstruct (timeval|iovec)\b/],
// BrotliDecoder helpers no caller used (callers use the raw
// BrotliDecoderGetErrorCode directly).
["src/brotli_sys/brotli_c.rs", /\bBrotliDecoderIsFinished\b/],
["src/brotli_sys/brotli_c.rs", /\bBrotliDecoderVersion\b/],
["src/brotli_sys/brotli_c.rs", /\bfn (is_finished|get_error_code|version)\b/],
]),
).toEqual([]);
});
test("dead simdutf wrappers stay removed on both sides of the FFI", () => {
// The complete set of wrappers removed (22 had an unused Rust declaration,
// 8 had already lost theirs). The trailing \b keeps the still-live
// `*_with_errors` / `*be` siblings of several of these from matching.
const removed = [
"base64_decode_from_binary16",
"change_endianness_utf16",
"convert_latin1_to_utf8",
"convert_utf16be_to_utf32",
"convert_utf16be_to_utf8",
"convert_utf16le_to_utf32",
"convert_utf16le_to_utf32_with_errors",
"convert_utf16le_to_utf8",
"convert_utf32_to_utf16be",
"convert_utf32_to_utf16le",
"convert_utf32_to_utf16le_with_errors",
"convert_utf32_to_utf8",
"convert_utf8_to_utf32",
"convert_valid_utf16le_to_utf32",
"convert_valid_utf32_to_utf16le",
"convert_valid_utf8_to_utf16be",
"convert_valid_utf8_to_utf16le",
"count_utf16be",
"count_utf16le",
"count_utf8",
"detect_encodings",
"utf16_length_from_latin1",
"utf16_length_from_utf32",
"utf32_length_from_utf16le",
"utf8_length_from_utf32",
"validate_utf16be",
"validate_utf16be_with_errors",
"validate_utf16le_with_errors",
"validate_utf32",
"validate_utf32_with_errors",
];
const names = new RegExp(`\\bsimdutf__(?:${removed.join("|")})\\b`);
expect(
resurrected([
["src/simdutf_sys/simdutf.rs", names],
["src/simdutf_sys/bun-simdutf.cpp", names],
["src/parsers/benches/support/simdutf_shim.cpp", names],
]),
).toEqual([]);
// Every wrapper the C++ side still defines must have a Rust declaration and
// vice versa, so the two files cannot drift apart again.
const collect = (text: string) => new Set([...text.matchAll(/\bsimdutf__\w+/g)].map(m => m[0]));
const rust = collect(src("src/simdutf_sys/simdutf.rs"));
const cpp = collect(src("src/simdutf_sys/bun-simdutf.cpp"));
expect([...rust].filter(n => !cpp.has(n)).sort()).toEqual([]);
expect([...cpp].filter(n => !rust.has(n)).sort()).toEqual([]);
// The parser bench shim only exists to satisfy those same declarations, so
// it must not define wrappers the Rust side never declares (it carried
// six such orphans before this sweep).
const shim = collect(src("src/parsers/benches/support/simdutf_shim.cpp"));
expect([...shim].filter(n => !rust.has(n)).sort()).toEqual([]);
});
test("dead bun_runtime helpers do not reappear", () => {
expect(
resurrected([
// JSValueTestExt forwarders shadowed by the same-named bun_jsc inherent
// methods, so no call site could ever resolve to them.
[
"src/runtime/test_runner/mod.rs",
/\bfn (to_fmt|jest_deep_equals|is_uint32_as_any_int|to_u32|string_includes)\b/,
],
// Never-read fields.
["src/runtime/test_runner/expect.rs", /struct CustomMatcherParamsFormatter<'a>/],
["src/runtime/test_runner/expect.rs", /struct SuccessfulReturnsFormatter<'g, 'f> \{\s*pub global_this:/],
["src/runtime/webcore/fetch/FetchTasklet.rs", /\bglobal_this: Option<GlobalRef>/],
// S3ErrorJsc::to_js duplicated the s3_error_to_js free fn nobody called
// through the trait.
["src/runtime/webcore/s3/error_jsc.rs", /\bfn to_js\(/],
]),
).toEqual([]);
});
test("dead C++ bindings do not reappear", () => {
expect(
resurrected([
// simdutf C++ wrappers are covered above; these are JSC host functions
// that were declared and defined but never installed on any object.
["src/jsc/bindings/webcore/JSEventEmitter.cpp", /\bEvents_function(GetEventListeners|ListenerCount|Once|On)\b/],
["src/jsc/bindings/webcore/JSEventEmitter.h", /\bEvents_function\w+/],
["src/jsc/bindings/webcore/JSEventEmitterCustom.h", /\bJSEventEmitterWrapper\b|\bjsEventEmitterCast\b/],
["src/jsc/modules/NodeModuleModule.cpp", /\bjsFunction(DebugNoop|SyncBuiltinExports)\b/],
["src/jsc/bindings/webcore/JSCookie.cpp", /\bjsCookieStaticFunctionSerialize\b/],
["src/jsc/bindings/Cookie.h", /static String serialize\(/],
// Declarations with no definition anywhere.
["src/jsc/bindings/JSBakeResponse.cpp", /JSC_DECLARE_CUSTOM_GETTER\(jsBakeResponsePrototypeGet/],
// Helpers with zero callers.
["src/jsc/bindings/TextEncoding.h", /\bUTF8Encoding\b/],
["src/jsc/bindings/JSMockFunction.cpp", /\bcreateMockResultStructure\b/],
[
"src/jsc/bindings/webcore/ActiveDOMObject.h",
/\bqueueTaskToDispatchEvent(Internal)?\b|\bisAllowedToRunScript\b/,
],
["src/jsc/bindings/webcore/streams/StreamQueue.h", /\bsetTotalSize\b/],
["src/jsc/bindings/webcore/WorkerMessagingProxy.h", /\baskedToTerminate\(\)|\bloaderContextIdentifier\(\)/],
["src/jsc/bindings/BunHttp2CommonStrings.h", /\bcommonStringInitializer\b/],
["src/jsc/modules/NativeModuleList.h", /\bBUN_FOREACH_CJS_NATIVE_MODULE\b/],
// Iso-subspace slots no class ever allocated from.
[
"src/jsc/bindings/webcore/DOMIsoSubspaces.h",
/m_subspaceFor(JSS3File|JSS3Bucket|NapiPrototype|EventListener)\b/,
],
[
"src/jsc/bindings/webcore/DOMClientIsoSubspaces.h",
/m_clientSubspaceFor(JSS3File|JSS3Bucket|NapiPrototype|EventListener)\b/,
],
// Transferable-stream stubs that only threw "not implemented" and had
// no callers.
[
"src/jsc/bindings/webcore/streams/WebStreamsInternals.h",
/\bsetUpCrossRealmTransform(Readable|Writable)\b|\bpackAndPostMessage\b/,
],
[
"src/jsc/bindings/webcore/streams/CrossRealmTransform.cpp",
/\bsetUpCrossRealmTransform(Readable|Writable)\b|\bpackAndPostMessage\b/,
],
["src/jsc/bindings/webcore/streams/StreamsForward.h", /\bCrossRealmMessageType\b/],
]),
).toEqual([]);
});
test("WebCore event interface enums stay trimmed to the interfaces bun implements", () => {
// These checked-in copies of WebCore's generated enums listed every event /
// event-target interface in WebKit; only the entries bun's Event and
// EventTarget subclasses report survive.
expect(
resurrected([
[
"src/jsc/bindings/webcore/EventInterfaces.h",
/\b(ApplePayCancelEvent|WebGLContextEvent|MouseEvent|ProgressEvent)InterfaceType\b/,
],
["src/jsc/bindings/webcore/EventInterfaces.h", /#if ENABLE\(/],
[
"src/jsc/bindings/webcore/EventTargetInterfaces.h",
/\b(ApplePaySession|MediaKeySession|Node|XMLHttpRequest)EventTargetInterfaceType\b/,
],
["src/jsc/bindings/webcore/EventTargetInterfaces.h", /#if ENABLE\(/],
]),
).toEqual([]);
// The survivors keep their original numeric values (Event::m_eventInterface
// is a 7-bit field and the factories switch on these constants).
expect(src("src/jsc/bindings/webcore/EventInterfaces.h")).toMatch(/\bMessageEventInterfaceType = 67\b/);
expect(src("src/jsc/bindings/webcore/EventTargetInterfaces.h")).toMatch(/\bEventTargetInterfaceType = 45\b/);
});