mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Link AbortController without the fetch unit (#2412)
This commit is contained in:
@@ -36,6 +36,7 @@ export function executableLinkFeatures(mod: IrModule, largePages = false): Nativ
|
||||
...(features.dynForAwait ? { dynForAwait: true } : {}),
|
||||
...(features.eventsOnceSignal ? { eventsOnceSignal: true } : {}),
|
||||
...(features.mime ? { mime: true } : {}),
|
||||
...(features.abortSignal ? { abortSignal: true } : {}),
|
||||
...(features.utilTiming ? { utilTiming: true } : {}),
|
||||
...(features.dynBuffer ? { dynBuffer: true } : {}),
|
||||
...(features.builtinValues ? { builtinValues: true } : {}),
|
||||
|
||||
@@ -1030,10 +1030,6 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"fetch.responseJson": "scr_fetch_response_json",
|
||||
"fetch.responseText": "scr_fetch_response_text",
|
||||
"fetch.responseBytes": "scr_fetch_response_bytes",
|
||||
"fetch.abortControllerNew": "scr_fetch_abort_controller_new",
|
||||
"fetch.abortTimeout": "scr_fetch_abort_timeout",
|
||||
"fetch.abortNow": "scr_fetch_abort_now",
|
||||
"fetch.abortAny": "scr_fetch_abort_any",
|
||||
"fetch.webIs": "scr_fetch_web_is",
|
||||
"fetch.streamIs": "scr_fetch_stream_is",
|
||||
"zlib.streamIs": "scr_zlib_stream_is",
|
||||
@@ -1838,14 +1834,11 @@ export const USES_TIMERS_LIB_FNS = new Set<string>([
|
||||
"fetch.input",
|
||||
"fetch.function",
|
||||
"fetch.requestNew",
|
||||
"fetch.abortTimeout",
|
||||
"abort.timeout",
|
||||
// EventTarget listeners report their exceptions from a nextTick.
|
||||
"event.targetNew",
|
||||
"abort.controllerNew",
|
||||
"abort.any",
|
||||
"fetch.abortControllerNew",
|
||||
"fetch.abortAny",
|
||||
"fetch.streamNew",
|
||||
"fetch.streamFrom",
|
||||
"fetch.transformNew",
|
||||
|
||||
@@ -67,6 +67,9 @@ export interface NativeLinkFeatures {
|
||||
/** `--large-pages`: scr_text_pages.c, which moves the executable's code
|
||||
* onto transparent huge pages at startup. Absent (false) without it. */
|
||||
largePages?: boolean;
|
||||
/** AbortController and AbortSignal: scr_abort.c, unless the program
|
||||
* links the fetch unit, which has them. */
|
||||
abortSignal?: boolean;
|
||||
/** util.debounce and util.throttle: scr_util_timing.c. */
|
||||
utilTiming?: boolean;
|
||||
/** Buffer.prototype methods called on checked-dynamic values link
|
||||
|
||||
@@ -131,6 +131,9 @@ export interface CcOptions {
|
||||
mime?: boolean;
|
||||
/** `--large-pages`: compiles scr_text_pages.c. */
|
||||
largePages?: boolean;
|
||||
/** AbortController and AbortSignal: compiles scr_abort.c, unless the
|
||||
* program compiles the fetch unit, which has them. */
|
||||
abortSignal?: boolean;
|
||||
/** util.debounce and util.throttle: compiles scr_util_timing.c. */
|
||||
utilTiming?: boolean;
|
||||
/** Buffer.prototype methods called on checked-dynamic values:
|
||||
|
||||
@@ -195,7 +195,10 @@ const RUNTIME_UNITS: readonly RuntimeUnit[] = [
|
||||
unit((u) => u.opts.computedRequire === true, "scr_require.c"),
|
||||
unit((u) => u.opts.dc, "scr_dc.c"),
|
||||
unit((u) => u.opts.dynTier, "scr_js_value.c", "scr_js_object.c"),
|
||||
unit((u) => u.opts.eventTarget || u.nativeFetch, "scr_event_target.c"),
|
||||
unit(
|
||||
(u) => u.opts.eventTarget || u.nativeFetch || u.opts.abortSignal === true,
|
||||
"scr_event_target.c",
|
||||
),
|
||||
unit((u) => u.opts.wasm, "scr_wasm.c"),
|
||||
// Module namespace objects as checked-dynamic values.
|
||||
unit((u) => u.opts.moduleNamespace === true, "scr_module_namespace.c"),
|
||||
@@ -248,6 +251,7 @@ const RUNTIME_UNITS: readonly RuntimeUnit[] = [
|
||||
unit((u) => u.opts.eventsOnceSignal === true, "scr_events_once_signal.c"),
|
||||
unit((u) => u.opts.mime === true, "scr_mime.c"),
|
||||
unit((u) => u.opts.largePages === true, "scr_text_pages.c"),
|
||||
unit((u) => u.opts.abortSignal === true && !u.nativeFetch, "scr_abort.c"),
|
||||
unit((u) => u.opts.utilTiming === true, "scr_util_timing.c"),
|
||||
// The checked-dynamic HANDLE support unit (listener gate + runtime
|
||||
// adapter closures): every referencing unit is one of the emitter or
|
||||
|
||||
@@ -92,6 +92,7 @@ export interface RuntimeFeatureSet extends NativeLinkFeatures {
|
||||
eventsOnceSignal: boolean;
|
||||
mime: boolean;
|
||||
largePages: boolean;
|
||||
abortSignal: boolean;
|
||||
utilTiming: boolean;
|
||||
dynBuffer: boolean;
|
||||
builtinValues: boolean;
|
||||
@@ -101,6 +102,7 @@ export interface RuntimeFeatureSet extends NativeLinkFeatures {
|
||||
brotli: boolean;
|
||||
moduleNamespace: boolean;
|
||||
nativeFetch: boolean;
|
||||
abortSignalOnly: boolean;
|
||||
netEffective: boolean;
|
||||
httpEffective: boolean;
|
||||
tlsEffective: boolean;
|
||||
@@ -297,6 +299,7 @@ export function effectiveRuntimeFeatures(
|
||||
eventsOnceSignal: features.eventsOnceSignal === true,
|
||||
mime: features.mime === true,
|
||||
largePages: features.largePages === true,
|
||||
abortSignal: features.abortSignal === true,
|
||||
utilTiming: features.utilTiming === true,
|
||||
dynBuffer: features.dynBuffer === true,
|
||||
builtinValues: features.builtinValues === true,
|
||||
@@ -306,6 +309,9 @@ export function effectiveRuntimeFeatures(
|
||||
brotli: features.brotli === true,
|
||||
moduleNamespace: features.moduleNamespace === true,
|
||||
nativeFetch,
|
||||
// The fetch unit has AbortController and AbortSignal; scr_abort.c is
|
||||
// the same implementation without the fetch transport.
|
||||
abortSignalOnly: features.abortSignal === true && !nativeFetch,
|
||||
netEffective,
|
||||
httpEffective,
|
||||
tlsEffective,
|
||||
|
||||
@@ -117,7 +117,7 @@ test("prototype tables store optional methods without invoking their host capabi
|
||||
test("local abort signals remain available without a reflection dispatcher", () => {
|
||||
const mod = module();
|
||||
mod.functions = [
|
||||
fn("%main", [{ kind: "libCall", fn: "fetch.abortControllerNew", args: [], type: DYN, loc }]),
|
||||
fn("%main", [{ kind: "libCall", fn: "abort.controllerNew", args: [], type: DYN, loc }]),
|
||||
];
|
||||
fenceSpeculativeWasiFunctions(mod);
|
||||
expect(moduleWasiUnavailableSurface(mod)).toBeNull();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { everyModuleNode, everyStmtList, transformStmtList } from "../ir/traverse.js";
|
||||
import { everyModuleNode, everyStmtList } from "../ir/traverse.js";
|
||||
import type { LlvmUnsupportedError } from "./llvm/emitter.js";
|
||||
import type { ScrDiagnostic } from "../diagnostics/diagnostic.js";
|
||||
import { type IrModule, type SrcLoc } from "../ir/ir.js";
|
||||
@@ -14,24 +14,6 @@ import {
|
||||
* calls, escaped callables, constructors, and exported roots still retain
|
||||
* the ordinary target diagnostic. ABI types remain checked by the gate. */
|
||||
export function fenceSpeculativeWasiFunctions(mod: IrModule): void {
|
||||
for (const fn of mod.functions)
|
||||
fn.body = transformStmtList(fn.body, {
|
||||
stmt: (statement) => statement,
|
||||
expr: (expr) => {
|
||||
if (expr.kind !== "libCall") return expr;
|
||||
const portable =
|
||||
expr.fn === "fetch.abortControllerNew"
|
||||
? "abort.controllerNew"
|
||||
: expr.fn === "fetch.abortTimeout"
|
||||
? "abort.timeout"
|
||||
: expr.fn === "fetch.abortNow"
|
||||
? "abort.now"
|
||||
: expr.fn === "fetch.abortAny"
|
||||
? "abort.any"
|
||||
: null;
|
||||
return portable ? { ...expr, fn: portable } : expr;
|
||||
},
|
||||
});
|
||||
if (!mod.functions.some((fn) => fn.speculativeDispatch)) return;
|
||||
const functions = new Map(mod.functions.map((fn) => [fn.name, fn]));
|
||||
const classes = new Map((mod.classes ?? []).map((info) => [info.name, info]));
|
||||
|
||||
@@ -81,6 +81,7 @@ export interface EarlyExecutableNativeFeatures {
|
||||
dynForAwait?: boolean;
|
||||
eventsOnceSignal?: boolean;
|
||||
mime?: boolean;
|
||||
abortSignal?: boolean;
|
||||
utilTiming?: boolean;
|
||||
dynBuffer?: boolean;
|
||||
builtinValues?: boolean;
|
||||
|
||||
@@ -1500,7 +1500,7 @@ export function lowerStaticFetchCompanionCall(
|
||||
: dynUndefinedExpr(loc),
|
||||
(first) => ({
|
||||
kind: "libCall",
|
||||
fn: "fetch.abortTimeout",
|
||||
fn: "abort.timeout",
|
||||
args: [first],
|
||||
type: DYN,
|
||||
loc,
|
||||
@@ -1510,7 +1510,7 @@ export function lowerStaticFetchCompanionCall(
|
||||
if (call.arguments.nodes.length === 0) {
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "fetch.abortNow",
|
||||
fn: "abort.now",
|
||||
args: [dynUndefinedExpr(loc)],
|
||||
type: DYN,
|
||||
loc,
|
||||
@@ -1523,7 +1523,7 @@ export function lowerStaticFetchCompanionCall(
|
||||
lowerLiveWebValue(lowerer, call.arguments.nodes[0]!),
|
||||
(first) => ({
|
||||
kind: "libCall",
|
||||
fn: "fetch.abortNow",
|
||||
fn: "abort.now",
|
||||
args: [first],
|
||||
type: DYN,
|
||||
loc,
|
||||
@@ -1541,7 +1541,7 @@ export function lowerStaticFetchCompanionCall(
|
||||
: dynUndefinedExpr(loc),
|
||||
(first) => ({
|
||||
kind: "libCall",
|
||||
fn: "fetch.abortAny",
|
||||
fn: "abort.any",
|
||||
args: [first],
|
||||
type: DYN,
|
||||
loc,
|
||||
@@ -1886,7 +1886,7 @@ export function lowerAbortControllerNew(lowerer: Lowerer, expr: ts.NewExpression
|
||||
|
||||
const result: IrExpr = {
|
||||
kind: "libCall",
|
||||
fn: "fetch.abortControllerNew",
|
||||
fn: "abort.controllerNew",
|
||||
args: [],
|
||||
type: DYN,
|
||||
loc,
|
||||
|
||||
@@ -5176,7 +5176,7 @@ function lowerPipelineStages(
|
||||
{
|
||||
kind: "varDecl",
|
||||
localId: ac.id,
|
||||
init: { kind: "libCall", fn: "fetch.abortControllerNew", args: [], type: DYN, loc },
|
||||
init: { kind: "libCall", fn: "abort.controllerNew", args: [], type: DYN, loc },
|
||||
loc,
|
||||
},
|
||||
],
|
||||
@@ -5502,7 +5502,7 @@ function duplexFromBody(lowerer: Lowerer, node: ts.Expression, loc: SrcLoc): IrE
|
||||
{
|
||||
kind: "varDecl",
|
||||
localId: acLocal.id,
|
||||
init: { kind: "libCall", fn: "fetch.abortControllerNew", args: [], type: DYN, loc },
|
||||
init: { kind: "libCall", fn: "abort.controllerNew", args: [], type: DYN, loc },
|
||||
loc,
|
||||
},
|
||||
];
|
||||
@@ -5954,7 +5954,7 @@ export function operatorSignalOptions(lowerer: Lowerer, options: IrExpr, loc: Sr
|
||||
type: DYN,
|
||||
loc,
|
||||
};
|
||||
const signal: IrExpr = { kind: "libCall", fn: "fetch.abortAny", args: [list], type: DYN, loc };
|
||||
const signal: IrExpr = { kind: "libCall", fn: "abort.any", args: [list], type: DYN, loc };
|
||||
return { kind: "libCall", fn: "readable.opSignalOpts", args: [signal], type: DYN, loc };
|
||||
}
|
||||
|
||||
@@ -5978,7 +5978,7 @@ export function reduceSignalSetup(
|
||||
{
|
||||
kind: "varDecl",
|
||||
localId: acLocal.id,
|
||||
init: { kind: "libCall", fn: "fetch.abortControllerNew", args: [], type: DYN, loc },
|
||||
init: { kind: "libCall", fn: "abort.controllerNew", args: [], type: DYN, loc },
|
||||
loc,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -702,6 +702,7 @@ async function compileExecutableNative(
|
||||
...(features.eventsOnceSignal ? { eventsOnceSignal: true } : {}),
|
||||
...(features.mime ? { mime: true } : {}),
|
||||
...(features.largePages ? { largePages: true } : {}),
|
||||
...(features.abortSignal ? { abortSignal: true } : {}),
|
||||
...(features.utilTiming ? { utilTiming: true } : {}),
|
||||
...(features.dynBuffer ? { dynBuffer: true } : {}),
|
||||
...(features.builtinValues ? { builtinValues: true } : {}),
|
||||
|
||||
@@ -151,8 +151,6 @@ export const MAY_THROW_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"fetch.headersNew",
|
||||
"fetch.responseNew",
|
||||
"fetch.responseJsonStatic",
|
||||
"fetch.abortTimeout",
|
||||
"fetch.abortAny",
|
||||
"abort.timeout",
|
||||
"abort.any",
|
||||
"event.new",
|
||||
|
||||
@@ -159,10 +159,6 @@ function buildLibFnSignatures() {
|
||||
"fetch.responseJson": { argTypes: [DYN], result: { kind: "promise", inner: DYN } },
|
||||
"fetch.responseText": { argTypes: [DYN], result: { kind: "promise", inner: STRING } },
|
||||
"fetch.responseBytes": { argTypes: [DYN], result: { kind: "promise", inner: BYTES_U8 } },
|
||||
"fetch.abortControllerNew": { argTypes: [], result: DYN },
|
||||
"fetch.abortTimeout": { argTypes: [DYN], result: DYN },
|
||||
"fetch.abortNow": { argTypes: [DYN], result: DYN },
|
||||
"fetch.abortAny": { argTypes: [DYN], result: DYN },
|
||||
"fetch.streamNew": { argTypes: [DYN], result: DYN },
|
||||
"fetch.transformNew": { argTypes: [], result: DYN },
|
||||
// Program-dependent iterable: typed arrays/bytes/string stay intact so
|
||||
|
||||
@@ -1033,6 +1033,8 @@ const RUNTIME_FEATURE_RULES = {
|
||||
}),
|
||||
/** util.MIMEType and util.MIMEParams (scr_mime.c). */
|
||||
mime: rule({ prefixes: ["mime."] }),
|
||||
/** AbortController and AbortSignal (scr_abort.c, or the fetch unit). */
|
||||
abortSignal: rule({ prefixes: ["abort."] }),
|
||||
/** util.debounce and util.throttle (scr_util_timing.c). */
|
||||
utilTiming: rule({ fns: ["util.debounce", "util.throttle"] }),
|
||||
/** events.once(emitter, name, { signal }) (scr_events_once_signal.c). */
|
||||
@@ -1254,6 +1256,7 @@ function noRuntimeFeatures(): RuntimeFeatures {
|
||||
inspect: false,
|
||||
legacyTextDecoder: false,
|
||||
mime: false,
|
||||
abortSignal: false,
|
||||
utilTiming: false,
|
||||
mockTimers: false,
|
||||
net: false,
|
||||
|
||||
@@ -23,6 +23,7 @@ const features = {
|
||||
eventsOnceSignal: false,
|
||||
mime: false,
|
||||
largePages: false,
|
||||
abortSignal: false,
|
||||
utilTiming: false,
|
||||
dynBuffer: false,
|
||||
builtinValues: false,
|
||||
@@ -74,6 +75,7 @@ const features = {
|
||||
webcrypto: false,
|
||||
perfLoop: false,
|
||||
nativeFetch: false,
|
||||
abortSignalOnly: false,
|
||||
netEffective: false,
|
||||
httpEffective: false,
|
||||
tlsEffective: false,
|
||||
|
||||
@@ -80,7 +80,7 @@ export const OPTIONAL_RUNTIME_SOURCES = [
|
||||
// programs whose IR has dynamic-tier operations.
|
||||
["scr_js_value.c", "dynTier"],
|
||||
["scr_js_object.c", "dynTier"],
|
||||
["scr_event_target.c", any("eventTarget", "nativeFetch")],
|
||||
["scr_event_target.c", any("eventTarget", "nativeFetch", "abortSignal")],
|
||||
["scr_async_dyn.c", any("dynAsync", "dynInvoke", "dc", "fileHandle", "nativeFetch")],
|
||||
// The crypto global's object extends scr_async_dyn.c's crypto object.
|
||||
["scr_webcrypto_object.c", all("webcrypto", "dynAsync")],
|
||||
@@ -184,6 +184,7 @@ export const OPTIONAL_RUNTIME_SOURCES = [
|
||||
["scr_tls_x509.c", all("tlsEffective", "x509")],
|
||||
// WebCrypto's AES ciphers run over the cipher feature's mbedTLS archive.
|
||||
["scr_webcrypto_aes.c", all("cipher", "webcrypto")],
|
||||
["scr_abort.c", "abortSignalOnly"],
|
||||
["scr_fetch.c", "nativeFetch"],
|
||||
["scr_stream_from_web.c", all("stream", "nativeFetch")],
|
||||
];
|
||||
|
||||
@@ -8,6 +8,7 @@ process.env.SCRIPTC_RUNTIME_PACK_CONFIG = JSON.stringify({
|
||||
runtimeDefines: ["_GNU_SOURCE", "_WASI_EMULATED_SIGNAL", "_WASI_EMULATED_PROCESS_CLOCKS"],
|
||||
omitRuntimeSources: ["scr_child.c"],
|
||||
omitOptionalSources: [
|
||||
"scr_abort.c",
|
||||
"scr_event_target.c",
|
||||
"scr_loop_kqueue.c",
|
||||
"scr_loop_epoll.c",
|
||||
|
||||
Reference in New Issue
Block a user