Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions Dockerfile.sandbox
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,30 @@ ARG PNPM_VERSION=11.1.3
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
&& apt-get install --yes --no-install-recommends \
ca-certificates curl gnupg \
&& curl --fail --silent --show-error --location https://apt.llvm.org/llvm-snapshot.gpg.key \
| gpg --dearmor --output /usr/share/keyrings/llvm-snapshot.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg] https://apt.llvm.org/noble/ llvm-toolchain-noble-22 main" \
> /etc/apt/sources.list.d/llvm22.list \
&& apt-get update \
&& apt-get install --yes --no-install-recommends \
build-essential \
ca-certificates \
ccache \
clang \
clang-22 \
cmake \
curl \
git \
libclang-rt-dev \
llvm \
libclang-rt-22-dev \
libzstd-dev \
llvm-22-dev \
ninja-build \
xz-utils \
zlib1g-dev \
&& ln -sf clang-22 /usr/bin/clang \
&& ln -sf clang++-22 /usr/bin/clang++ \
&& test "$(llvm-config-22 --version)" = 22.1.8 \
&& rm -rf /var/lib/apt/lists/*

RUN curl --fail --silent --show-error --location \
Expand Down
2 changes: 1 addition & 1 deletion docs/src/app/limitations/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const who = process.argv.length > 2 ? process.argv[2] : "world";

**Process shape** — `process.argv[0]` is `"scriptc"` and `argv[1]` is the binary's path (positions line up with Node; `argv[2]` onward are your args). The uncaught-exception stderr line reads `Uncaught <value>` instead of Node's stack-trace block (exit code and pre-throw stdout are identical). Runtime errors carry `message` and Node's `code`, but not `errno`/`syscall`/`path`.

**Comparator call sequences differ in `sort` and `toSorted`** (stable insertion sort here, TimSort in V8 — sorted results are byte-identical for consistent comparators), and **`localeCompare` compares code units**, not ICU collation.
**Comparator call sequences differ in `sort` and `toSorted`** (scriptc uses a stable bottom-up merge sort, while V8 uses TimSort). Sorted results are byte-identical for consistent comparators. Ordered inputs use a linear number of comparator calls, but merge-buffer movement remains O(n log n) even when every boundary is already ordered. **`localeCompare` compares code units**, not ICU collation.

## Dynamic-tier limits

Expand Down
14 changes: 13 additions & 1 deletion packages/cli/test/bootstrap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import { release as osRelease, tmpdir } from "node:os";
import { join } from "node:path";
import { promisify } from "node:util";
import { expect, test } from "vitest";
import { precompiledRuntimePackTarget } from "../../compiler/src/startup-cache.js";
import { platformLinkerSupportsPersistentCache } from "../../compiler/src/backend/linker.js";

const execFileAsync = promisify(execFile);
const repoRoot = join(import.meta.dirname, "../../..");
const bootstrap = join(repoRoot, "packages/cli/dist/bootstrap.js");
const runtimePackHost = process.platform === "darwin" && process.arch === "arm64" &&
Number.parseInt(osRelease().split(".", 1)[0] ?? "", 10) >= 24;
const helperTarget = precompiledRuntimePackTarget();
const persistentExecutable = helperTarget === null || platformLinkerSupportsPersistentCache(process.env, helperTarget);

test("bootstrap serves version and help without loading the compiler graph", async () => {
const preloadDir = await mkdtemp(join(tmpdir(), "scriptc-bootstrap-preload-"));
Expand Down Expand Up @@ -83,7 +87,15 @@ test("bootstrap exact builds use the routed cache and source edits fall through"
rm(join(cacheRoot, "early-exe-implementation"), { recursive: true, force: true }),
]);
expect((await build()).stderr).not.toContain("scriptc lowering");
await expect(build(true)).resolves.toMatchObject({ stderr: "" });
if (persistentExecutable) {
await expect(build(true)).resolves.toMatchObject({ stderr: "" });
} else {
// This target intentionally lacks a persistent native dependency proof.
// It must enter the full compiler to relink, even on a frontend hit.
await expect(build(true)).rejects.toMatchObject({ stderr: expect.stringContaining("compiler graph loaded") });
expect((await build()).stderr).not.toContain("scriptc lowering");
expect((await execFileAsync(outPath)).stdout).toBe("one\n");
}

await writeFile(entry, 'console.log("two");\n');
expect((await build()).stderr).toContain("scriptc lowering");
Expand Down
12 changes: 9 additions & 3 deletions packages/cli/test/executable-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { release as osRelease, tmpdir } from "node:os";
import { delimiter, dirname, join } from "node:path";
import { promisify } from "node:util";
import { expect, test } from "vitest";
import { precompiledRuntimePackTarget } from "../../compiler/src/startup-cache.js";
import { platformLinkerSupportsPersistentCache } from "../../compiler/src/backend/linker.js";

const execFileAsync = promisify(execFile);
const require = createRequire(import.meta.url);
Expand All @@ -13,6 +15,8 @@ const cliEntry = join(repoRoot, "packages/cli/src/main.ts");
const tsxLoader = join(dirname(require.resolve("tsx/package.json")), "dist/loader.mjs");
const runtimePackHost = process.platform === "darwin" && process.arch === "arm64" &&
Number.parseInt(osRelease().split(".", 1)[0] ?? "", 10) >= 24;
const helperTarget = precompiledRuntimePackTarget();
const persistentExecutable = helperTarget === null || platformLinkerSupportsPersistentCache(process.env, helperTarget);

test("exact executable repeats skip lowering while edits and damaged outputs stay correct", async () => {
const dir = await mkdtemp(join(tmpdir(), "scriptc-cli-executable-cache-"));
Expand Down Expand Up @@ -50,10 +54,12 @@ test("exact executable repeats skip lowering while edits and damaged outputs sta
const originalBinaryTime = (await stat(outPath)).mtimeMs;
const originalTuTime = (await stat(tuPath)).mtimeMs;

// The exact repeat returns from the whole-program cache before tsgo and
// leaves already-correct caller outputs on their existing inodes.
// Every route reuses the frontend. Only routes with a complete native
// dependency proof can also retain the executable inode; other targets
// deliberately relink their cached translation unit.
expect((await build()).stderr).not.toContain("scriptc lowering");
expect((await stat(outPath)).mtimeMs).toBe(originalBinaryTime);
if (persistentExecutable) expect((await stat(outPath)).mtimeMs).toBe(originalBinaryTime);
expect(await run()).toBe("one\n");
expect((await stat(tuPath)).mtimeMs).toBe(originalTuTime);

// Output-kind cleanup is honest even on an exact early hit: an IR file
Expand Down
8 changes: 5 additions & 3 deletions packages/cli/test/native-output.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ function cli(args: string[], env: NodeJS.ProcessEnv = process.env) {
});
}

test("unsupported hosts fail with SC3002 before creating an artifact", async () => {
if (supported) return;
test("unsupported targets fail with SC3002 before creating an artifact", async () => {
const { dir, entry } = await fixture();
const output = join(dir, "hello.o");
await expect(cli(["build", entry, "--emit=obj", "-o", output])).rejects.toMatchObject({
await expect(cli(["build", entry, "--emit=obj", "-o", output], {
...process.env,
SCRIPTC_TARGET: "scriptc-unsupported-target",
})).rejects.toMatchObject({
code: 1,
stderr: expect.stringContaining("SC3002"),
});
Expand Down
1 change: 1 addition & 0 deletions packages/compiler/src/backend/native-codegen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ function request(root: string, packageJson: string, output = join(root, "program
outputKind: "obj" as const,
sourcePath: "/source/app.ts",
target,
helperHost: { platform: "darwin" as const, arch: "arm64" },
resolvePackageJson: () => packageJson,
cacheRoot: join(root, "cache"),
};
Expand Down
6 changes: 5 additions & 1 deletion packages/compiler/src/backend/native-toolchain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4178,7 +4178,11 @@ test.skipIf(
probeSource,
"long long public_value(void);\nint main(void) { return public_value() != 7; }\n",
);
execFileSync(clangExecutable!, [probeSource, outPath, "-lm", "-o", probe]);
// The failed linker belongs only to the shard-merge probe. Link the
// resulting archive with the real host tools to verify its fallback.
execFileSync(clangExecutable!, [probeSource, outPath, "-lm", "-o", probe], {
env: { ...process.env, PATH: oldPath },
});
expect(execFileSync(probe, { encoding: "utf8" })).toBe("");
// The canonical retry is valid for this invocation, but must not occupy a
// key describing merged shard bytes. A repaired merge tool retries the
Expand Down
Loading
Loading