@scriptc/compiler 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +200 -51
- package/dist/backend/c/async.d.ts +25 -0
- package/dist/backend/c/async.js +167 -0
- package/dist/backend/c/async.js.map +1 -1
- package/dist/backend/c/c-emitter.d.ts +10 -0
- package/dist/backend/c/c-emitter.js +14 -2
- package/dist/backend/c/c-emitter.js.map +1 -1
- package/dist/backend/c/exprs.js +293 -51
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/c/types.js +2 -0
- package/dist/backend/c/types.js.map +1 -1
- package/dist/backend/link-plan.d.ts +2 -1
- package/dist/backend/link-plan.js +2 -1
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/llvm/emitter.js +20 -6
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.d.ts +1 -1
- package/dist/backend/llvm/expr-bytes.js +8 -8
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-callbacks.d.ts +9 -0
- package/dist/backend/llvm/expr-callbacks.js +125 -0
- package/dist/backend/llvm/expr-callbacks.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +14 -2
- package/dist/backend/llvm/expr-dispatch.js +1 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +1 -1
- package/dist/backend/llvm/expr-primitives.js +30 -9
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.d.ts +1 -0
- package/dist/backend/llvm/expr-primitives.test.js +38 -0
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -0
- package/dist/backend/llvm/expr-stream-callbacks.d.ts +3 -0
- package/dist/backend/llvm/expr-stream-callbacks.js +69 -0
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +68 -10
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-network.js +18 -2
- package/dist/backend/llvm/lib-network.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +156 -6
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +36 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/lib-stream.js +23 -0
- package/dist/backend/llvm/lib-stream.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +3 -0
- package/dist/backend/native-toolchain.js +3 -1
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +28 -10
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/targets.d.ts +4 -0
- package/dist/backend/targets.js +13 -0
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +8 -1
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/coverage/surface-manifest.js +8 -0
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +1 -1
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +2 -0
- package/dist/executable/executable-cache.js +3 -0
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/frontend/fork-target.d.ts +17 -0
- package/dist/frontend/fork-target.js +256 -0
- package/dist/frontend/fork-target.js.map +1 -0
- package/dist/frontend/lowering/abstract-equality.d.ts +6 -0
- package/dist/frontend/lowering/abstract-equality.js +250 -0
- package/dist/frontend/lowering/abstract-equality.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.js +71 -2
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +50 -23
- package/dist/frontend/lowering/lower-builtins.js +939 -106
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +29 -21
- package/dist/frontend/lowering/lower-calls.js +573 -121
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +39 -8
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +150 -0
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +212 -21
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +135 -83
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.d.ts +4 -0
- package/dist/frontend/lowering/lower-inspect.js +1 -1
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +6 -3
- package/dist/frontend/lowering/lower-island.js +165 -6
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +6 -0
- package/dist/frontend/lowering/lower-modules.js +184 -38
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.js +7 -0
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +1 -1
- package/dist/frontend/lowering/lower-server.js +130 -27
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +118 -15
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.d.ts +3 -0
- package/dist/frontend/lowering/lower-stream.js +7 -0
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +86 -9
- package/dist/frontend/lowering/lowerer.js +412 -72
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +26 -0
- package/dist/frontend/lowering/surfaces.js +140 -77
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-declarations.d.ts +4 -0
- package/dist/frontend/npm-static-declarations.js +56 -0
- package/dist/frontend/npm-static-declarations.js.map +1 -1
- package/dist/frontend/npm-static-declarations.test.js +23 -2
- package/dist/frontend/npm-static-declarations.test.js.map +1 -1
- package/dist/frontend/npm-static.js +6 -5
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +15 -3
- package/dist/frontend/npm.js +80 -8
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.js +76 -9
- package/dist/frontend/npm.test.js.map +1 -1
- package/dist/frontend/program.js +92 -10
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.d.ts +6 -0
- package/dist/frontend/resolve.js +7 -4
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +5 -0
- package/dist/frontend/type-mapper.js +64 -24
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +24 -3
- package/dist/index.js.map +1 -1
- package/dist/ir/ir.d.ts +93 -47
- package/dist/ir/ir.js +63 -4
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +14 -1
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/validate.js +165 -13
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/library-cache.test.js +5 -5
- package/dist/library/library-cache.test.js.map +1 -1
- package/package.json +17 -17
- package/surface-manifest.json +132 -38
|
@@ -7,14 +7,14 @@ import { builtinModules } from "node:module";
|
|
|
7
7
|
import { dirname, resolve } from "node:path";
|
|
8
8
|
import * as ts from "../ts7/adapter.js";
|
|
9
9
|
import { PoisonError, dynUndefinedExpr, ladderFenceExpr, nodeThrowExpr, own } from "./lowerer.js";
|
|
10
|
-
import { canonicalBuiltinModule, isJsSourceFile, isNodeEsmFile, locOf, requireSpecOf } from "../program.js";
|
|
10
|
+
import { canonicalBuiltinModule, isJsSourceFile, isNodeEsmFile, locOf, npmStaticDepSf7, requireSpecOf, resolveImport } from "../program.js";
|
|
11
11
|
import { isRelativeSpecifier } from "../workspace-registry.js";
|
|
12
12
|
import { probeNodeRequireRefusal } from "../npm.js";
|
|
13
13
|
import { isNpmStaticPackage } from "../npm-static.js";
|
|
14
14
|
import { trackedReadFile } from "../input-tracker.js";
|
|
15
15
|
import { requireResolvePathsRuntime, resolveImportMetaRuntime, resolveRequireRuntime } from "../runtime-resolve.js";
|
|
16
16
|
import { invalidJsonModuleDiag, requiresDynamicImportDiag } from "../../diagnostics/diagnostic.js";
|
|
17
|
-
import { builtinModuleFnOf, FS_READDIR_DOCUMENTED_OPTIONS, FS_WATCH_DOCUMENTED_OPTIONS, FS_WRITE_FILE_DOCUMENTED_OPTIONS, QS_PARSE_DOCUMENTED_OPTIONS, QS_STRINGIFY_DOCUMENTED_OPTIONS, READLINE_DOCUMENTED_OPTIONS, builtinConstLit, fenceOrDropOptionKey, isChildSurfaceMember, } from "./surfaces.js";
|
|
17
|
+
import { builtinModuleFnOf, FS_READDIR_DOCUMENTED_OPTIONS, FS_WATCH_DOCUMENTED_OPTIONS, FS_WRITE_FILE_DOCUMENTED_OPTIONS, QS_PARSE_DOCUMENTED_OPTIONS, QS_STRINGIFY_DOCUMENTED_OPTIONS, READLINE_DOCUMENTED_OPTIONS, builtinConstLit, fenceOrDropOptionKey, isChildSurfaceMember, NODE_BUILTIN_MODULES_V24, } from "./surfaces.js";
|
|
18
18
|
import { lowerAbsenceProbe } from "./lower-exprs.js";
|
|
19
19
|
import { conditionalSpreadOf, lowerDynObjectLiteral } from "./expressions/object-literals.js";
|
|
20
20
|
import { isSafeToDiscard } from "./expressions/evaluation-safety.js";
|
|
@@ -24,8 +24,11 @@ import { CRYPTO_CIPHERS, CRYPTO_CONSTANTS, CRYPTO_CURVES, CRYPTO_HASHES } from "
|
|
|
24
24
|
import { generatorMeta, timerStyleCallback } from "./lower-calls.js";
|
|
25
25
|
import { registerHttpClientFnBinding, voidizedCallback } from "./lower-server.js";
|
|
26
26
|
import { pairsSnapshotHelper } from "./pairs-snapshot.js";
|
|
27
|
+
import { bufEncoding } from "./containers/bytes.js";
|
|
27
28
|
import { BOOL, BYTES_U8, CHILD_T, CHILDSTREAM_T, CHILDWRITER_T, CRYPTOHASH_T, CRYPTOHMAC_T, DYN, F64, FILEHANDLE_T, FSWATCHER_T, PROCSTREAM_T, JSVAL, NULL_T, SEARCH_PARAMS_T, SPAWNRES_T, STRING, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, funcOf, isUnitType, typeEquals, typeKey } from "../../ir/ir.js";
|
|
28
29
|
import { boolLit, countedFor, numLit, strLit, varRef } from "../../ir/build.js";
|
|
30
|
+
import { staticForkModulePath } from "../fork-target.js";
|
|
31
|
+
import { tsgoPath } from "../dts-paths.js";
|
|
29
32
|
function optionalStringTags(lowerer, type) {
|
|
30
33
|
if (type.kind !== "union")
|
|
31
34
|
return null;
|
|
@@ -439,6 +442,108 @@ export function createRequireNamespaceDecl(lowerer, nameNode, init) {
|
|
|
439
442
|
const cr = createRequireSpecOf(lowerer, call);
|
|
440
443
|
return cr !== null && cr.spec !== null && canonicalBuiltinModule(cr.spec) !== null;
|
|
441
444
|
}
|
|
445
|
+
/** A statically compiled program module reached through a canonical
|
|
446
|
+
* createRequire binding. This is the ESM twin of an ordinary CommonJS
|
|
447
|
+
* require edge: the same project resolver, require-condition npm-static
|
|
448
|
+
* entry, module initializer, and export registrations own the target. */
|
|
449
|
+
export function createRequireProgramModuleOf(lowerer, expr) {
|
|
450
|
+
if (expr === undefined)
|
|
451
|
+
return null;
|
|
452
|
+
const call = stripTypeCasts(expr);
|
|
453
|
+
if (!ts.isCallExpression(call))
|
|
454
|
+
return null;
|
|
455
|
+
const cr = createRequireSpecOf(lowerer, call);
|
|
456
|
+
if (cr === null || cr.spec === null || canonicalBuiltinModule(cr.spec) !== null)
|
|
457
|
+
return null;
|
|
458
|
+
const dep = resolveImport(lowerer.program, cr.baseFile, cr.spec) ??
|
|
459
|
+
npmStaticDepSf7(lowerer.program, cr.baseFile, cr.spec);
|
|
460
|
+
if (dep === null || dep.fileName.endsWith(".json"))
|
|
461
|
+
return null;
|
|
462
|
+
return { spec: cr.spec, baseFile: cr.baseFile, dep };
|
|
463
|
+
}
|
|
464
|
+
/** True for a const identifier whose initializer is a createRequire call
|
|
465
|
+
* reaching a compiled program module. The binding is namespace/value
|
|
466
|
+
* alias plumbing; its declaration emits only the dependency's run-once
|
|
467
|
+
* initializer at the source position. */
|
|
468
|
+
export function createRequireProgramModuleDecl(lowerer, nameNode, init) {
|
|
469
|
+
if (createRequireProgramModuleOf(lowerer, init) === null)
|
|
470
|
+
return false;
|
|
471
|
+
if (ts.isIdentifier(nameNode))
|
|
472
|
+
return true;
|
|
473
|
+
if (!ts.isObjectBindingPattern(nameNode))
|
|
474
|
+
return false;
|
|
475
|
+
return nameNode.elements.every((element) => element.name !== undefined && element.dotDotDotToken === undefined && element.initializer === undefined &&
|
|
476
|
+
ts.isIdentifier(element.name) &&
|
|
477
|
+
(element.propertyName === undefined || ts.isIdentifier(element.propertyName) || ts.isStringLiteralLike(element.propertyName)));
|
|
478
|
+
}
|
|
479
|
+
/** node:module's two compiler-only calls. isBuiltin compares one evaluated
|
|
480
|
+
* string against the pinned Node 24 list (bare builtins also accept their
|
|
481
|
+
* node: spelling; prefix-only entries do not gain a bare alias).
|
|
482
|
+
* syncBuiltinESMExports is observably a no-op inside the static surface:
|
|
483
|
+
* builtin exports cannot be mutated, so its only supported behavior is
|
|
484
|
+
* evaluating no arguments and returning undefined. */
|
|
485
|
+
export function lowerNodeModuleCall(lowerer, expr, bi, loc) {
|
|
486
|
+
if (bi.module !== "module")
|
|
487
|
+
return null;
|
|
488
|
+
if (bi.member === "syncBuiltinESMExports") {
|
|
489
|
+
if (expr.arguments.length !== 0 || expr.arguments.some(ts.isSpreadElement)) {
|
|
490
|
+
lowerer.noLowering(`module.syncBuiltinESMExports with ${expr.arguments.length} arguments`, expr, "syncBuiltinESMExports() takes no arguments");
|
|
491
|
+
}
|
|
492
|
+
if (!ts.isExpressionStatement(expr.parent)) {
|
|
493
|
+
lowerer.noLowering("module.syncBuiltinESMExports used as a value", expr, "call syncBuiltinESMExports() as its own statement; the supported static effect is a no-op");
|
|
494
|
+
}
|
|
495
|
+
return { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc };
|
|
496
|
+
}
|
|
497
|
+
if (bi.member !== "isBuiltin")
|
|
498
|
+
return null;
|
|
499
|
+
if (expr.arguments.length !== 1 || expr.arguments.some(ts.isSpreadElement)) {
|
|
500
|
+
lowerer.noLowering(`module.isBuiltin with ${expr.arguments.length} arguments`, expr, "isBuiltin(moduleName) takes one string");
|
|
501
|
+
}
|
|
502
|
+
const argumentNode = expr.arguments[0];
|
|
503
|
+
const argument = lowerer.lowerExpr(argumentNode);
|
|
504
|
+
if (argument.type.kind !== "string") {
|
|
505
|
+
if (argument.type.kind === "dyn" || argument.type.kind === "jsval" || argument.type.kind === "union") {
|
|
506
|
+
lowerer.noLowering("module.isBuiltin with a runtime-polymorphic argument", argumentNode, "narrow the module name to a string first; statically non-string values return false");
|
|
507
|
+
}
|
|
508
|
+
return {
|
|
509
|
+
kind: "seqExpr",
|
|
510
|
+
stmts: [{ kind: "exprStmt", expr: argument, loc: locOf(argumentNode) }],
|
|
511
|
+
result: boolLit(false, loc),
|
|
512
|
+
type: BOOL,
|
|
513
|
+
loc,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
const slot = lowerer.declareHiddenLocal("%builtinName", STRING);
|
|
517
|
+
const ref = () => varRef(slot.id, STRING, loc);
|
|
518
|
+
const accepted = NODE_BUILTIN_MODULES_V24.flatMap((name) => name.startsWith("node:") ? [name] : [name, `node:${name}`]);
|
|
519
|
+
let staticArgument = argumentNode;
|
|
520
|
+
while (ts.isParenthesizedExpression(staticArgument) || ts.isAsExpression(staticArgument) ||
|
|
521
|
+
ts.isTypeAssertion(staticArgument) || ts.isNonNullExpression(staticArgument)) {
|
|
522
|
+
staticArgument = staticArgument.expression;
|
|
523
|
+
}
|
|
524
|
+
if (ts.isStringLiteralLike(staticArgument)) {
|
|
525
|
+
return boolLit(accepted.includes(staticArgument.text), loc);
|
|
526
|
+
}
|
|
527
|
+
let result = boolLit(false, loc);
|
|
528
|
+
for (let i = accepted.length - 1; i >= 0; i--) {
|
|
529
|
+
const equal = {
|
|
530
|
+
kind: "strEq",
|
|
531
|
+
negated: false,
|
|
532
|
+
left: ref(),
|
|
533
|
+
right: strLit(accepted[i], loc),
|
|
534
|
+
type: BOOL,
|
|
535
|
+
loc,
|
|
536
|
+
};
|
|
537
|
+
result = { kind: "logical", op: "||", left: equal, right: result, type: BOOL, loc };
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
kind: "seqExpr",
|
|
541
|
+
stmts: [{ kind: "varDecl", localId: slot.id, init: argument, loc }],
|
|
542
|
+
result,
|
|
543
|
+
type: BOOL,
|
|
544
|
+
loc,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
442
547
|
/** `require("spec")` through a createRequire binding — the erasure per
|
|
443
548
|
* target. Builtins are reached here only OUTSIDE the const-namespace-
|
|
444
549
|
* binding shape (that declaration erases; member uses resolve through
|
|
@@ -465,12 +570,16 @@ export function lowerCreateRequireCall(lowerer, call, loc) {
|
|
|
465
570
|
if (canonicalBuiltinModule(spec) !== null) {
|
|
466
571
|
lowerer.unsupported("SC1090", call, `module namespace objects as values (bind it first: const m = require("${spec}"), then access members through the binding)`);
|
|
467
572
|
}
|
|
573
|
+
const programModule = createRequireProgramModuleOf(lowerer, call);
|
|
574
|
+
if (programModule !== null) {
|
|
575
|
+
lowerer.noLowering(`createRequire's module namespace value for '${spec}'`, call, `bind it once (const m = require(${JSON.stringify(spec)})) and access statically-known members through that binding`);
|
|
576
|
+
}
|
|
468
577
|
if (spec.startsWith("#")) {
|
|
469
578
|
lowerer.noLowering(`createRequire's require of the '${spec}' project import`, call, "imports-field specifiers have no require lowering yet — import the target statically");
|
|
470
579
|
}
|
|
471
580
|
if (isRelativeSpecifier(spec) || spec.startsWith("/")) {
|
|
472
581
|
if (!spec.endsWith(".json")) {
|
|
473
|
-
lowerer.noLowering(`createRequire's require of '${spec}'`, call, "relative
|
|
582
|
+
lowerer.noLowering(`createRequire's require of '${spec}'`, call, "relative program modules lower when they resolve into the compiled graph; relative .json documents bake at build time");
|
|
474
583
|
}
|
|
475
584
|
const abs = spec.startsWith("/")
|
|
476
585
|
? spec
|
|
@@ -891,6 +1000,86 @@ function optionMember(p) {
|
|
|
891
1000
|
}
|
|
892
1001
|
return null;
|
|
893
1002
|
}
|
|
1003
|
+
/** Normalize fs.readSync/fs.writeSync's shorthand and inline-options buffer
|
|
1004
|
+
* forms into the existing fixed-width descriptor IR. fd and buffer bind
|
|
1005
|
+
* first, option values bind in object-literal order, and only then do the
|
|
1006
|
+
* default window expressions read the bound buffer length. */
|
|
1007
|
+
function lowerFsSyncBufferWindow(lowerer, expr, loc, fn, source, supported) {
|
|
1008
|
+
const fdValue = lowerer.lowerExprExpecting(expr.arguments[0], F64);
|
|
1009
|
+
const bufferValue = lowerer.lowerExprExpecting(expr.arguments[1], BYTES_U8);
|
|
1010
|
+
const fdLocal = lowerer.declareHiddenLocal("%fsSyncFd", F64);
|
|
1011
|
+
const bufferLocal = lowerer.declareHiddenLocal("%fsSyncBuffer", BYTES_U8);
|
|
1012
|
+
const stmts = [
|
|
1013
|
+
{ kind: "varDecl", localId: fdLocal.id, init: fdValue, loc: fdValue.loc },
|
|
1014
|
+
{ kind: "varDecl", localId: bufferLocal.id, init: bufferValue, loc: bufferValue.loc },
|
|
1015
|
+
];
|
|
1016
|
+
const fd = () => varRef(fdLocal.id, fdLocal.type, loc);
|
|
1017
|
+
const buffer = () => varRef(bufferLocal.id, bufferLocal.type, loc);
|
|
1018
|
+
const number = (value) => numLit(value, loc);
|
|
1019
|
+
const stageNumber = (name, node) => {
|
|
1020
|
+
const mapped = lowerer.mapTypeOf(lowerer.typeOf(node));
|
|
1021
|
+
if (mapped?.kind !== "f64") {
|
|
1022
|
+
lowerer.noLowering(`${fn} with a '${mapped ? lowerer.fmt(mapped) : lowerer.checker.typeToString(lowerer.typeOf(node))}' ${name}`, node, supported);
|
|
1023
|
+
}
|
|
1024
|
+
const value = lowerer.lowerExprExpecting(node, F64);
|
|
1025
|
+
const local = lowerer.declareHiddenLocal(`%fsSync${name}`, F64);
|
|
1026
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: value, loc: value.loc });
|
|
1027
|
+
return varRef(local.id, local.type, loc);
|
|
1028
|
+
};
|
|
1029
|
+
const isNull = (node) => {
|
|
1030
|
+
let value = node;
|
|
1031
|
+
while (ts.isParenthesizedExpression(value))
|
|
1032
|
+
value = value.expression;
|
|
1033
|
+
return value.kind === ts.SyntaxKind.NullKeyword;
|
|
1034
|
+
};
|
|
1035
|
+
let offset = null;
|
|
1036
|
+
let length = null;
|
|
1037
|
+
let position = null;
|
|
1038
|
+
if (source.kind === "offset") {
|
|
1039
|
+
offset = stageNumber("offset", source.node);
|
|
1040
|
+
}
|
|
1041
|
+
else if (source.node !== undefined && !isNull(source.node)) {
|
|
1042
|
+
if (!ts.isObjectLiteralExpression(source.node)) {
|
|
1043
|
+
lowerer.noLowering(`${fn} with a non-literal options argument`, source.node, supported);
|
|
1044
|
+
}
|
|
1045
|
+
for (const property of source.node.properties) {
|
|
1046
|
+
const member = optionMember(property);
|
|
1047
|
+
if (member === null) {
|
|
1048
|
+
lowerer.noLowering(`${fn} with a non-plain options member`, property, supported);
|
|
1049
|
+
}
|
|
1050
|
+
if (member.name === "offset") {
|
|
1051
|
+
offset = stageNumber("offset", member.value);
|
|
1052
|
+
}
|
|
1053
|
+
else if (member.name === "length") {
|
|
1054
|
+
length = stageNumber("length", member.value);
|
|
1055
|
+
}
|
|
1056
|
+
else if (member.name === "position") {
|
|
1057
|
+
position = isNull(member.value) ? null : stageNumber("position", member.value);
|
|
1058
|
+
}
|
|
1059
|
+
else {
|
|
1060
|
+
const effect = lowerer.lowerExpr(member.value);
|
|
1061
|
+
stmts.push({ kind: "exprStmt", expr: effect, loc: effect.loc });
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
const normalizedOffset = offset ?? number(0);
|
|
1066
|
+
const normalizedLength = length ?? {
|
|
1067
|
+
kind: "bin",
|
|
1068
|
+
op: "-",
|
|
1069
|
+
left: { kind: "bytesIntrinsic", method: "length", receiver: buffer(), args: [], type: F64, loc },
|
|
1070
|
+
right: normalizedOffset,
|
|
1071
|
+
type: F64,
|
|
1072
|
+
loc,
|
|
1073
|
+
};
|
|
1074
|
+
const result = {
|
|
1075
|
+
kind: "libCall",
|
|
1076
|
+
fn,
|
|
1077
|
+
args: [fd(), buffer(), normalizedOffset, normalizedLength, position ?? number(-1)],
|
|
1078
|
+
type: F64,
|
|
1079
|
+
loc,
|
|
1080
|
+
};
|
|
1081
|
+
return { kind: "seqExpr", stmts, result, type: F64, loc };
|
|
1082
|
+
}
|
|
894
1083
|
/** One builtin-module function call → its libCall. Completes the call to
|
|
895
1084
|
* the table's exact shape: variadicPack functions (path.join/resolve)
|
|
896
1085
|
* accept any arity — or ONE spread of a string[] — and pack the
|
|
@@ -1061,14 +1250,152 @@ export function lowerFsLadderCall(lowerer, expr, bi, loc) {
|
|
|
1061
1250
|
return null;
|
|
1062
1251
|
}
|
|
1063
1252
|
}
|
|
1253
|
+
const ZLIB_SYNC_FNS = {
|
|
1254
|
+
deflateSync: "zlib.deflateSync",
|
|
1255
|
+
inflateSync: "zlib.inflateSync",
|
|
1256
|
+
deflateRawSync: "zlib.deflateRawSync",
|
|
1257
|
+
inflateRawSync: "zlib.inflateRawSync",
|
|
1258
|
+
gzipSync: "zlib.gzipSync",
|
|
1259
|
+
gunzipSync: "zlib.gunzipSync",
|
|
1260
|
+
unzipSync: "zlib.unzipSync",
|
|
1261
|
+
};
|
|
1262
|
+
const ZLIB_CALLBACK_FNS = {
|
|
1263
|
+
deflate: "zlib.deflateCb",
|
|
1264
|
+
inflate: "zlib.inflateCb",
|
|
1265
|
+
deflateRaw: "zlib.deflateRawCb",
|
|
1266
|
+
inflateRaw: "zlib.inflateRawCb",
|
|
1267
|
+
gzip: "zlib.gzipCb",
|
|
1268
|
+
gunzip: "zlib.gunzipCb",
|
|
1269
|
+
unzip: "zlib.unzipCb",
|
|
1270
|
+
};
|
|
1271
|
+
function zlibInputBytes(lowerer, node, loc) {
|
|
1272
|
+
const value = lowerer.lowerExpr(node);
|
|
1273
|
+
if (value.type.kind === "bytes" && value.type.elem === "u8")
|
|
1274
|
+
return value;
|
|
1275
|
+
if (value.type.kind === "string") {
|
|
1276
|
+
return {
|
|
1277
|
+
kind: "libCall",
|
|
1278
|
+
fn: "buffer.fromStr",
|
|
1279
|
+
args: [value, { kind: "strLit", value: "utf8", type: STRING, loc }],
|
|
1280
|
+
type: BYTES_U8,
|
|
1281
|
+
loc,
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
lowerer.noLowering(`zlib byte input of '${lowerer.fmt(value.type)}' values`, node, "string and Buffer/Uint8Array values are supported");
|
|
1285
|
+
}
|
|
1286
|
+
function lowerZlibModuleCall(lowerer, expr, bi, loc) {
|
|
1287
|
+
if (expr.arguments.some(ts.isSpreadElement)) {
|
|
1288
|
+
lowerer.noLowering(`zlib.${bi.member} with spread arguments`, expr);
|
|
1289
|
+
}
|
|
1290
|
+
const syncFn = ZLIB_SYNC_FNS[bi.member];
|
|
1291
|
+
if (syncFn !== undefined) {
|
|
1292
|
+
if (expr.arguments.length !== 1) {
|
|
1293
|
+
const site = expr.arguments[1] ?? expr;
|
|
1294
|
+
lowerer.noLowering(`${bi.member} with explicit options`, site, `${bi.member}(data) with Node's default options is supported`);
|
|
1295
|
+
}
|
|
1296
|
+
return {
|
|
1297
|
+
kind: "libCall",
|
|
1298
|
+
fn: syncFn,
|
|
1299
|
+
args: [zlibInputBytes(lowerer, expr.arguments[0], loc)],
|
|
1300
|
+
type: BYTES_U8,
|
|
1301
|
+
loc,
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
const callbackFn = ZLIB_CALLBACK_FNS[bi.member];
|
|
1305
|
+
if (callbackFn !== undefined) {
|
|
1306
|
+
if (expr.arguments.length !== 2) {
|
|
1307
|
+
const site = expr.arguments.length >= 3 ? expr.arguments[1] : expr;
|
|
1308
|
+
lowerer.noLowering(`${bi.member} with ${expr.arguments.length} arguments`, site, `${bi.member}(data, callback) with Node's default options is supported; explicit options are not yet lowered`);
|
|
1309
|
+
}
|
|
1310
|
+
return {
|
|
1311
|
+
kind: "libCall",
|
|
1312
|
+
fn: callbackFn,
|
|
1313
|
+
args: [
|
|
1314
|
+
zlibInputBytes(lowerer, expr.arguments[0], loc),
|
|
1315
|
+
errorFirstBytesCallback(lowerer, expr.arguments[1], "zlib"),
|
|
1316
|
+
],
|
|
1317
|
+
type: VOID,
|
|
1318
|
+
loc,
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
if (bi.member === "crc32") {
|
|
1322
|
+
if (expr.arguments.length < 1 || expr.arguments.length > 2) {
|
|
1323
|
+
lowerer.noLowering(`zlib.crc32 with ${expr.arguments.length} arguments`, expr, "crc32(data[, initialValue]) is supported");
|
|
1324
|
+
}
|
|
1325
|
+
return {
|
|
1326
|
+
kind: "libCall",
|
|
1327
|
+
fn: "zlib.crc32",
|
|
1328
|
+
args: [
|
|
1329
|
+
zlibInputBytes(lowerer, expr.arguments[0], loc),
|
|
1330
|
+
expr.arguments[1]
|
|
1331
|
+
? lowerer.lowerExprExpecting(expr.arguments[1], F64)
|
|
1332
|
+
: { kind: "numLit", value: 0, type: F64, loc },
|
|
1333
|
+
],
|
|
1334
|
+
type: F64,
|
|
1335
|
+
loc,
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
throw new InternalCompilerError(`unhandled lowered zlib member ${bi.member}`);
|
|
1339
|
+
}
|
|
1064
1340
|
export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
1065
1341
|
const name = expr.expression.getText();
|
|
1342
|
+
// Numeric open flags are interpreted symbolically at the call site. The
|
|
1343
|
+
// O_* bit values differ between Darwin and Linux, so emit a stable mask
|
|
1344
|
+
// and let the target runtime select its own native constants.
|
|
1345
|
+
if (bi.module === "fs" && bi.member === "openSync" && expr.arguments.length >= 2 &&
|
|
1346
|
+
lowerer.mapTypeOf(lowerer.typeOf(expr.arguments[1]))?.kind === "f64") {
|
|
1347
|
+
if (expr.arguments.length > 3 || expr.arguments.some(ts.isSpreadElement)) {
|
|
1348
|
+
lowerer.noLowering("openSync with numeric flags and this argument shape", expr);
|
|
1349
|
+
}
|
|
1350
|
+
const bits = {
|
|
1351
|
+
O_RDONLY: 0, O_WRONLY: 1, O_RDWR: 2, O_CREAT: 4, O_EXCL: 8,
|
|
1352
|
+
O_NOFOLLOW: 16, O_NONBLOCK: 32, O_TRUNC: 64, O_APPEND: 128,
|
|
1353
|
+
};
|
|
1354
|
+
const flagsOf = (input) => {
|
|
1355
|
+
let node = input;
|
|
1356
|
+
while (ts.isParenthesizedExpression(node) || ts.isAsExpression(node))
|
|
1357
|
+
node = node.expression;
|
|
1358
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.BarToken) {
|
|
1359
|
+
const left = flagsOf(node.left);
|
|
1360
|
+
const right = flagsOf(node.right);
|
|
1361
|
+
return left === null || right === null ? null : left | right;
|
|
1362
|
+
}
|
|
1363
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression)) {
|
|
1364
|
+
const imported = lowerer.builtinImportOf(node.expression);
|
|
1365
|
+
if (imported?.module === "fs" && imported.member === "constants") {
|
|
1366
|
+
return bits[node.name.text] ?? null;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
if (ts.isPropertyAccessExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
1370
|
+
const imported = lowerer.builtinMemberOf(node.expression);
|
|
1371
|
+
if (imported?.module === "fs" && imported.member === "constants") {
|
|
1372
|
+
return bits[node.name.text] ?? null;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return null;
|
|
1376
|
+
};
|
|
1377
|
+
const mask = flagsOf(expr.arguments[1]);
|
|
1378
|
+
if (mask === null) {
|
|
1379
|
+
lowerer.noLowering("openSync with computed numeric flags", expr.arguments[1], "use an inline bitwise OR of fs.constants.O_RDONLY/O_WRONLY/O_RDWR/O_CREAT/O_EXCL/O_NOFOLLOW/O_NONBLOCK/O_TRUNC/O_APPEND");
|
|
1380
|
+
}
|
|
1381
|
+
const path = lowerer.lowerExprExpecting(expr.arguments[0], STRING);
|
|
1382
|
+
const mode = expr.arguments[2]
|
|
1383
|
+
? lowerer.lowerExprExpecting(expr.arguments[2], F64)
|
|
1384
|
+
: { kind: "numLit", value: 0o666, type: F64, loc };
|
|
1385
|
+
return { kind: "libCall", fn: "fs.openNumericSync", args: [path,
|
|
1386
|
+
{ kind: "numLit", value: mask, type: F64, loc }, mode], type: F64, loc };
|
|
1387
|
+
}
|
|
1388
|
+
if (bi.module === "zlib")
|
|
1389
|
+
return lowerZlibModuleCall(lowerer, expr, bi, loc);
|
|
1066
1390
|
if (bi.module === "child_process" && bi.member === "spawnSync") {
|
|
1067
1391
|
return lowerer.lowerSpawnSyncCall(expr, loc);
|
|
1068
1392
|
}
|
|
1069
1393
|
if (bi.module === "child_process" && bi.member === "spawn") {
|
|
1070
1394
|
return lowerer.lowerSpawnCall(expr, loc);
|
|
1071
1395
|
}
|
|
1396
|
+
if (bi.module === "child_process" && bi.member === "fork") {
|
|
1397
|
+
return lowerForkCall(lowerer, expr, loc);
|
|
1398
|
+
}
|
|
1072
1399
|
if (bi.module === "fs" && bi.member === "watch") {
|
|
1073
1400
|
return lowerFsWatchCall(lowerer, expr, loc);
|
|
1074
1401
|
}
|
|
@@ -1148,18 +1475,29 @@ export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
|
1148
1475
|
callback = voidizedCallback(lowerer, callback, locOf(expr.arguments[2]));
|
|
1149
1476
|
return { kind: "libCall", fn: "fs.renameCb", args: [oldPath, newPath, callback], type: VOID, loc };
|
|
1150
1477
|
}
|
|
1151
|
-
// fs.readSync
|
|
1152
|
-
//
|
|
1153
|
-
//
|
|
1154
|
-
// and therefore must not advance the descriptor. Literal null is the
|
|
1155
|
-
// documented current-offset spelling; richer nullable expressions must
|
|
1156
|
-
// narrow first so no effectful evaluation is silently discarded.
|
|
1478
|
+
// fs.readSync's classic and options-object buffer forms normalize into
|
|
1479
|
+
// one fixed-width IR call. Current-offset forms use Node/libuv's -1
|
|
1480
|
+
// sentinel; a numeric position performs offset-preserving I/O.
|
|
1157
1481
|
if (bi.module === "fs" && bi.member === "readSync") {
|
|
1158
|
-
const supported = "use readSync(fd, buffer, offset, length[, position])
|
|
1159
|
-
if (
|
|
1482
|
+
const supported = "use readSync(fd, buffer), readSync(fd, buffer, { offset?, length?, position? }), or readSync(fd, buffer, offset, length[, position]); bigint positions and non-literal options objects have no lowering";
|
|
1483
|
+
if (expr.arguments.length < 2 ||
|
|
1484
|
+
expr.arguments.length > 5 ||
|
|
1160
1485
|
expr.arguments.some(ts.isSpreadElement)) {
|
|
1161
1486
|
lowerer.noLowering(`readSync with ${expr.arguments.length} argument${expr.arguments.length === 1 ? "" : "s"}`, expr, supported);
|
|
1162
1487
|
}
|
|
1488
|
+
if (expr.arguments.length === 2) {
|
|
1489
|
+
return lowerFsSyncBufferWindow(lowerer, expr, loc, "fs.readSync", { kind: "options", node: undefined }, supported);
|
|
1490
|
+
}
|
|
1491
|
+
if (expr.arguments.length === 3) {
|
|
1492
|
+
const optionsNode = expr.arguments[2];
|
|
1493
|
+
let valueNode = optionsNode;
|
|
1494
|
+
while (ts.isParenthesizedExpression(valueNode))
|
|
1495
|
+
valueNode = valueNode.expression;
|
|
1496
|
+
if (ts.isObjectLiteralExpression(valueNode) || valueNode.kind === ts.SyntaxKind.NullKeyword) {
|
|
1497
|
+
return lowerFsSyncBufferWindow(lowerer, expr, loc, "fs.readSync", { kind: "options", node: valueNode }, supported);
|
|
1498
|
+
}
|
|
1499
|
+
lowerer.noLowering("readSync with a non-literal options argument", optionsNode, supported);
|
|
1500
|
+
}
|
|
1163
1501
|
const args = [
|
|
1164
1502
|
lowerer.lowerExprExpecting(expr.arguments[0], F64),
|
|
1165
1503
|
lowerer.lowerExprExpecting(expr.arguments[1], BYTES_U8),
|
|
@@ -1183,20 +1521,18 @@ export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
|
1183
1521
|
}
|
|
1184
1522
|
return { kind: "libCall", fn: "fs.readSync", args, type: F64, loc };
|
|
1185
1523
|
}
|
|
1186
|
-
// fs.writeSync has
|
|
1187
|
-
//
|
|
1188
|
-
// (fd, string[, position[, "utf8"]]). The runtime uses -1 for
|
|
1189
|
-
//
|
|
1190
|
-
//
|
|
1191
|
-
// preserve the value here so the runtime can make that dispatch.
|
|
1524
|
+
// fs.writeSync has buffer and string families. Buffer writes accept
|
|
1525
|
+
// omitted offset/length and inline options; string writes accept
|
|
1526
|
+
// (fd, string[, position[, "utf8"]]). The runtime uses -1 for current
|
|
1527
|
+
// offset. Invalid numeric WRITE positions intentionally reach the
|
|
1528
|
+
// runtime, where Node normalizes them to the current descriptor offset.
|
|
1192
1529
|
if (bi.module === "fs" && bi.member === "writeSync") {
|
|
1193
|
-
const supported = 'use writeSync(fd, buffer, offset, length[, position]) or writeSync(fd, string[, position[, "utf8"]]); options objects, bigint positions, and other encodings have no lowering';
|
|
1530
|
+
const supported = 'use writeSync(fd, buffer[, offset[, length[, position]]]), writeSync(fd, buffer, { offset?, length?, position? }), or writeSync(fd, string[, position[, "utf8"]]); non-literal options objects, bigint positions, and other encodings have no lowering';
|
|
1194
1531
|
if (expr.arguments.some(ts.isSpreadElement) || expr.arguments.length < 2) {
|
|
1195
1532
|
lowerer.noLowering(`writeSync with ${expr.arguments.length} argument${expr.arguments.length === 1 ? "" : "s"}`, expr, supported);
|
|
1196
1533
|
}
|
|
1197
1534
|
const dataNode = expr.arguments[1];
|
|
1198
1535
|
const dataType = lowerer.mapTypeOf(lowerer.typeOf(dataNode));
|
|
1199
|
-
const fd = lowerer.lowerExprExpecting(expr.arguments[0], F64);
|
|
1200
1536
|
const position = (node) => {
|
|
1201
1537
|
let valueNode = node;
|
|
1202
1538
|
while (valueNode && ts.isParenthesizedExpression(valueNode))
|
|
@@ -1214,9 +1550,26 @@ export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
|
1214
1550
|
if (dataType.elem !== "u8") {
|
|
1215
1551
|
lowerer.noLowering(`writeSync of '${lowerer.fmt(dataType)}' data`, dataNode, "byte writes take Uint8Array/Buffer data");
|
|
1216
1552
|
}
|
|
1553
|
+
if (expr.arguments.length === 2) {
|
|
1554
|
+
return lowerFsSyncBufferWindow(lowerer, expr, loc, "fs.writeSync", { kind: "options", node: undefined }, supported);
|
|
1555
|
+
}
|
|
1556
|
+
if (expr.arguments.length === 3) {
|
|
1557
|
+
const thirdNode = expr.arguments[2];
|
|
1558
|
+
let valueNode = thirdNode;
|
|
1559
|
+
while (ts.isParenthesizedExpression(valueNode))
|
|
1560
|
+
valueNode = valueNode.expression;
|
|
1561
|
+
if (ts.isObjectLiteralExpression(valueNode) || valueNode.kind === ts.SyntaxKind.NullKeyword) {
|
|
1562
|
+
return lowerFsSyncBufferWindow(lowerer, expr, loc, "fs.writeSync", { kind: "options", node: valueNode }, supported);
|
|
1563
|
+
}
|
|
1564
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(thirdNode))?.kind === "f64") {
|
|
1565
|
+
return lowerFsSyncBufferWindow(lowerer, expr, loc, "fs.writeSync", { kind: "offset", node: thirdNode }, supported);
|
|
1566
|
+
}
|
|
1567
|
+
lowerer.noLowering("writeSync of Buffer data with a non-numeric offset", thirdNode, supported);
|
|
1568
|
+
}
|
|
1217
1569
|
if (expr.arguments.length !== 4 && expr.arguments.length !== 5) {
|
|
1218
1570
|
lowerer.noLowering(`writeSync of Buffer data with ${expr.arguments.length} arguments`, expr, supported);
|
|
1219
1571
|
}
|
|
1572
|
+
const fd = lowerer.lowerExprExpecting(expr.arguments[0], F64);
|
|
1220
1573
|
return {
|
|
1221
1574
|
kind: "libCall",
|
|
1222
1575
|
fn: "fs.writeSync",
|
|
@@ -1247,6 +1600,7 @@ export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
|
1247
1600
|
// a call/getter whose type is the accepted literal cannot vanish.
|
|
1248
1601
|
enc = lowerer.lowerExprExpecting(encNode, STRING);
|
|
1249
1602
|
}
|
|
1603
|
+
const fd = lowerer.lowerExprExpecting(expr.arguments[0], F64);
|
|
1250
1604
|
return {
|
|
1251
1605
|
kind: "libCall",
|
|
1252
1606
|
fn: "fs.writeStrSync",
|
|
@@ -1264,14 +1618,17 @@ export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
|
1264
1618
|
if (bi.module === "os" && bi.member === "networkInterfaces") {
|
|
1265
1619
|
return lowerOsNetworkInterfacesCall(lowerer, expr, loc);
|
|
1266
1620
|
}
|
|
1267
|
-
// fs.readdirSync(path, { withFileTypes: true }) —
|
|
1268
|
-
// routed BEFORE the 1-arg table completion. The
|
|
1269
|
-
// object literal with withFileTypes: true (encoding
|
|
1270
|
-
// accepted as the default it is; recursive and
|
|
1271
|
-
// fence), and the call site's mapped type must
|
|
1272
|
-
// record array (type-mapper.ts) — the userInfo
|
|
1273
|
-
|
|
1274
|
-
|
|
1621
|
+
// fs.readdirSync/fs.promises.readdir(path, { withFileTypes: true }) —
|
|
1622
|
+
// the Dirent forms, routed BEFORE the 1-arg table completion. The
|
|
1623
|
+
// options must be an object literal with withFileTypes: true (encoding
|
|
1624
|
+
// "utf8"/"utf-8" is accepted as the default it is; recursive and
|
|
1625
|
+
// encoding:'buffer' fence), and the call site's mapped type must carry
|
|
1626
|
+
// the interned Dirent record array (type-mapper.ts) — the userInfo
|
|
1627
|
+
// verification stance.
|
|
1628
|
+
const syncReaddirTypes = bi.module === "fs" && bi.member === "readdirSync";
|
|
1629
|
+
const promiseReaddirTypes = bi.module === "fs/promises" && bi.member === "readdir";
|
|
1630
|
+
if ((syncReaddirTypes || promiseReaddirTypes) && expr.arguments.length === 2) {
|
|
1631
|
+
return lowerFsReaddirTypesCall(lowerer, expr, loc, promiseReaddirTypes);
|
|
1275
1632
|
}
|
|
1276
1633
|
if (bi.module === "os" && bi.member === "userInfo") {
|
|
1277
1634
|
return lowerOsUserInfoCall(lowerer, expr, loc);
|
|
@@ -1717,14 +2074,6 @@ export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
|
1717
2074
|
}
|
|
1718
2075
|
return finishWrite(optionValues);
|
|
1719
2076
|
}
|
|
1720
|
-
// zlib takes Buffers; a string argument (the lib admits it) gets the
|
|
1721
|
-
// wrap-it-first hint instead of a generic type mismatch.
|
|
1722
|
-
if (bi.module === "zlib" && expr.arguments.length >= 1) {
|
|
1723
|
-
const dataIr = lowerer.mapTypeOf(lowerer.typeOf(expr.arguments[0]));
|
|
1724
|
-
if (!(dataIr?.kind === "bytes" && dataIr.elem === "u8")) {
|
|
1725
|
-
lowerer.noLowering(`${bi.member} of '${dataIr ? lowerer.fmt(dataIr) : lowerer.checker.typeToString(lowerer.typeOf(expr.arguments[0]))}' data`, expr.arguments[0], `zlib works on Buffers: ${bi.member}(Buffer.from(s, "utf8"))`);
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
2077
|
if (fn.variadicPack) {
|
|
1729
2078
|
// join(...parts) forwards the array itself; mixing spread and plain
|
|
1730
2079
|
// arguments (or spreading anything but a string[]) stays out.
|
|
@@ -1784,12 +2133,21 @@ export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
|
1784
2133
|
}
|
|
1785
2134
|
}
|
|
1786
2135
|
const required = fn.params.length - (fn.defaults?.length ?? 0);
|
|
1787
|
-
|
|
2136
|
+
const hasSpread = expr.arguments.some(ts.isSpreadElement);
|
|
2137
|
+
if (hasSpread && bi.module === "url" && bi.member === "fileURLToPath") {
|
|
2138
|
+
lowerer.noLowering("fileURLToPath with spread arguments", expr, "call fileURLToPath(value) directly so the URL-or-string overload remains statically visible");
|
|
2139
|
+
}
|
|
2140
|
+
if (hasSpread &&
|
|
2141
|
+
((bi.module === "fs" && bi.member === "readFileSync") ||
|
|
2142
|
+
(bi.module === "fs/promises" && bi.member === "readFile"))) {
|
|
2143
|
+
lowerer.noLowering(`${bi.member} with spread arguments`, expr, `call ${bi.member}(path, "utf8") directly so the required literal encoding remains statically visible`);
|
|
2144
|
+
}
|
|
2145
|
+
if (!hasSpread && (expr.arguments.length < required || expr.arguments.length > fn.params.length)) {
|
|
1788
2146
|
lowerer.noLowering(`${name} with ${expr.arguments.length} argument${expr.arguments.length === 1 ? "" : "s"}`, expr, bi.member === "readFileSync" || bi.member === "readFile"
|
|
1789
2147
|
? `pass the encoding: ${bi.member}(path, "utf8") — Buffer reads and options objects have no lowering`
|
|
1790
2148
|
: `the supported form takes ${fn.params.length} argument${fn.params.length === 1 ? "" : "s"} (no options objects)`);
|
|
1791
2149
|
}
|
|
1792
|
-
if (bi.module === "url" && bi.member === "fileURLToPath") {
|
|
2150
|
+
if (!hasSpread && bi.module === "url" && bi.member === "fileURLToPath") {
|
|
1793
2151
|
// Node accepts a URL value or a URL string — one libFn per receiver
|
|
1794
2152
|
// form, picked by the argument's static type. Unions (URL |
|
|
1795
2153
|
// undefined, ...) must narrow first, like everywhere else.
|
|
@@ -1803,8 +2161,8 @@ export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
|
1803
2161
|
}
|
|
1804
2162
|
lowerer.noLowering(`fileURLToPath of '${lowerer.fmt(arg.type)}' values`, argNode, "pass a URL value or a URL string (narrow unions first)");
|
|
1805
2163
|
}
|
|
1806
|
-
if ((bi.module === "fs" && bi.member === "readFileSync") ||
|
|
1807
|
-
(bi.module === "fs/promises" && bi.member === "readFile")) {
|
|
2164
|
+
if (!hasSpread && ((bi.module === "fs" && bi.member === "readFileSync") ||
|
|
2165
|
+
(bi.module === "fs/promises" && bi.member === "readFile"))) {
|
|
1808
2166
|
// The runtime reads utf8 unconditionally; any other encoding would
|
|
1809
2167
|
// silently decode wrong, so the ARGUMENT'S TYPE must be the literal
|
|
1810
2168
|
// "utf8" — or Node's "utf-8" alias, the same decoder (the fallback
|
|
@@ -1815,10 +2173,48 @@ export function lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc) {
|
|
|
1815
2173
|
lowerer.noLowering(`${bi.member} with a non-"utf8" encoding`, expr.arguments[1], `only utf8 reads are supported: ${bi.member}(path, "utf8")`);
|
|
1816
2174
|
}
|
|
1817
2175
|
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
2176
|
+
// A value-enabled builtin with defaults uses its generated adapter for
|
|
2177
|
+
// direct calls too. That keeps explicit undefined identical to omission:
|
|
2178
|
+
// completeArgs wraps the undefined arm, then the adapter selects the
|
|
2179
|
+
// descriptor's default before entering the fixed runtime libCall ABI.
|
|
2180
|
+
// The call has already passed this member's ordinary arity/shape gates.
|
|
2181
|
+
if (fn.defaults && fn.valueParams) {
|
|
2182
|
+
const callee = lowerer.lowerBuiltinCallableValue(bi, loc);
|
|
2183
|
+
if (!callee || callee.type.kind !== "func") {
|
|
2184
|
+
throw new InternalCompilerError(`missing callable adapter for ${bi.module}.${bi.member}`);
|
|
2185
|
+
}
|
|
2186
|
+
const funcType = callee.type;
|
|
2187
|
+
const shapes = fn.valueParams.map((param, index) => {
|
|
2188
|
+
const type = funcType.params[index];
|
|
2189
|
+
if (!type)
|
|
2190
|
+
throw new InternalCompilerError(`missing callable parameter ${index} for ${bi.module}.${bi.member}`);
|
|
2191
|
+
return { type, mode: param.mode === "optional" ? "omittable" : "required" };
|
|
2192
|
+
});
|
|
2193
|
+
const args = lowerer.completeArgs(expr.arguments, shapes, loc, expr);
|
|
2194
|
+
return { kind: "callValue", callee, args, type: callee.type.ret, loc };
|
|
2195
|
+
}
|
|
2196
|
+
let args;
|
|
2197
|
+
if (hasSpread) {
|
|
2198
|
+
const shapes = fn.params.map((type, i) => i < required
|
|
2199
|
+
? { type, mode: "required" }
|
|
2200
|
+
: {
|
|
2201
|
+
type,
|
|
2202
|
+
mode: "omittable",
|
|
2203
|
+
callDefault: {
|
|
2204
|
+
kind: "strLit",
|
|
2205
|
+
value: fn.defaults[i - required],
|
|
2206
|
+
type: STRING,
|
|
2207
|
+
loc,
|
|
2208
|
+
},
|
|
2209
|
+
});
|
|
2210
|
+
args = lowerer.completeArgs(expr.arguments, shapes, loc, expr);
|
|
2211
|
+
}
|
|
2212
|
+
else {
|
|
2213
|
+
args = expr.arguments.map((a, i) => lowerer.lowerExprExpecting(a, fn.params[i]));
|
|
2214
|
+
for (let i = args.length; i < fn.params.length; i++) {
|
|
2215
|
+
const dflt = fn.defaults[i - required];
|
|
2216
|
+
args.push({ kind: "strLit", value: dflt, type: STRING, loc });
|
|
2217
|
+
}
|
|
1822
2218
|
}
|
|
1823
2219
|
return { kind: "libCall", fn: fn.fn, args, type: fn.result, loc };
|
|
1824
2220
|
}
|
|
@@ -2063,7 +2459,7 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2063
2459
|
optsNode = second;
|
|
2064
2460
|
}
|
|
2065
2461
|
if (optsNode !== undefined && !ts.isObjectLiteralExpression(optsNode)) {
|
|
2066
|
-
lowerer.noLowering("spawn with a non-literal options argument", optsNode, "pass stdio, detached, env, cwd, and
|
|
2462
|
+
lowerer.noLowering("spawn with a non-literal options argument", optsNode, "pass stdio, detached, env, cwd, windowsHide, and shell in an inline object literal");
|
|
2067
2463
|
}
|
|
2068
2464
|
const emptyStr = { kind: "strLit", value: "", type: STRING, loc };
|
|
2069
2465
|
// Per-slot stdio modes (scr_child.c: 0 ignore, 1 inherit, 2 fd) and
|
|
@@ -2074,6 +2470,8 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2074
2470
|
let outFd = numLit(0, loc);
|
|
2075
2471
|
let errFd = numLit(0, loc);
|
|
2076
2472
|
let detached = boolLit(false, loc);
|
|
2473
|
+
let shell = boolLit(false, loc);
|
|
2474
|
+
let shellEnabled = false;
|
|
2077
2475
|
let hasEnv = boolLit(false, loc);
|
|
2078
2476
|
let envPairs = { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc };
|
|
2079
2477
|
let cwd = emptyStr;
|
|
@@ -2194,8 +2592,21 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2194
2592
|
case "windowsHide":
|
|
2195
2593
|
lowerer.lowerExpr(m.value); // Node no-op on POSIX
|
|
2196
2594
|
break;
|
|
2595
|
+
case "shell":
|
|
2596
|
+
if (m.value.kind === ts.SyntaxKind.TrueKeyword) {
|
|
2597
|
+
shell = boolLit(true, loc);
|
|
2598
|
+
shellEnabled = true;
|
|
2599
|
+
plain = false;
|
|
2600
|
+
}
|
|
2601
|
+
else if (m.value.kind === ts.SyntaxKind.FalseKeyword) {
|
|
2602
|
+
shell = boolLit(false, loc);
|
|
2603
|
+
}
|
|
2604
|
+
else {
|
|
2605
|
+
lowerer.noLowering("spawn with a non-literal shell option", m.value, "shell must be a boolean literal");
|
|
2606
|
+
}
|
|
2607
|
+
break;
|
|
2197
2608
|
default:
|
|
2198
|
-
lowerer.noLowering(`spawn option '${m.name}'`, p, "stdio, detached, env, cwd, and
|
|
2609
|
+
lowerer.noLowering(`spawn option '${m.name}'`, p, "stdio, detached, env, cwd, windowsHide, and shell are the supported options");
|
|
2199
2610
|
}
|
|
2200
2611
|
}
|
|
2201
2612
|
}
|
|
@@ -2203,6 +2614,12 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2203
2614
|
inMode = outMode = errMode = 3;
|
|
2204
2615
|
plain = false;
|
|
2205
2616
|
}
|
|
2617
|
+
if (shellEnabled && argsNode !== undefined) {
|
|
2618
|
+
const shellArgs = stripTypeCasts(argsNode);
|
|
2619
|
+
if (!ts.isArrayLiteralExpression(shellArgs) || shellArgs.elements.length !== 0) {
|
|
2620
|
+
lowerer.noLowering("spawn with shell enabled and separate arguments", argsNode, "put the complete shell command in the first argument and pass [] (Node deprecates unescaped shell argument concatenation)");
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2206
2623
|
const argv = lowerer.lowerChildArgsArg(argsNode, loc);
|
|
2207
2624
|
if (plain) {
|
|
2208
2625
|
return { kind: "libCall", fn: "cp.spawn", args: [cmd, argv], type: CHILD_T, loc };
|
|
@@ -2210,11 +2627,213 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2210
2627
|
return {
|
|
2211
2628
|
kind: "libCall",
|
|
2212
2629
|
fn: "cp.spawnOpts",
|
|
2213
|
-
args: [cmd, argv, numLit(inMode, loc), numLit(outMode, loc), numLit(errMode, loc), outFd, errFd, detached, hasEnv, envPairs, cwd],
|
|
2630
|
+
args: [cmd, argv, numLit(inMode, loc), numLit(outMode, loc), numLit(errMode, loc), outFd, errFd, detached, shell, hasEnv, envPairs, cwd],
|
|
2631
|
+
type: CHILD_T,
|
|
2632
|
+
loc,
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
/** `fork(staticModulePath, args?, options?)` embeds the selected program
|
|
2636
|
+
* module and re-executes the native artifact with one private target id.
|
|
2637
|
+
* Node's default JSON IPC channel is always present. */
|
|
2638
|
+
export function lowerForkCall(lowerer, expr, loc) {
|
|
2639
|
+
if (expr.arguments.length < 1 || expr.arguments.length > 3 || expr.arguments.some(ts.isSpreadElement)) {
|
|
2640
|
+
lowerer.noLowering("fork with this argument shape", expr, "use fork(staticModulePath[, stringArgs][, inlineOptions])");
|
|
2641
|
+
}
|
|
2642
|
+
const targetPath = staticForkModulePath(lowerer.program, expr.arguments[0]);
|
|
2643
|
+
const targetId = targetPath === null
|
|
2644
|
+
? undefined
|
|
2645
|
+
: lowerer.forkTargetIdByPath.get(tsgoPath(resolve(targetPath)));
|
|
2646
|
+
if (targetId === undefined) {
|
|
2647
|
+
lowerer.noLowering("fork with a runtime-valued module path", expr.arguments[0], 'resolve a relative worker with new URL("./worker.ts", import.meta.url) or fileURLToPath(...) and bind it to a const if needed');
|
|
2648
|
+
}
|
|
2649
|
+
let argsNode;
|
|
2650
|
+
let optsNode;
|
|
2651
|
+
if (expr.arguments.length === 3) {
|
|
2652
|
+
argsNode = expr.arguments[1];
|
|
2653
|
+
optsNode = expr.arguments[2];
|
|
2654
|
+
}
|
|
2655
|
+
else if (expr.arguments.length === 2) {
|
|
2656
|
+
const second = expr.arguments[1];
|
|
2657
|
+
const secondType = lowerer.typeOf(second);
|
|
2658
|
+
const mapped = lowerer.mapTypeOf(secondType);
|
|
2659
|
+
if (lowerer.checker.isArrayType(secondType) ||
|
|
2660
|
+
lowerer.checker.isTupleType(secondType) ||
|
|
2661
|
+
(mapped?.kind === "array" && mapped.elem.kind === "string")) {
|
|
2662
|
+
argsNode = second;
|
|
2663
|
+
}
|
|
2664
|
+
else {
|
|
2665
|
+
optsNode = second;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
if (optsNode !== undefined && !ts.isObjectLiteralExpression(optsNode)) {
|
|
2669
|
+
lowerer.noLowering("fork with a non-literal options argument", optsNode, "pass cwd, env, execArgv, silent, stdio, serialization, and windowsHide in an inline object literal");
|
|
2670
|
+
}
|
|
2671
|
+
let inMode = 1;
|
|
2672
|
+
let outMode = 1;
|
|
2673
|
+
let errMode = 1;
|
|
2674
|
+
let sawStdio = false;
|
|
2675
|
+
let silent = false;
|
|
2676
|
+
let hasEnv = boolLit(false, loc);
|
|
2677
|
+
let envPairs = { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc };
|
|
2678
|
+
let cwd = { kind: "strLit", value: "", type: STRING, loc };
|
|
2679
|
+
const stdioMode = (node, label) => {
|
|
2680
|
+
const type = lowerer.typeOf(node);
|
|
2681
|
+
const value = type.isStringLiteralType() ? type.value : null;
|
|
2682
|
+
if (value !== "ignore" && value !== "inherit" && value !== "pipe") {
|
|
2683
|
+
lowerer.noLowering(`fork with ${label} stdio`, node, '"ignore", "inherit", and "pipe" are the supported stdio modes');
|
|
2684
|
+
}
|
|
2685
|
+
return value === "ignore" ? 0 : value === "inherit" ? 1 : 3;
|
|
2686
|
+
};
|
|
2687
|
+
if (optsNode && ts.isObjectLiteralExpression(optsNode)) {
|
|
2688
|
+
for (const property of optsNode.properties) {
|
|
2689
|
+
const member = optionMember(property);
|
|
2690
|
+
if (!member) {
|
|
2691
|
+
lowerer.noLowering("fork with this options shape", property, "spreads and computed keys have no lowering — write each member inline");
|
|
2692
|
+
}
|
|
2693
|
+
switch (member.name) {
|
|
2694
|
+
case "cwd":
|
|
2695
|
+
cwd = lowerer.lowerExprExpecting(member.value, STRING);
|
|
2696
|
+
break;
|
|
2697
|
+
case "env":
|
|
2698
|
+
hasEnv = boolLit(true, loc);
|
|
2699
|
+
envPairs = lowerer.recordToEnvPairs(member.value);
|
|
2700
|
+
break;
|
|
2701
|
+
case "silent":
|
|
2702
|
+
if (member.value.kind === ts.SyntaxKind.TrueKeyword)
|
|
2703
|
+
silent = true;
|
|
2704
|
+
else if (member.value.kind !== ts.SyntaxKind.FalseKeyword) {
|
|
2705
|
+
lowerer.noLowering("fork with a non-literal silent option", member.value, "silent must be a boolean literal");
|
|
2706
|
+
}
|
|
2707
|
+
break;
|
|
2708
|
+
case "stdio": {
|
|
2709
|
+
if (ts.isArrayLiteralExpression(member.value)) {
|
|
2710
|
+
if (member.value.elements.length !== 4) {
|
|
2711
|
+
lowerer.noLowering("fork with this stdio tuple", member.value, 'use [stdin, stdout, stderr, "ipc"] with exactly four literal entries');
|
|
2712
|
+
}
|
|
2713
|
+
const ipc = member.value.elements[3];
|
|
2714
|
+
const ipcType = lowerer.typeOf(ipc);
|
|
2715
|
+
if (!ipcType.isStringLiteralType() || ipcType.value !== "ipc") {
|
|
2716
|
+
lowerer.noLowering("fork without a fourth-slot IPC channel", ipc, 'the fourth stdio entry must be "ipc"');
|
|
2717
|
+
}
|
|
2718
|
+
inMode = stdioMode(member.value.elements[0], "stdin");
|
|
2719
|
+
outMode = stdioMode(member.value.elements[1], "stdout");
|
|
2720
|
+
errMode = stdioMode(member.value.elements[2], "stderr");
|
|
2721
|
+
}
|
|
2722
|
+
else {
|
|
2723
|
+
const mode = stdioMode(member.value, "scalar");
|
|
2724
|
+
inMode = outMode = errMode = mode;
|
|
2725
|
+
}
|
|
2726
|
+
sawStdio = true;
|
|
2727
|
+
break;
|
|
2728
|
+
}
|
|
2729
|
+
case "serialization": {
|
|
2730
|
+
const type = lowerer.typeOf(member.value);
|
|
2731
|
+
if (!type.isStringLiteralType() || type.value !== "json") {
|
|
2732
|
+
lowerer.noLowering("fork with non-JSON serialization", member.value, 'the static IPC channel supports Node\'s default serialization or serialization: "json"');
|
|
2733
|
+
}
|
|
2734
|
+
break;
|
|
2735
|
+
}
|
|
2736
|
+
case "execArgv":
|
|
2737
|
+
// Loader flags configure the Node executable. The native worker is
|
|
2738
|
+
// already compiled into this artifact, so no loader starts.
|
|
2739
|
+
break;
|
|
2740
|
+
case "windowsHide":
|
|
2741
|
+
lowerer.lowerExpr(member.value); // accepted platform presentation option
|
|
2742
|
+
break;
|
|
2743
|
+
default:
|
|
2744
|
+
lowerer.noLowering(`fork with the '${member.name}' option`, member.value, "cwd, env, execArgv, silent, stdio, serialization, and windowsHide are supported");
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
if (!sawStdio && silent)
|
|
2749
|
+
inMode = outMode = errMode = 3;
|
|
2750
|
+
return {
|
|
2751
|
+
kind: "libCall",
|
|
2752
|
+
fn: "cp.fork",
|
|
2753
|
+
args: [
|
|
2754
|
+
numLit(targetId, loc),
|
|
2755
|
+
lowerChildArgsArg(lowerer, argsNode, loc),
|
|
2756
|
+
numLit(inMode, loc),
|
|
2757
|
+
numLit(outMode, loc),
|
|
2758
|
+
numLit(errMode, loc),
|
|
2759
|
+
hasEnv,
|
|
2760
|
+
envPairs,
|
|
2761
|
+
cwd,
|
|
2762
|
+
],
|
|
2214
2763
|
type: CHILD_T,
|
|
2215
2764
|
loc,
|
|
2216
2765
|
};
|
|
2217
2766
|
}
|
|
2767
|
+
/** `execFile(file[, args][, options], callback)` — the asynchronous callback
|
|
2768
|
+
* slice. The child starts immediately with stdin/stdout/stderr piped; the
|
|
2769
|
+
* runtime captures both outputs and invokes the error-first callback after
|
|
2770
|
+
* settlement. The callback may ignore a suffix of `(error, stdout,
|
|
2771
|
+
* stderr)`, but every declared parameter must have the Node shape. Inline
|
|
2772
|
+
* `encoding: "utf8"` and a pure numeric `maxBuffer` are accepted; the latter
|
|
2773
|
+
* is not enforced by the growing native capture, as
|
|
2774
|
+
* with the synchronous and promisified capture forms. Other options remain
|
|
2775
|
+
* fenced until their lifecycle can share this asynchronous core. */
|
|
2776
|
+
export function lowerExecFileCall(lowerer, expr, loc) {
|
|
2777
|
+
if (expr.arguments.some(ts.isSpreadElement) || expr.arguments.length < 2 || expr.arguments.length > 4) {
|
|
2778
|
+
lowerer.noLowering(`execFile with ${expr.arguments.length} arguments`, expr, "the supported callback forms are execFile(file, callback), execFile(file, args, callback), and execFile(file, args, { encoding: 'utf8', maxBuffer: N }, callback)");
|
|
2779
|
+
}
|
|
2780
|
+
const cmd = lowerer.lowerExprExpecting(expr.arguments[0], STRING);
|
|
2781
|
+
const argsNode = expr.arguments.length >= 3 ? expr.arguments[1] : undefined;
|
|
2782
|
+
const callbackNode = expr.arguments[expr.arguments.length - 1];
|
|
2783
|
+
const argv = lowerer.lowerChildArgsArg(argsNode, loc);
|
|
2784
|
+
if (expr.arguments.length === 4) {
|
|
2785
|
+
const options = expr.arguments[2];
|
|
2786
|
+
if (!ts.isObjectLiteralExpression(options)) {
|
|
2787
|
+
lowerer.noLowering("execFile with a non-literal options argument", options, "pass encoding and maxBuffer in an inline object literal");
|
|
2788
|
+
}
|
|
2789
|
+
const pureNumber = (node) => ts.isNumericLiteral(node) ||
|
|
2790
|
+
(ts.isParenthesizedExpression(node) && pureNumber(node.expression)) ||
|
|
2791
|
+
(ts.isBinaryExpression(node) &&
|
|
2792
|
+
[ts.SyntaxKind.PlusToken, ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.SlashToken].includes(node.operatorToken.kind) &&
|
|
2793
|
+
pureNumber(node.left) && pureNumber(node.right));
|
|
2794
|
+
for (const property of options.properties) {
|
|
2795
|
+
const member = optionMember(property);
|
|
2796
|
+
if (!member)
|
|
2797
|
+
lowerer.noLowering("execFile with this options shape", property, "use plain encoding and maxBuffer properties without spreads or computed keys");
|
|
2798
|
+
if (member.name === "encoding") {
|
|
2799
|
+
if (!ts.isStringLiteral(member.value) || (member.value.text !== "utf8" && member.value.text !== "utf-8")) {
|
|
2800
|
+
lowerer.noLowering("execFile with a non-literal utf8 encoding", member.value, "pass the literal 'utf8' or 'utf-8' (other expressions might have side effects)");
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
else if (member.name === "maxBuffer") {
|
|
2804
|
+
if (!pureNumber(member.value)) {
|
|
2805
|
+
lowerer.noLowering("execFile with a non-literal maxBuffer", member.value, "pass a numeric literal or literal arithmetic (the native capture grows without enforcing this limit)");
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
else {
|
|
2809
|
+
lowerer.noLowering(`execFile option '${member.name}'`, property, "only encoding: 'utf8' and a pure numeric maxBuffer are supported in the callback form");
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
const callback = lowerer.lowerExpr(callbackNode);
|
|
2814
|
+
if (callback.type.kind !== "func" || callback.type.rest === true || callback.type.params.length > 3) {
|
|
2815
|
+
lowerer.unsupported("SC1090", callbackNode, "execFile callbacks take (error), (error, stdout), (error, stdout, stderr), or no parameters");
|
|
2816
|
+
}
|
|
2817
|
+
if (callback.type.ret.kind !== "void") {
|
|
2818
|
+
lowerer.unsupported("SC1090", callbackNode, "execFile callbacks returning a value (make the callback body a block, or return nothing)");
|
|
2819
|
+
}
|
|
2820
|
+
const errorParam = callback.type.params[0];
|
|
2821
|
+
if (errorParam !== undefined) {
|
|
2822
|
+
const def = errorParam.kind === "union" ? lowerer.unions.get(errorParam.unionId) : undefined;
|
|
2823
|
+
const valid = !!def && def.arms.length === 2 &&
|
|
2824
|
+
def.arms.some((arm) => arm.kind === "nullT") &&
|
|
2825
|
+
def.arms.some((arm) => arm.kind === "object" && arm.className === "%Error");
|
|
2826
|
+
if (!valid) {
|
|
2827
|
+
lowerer.unsupported("SC1090", callbackNode, `execFile callbacks whose first parameter is not 'Error | null' (got '${lowerer.fmt(errorParam)}')`);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
for (let i = 1; i < callback.type.params.length; i++) {
|
|
2831
|
+
if (callback.type.params[i].kind !== "string") {
|
|
2832
|
+
lowerer.unsupported("SC1090", callbackNode, `execFile callbacks whose ${i === 1 ? "stdout" : "stderr"} parameter is not 'string'`);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
return { kind: "libCall", fn: "cp.execFile", args: [cmd, argv, callback], type: CHILD_T, loc };
|
|
2836
|
+
}
|
|
2218
2837
|
/** `execFileSync(file, args?, options?)` / `execSync(command, options?)`
|
|
2219
2838
|
* → the ONE cp.execSync libCall. execSync wraps the command in
|
|
2220
2839
|
* `/bin/sh -c` (Node's shell semantics — a single command string, no
|
|
@@ -2714,13 +3333,10 @@ function execStdioModeHelper(lowerer, shapeId, stdioT, fd, loc) {
|
|
|
2714
3333
|
});
|
|
2715
3334
|
return name;
|
|
2716
3335
|
}
|
|
2717
|
-
/**
|
|
2718
|
-
*
|
|
2719
|
-
*
|
|
2720
|
-
*
|
|
2721
|
-
* over anything else fences HERE with the supported-target hint (the
|
|
2722
|
-
* declaration is where the target is visible). False for non-promisify
|
|
2723
|
-
* initializers, so the ordinary declaration paths apply. */
|
|
3336
|
+
/** Compile-time util.promisify projections. The immutable result binding
|
|
3337
|
+
* owns no runtime slot: calls rewrite to the target's promise lowering,
|
|
3338
|
+
* while value uses remain explicit fences. Unsupported targets report at
|
|
3339
|
+
* the declaration, where their identity is still visible. */
|
|
2724
3340
|
export function isPromisifyCall(lowerer, init) {
|
|
2725
3341
|
let e = init;
|
|
2726
3342
|
while (ts.isParenthesizedExpression(e))
|
|
@@ -2734,7 +3350,38 @@ export function isPromisifyCall(lowerer, init) {
|
|
|
2734
3350
|
return null;
|
|
2735
3351
|
return e;
|
|
2736
3352
|
}
|
|
2737
|
-
|
|
3353
|
+
/** Immutable aliases of table-backed builtin functions. The alias is a
|
|
3354
|
+
* compile-time call target, so direct calls keep the builtin's validated
|
|
3355
|
+
* lowering without materializing a JavaScript function object. Value
|
|
3356
|
+
* uses remain fenced until an escaping closure adapter exists. */
|
|
3357
|
+
export function registerBuiltinCallableAlias(lowerer, nameNode, init) {
|
|
3358
|
+
if (!ts.isIdentifier(nameNode) || !init)
|
|
3359
|
+
return false;
|
|
3360
|
+
let targetExpr = init;
|
|
3361
|
+
while (ts.isParenthesizedExpression(targetExpr))
|
|
3362
|
+
targetExpr = targetExpr.expression;
|
|
3363
|
+
if (!ts.isIdentifier(targetExpr))
|
|
3364
|
+
return false;
|
|
3365
|
+
const sourceSymbol = lowerer.resolveValueSymbol(targetExpr);
|
|
3366
|
+
const existing = sourceSymbol ? lowerer.staticCallables.get(sourceSymbol) : undefined;
|
|
3367
|
+
const target = existing?.kind === "builtin-function"
|
|
3368
|
+
? { module: existing.module, member: existing.member }
|
|
3369
|
+
: lowerer.builtinImportOf(targetExpr);
|
|
3370
|
+
if (!target || !builtinModuleFnOf(lowerer, target.module, target.member))
|
|
3371
|
+
return false;
|
|
3372
|
+
if (lowerer.checker.getCallSignatures(lowerer.typeOf(nameNode)).length === 0)
|
|
3373
|
+
return false;
|
|
3374
|
+
const symbol = lowerer.checker.getSymbolAtLocation(nameNode);
|
|
3375
|
+
if (!symbol)
|
|
3376
|
+
return false;
|
|
3377
|
+
lowerer.staticCallables.set(symbol, {
|
|
3378
|
+
kind: "builtin-function",
|
|
3379
|
+
module: target.module,
|
|
3380
|
+
member: target.member,
|
|
3381
|
+
});
|
|
3382
|
+
return true;
|
|
3383
|
+
}
|
|
3384
|
+
export function registerPromisifiedBuiltinDecl(lowerer, nameNode, init) {
|
|
2738
3385
|
if (!init)
|
|
2739
3386
|
return false;
|
|
2740
3387
|
// The OTHER special const-binding form this decl hook serves: the
|
|
@@ -2748,12 +3395,17 @@ export function promisifiedExecFileDecl(lowerer, nameNode, init) {
|
|
|
2748
3395
|
return false;
|
|
2749
3396
|
const argNode = e.arguments.length === 1 ? e.arguments[0] : null;
|
|
2750
3397
|
const target = argNode && ts.isIdentifier(argNode) ? lowerer.builtinImportOf(argNode) : null;
|
|
2751
|
-
|
|
2752
|
-
|
|
3398
|
+
const projection = target?.module === "child_process" && target.member === "execFile"
|
|
3399
|
+
? { kind: "promisified-exec-file" }
|
|
3400
|
+
: target?.module === "fs" && target.member === "readFile"
|
|
3401
|
+
? { kind: "promisified-builtin", module: "fs/promises", member: "readFile" }
|
|
3402
|
+
: null;
|
|
3403
|
+
if (!projection) {
|
|
3404
|
+
lowerer.noLowering("util.promisify of this target", argNode ?? e, "child_process.execFile and fs.readFile are the supported targets");
|
|
2753
3405
|
}
|
|
2754
3406
|
const symbol = lowerer.checker.getSymbolAtLocation(nameNode);
|
|
2755
3407
|
if (symbol)
|
|
2756
|
-
lowerer.
|
|
3408
|
+
lowerer.staticCallables.set(symbol, projection);
|
|
2757
3409
|
return true;
|
|
2758
3410
|
}
|
|
2759
3411
|
/** A call THROUGH a promisified-execFile binding:
|
|
@@ -3862,18 +4514,18 @@ function cryptoInputBytes(lowerer, node, loc) {
|
|
|
3862
4514
|
}
|
|
3863
4515
|
lowerer.noLowering(`crypto byte input of '${lowerer.fmt(value.type)}' values`, node, "string and Buffer/Uint8Array values are supported");
|
|
3864
4516
|
}
|
|
3865
|
-
function
|
|
4517
|
+
function errorFirstBytesCallback(lowerer, node, api) {
|
|
3866
4518
|
let callback = lowerer.lowerExpr(node);
|
|
3867
4519
|
if (callback.type.kind === "dyn") {
|
|
3868
4520
|
callback = { kind: "dynCheck", value: callback, type: funcOf([DYN, DYN], VOID), loc: locOf(node) };
|
|
3869
4521
|
}
|
|
3870
4522
|
if (callback.type.kind !== "func" || callback.type.params.length > 2) {
|
|
3871
|
-
lowerer.unsupported("SC1090", node,
|
|
4523
|
+
lowerer.unsupported("SC1090", node, `${api} callbacks must accept at most (error, buffer)`);
|
|
3872
4524
|
}
|
|
3873
4525
|
const error = callback.type.params[0];
|
|
3874
4526
|
if (error !== undefined && error.kind !== "dyn") {
|
|
3875
4527
|
if (error.kind !== "union") {
|
|
3876
|
-
lowerer.unsupported("SC1090", node,
|
|
4528
|
+
lowerer.unsupported("SC1090", node, `${api} callback error parameters must be Error | null`);
|
|
3877
4529
|
}
|
|
3878
4530
|
const def = lowerer.unions.get(error.unionId);
|
|
3879
4531
|
const valid = !!def &&
|
|
@@ -3881,11 +4533,11 @@ function cryptoBytesCallback(lowerer, node) {
|
|
|
3881
4533
|
def.arms.some((arm) => arm.kind === "object" && arm.className === "%Error") &&
|
|
3882
4534
|
def.arms.every((arm) => arm.kind === "nullT" || arm.kind === "undefinedT" || (arm.kind === "object" && arm.className === "%Error"));
|
|
3883
4535
|
if (!valid)
|
|
3884
|
-
lowerer.unsupported("SC1090", node,
|
|
4536
|
+
lowerer.unsupported("SC1090", node, `${api} callback error parameters must be Error | null`);
|
|
3885
4537
|
}
|
|
3886
4538
|
const value = callback.type.params[1];
|
|
3887
4539
|
if (value !== undefined && value.kind !== "dyn" && !(value.kind === "bytes" && value.elem === "u8")) {
|
|
3888
|
-
lowerer.unsupported("SC1090", node,
|
|
4540
|
+
lowerer.unsupported("SC1090", node, `${api} callback result parameters must be Buffer/Uint8Array values`);
|
|
3889
4541
|
}
|
|
3890
4542
|
return voidizedCallback(lowerer, callback, locOf(node));
|
|
3891
4543
|
}
|
|
@@ -3909,7 +4561,7 @@ export function lowerCryptoModuleCall(lowerer, expr, bi, loc) {
|
|
|
3909
4561
|
return {
|
|
3910
4562
|
kind: "libCall",
|
|
3911
4563
|
fn: "crypto.randomBytesCb",
|
|
3912
|
-
args: [lowerer.lowerExprExpecting(args[0], F64),
|
|
4564
|
+
args: [lowerer.lowerExprExpecting(args[0], F64), errorFirstBytesCallback(lowerer, args[1], "crypto")],
|
|
3913
4565
|
type: VOID,
|
|
3914
4566
|
loc,
|
|
3915
4567
|
};
|
|
@@ -3927,7 +4579,7 @@ export function lowerCryptoModuleCall(lowerer, expr, bi, loc) {
|
|
|
3927
4579
|
lowerer.lowerExprExpecting(args[2], F64),
|
|
3928
4580
|
lowerer.lowerExprExpecting(args[3], F64),
|
|
3929
4581
|
cryptoAlgorithm(lowerer, args[4], "crypto.pbkdf2"),
|
|
3930
|
-
|
|
4582
|
+
errorFirstBytesCallback(lowerer, args[5], "crypto"),
|
|
3931
4583
|
],
|
|
3932
4584
|
type: VOID,
|
|
3933
4585
|
loc,
|
|
@@ -4055,7 +4707,7 @@ export function lowerUrlMethodCall(lowerer, call, access) {
|
|
|
4055
4707
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
4056
4708
|
return { kind: "libCall", fn: "url.href", args: [receiver], type: STRING, loc: locOf(call) };
|
|
4057
4709
|
}
|
|
4058
|
-
lowerer.noLowering(`URL.${name}`, call, "protocol, pathname, href, and toString() are the supported URL members", lowerer.checker.getSymbolAtLocation(access.name));
|
|
4710
|
+
lowerer.noLowering(`URL.${name}`, call, "protocol, origin, username, password, pathname, href, host, hostname, port, search, hash, searchParams, and toString() are the supported URL members", lowerer.checker.getSymbolAtLocation(access.name));
|
|
4059
4711
|
}
|
|
4060
4712
|
/** `new URLSearchParams(init?)` — the WHATWG constructor's lowered init
|
|
4061
4713
|
* shapes: omitted / literal `undefined` (empty list), a string (parsed,
|
|
@@ -4637,7 +5289,7 @@ export function lowerStatsMethodCall(lowerer, call, access) {
|
|
|
4637
5289
|
: "stats.isSymbolicLink";
|
|
4638
5290
|
return { kind: "libCall", fn, args: [receiver], type: BOOL, loc: locOf(call) };
|
|
4639
5291
|
}
|
|
4640
|
-
lowerer.noLowering(`Stats.${name}`, call, "isFile(), isDirectory(), isSymbolicLink(), size, blocks, nlink, atimeMs, and
|
|
5292
|
+
lowerer.noLowering(`Stats.${name}`, call, "isFile(), isDirectory(), isSymbolicLink(), dev, ino, size, blocks, nlink, atimeMs, mtimeMs, and ctimeMs are the supported Stats members", lowerer.checker.getSymbolAtLocation(access.name));
|
|
4641
5293
|
}
|
|
4642
5294
|
/** Calls on native crypto.Hash/Hmac handles. The handle survives locals,
|
|
4643
5295
|
* aliases, loops, and returns; update() retains and returns the receiver,
|
|
@@ -4810,6 +5462,15 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
|
|
|
4810
5462
|
};
|
|
4811
5463
|
return lowerer.maybeNarrow(read, expr);
|
|
4812
5464
|
}
|
|
5465
|
+
if (kind === "child" && name === "connected") {
|
|
5466
|
+
return {
|
|
5467
|
+
kind: "libCall",
|
|
5468
|
+
fn: "child.connected",
|
|
5469
|
+
args: [lowerer.lowerExpr(expr.expression)],
|
|
5470
|
+
type: BOOL,
|
|
5471
|
+
loc,
|
|
5472
|
+
};
|
|
5473
|
+
}
|
|
4813
5474
|
if (kind === "child")
|
|
4814
5475
|
return null; // pid/exitCode/killed live in lowerIntrinsicProperty
|
|
4815
5476
|
if (kind === "childWriter") {
|
|
@@ -4823,7 +5484,8 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
|
|
|
4823
5484
|
lowerer.noLowering(`Writable.${name}`, expr, 'write(string | Uint8Array), end(), destroy(), writable, and on/once("drain" | "finish" | "error", cb) are supported', lowerer.checker.getSymbolAtLocation(expr.name));
|
|
4824
5485
|
}
|
|
4825
5486
|
if (kind === "stats" &&
|
|
4826
|
-
(name === "
|
|
5487
|
+
(name === "dev" || name === "ino" || name === "blocks" || name === "nlink" ||
|
|
5488
|
+
name === "atimeMs" || name === "mtimeMs" || name === "ctimeMs")) {
|
|
4827
5489
|
const receiver = lowerer.lowerExpr(expr.expression);
|
|
4828
5490
|
const fn = `stats.${name}`;
|
|
4829
5491
|
return { kind: "libCall", fn, args: [receiver], type: F64, loc };
|
|
@@ -4850,6 +5512,63 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
|
|
|
4850
5512
|
* lowerIntrinsicProperty). Everything else @types/node declares on
|
|
4851
5513
|
* ChildProcess (stdout, once, ...) fences member-qualified. Null for
|
|
4852
5514
|
* non-child receivers. */
|
|
5515
|
+
function ignoredListenerReturn(type) {
|
|
5516
|
+
return type.kind === "void" || (type.kind === "promise" && type.inner.kind === "void");
|
|
5517
|
+
}
|
|
5518
|
+
function ipcJsonMessage(lowerer, node, loc) {
|
|
5519
|
+
const value = lowerer.lowerExpr(node);
|
|
5520
|
+
if (!lowerer.jsonStringifySafe(value.type) && value.type.kind !== "dyn") {
|
|
5521
|
+
lowerer.unsupported("SC1090", node, `IPC messages of '${lowerer.fmt(value.type)}' values (messages must be JSON-stringifiable records, arrays, primitives, unions of those, or unknown)`);
|
|
5522
|
+
}
|
|
5523
|
+
return { kind: "jsonStringify", value, type: STRING, loc };
|
|
5524
|
+
}
|
|
5525
|
+
function ipcMessageListener(lowerer, node) {
|
|
5526
|
+
const cb = lowerer.lowerExpr(node);
|
|
5527
|
+
if (cb.type.kind !== "func" || cb.type.params.length > 1 || !ignoredListenerReturn(cb.type.ret)) {
|
|
5528
|
+
lowerer.unsupported("SC1090", node, "message listeners take zero or one parameter and may return void or Promise<void>");
|
|
5529
|
+
}
|
|
5530
|
+
const param = cb.type.params[0];
|
|
5531
|
+
if (param !== undefined && param.kind !== "dyn" && !lowerer.jsonSafe(param)) {
|
|
5532
|
+
lowerer.unsupported("SC1090", node, `message listeners whose parameter is not JSON-shaped or unknown (got '${lowerer.fmt(param)}')`);
|
|
5533
|
+
}
|
|
5534
|
+
return cb;
|
|
5535
|
+
}
|
|
5536
|
+
function ipcDisconnectListener(lowerer, node) {
|
|
5537
|
+
const cb = lowerer.lowerExpr(node);
|
|
5538
|
+
if (cb.type.kind !== "func" || cb.type.params.length !== 0 || cb.type.ret.kind !== "void") {
|
|
5539
|
+
lowerer.unsupported("SC1090", node, "disconnect listeners take no parameters and return void");
|
|
5540
|
+
}
|
|
5541
|
+
return cb;
|
|
5542
|
+
}
|
|
5543
|
+
function ipcSendCallback(lowerer, node) {
|
|
5544
|
+
const cb = lowerer.lowerExpr(node);
|
|
5545
|
+
if (cb.type.kind !== "func" || cb.type.params.length > 1 || cb.type.ret.kind !== "void") {
|
|
5546
|
+
lowerer.unsupported("SC1090", node, "send callbacks take () or (error: Error | null) and return void");
|
|
5547
|
+
}
|
|
5548
|
+
const param = cb.type.params[0];
|
|
5549
|
+
if (param !== undefined) {
|
|
5550
|
+
const def = param.kind === "union" ? lowerer.unions.get(param.unionId) : undefined;
|
|
5551
|
+
const valid = !!def && def.arms.length === 2 &&
|
|
5552
|
+
def.arms.some((arm) => arm.kind === "nullT") &&
|
|
5553
|
+
def.arms.some((arm) => arm.kind === "object" && arm.className === "%Error");
|
|
5554
|
+
if (!valid) {
|
|
5555
|
+
lowerer.unsupported("SC1090", node, `send callbacks whose parameter is not 'Error | null' (got '${lowerer.fmt(param)}')`);
|
|
5556
|
+
}
|
|
5557
|
+
}
|
|
5558
|
+
return cb;
|
|
5559
|
+
}
|
|
5560
|
+
function lowerProcessIpcSend(lowerer, call) {
|
|
5561
|
+
const loc = locOf(call);
|
|
5562
|
+
if (call.arguments.length < 1 || call.arguments.length > 2 || call.arguments.some(ts.isSpreadElement)) {
|
|
5563
|
+
lowerer.noLowering(`process.send with ${call.arguments.length} arguments`, call, "send(message[, callback]) is supported; handle transfer and options are not");
|
|
5564
|
+
}
|
|
5565
|
+
const json = ipcJsonMessage(lowerer, call.arguments[0], loc);
|
|
5566
|
+
if (call.arguments[1] === undefined) {
|
|
5567
|
+
return { kind: "libCall", fn: "process.send", args: [json], type: BOOL, loc };
|
|
5568
|
+
}
|
|
5569
|
+
const callback = ipcSendCallback(lowerer, call.arguments[1]);
|
|
5570
|
+
return { kind: "libCall", fn: "process.sendCb", args: [json, callback], type: BOOL, loc };
|
|
5571
|
+
}
|
|
4853
5572
|
export function lowerChildMethodCall(lowerer, call, access) {
|
|
4854
5573
|
if (call.questionDotToken || access.questionDotToken)
|
|
4855
5574
|
return null;
|
|
@@ -4859,20 +5578,63 @@ export function lowerChildMethodCall(lowerer, call, access) {
|
|
|
4859
5578
|
return null;
|
|
4860
5579
|
const name = access.name.text;
|
|
4861
5580
|
const loc = locOf(call);
|
|
4862
|
-
if (name === "
|
|
5581
|
+
if (name === "send") {
|
|
5582
|
+
if (call.arguments.length < 1 || call.arguments.length > 2 || call.arguments.some(ts.isSpreadElement)) {
|
|
5583
|
+
lowerer.noLowering(`child.send with ${call.arguments.length} arguments`, call, "send(message[, callback]) is supported; handle transfer and options are not");
|
|
5584
|
+
}
|
|
5585
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
5586
|
+
const json = ipcJsonMessage(lowerer, call.arguments[0], loc);
|
|
5587
|
+
if (call.arguments[1] === undefined) {
|
|
5588
|
+
return { kind: "libCall", fn: "child.send", args: [receiver, json], type: BOOL, loc };
|
|
5589
|
+
}
|
|
5590
|
+
const callback = ipcSendCallback(lowerer, call.arguments[1]);
|
|
5591
|
+
return { kind: "libCall", fn: "child.sendCb", args: [receiver, json, callback], type: BOOL, loc };
|
|
5592
|
+
}
|
|
5593
|
+
if (name === "disconnect" && call.arguments.length === 0) {
|
|
5594
|
+
if (!ts.isExpressionStatement(call.parent)) {
|
|
5595
|
+
lowerer.unsupported("SC1090", call, "using the result of child.disconnect() (call it as its own statement)");
|
|
5596
|
+
}
|
|
5597
|
+
return {
|
|
5598
|
+
kind: "libCall",
|
|
5599
|
+
fn: "child.disconnect",
|
|
5600
|
+
args: [lowerer.lowerExpr(access.expression)],
|
|
5601
|
+
type: VOID,
|
|
5602
|
+
loc,
|
|
5603
|
+
};
|
|
5604
|
+
}
|
|
5605
|
+
if ((name === "on" || name === "once") && call.arguments.length === 2) {
|
|
4863
5606
|
const evT = lowerer.typeOf(call.arguments[0]);
|
|
4864
5607
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
4865
|
-
if (event !== "exit" && event !== "error") {
|
|
4866
|
-
lowerer.noLowering(`child
|
|
5608
|
+
if (event !== "exit" && event !== "close" && event !== "error" && event !== "message" && event !== "disconnect") {
|
|
5609
|
+
lowerer.noLowering(`child.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, call.arguments[0], '"exit", "close", "error", "message", and "disconnect" are the supported child events (as literals)');
|
|
4867
5610
|
}
|
|
4868
5611
|
if (!ts.isExpressionStatement(call.parent)) {
|
|
4869
|
-
lowerer.unsupported("SC1090", call,
|
|
5612
|
+
lowerer.unsupported("SC1090", call, `chaining child.${name}(...) (the result is void here — register each listener as its own statement)`);
|
|
4870
5613
|
}
|
|
4871
5614
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
5615
|
+
const once = { kind: "boolLit", value: name === "once", type: BOOL, loc };
|
|
5616
|
+
if (event === "message") {
|
|
5617
|
+
return {
|
|
5618
|
+
kind: "libCall",
|
|
5619
|
+
fn: "child.onMessage",
|
|
5620
|
+
args: [receiver, ipcMessageListener(lowerer, call.arguments[1]), once],
|
|
5621
|
+
type: VOID,
|
|
5622
|
+
loc,
|
|
5623
|
+
};
|
|
5624
|
+
}
|
|
5625
|
+
if (event === "disconnect") {
|
|
5626
|
+
return {
|
|
5627
|
+
kind: "libCall",
|
|
5628
|
+
fn: "child.onDisconnect",
|
|
5629
|
+
args: [receiver, ipcDisconnectListener(lowerer, call.arguments[1]), once],
|
|
5630
|
+
type: VOID,
|
|
5631
|
+
loc,
|
|
5632
|
+
};
|
|
5633
|
+
}
|
|
4872
5634
|
const cb = lowerer.lowerExpr(call.arguments[1]);
|
|
4873
|
-
if (cb.type.kind !== "func" || cb.type.params.length > (event === "
|
|
4874
|
-
lowerer.unsupported("SC1090", call.arguments[1], event
|
|
4875
|
-
?
|
|
5635
|
+
if (cb.type.kind !== "func" || cb.type.params.length > (event === "error" ? 1 : 2)) {
|
|
5636
|
+
lowerer.unsupported("SC1090", call.arguments[1], event !== "error"
|
|
5637
|
+
? `${event} listeners with more than two parameters (use (code, signal), (code), or ())`
|
|
4876
5638
|
: "error listeners with more than one parameter (use (err) or ())");
|
|
4877
5639
|
}
|
|
4878
5640
|
if (cb.type.ret.kind !== "void") {
|
|
@@ -4882,7 +5644,7 @@ export function lowerChildMethodCall(lowerer, call, access) {
|
|
|
4882
5644
|
lowerer.unsupported("SC1090", call.arguments[1], "listeners returning a value (make the callback body a block, or return nothing)");
|
|
4883
5645
|
}
|
|
4884
5646
|
const param = cb.type.params[0];
|
|
4885
|
-
if (event === "exit") {
|
|
5647
|
+
if (event === "exit" || event === "close") {
|
|
4886
5648
|
const armsOk = param === undefined ||
|
|
4887
5649
|
(param.kind === "union" &&
|
|
4888
5650
|
(() => {
|
|
@@ -4892,7 +5654,7 @@ export function lowerChildMethodCall(lowerer, call, access) {
|
|
|
4892
5654
|
def.arms[1].kind === "nullT");
|
|
4893
5655
|
})());
|
|
4894
5656
|
if (!armsOk) {
|
|
4895
|
-
lowerer.unsupported("SC1090", call.arguments[1],
|
|
5657
|
+
lowerer.unsupported("SC1090", call.arguments[1], `${event} listeners whose parameter is not 'number | null' (got '${lowerer.fmt(param)}')`);
|
|
4896
5658
|
}
|
|
4897
5659
|
// The optional SECOND parameter is Node's signal: the terminating
|
|
4898
5660
|
// signal's name as `Signals | null` — a string | null union here
|
|
@@ -4907,9 +5669,9 @@ export function lowerChildMethodCall(lowerer, call, access) {
|
|
|
4907
5669
|
def.arms.some((a) => a.kind === "nullT"));
|
|
4908
5670
|
})());
|
|
4909
5671
|
if (!sigOk) {
|
|
4910
|
-
lowerer.unsupported("SC1090", call.arguments[1],
|
|
5672
|
+
lowerer.unsupported("SC1090", call.arguments[1], `${event} listeners whose signal parameter is not 'Signals | null' (got '${lowerer.fmt(sigParam)}')`);
|
|
4911
5673
|
}
|
|
4912
|
-
return { kind: "libCall", fn: "child.onExit", args: [receiver, cb], type: VOID, loc };
|
|
5674
|
+
return { kind: "libCall", fn: event === "close" ? "child.onClose" : "child.onExit", args: [receiver, cb], type: VOID, loc };
|
|
4913
5675
|
}
|
|
4914
5676
|
if (param !== undefined && !(param.kind === "object" && param.className === "%Error")) {
|
|
4915
5677
|
lowerer.unsupported("SC1090", call.arguments[1], `error listeners whose parameter is not 'Error' (got '${lowerer.fmt(param)}')`);
|
|
@@ -4948,7 +5710,7 @@ export function lowerChildMethodCall(lowerer, call, access) {
|
|
|
4948
5710
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
4949
5711
|
return { kind: "libCall", fn: "child.unref", args: [receiver], type: VOID, loc };
|
|
4950
5712
|
}
|
|
4951
|
-
lowerer.noLowering(`ChildProcess.${name}`, call, "on(\"exit\" | \"error\", cb), pid, exitCode, killed, kill(signal?), and unref() are
|
|
5713
|
+
lowerer.noLowering(`ChildProcess.${name}`, call, "send(), connected, disconnect(), on/once(\"message\" | \"disconnect\" | \"exit\" | \"close\" | \"error\", cb), pid, exitCode, killed, kill(signal?), and unref() are supported", lowerer.checker.getSymbolAtLocation(access.name));
|
|
4952
5714
|
}
|
|
4953
5715
|
/** Method calls on piped child-output stream receivers (child.stdout /
|
|
4954
5716
|
* child.stderr — the childStream kind): on/once("data" | "end").
|
|
@@ -4970,6 +5732,11 @@ export function lowerChildStreamMethodCall(lowerer, call, access) {
|
|
|
4970
5732
|
return null;
|
|
4971
5733
|
const name = access.name.text;
|
|
4972
5734
|
const loc = locOf(call);
|
|
5735
|
+
if (name === "setEncoding" && call.arguments.length === 1) {
|
|
5736
|
+
const receiver = lowerer.lowerExprExpecting(access.expression, CHILDSTREAM_T);
|
|
5737
|
+
const enc = bufEncoding(lowerer, "child stream setEncoding", call.arguments[0]);
|
|
5738
|
+
return { kind: "libCall", fn: "stream.childSetEncoding", args: [receiver, strLit(enc, loc)], type: CHILDSTREAM_T, loc };
|
|
5739
|
+
}
|
|
4973
5740
|
if ((name === "on" || name === "once") && call.arguments.length === 2) {
|
|
4974
5741
|
const evT = lowerer.typeOf(call.arguments[0]);
|
|
4975
5742
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
@@ -4998,12 +5765,12 @@ export function lowerChildStreamMethodCall(lowerer, call, access) {
|
|
|
4998
5765
|
const def = lowerer.unions.get(p.unionId);
|
|
4999
5766
|
return !!def && def.arms.some((a) => a.kind === "bytes" && a.elem === "u8");
|
|
5000
5767
|
};
|
|
5001
|
-
if (param !== undefined && !(param.kind === "bytes" && param.elem === "u8") && !unionOk(param)) {
|
|
5002
|
-
lowerer.unsupported("SC1090", call.arguments[1], `data listeners whose parameter is not 'Buffer'
|
|
5768
|
+
if (param !== undefined && param.kind !== "string" && !(param.kind === "bytes" && param.elem === "u8") && !unionOk(param)) {
|
|
5769
|
+
lowerer.unsupported("SC1090", call.arguments[1], `data listeners whose parameter is not 'Buffer', 'string', or a Buffer-armed union (got '${lowerer.fmt(param)}')`);
|
|
5003
5770
|
}
|
|
5004
|
-
return { kind: "libCall", fn: "stream.onData", args: [receiver, cb, once], type: VOID, loc };
|
|
5771
|
+
return { kind: "libCall", fn: param?.kind === "string" ? "stream.onDataStr" : "stream.onData", args: [receiver, cb, once], type: VOID, loc };
|
|
5005
5772
|
}
|
|
5006
|
-
lowerer.noLowering(`ReadableStream.${name}`, call, 'on/once("data" | "end", cb) are the supported child-stream members', lowerer.checker.getSymbolAtLocation(access.name));
|
|
5773
|
+
lowerer.noLowering(`ReadableStream.${name}`, call, 'setEncoding(encoding) and on/once("data" | "end", cb) are the supported child-stream members', lowerer.checker.getSymbolAtLocation(access.name));
|
|
5007
5774
|
}
|
|
5008
5775
|
/** Method calls on a piped child stdin writer. Writes copy one string or
|
|
5009
5776
|
* Uint8Array into the nonblocking runtime queue and return the backpressure
|
|
@@ -5290,6 +6057,19 @@ export function lowerProcessStreamProperty(lowerer, expr) {
|
|
|
5290
6057
|
let recv = expr.expression;
|
|
5291
6058
|
while (ts.isParenthesizedExpression(recv) || ts.isAsExpression(recv) || ts.isTypeAssertion(recv))
|
|
5292
6059
|
recv = recv.expression;
|
|
6060
|
+
// Browser-compatible packages commonly write
|
|
6061
|
+
// `(process.stdout || {}).isTTY`. Node's stream object is always
|
|
6062
|
+
// present and truthy, so the fallback cannot run; match the left
|
|
6063
|
+
// receiver exactly as the value-level always-truthy `||` lowering
|
|
6064
|
+
// does, preserving the native isatty/columns answer.
|
|
6065
|
+
if (ts.isBinaryExpression(recv) &&
|
|
6066
|
+
recv.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
6067
|
+
let left = recv.left;
|
|
6068
|
+
while (ts.isParenthesizedExpression(left) || ts.isAsExpression(left) || ts.isTypeAssertion(left))
|
|
6069
|
+
left = left.expression;
|
|
6070
|
+
if (ts.isPropertyAccessExpression(left))
|
|
6071
|
+
recv = left;
|
|
6072
|
+
}
|
|
5293
6073
|
if (!ts.isPropertyAccessExpression(recv))
|
|
5294
6074
|
return null;
|
|
5295
6075
|
const stream = lowerer.stdlibGlobalMember(recv, "process");
|
|
@@ -5399,6 +6179,9 @@ export function lowerProcessProperty(lowerer, expr) {
|
|
|
5399
6179
|
if (member === "argv") {
|
|
5400
6180
|
return { kind: "libCall", fn: "process.argv", args: [], type: arrayOf(STRING), loc };
|
|
5401
6181
|
}
|
|
6182
|
+
if (member === "connected") {
|
|
6183
|
+
return { kind: "libCall", fn: "process.connected", args: [], type: BOOL, loc };
|
|
6184
|
+
}
|
|
5402
6185
|
// process.execArgv: the extra CLI arguments Node itself consumed — a
|
|
5403
6186
|
// compiled binary consumed none, so the honest answer is a fresh [].
|
|
5404
6187
|
if (member === "execArgv") {
|
|
@@ -5477,48 +6260,55 @@ export function envValueType(lowerer) {
|
|
|
5477
6260
|
* @types/node, a user alias reshaping the result) fences honestly. Key
|
|
5478
6261
|
* and row order follow getifaddrs enumeration — Node itself guarantees
|
|
5479
6262
|
* no order (compare structurally). */
|
|
5480
|
-
/** `fs.readdirSync
|
|
5481
|
-
* parentPath (the path argument as given, Node's
|
|
5482
|
-
* %dtype entry kind (libuv's UV_DIRENT encoding;
|
|
5483
|
-
* to lstat, Node's getDirents rule). The options
|
|
5484
|
-
* member-by-member; the result type must
|
|
5485
|
-
* array from type-mapper.ts — anything else
|
|
5486
|
-
* reshaping Dirent) fences honestly. */
|
|
5487
|
-
function lowerFsReaddirTypesCall(lowerer, call, loc) {
|
|
6263
|
+
/** `fs.readdirSync` / `fs.promises.readdir` with `{ withFileTypes: true }`
|
|
6264
|
+
* — Dirent rows: name + parentPath (the path argument as given, Node's
|
|
6265
|
+
* own rule) + the hidden %dtype entry kind (libuv's UV_DIRENT encoding;
|
|
6266
|
+
* DT_UNKNOWN falls back to lstat, Node's getDirents rule). The options
|
|
6267
|
+
* literal is checked member-by-member; the result type must carry the
|
|
6268
|
+
* interned Dirent record array from type-mapper.ts — anything else
|
|
6269
|
+
* (encoding: 'buffer', a user alias reshaping Dirent) fences honestly. */
|
|
6270
|
+
function lowerFsReaddirTypesCall(lowerer, call, loc, promiseForm) {
|
|
6271
|
+
const operation = promiseForm ? "fs.promises.readdir" : "readdirSync";
|
|
6272
|
+
const spelling = promiseForm ? "readdir" : "readdirSync";
|
|
5488
6273
|
const optsNode = call.arguments[1];
|
|
5489
6274
|
if (!ts.isObjectLiteralExpression(optsNode)) {
|
|
5490
|
-
lowerer.noLowering(
|
|
6275
|
+
lowerer.noLowering(`${operation} with a non-literal options argument`, optsNode, `pass the options inline so each member can be checked: ${spelling}(path, { withFileTypes: true })`);
|
|
5491
6276
|
}
|
|
5492
6277
|
let sawWithFileTypes = false;
|
|
5493
6278
|
for (const p of optsNode.properties) {
|
|
5494
6279
|
const m = optionMember(p);
|
|
5495
6280
|
if (!m) {
|
|
5496
|
-
lowerer.noLowering(
|
|
6281
|
+
lowerer.noLowering(`${operation} with this options shape`, p, "spreads and computed keys have no lowering — write each member inline");
|
|
5497
6282
|
}
|
|
5498
6283
|
if (m.name === "withFileTypes") {
|
|
5499
6284
|
const t = lowerer.typeOf(m.value);
|
|
5500
6285
|
if (!(t.flags & ts.TypeFlags.BooleanLiteral) || lowerer.checker.typeToString(t) !== "true") {
|
|
5501
|
-
lowerer.noLowering(
|
|
6286
|
+
lowerer.noLowering(`${operation} with a non-literal-true withFileTypes`, m.value, "withFileTypes: true is the Dirent form; omit the options for plain names");
|
|
5502
6287
|
}
|
|
5503
6288
|
sawWithFileTypes = true;
|
|
5504
6289
|
}
|
|
5505
6290
|
else if (m.name === "encoding") {
|
|
5506
6291
|
const t = lowerer.typeOf(m.value);
|
|
5507
6292
|
if (!t.isStringLiteralType() || (t.value !== "utf8" && t.value !== "utf-8")) {
|
|
5508
|
-
lowerer.noLowering(
|
|
6293
|
+
lowerer.noLowering(`${operation} with a non-utf8 encoding`, m.value, "names decode as utf8 (the default); encoding: 'buffer' has no lowering");
|
|
5509
6294
|
}
|
|
5510
6295
|
}
|
|
5511
6296
|
else {
|
|
5512
6297
|
// The options-record stance: recursive (a documented knob with
|
|
5513
6298
|
// no lowering) fences by name; undocumented keys drop like Node.
|
|
5514
|
-
fenceOrDropOptionKey(lowerer, p, m.name,
|
|
6299
|
+
fenceOrDropOptionKey(lowerer, p, m.name, operation, FS_READDIR_DOCUMENTED_OPTIONS, "withFileTypes: true (and the default encoding) is the supported options surface — recursive listings want an explicit walk");
|
|
5515
6300
|
}
|
|
5516
6301
|
}
|
|
5517
6302
|
if (!sawWithFileTypes) {
|
|
5518
|
-
lowerer.noLowering(
|
|
6303
|
+
lowerer.noLowering(`${operation} with 2 arguments`, call, `${spelling}(path) lists names; ${spelling}(path, { withFileTypes: true }) lists Dirents`);
|
|
5519
6304
|
}
|
|
5520
|
-
const fence = () => lowerer.noLowering(
|
|
5521
|
-
const
|
|
6305
|
+
const fence = () => lowerer.noLowering(`${operation}(path, { withFileTypes: true }) where the result is not the Dirent array`, call, "{ name, parentPath, isFile(), isDirectory(), isSymbolicLink() } rows are the supported result shape");
|
|
6306
|
+
const callType = lowerer.mapTypeOf(lowerer.typeOf(call));
|
|
6307
|
+
if (!callType)
|
|
6308
|
+
fence();
|
|
6309
|
+
const result = promiseForm
|
|
6310
|
+
? callType.kind === "promise" ? callType.inner : fence()
|
|
6311
|
+
: callType;
|
|
5522
6312
|
if (result?.kind !== "array" || result.elem.kind !== "record")
|
|
5523
6313
|
fence();
|
|
5524
6314
|
const shape = lowerer.shapes.get(result.elem.shapeId);
|
|
@@ -5530,7 +6320,13 @@ function lowerFsReaddirTypesCall(lowerer, call, loc) {
|
|
|
5530
6320
|
fence();
|
|
5531
6321
|
}
|
|
5532
6322
|
const path = lowerer.lowerExprExpecting(call.arguments[0], STRING);
|
|
5533
|
-
return {
|
|
6323
|
+
return {
|
|
6324
|
+
kind: "libCall",
|
|
6325
|
+
fn: promiseForm ? "fsp.readdirTypes" : "fs.readdirTypesSync",
|
|
6326
|
+
args: [path],
|
|
6327
|
+
type: callType,
|
|
6328
|
+
loc,
|
|
6329
|
+
};
|
|
5534
6330
|
}
|
|
5535
6331
|
/** `d.isFile()` / `d.isDirectory()` / `d.isSymbolicLink()` on a Dirent-
|
|
5536
6332
|
* typed receiver (the interned record — provenance via the checker's
|
|
@@ -5864,13 +6660,23 @@ export function lowerProcessEnvGet(lowerer, expr) {
|
|
|
5864
6660
|
}
|
|
5865
6661
|
/** `process.exit(code)` / `process.cwd()` → libCall. The fallback
|
|
5866
6662
|
* declaration makes exit's code required; @types/node declares it
|
|
5867
|
-
* optional
|
|
5868
|
-
*
|
|
5869
|
-
* is fenced like every other unsupported process member, so "never set"
|
|
5870
|
-
* always holds in a compiled program). */
|
|
6663
|
+
* optional. A bare `process.exit()` uses the current exitCode, or zero
|
|
6664
|
+
* when unset. */
|
|
5871
6665
|
export function lowerProcessMethodCall(lowerer, call, access) {
|
|
5872
6666
|
if (call.questionDotToken)
|
|
5873
6667
|
return null;
|
|
6668
|
+
const directProcessMember = lowerer.stdlibGlobalMember(access, "process");
|
|
6669
|
+
if (directProcessMember === "send")
|
|
6670
|
+
return lowerProcessIpcSend(lowerer, call);
|
|
6671
|
+
if (directProcessMember === "disconnect") {
|
|
6672
|
+
if (call.arguments.length !== 0) {
|
|
6673
|
+
lowerer.noLowering(`process.disconnect with ${call.arguments.length} arguments`, call);
|
|
6674
|
+
}
|
|
6675
|
+
if (!ts.isExpressionStatement(call.parent)) {
|
|
6676
|
+
lowerer.unsupported("SC1090", call, "using the result of process.disconnect() (call it as its own statement)");
|
|
6677
|
+
}
|
|
6678
|
+
return { kind: "libCall", fn: "process.disconnect", args: [], type: VOID, loc: locOf(call) };
|
|
6679
|
+
}
|
|
5874
6680
|
// process.stdout.write(s[, encoding][, callback]) and stderr's twin:
|
|
5875
6681
|
// raw bytes, no newline or formatting. stdout shares console.log's
|
|
5876
6682
|
// promptly-submitted stream, preserving source order. Node's boolean
|
|
@@ -6131,6 +6937,21 @@ export function lowerProcessMethodCall(lowerer, call, access) {
|
|
|
6131
6937
|
}
|
|
6132
6938
|
const evT = lowerer.typeOf(call.arguments[0]);
|
|
6133
6939
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
6940
|
+
if ((event === "message" || event === "disconnect") && !isOff) {
|
|
6941
|
+
if (!ts.isExpressionStatement(call.parent)) {
|
|
6942
|
+
lowerer.unsupported("SC1090", call, "chaining process IPC listener registration (register each listener as its own statement)");
|
|
6943
|
+
}
|
|
6944
|
+
const callback = event === "message"
|
|
6945
|
+
? ipcMessageListener(lowerer, call.arguments[1])
|
|
6946
|
+
: ipcDisconnectListener(lowerer, call.arguments[1]);
|
|
6947
|
+
return {
|
|
6948
|
+
kind: "libCall",
|
|
6949
|
+
fn: event === "message" ? "process.onMessage" : "process.onDisconnect",
|
|
6950
|
+
args: [callback, boolLit(member === "once", loc)],
|
|
6951
|
+
type: VOID,
|
|
6952
|
+
loc,
|
|
6953
|
+
};
|
|
6954
|
+
}
|
|
6134
6955
|
// own(), not a bare index: the key is a USER-written event name,
|
|
6135
6956
|
// and `{ SIGINT: 2 }["__proto__"]` answers Object.prototype — an
|
|
6136
6957
|
// object flowed into a numLit and emitted itself into the C
|
|
@@ -6173,7 +6994,7 @@ export function lowerProcessMethodCall(lowerer, call, access) {
|
|
|
6173
6994
|
};
|
|
6174
6995
|
}
|
|
6175
6996
|
if (signo === undefined && event !== "exit") {
|
|
6176
|
-
lowerer.noLowering(`process.${member}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, call.arguments[0], '"SIGINT", "SIGTERM", "exit", "warning", "unhandledRejection", and "rejectionHandled" are the supported process events (as literals)');
|
|
6997
|
+
lowerer.noLowering(`process.${member}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, call.arguments[0], '"message", "disconnect", "SIGINT", "SIGTERM", "exit", "warning", "unhandledRejection", and "rejectionHandled" are the supported process events (as literals)');
|
|
6177
6998
|
}
|
|
6178
6999
|
if (!ts.isExpressionStatement(call.parent)) {
|
|
6179
7000
|
lowerer.unsupported("SC1090", call, "chaining process listener registration (the result is void here — register each listener as its own statement)");
|
|
@@ -6481,7 +7302,7 @@ export function lowerProcessMethodCall(lowerer, call, access) {
|
|
|
6481
7302
|
const arg = call.arguments[0];
|
|
6482
7303
|
const code = arg !== undefined
|
|
6483
7304
|
? lowerer.lowerExprExpecting(arg, F64)
|
|
6484
|
-
: { kind: "
|
|
7305
|
+
: { kind: "libCall", fn: "process.currentExitCode", args: [], type: F64, loc };
|
|
6485
7306
|
return { kind: "libCall", fn: "process.exit", args: [code], type: VOID, loc };
|
|
6486
7307
|
}
|
|
6487
7308
|
return null; // process.argv(...) etc. are tsc errors before lowering
|
|
@@ -6593,6 +7414,16 @@ export function lowerDateCall(lowerer, call, access) {
|
|
|
6593
7414
|
}
|
|
6594
7415
|
return { kind: "libCall", fn: "date.now", args: [], type: F64, loc };
|
|
6595
7416
|
}
|
|
7417
|
+
// Date.parse uses the same bounded date-string parser as
|
|
7418
|
+
// new Date(dateString).getTime(); keep their NaN and TimeClip behavior
|
|
7419
|
+
// identical for the ISO timestamps used by CLI applications.
|
|
7420
|
+
if (lowerer.stdlibGlobalMember(access, "Date") === "parse") {
|
|
7421
|
+
if (call.arguments.length !== 1 || ts.isSpreadElement(call.arguments[0])) {
|
|
7422
|
+
lowerer.noLowering(`Date.parse with ${call.arguments.length} arguments`, call);
|
|
7423
|
+
}
|
|
7424
|
+
const input = lowerer.lowerExprExpecting(call.arguments[0], STRING);
|
|
7425
|
+
return { kind: "libCall", fn: "date.parse", args: [input], type: F64, loc };
|
|
7426
|
+
}
|
|
6596
7427
|
// Date.UTC(year[, month[, date[, hours[, minutes[, seconds[, ms]]]]]]):
|
|
6597
7428
|
// a pure function of its numbers — the runtime's MakeDay/MakeTime/
|
|
6598
7429
|
// TimeClip. Omitted trailing arguments complete with the spec's
|
|
@@ -7432,6 +8263,8 @@ export function lowerProcessOptionalMethodCall(lowerer, expr) {
|
|
|
7432
8263
|
if (!ts.isPropertyAccessExpression(expr.expression))
|
|
7433
8264
|
return null;
|
|
7434
8265
|
const member = lowerer.stdlibGlobalMember(expr.expression, "process");
|
|
8266
|
+
if (member === "send")
|
|
8267
|
+
return lowerProcessIpcSend(lowerer, expr);
|
|
7435
8268
|
if (member !== "getuid" && member !== "getgid")
|
|
7436
8269
|
return null;
|
|
7437
8270
|
if (expr.arguments.length !== 0) {
|