mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-07 08:33:50 +08:00
Define new runtime globals with initializers so library localization hides them (#992)
This commit is contained in:
@@ -266,8 +266,10 @@ SCR_TL size_t scr_cyc_live = 0;
|
||||
SCR_TL size_t scr_cyc_old_freed = 0;
|
||||
#endif
|
||||
|
||||
/* Descriptor copies by registry id. Id 0 stays empty (an unregistered type). */
|
||||
ScrCycType scr_cyc_types[SCR_CYC_MAX_TYPES];
|
||||
/* Descriptor copies by registry id. Id 0 stays empty (an unregistered type).
|
||||
* Explicitly initialized so library localization can demote it: a tentative
|
||||
* definition is a COMMON symbol on Mach-O, and COMMON symbols stay global. */
|
||||
ScrCycType scr_cyc_types[SCR_CYC_MAX_TYPES] = {0};
|
||||
static uint32_t scr_cyc_ntypes = 1;
|
||||
|
||||
/* Assign `type` its id on its first allocation; any thread may get there
|
||||
|
||||
@@ -96,8 +96,10 @@ typedef struct ScrSidx {
|
||||
/* Keep short-string cursor traffic out of the sparse cache. A short-lived
|
||||
* one-byte receiver can be far more common than a large indexed one; sharing
|
||||
* the round-robin slots would otherwise rebuild a warm index every few calls.
|
||||
* Explicitly initialized so library localization can demote it: a tentative
|
||||
* definition is a COMMON symbol on Mach-O, and COMMON symbols stay global.
|
||||
*/
|
||||
SCR_TL ScrStrScan scr_str_scan;
|
||||
SCR_TL ScrStrScan scr_str_scan = {0};
|
||||
static SCR_TL ScrSidx scr_sidx_sparse_tab[SCR_SIDX_N];
|
||||
static SCR_TL ScrSidx scr_sidx_cursor_tab[SCR_SIDX_N];
|
||||
static SCR_TL unsigned scr_sidx_sparse_clock;
|
||||
@@ -936,7 +938,8 @@ static inline uint32_t scr_utf8_decode(const char *p, size_t *adv) {
|
||||
|
||||
/* ── WTF-8 lone surrogates (scr_runtime.h) ────────────────────────── */
|
||||
|
||||
bool scr_wtf8_lone;
|
||||
/* Explicitly initialized for library localization (see scr_str_scan). */
|
||||
bool scr_wtf8_lone = false;
|
||||
|
||||
void scr_wtf8_note(void) {
|
||||
if (!scr_wtf8_any()) __atomic_store_n(&scr_wtf8_lone, true, __ATOMIC_RELAXED);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { mkdir, readdir, readFile } from "node:fs/promises";
|
||||
import { availableParallelism } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const testDir = import.meta.dirname;
|
||||
const srcDir = join(testDir, "../src");
|
||||
const vendorDir = join(testDir, "../vendor");
|
||||
const buildDir = join(testDir, "build", "common-symbols");
|
||||
|
||||
// Library localization (abi.localize_runtime) keeps COMMON symbols global,
|
||||
// so a runtime global written as a tentative definition (`T x;` at file
|
||||
// scope with no initializer) is exported from every localized library
|
||||
// archive on toolchains that emit tentative definitions as COMMON symbols,
|
||||
// such as the macOS runtime build. Linux clang defaults to -fno-common, where
|
||||
// the same declaration is an ordinary definition, so the library-multi
|
||||
// symbol checks only catch it on macOS. This test compiles every runtime
|
||||
// source with -fcommon, in the plain and worker configurations, and expects
|
||||
// no COMMON symbols: every external runtime global needs an initializer.
|
||||
test("runtime globals are not tentative definitions", async () => {
|
||||
await mkdir(buildDir, { recursive: true });
|
||||
const sources = (await readdir(srcDir))
|
||||
.filter((name) => name.endsWith(".c") && !name.endsWith(".test.c"))
|
||||
.sort();
|
||||
const jobs = sources.flatMap((name) =>
|
||||
[[], ["-DSCR_WORKERS"]].map((defines) => ({ name, defines })),
|
||||
);
|
||||
const common: string[] = [];
|
||||
let next = 0;
|
||||
const worker = async (): Promise<void> => {
|
||||
while (next < jobs.length) {
|
||||
const { name, defines } = jobs[next++]!;
|
||||
const output = join(buildDir, `${name}${defines.join("")}.ll`);
|
||||
await execFileAsync("clang", [
|
||||
"-std=c11",
|
||||
"-O0",
|
||||
"-fcommon",
|
||||
"-S",
|
||||
"-emit-llvm",
|
||||
...(process.platform === "linux" ? ["-D_GNU_SOURCE"] : []),
|
||||
...defines,
|
||||
`-I${join(vendorDir, "quickjs-ng")}`,
|
||||
`-I${join(vendorDir, "mbedtls", "include")}`,
|
||||
"-o",
|
||||
output,
|
||||
join(srcDir, name),
|
||||
]);
|
||||
for (const match of (await readFile(output, "utf8")).matchAll(/^@(\S+) = common /gm)) {
|
||||
common.push(`${name}${defines.length > 0 ? ` (${defines.join(" ")})` : ""}: ${match[1]}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
await Promise.all(Array.from({ length: Math.min(8, availableParallelism()) }, worker));
|
||||
expect(common.sort()).toEqual([]);
|
||||
});
|
||||
Reference in New Issue
Block a user