mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
105 lines
3.5 KiB
TypeScript
105 lines
3.5 KiB
TypeScript
import type { CompileFailure, CompileRequestOptions } from "../compile-types.js";
|
|
import type { FfiProfile } from "../ffi/ffi-manifest.js";
|
|
import {
|
|
checkerPanicDiag,
|
|
iceDiag,
|
|
isCheckerPanic,
|
|
type ScrDiagnostic,
|
|
} from "../diagnostics/diagnostic.js";
|
|
import type { IrModule, SrcLoc } from "../ir/ir.js";
|
|
import { validateModule } from "../ir/validate.js";
|
|
import {
|
|
fenceSpeculativeWasiFunctions,
|
|
moduleWasiUnavailableSurface,
|
|
targetRefusalDiag,
|
|
} from "../backend/target-diagnostics.js";
|
|
import type { LowerResult, LowerStats } from "../frontend/lowering/lowerer.js";
|
|
import type { FrontendFactory } from "../frontend/pipeline.js";
|
|
import type { CompilationTiming } from "../timing.js";
|
|
|
|
export interface PreparedExecutableModule {
|
|
ok: true;
|
|
mod: IrModule;
|
|
sourceTexts: Map<string, string>;
|
|
stats: LowerStats;
|
|
/** Divergence warnings (SC6xxx): the build succeeds; these sites can
|
|
* behave differently from Node. */
|
|
warnings: ScrDiagnostic[];
|
|
}
|
|
|
|
/** Release the parser/checker before native object generation begins. */
|
|
export function prepareExecutableModule(
|
|
entryPath: string,
|
|
opts: CompileRequestOptions,
|
|
ffi: FfiProfile | null,
|
|
buildPlatform: string,
|
|
createFrontend: FrontendFactory,
|
|
timing: CompilationTiming = () => {},
|
|
): PreparedExecutableModule | CompileFailure {
|
|
const fe = createFrontend(entryPath, opts.npmStatic);
|
|
timing("frontend-load");
|
|
let lowered: LowerResult;
|
|
let sourceTexts: Map<string, string>;
|
|
// The frontend (and its tsgo server) is released as soon as lowering
|
|
// ends — clang and the link never hold it open.
|
|
try {
|
|
const fail = (diagnostics: ScrDiagnostic[]): CompileFailure => ({
|
|
ok: false,
|
|
diagnostics,
|
|
sourceTexts: fe.sourceTexts(),
|
|
});
|
|
|
|
if (fe.preflight.length > 0) return fail(fe.preflight);
|
|
|
|
try {
|
|
lowered = fe.lower({
|
|
dynamic: opts.dynamic ?? false,
|
|
targetPlatform: buildPlatform,
|
|
...(ffi !== null ? { ffiImports: ffi.functions } : {}),
|
|
});
|
|
timing("lower");
|
|
} catch (e) {
|
|
// The last-resort panic fence: an upstream tsgo panic that crossed a
|
|
// checker call no statement/collection fence wrapped still becomes a
|
|
// clean failed compile (anchored at the entry), never a crashed CLI.
|
|
if (!isCheckerPanic(e)) throw e;
|
|
return fail([
|
|
checkerPanicDiag(e.message.split("\n", 1)[0]!, { file: entryPath, start: 0, end: 0 }),
|
|
]);
|
|
}
|
|
if (lowered.module === null) return fail(lowered.diagnostics);
|
|
|
|
const validation = validateModule(lowered.module);
|
|
timing("ir-validate");
|
|
if (validation.length > 0) {
|
|
return fail(validation.map((v) => iceDiag(v.message, v.loc)));
|
|
}
|
|
if (buildPlatform === "wasi") {
|
|
const entryLoc: SrcLoc = { file: entryPath, start: 0, end: 0 };
|
|
if (opts.sanitize) {
|
|
return fail([targetRefusalDiag("wasm32-wasi", "--sanitize", entryLoc)]);
|
|
}
|
|
if (ffi !== null) {
|
|
return fail([targetRefusalDiag("wasm32-wasi", "native FFI manifests", entryLoc)]);
|
|
}
|
|
fenceSpeculativeWasiFunctions(lowered.module);
|
|
const unavailable = moduleWasiUnavailableSurface(lowered.module);
|
|
if (unavailable !== null) {
|
|
return fail([targetRefusalDiag("wasm32-wasi", unavailable.surface, unavailable.loc)]);
|
|
}
|
|
}
|
|
sourceTexts = fe.sourceTexts();
|
|
} finally {
|
|
fe.dispose();
|
|
timing("frontend-dispose");
|
|
}
|
|
|
|
return {
|
|
ok: true,
|
|
mod: lowered.module!,
|
|
sourceTexts,
|
|
stats: lowered.stats,
|
|
warnings: lowered.divergences ?? [],
|
|
};
|
|
}
|