@scriptc/compiler 0.1.1 → 0.1.3
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 +104 -31
- package/ambient/scriptc.d.ts +2 -1
- package/dist/backend/build-cache.js.map +1 -1
- package/dist/backend/c/async.d.ts +5 -0
- package/dist/backend/c/async.js +81 -2
- package/dist/backend/c/async.js.map +1 -1
- package/dist/backend/c/c-emitter.d.ts +25 -10
- package/dist/backend/c/c-emitter.js +74 -22
- package/dist/backend/c/c-emitter.js.map +1 -1
- package/dist/backend/c/exprs.d.ts +8 -1
- package/dist/backend/c/exprs.js +371 -18
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/c/island.js.map +1 -1
- package/dist/backend/c/may-throw.js.map +1 -1
- package/dist/backend/c/shapes.js +15 -13
- package/dist/backend/c/shapes.js.map +1 -1
- package/dist/backend/c/stmts.d.ts +3 -5
- package/dist/backend/c/stmts.js +58 -17
- package/dist/backend/c/stmts.js.map +1 -1
- package/dist/backend/c/types.d.ts +4 -0
- package/dist/backend/c/types.js +18 -1
- package/dist/backend/c/types.js.map +1 -1
- package/dist/backend/c/types.test.js +8 -1
- package/dist/backend/c/types.test.js.map +1 -1
- package/dist/backend/c/walkers.js +63 -16
- package/dist/backend/c/walkers.js.map +1 -1
- package/dist/backend/external-c.js.map +1 -1
- package/dist/backend/json-literal.d.ts +7 -0
- package/dist/backend/json-literal.js +17 -0
- package/dist/backend/json-literal.js.map +1 -0
- package/dist/backend/json-literal.test.d.ts +1 -0
- package/dist/backend/json-literal.test.js +17 -0
- package/dist/backend/json-literal.test.js.map +1 -0
- package/dist/backend/link-plan.d.ts +1 -1
- package/dist/backend/link-plan.js +5 -1
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/linker.js.map +1 -1
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/classes.js +8 -7
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +4 -0
- package/dist/backend/llvm/common.js +6 -0
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/common.test.d.ts +1 -0
- package/dist/backend/llvm/common.test.js +9 -0
- package/dist/backend/llvm/common.test.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +4 -0
- package/dist/backend/llvm/dyn.js +80 -16
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +132 -25
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +85 -3
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +5 -0
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +15 -3
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-island.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +8 -0
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +3 -3
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +222 -10
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-callbacks.d.ts +3 -0
- package/dist/backend/llvm/expr-stream-callbacks.js +53 -0
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +24 -0
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +50 -5
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +64 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/lib-stream.js +6 -3
- package/dist/backend/llvm/lib-stream.js.map +1 -1
- package/dist/backend/llvm/shapes.js +10 -8
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/unsupported.js.map +1 -1
- package/dist/backend/llvm/walkers.js +7 -5
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +2 -0
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info.d.ts +2 -1
- package/dist/backend/native-link-info.js +4 -0
- package/dist/backend/native-link-info.js.map +1 -1
- package/dist/backend/native-link-info.test.js +24 -1
- package/dist/backend/native-link-info.test.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +8 -6
- package/dist/backend/native-toolchain.js +28 -13
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +36 -1
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack-targets.test.js.map +1 -1
- package/dist/backend/runtime-pack.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +1 -0
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/targets.d.ts +5 -0
- package/dist/backend/targets.js +9 -0
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +6 -1
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/backend/vendor-archives.js.map +1 -1
- package/dist/compat/fetch-profile.js +4 -2
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/surface-manifest.js +13 -0
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +6 -5
- package/dist/diagnostics/diagnostic.js +10 -9
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +1 -0
- package/dist/executable/executable-cache.js +1 -0
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +1 -0
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/ffi/ffi-manifest.js.map +1 -1
- package/dist/frontend/cjs-lexer.js.map +1 -1
- package/dist/frontend/dts-paths.js.map +1 -1
- package/dist/frontend/input-tracker.d.ts +10 -0
- package/dist/frontend/input-tracker.js +26 -3
- package/dist/frontend/input-tracker.js.map +1 -1
- package/dist/frontend/input-tracker.test.js +8 -1
- package/dist/frontend/input-tracker.test.js.map +1 -1
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +4 -0
- package/dist/frontend/lowering/array-values.js +18 -2
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/callback-arg.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.d.ts +31 -0
- package/dist/frontend/lowering/containers/bytes.js +1105 -0
- package/dist/frontend/lowering/containers/bytes.js.map +1 -0
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +17 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js +345 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.d.ts +13 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js +76 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -0
- package/dist/frontend/lowering/expressions/object-literals.d.ts +44 -0
- package/dist/frontend/lowering/expressions/object-literals.js +1815 -0
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -0
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +31 -0
- package/dist/frontend/lowering/expressions/optional-chains.js +382 -0
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.d.ts +7 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.js +26 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.js +7 -2
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +16 -10
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-async-iteration.d.ts +7 -0
- package/dist/frontend/lowering/lower-async-iteration.js +657 -0
- package/dist/frontend/lowering/lower-async-iteration.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +40 -21
- package/dist/frontend/lowering/lower-builtins.js +611 -115
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +21 -10
- package/dist/frontend/lowering/lower-calls.js +516 -75
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +7 -13
- package/dist/frontend/lowering/lower-classes.js +128 -35
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-comptime.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +1 -47
- package/dist/frontend/lowering/lower-containers.js +147 -1475
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +51 -5
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +0 -78
- package/dist/frontend/lowering/lower-exprs.js +503 -2353
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +18 -3
- package/dist/frontend/lowering/lower-generators.js +145 -9
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +10 -3
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +6 -0
- package/dist/frontend/lowering/lower-island.js +37 -5
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +60 -15
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-node-module.d.ts +18 -0
- package/dist/frontend/lowering/lower-node-module.js +302 -0
- package/dist/frontend/lowering/lower-node-module.js.map +1 -0
- package/dist/frontend/lowering/lower-server.js +1 -1
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +10 -28
- package/dist/frontend/lowering/lower-stmts.js +542 -259
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +4 -4
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +41 -38
- package/dist/frontend/lowering/lowerer.js +148 -62
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/optional-arguments.d.ts +9 -0
- package/dist/frontend/lowering/optional-arguments.js +57 -0
- package/dist/frontend/lowering/optional-arguments.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +5 -3
- package/dist/frontend/lowering/surfaces.js +33 -13
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-declarations.d.ts +35 -0
- package/dist/frontend/npm-static-declarations.js +519 -0
- package/dist/frontend/npm-static-declarations.js.map +1 -0
- package/dist/frontend/npm-static-declarations.test.d.ts +1 -0
- package/dist/frontend/npm-static-declarations.test.js +132 -0
- package/dist/frontend/npm-static-declarations.test.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +2 -0
- package/dist/frontend/npm-static.js +26 -10
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +60 -0
- package/dist/frontend/npm.js +38 -4
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.d.ts +1 -0
- package/dist/frontend/npm.test.js +50 -0
- package/dist/frontend/npm.test.js.map +1 -0
- package/dist/frontend/program.d.ts +3 -2
- package/dist/frontend/program.js +513 -70
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.d.ts +26 -3
- package/dist/frontend/resolve.js +166 -29
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/resolve.test.js +24 -1
- package/dist/frontend/resolve.test.js.map +1 -1
- package/dist/frontend/runtime-resolve.d.ts +22 -0
- package/dist/frontend/runtime-resolve.js +101 -0
- package/dist/frontend/runtime-resolve.js.map +1 -0
- package/dist/frontend/runtime-resolve.test.d.ts +1 -0
- package/dist/frontend/runtime-resolve.test.js +18 -0
- package/dist/frontend/runtime-resolve.test.js.map +1 -0
- package/dist/frontend/ts7/adapter.js +12 -13
- package/dist/frontend/ts7/adapter.js.map +1 -1
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/census-check.js +3 -3
- package/dist/frontend/ts7/census-check.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +3 -0
- package/dist/frontend/ts7/checker.js +12 -3
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/enums.js.map +1 -1
- package/dist/frontend/ts7/program-adapter.js +42 -3
- package/dist/frontend/ts7/program-adapter.js.map +1 -1
- package/dist/frontend/type-mapper.js +171 -16
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.js +1 -0
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/ir.d.ts +125 -52
- package/dist/ir/ir.js +136 -7
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/validate.js +120 -63
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/cache-primitives.js.map +1 -1
- package/dist/library/cache-reset.test.js.map +1 -1
- package/dist/library/compiler-self-identity.js.map +1 -1
- package/dist/library/int-infer.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.d.ts +1 -0
- package/dist/library/library-cache.js +1 -0
- package/dist/library/library-cache.js.map +1 -1
- package/dist/library/library-cache.test.js +18 -5
- package/dist/library/library-cache.test.js.map +1 -1
- package/dist/library/library-profile.js.map +1 -1
- package/dist/library/sidecar-validate.js.map +1 -1
- package/dist/library/sidecar.js.map +1 -1
- package/dist/startup-cache.js.map +1 -1
- package/dist/wasi-paths.js.map +1 -1
- package/package.json +18 -18
- package/surface-manifest.json +91 -20
|
@@ -7,20 +7,24 @@ 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, locOf, requireSpecOf } from "../program.js";
|
|
10
|
+
import { canonicalBuiltinModule, isJsSourceFile, isNodeEsmFile, locOf, requireSpecOf } 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
|
+
import { requireResolvePathsRuntime, resolveImportMetaRuntime, resolveRequireRuntime } from "../runtime-resolve.js";
|
|
15
16
|
import { invalidJsonModuleDiag, requiresDynamicImportDiag } from "../../diagnostics/diagnostic.js";
|
|
16
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 {
|
|
18
|
+
import { lowerAbsenceProbe } from "./lower-exprs.js";
|
|
19
|
+
import { conditionalSpreadOf, lowerDynObjectLiteral } from "./expressions/object-literals.js";
|
|
20
|
+
import { isSafeToDiscard } from "./expressions/evaluation-safety.js";
|
|
21
|
+
import { defaultAfterUndefined, lowerStaticallyUndefinedArgument } from "./optional-arguments.js";
|
|
18
22
|
import { HTTP2_CONSTANTS } from "./http2-constants.js";
|
|
19
23
|
import { CRYPTO_CIPHERS, CRYPTO_CONSTANTS, CRYPTO_CURVES, CRYPTO_HASHES } from "./crypto-tables.js";
|
|
20
|
-
import { timerStyleCallback } from "./lower-calls.js";
|
|
24
|
+
import { generatorMeta, timerStyleCallback } from "./lower-calls.js";
|
|
21
25
|
import { registerHttpClientFnBinding, voidizedCallback } from "./lower-server.js";
|
|
22
26
|
import { pairsSnapshotHelper } from "./pairs-snapshot.js";
|
|
23
|
-
import { BOOL, BYTES_U8, CHILD_T, CHILDSTREAM_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";
|
|
27
|
+
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";
|
|
24
28
|
import { boolLit, countedFor, numLit, strLit, varRef } from "../../ir/build.js";
|
|
25
29
|
function optionalStringTags(lowerer, type) {
|
|
26
30
|
if (type.kind !== "union")
|
|
@@ -32,6 +36,74 @@ function optionalStringTags(lowerer, type) {
|
|
|
32
36
|
const undefinedTag = lowerer.armTag(type.unionId, UNDEFINED_T);
|
|
33
37
|
return stringTag >= 0 && undefinedTag >= 0 ? { stringTag, undefinedTag } : null;
|
|
34
38
|
}
|
|
39
|
+
/** timers/promises.setInterval(delay, value), the first Node API built on
|
|
40
|
+
* the generic async-generator protocol. The supported form has an explicit
|
|
41
|
+
* value and no AbortSignal options. It lowers to a generated typed async
|
|
42
|
+
* generator whose loop awaits the existing promise timeout then yields the
|
|
43
|
+
* retained value; creating the iterator remains lazy. */
|
|
44
|
+
export function lowerTimersPromisesSetInterval(lowerer, expr, bi, loc) {
|
|
45
|
+
if (bi.module !== "timers/promises" || bi.member !== "setInterval")
|
|
46
|
+
return null;
|
|
47
|
+
if (expr.arguments.some(ts.isSpreadElement)) {
|
|
48
|
+
lowerer.unsupported("SC1090", expr, "spread arguments");
|
|
49
|
+
}
|
|
50
|
+
if (expr.arguments.length !== 2) {
|
|
51
|
+
lowerer.noLowering(`timers/promises.setInterval with ${expr.arguments.length} arguments`, expr, "the lowered form is setInterval(delay, value) with an explicit yielded value; AbortSignal options are not supported yet");
|
|
52
|
+
}
|
|
53
|
+
const delayNode = expr.arguments[0];
|
|
54
|
+
const valueNode = expr.arguments[1];
|
|
55
|
+
const delay = lowerer.lowerExpr(delayNode);
|
|
56
|
+
const ms = delay.kind === "unitLit"
|
|
57
|
+
? { kind: "numLit", value: 1, type: F64, loc }
|
|
58
|
+
: lowerer.coerceInto(delayNode, delay, F64);
|
|
59
|
+
const value = lowerer.lowerExpr(valueNode);
|
|
60
|
+
const callType = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
61
|
+
if (callType?.kind !== "generator" || !callType.async) {
|
|
62
|
+
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
63
|
+
}
|
|
64
|
+
const genT = callType;
|
|
65
|
+
const yielded = lowerer.coerceInto(valueNode, value, genT.yieldT);
|
|
66
|
+
const fnName = `%fn${lowerer.lambdaCounter++}_tpInterval`;
|
|
67
|
+
const msParam = { id: "%tp.ms", name: "delay", type: F64, mutable: false };
|
|
68
|
+
const valueParam = { id: "%tp.value", name: "value", type: genT.yieldT, mutable: false };
|
|
69
|
+
const promiseVoid = { kind: "promise", inner: VOID };
|
|
70
|
+
const msRef = () => ({ kind: "varRef", localId: msParam.id, type: F64, loc });
|
|
71
|
+
const valueRef = () => ({ kind: "varRef", localId: valueParam.id, type: genT.yieldT, loc });
|
|
72
|
+
const fn = {
|
|
73
|
+
name: fnName,
|
|
74
|
+
params: [
|
|
75
|
+
{ localId: msParam.id, name: msParam.name, type: msParam.type },
|
|
76
|
+
{ localId: valueParam.id, name: valueParam.name, type: valueParam.type },
|
|
77
|
+
],
|
|
78
|
+
returnType: VOID,
|
|
79
|
+
locals: [msParam, valueParam],
|
|
80
|
+
async: true,
|
|
81
|
+
generator: generatorMeta(lowerer, genT),
|
|
82
|
+
body: [
|
|
83
|
+
{
|
|
84
|
+
kind: "while",
|
|
85
|
+
cond: { kind: "boolLit", value: true, type: BOOL, loc },
|
|
86
|
+
body: [
|
|
87
|
+
{
|
|
88
|
+
kind: "exprStmt",
|
|
89
|
+
expr: {
|
|
90
|
+
kind: "awaitExpr",
|
|
91
|
+
value: { kind: "libCall", fn: "tp.setTimeout", args: [msRef()], type: promiseVoid, loc },
|
|
92
|
+
type: VOID,
|
|
93
|
+
loc,
|
|
94
|
+
},
|
|
95
|
+
loc,
|
|
96
|
+
},
|
|
97
|
+
{ kind: "exprStmt", expr: { kind: "yieldExpr", value: valueRef(), type: VOID, loc }, loc },
|
|
98
|
+
],
|
|
99
|
+
loc,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
loc,
|
|
103
|
+
};
|
|
104
|
+
lowerer.liftedFns.push(fn);
|
|
105
|
+
return { kind: "call", callee: fnName, args: [ms, yielded], type: genT, loc };
|
|
106
|
+
}
|
|
35
107
|
function lowerBuiltinValuePreservingUndefined(lowerer, node) {
|
|
36
108
|
return lowerer.runtimeOptionalIdentifierValue(node)?.value ??
|
|
37
109
|
lowerAbsenceProbe(lowerer, node) ??
|
|
@@ -90,49 +162,11 @@ function lowerOptionalNumberPredicate(lowerer, value, fn, loc) {
|
|
|
90
162
|
}
|
|
91
163
|
return { kind: "call", callee: helper, args: [value], type: BOOL, loc };
|
|
92
164
|
}
|
|
93
|
-
/** Lower an optional builtin argument whose checker type is statically
|
|
94
|
-
* undefined/void. The returned expression exists only to preserve effects;
|
|
95
|
-
* callers replace its value with the API default. */
|
|
96
|
-
function lowerStaticallyUndefinedBuiltinArg(lowerer, node) {
|
|
97
|
-
const peel = (value) => {
|
|
98
|
-
let expr = value;
|
|
99
|
-
while (ts.isParenthesizedExpression(expr) ||
|
|
100
|
-
ts.isAsExpression(expr) ||
|
|
101
|
-
ts.isTypeAssertion(expr) ||
|
|
102
|
-
ts.isSatisfiesExpression(expr)) {
|
|
103
|
-
expr = expr.expression;
|
|
104
|
-
}
|
|
105
|
-
return expr;
|
|
106
|
-
};
|
|
107
|
-
let expr = node;
|
|
108
|
-
while (ts.isParenthesizedExpression(expr))
|
|
109
|
-
expr = expr.expression;
|
|
110
|
-
if ((lowerer.typeOf(expr).flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) === 0)
|
|
111
|
-
return null;
|
|
112
|
-
expr = peel(expr);
|
|
113
|
-
let sawVoid = false;
|
|
114
|
-
while (ts.isVoidExpression(expr)) {
|
|
115
|
-
sawVoid = true;
|
|
116
|
-
expr = peel(expr.expression);
|
|
117
|
-
}
|
|
118
|
-
return lowerer.lowerExpr(sawVoid ? expr : node);
|
|
119
|
-
}
|
|
120
|
-
function defaultAfterUndefined(value, dflt) {
|
|
121
|
-
if (droppableStatic(value))
|
|
122
|
-
return dflt;
|
|
123
|
-
return {
|
|
124
|
-
kind: "seqExpr",
|
|
125
|
-
stmts: [{ kind: "exprStmt", expr: value, loc: value.loc }],
|
|
126
|
-
result: dflt,
|
|
127
|
-
type: dflt.type,
|
|
128
|
-
loc: value.loc,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
165
|
/** Complete an optional builtin argument. Statically undefined spellings
|
|
132
166
|
* preserve their effects and select the default; a runtime unit-armed union
|
|
133
167
|
* uses nullish selection when every non-unit arm is the expected type. */
|
|
134
168
|
function lowerBuiltinOptionalDefault(lowerer, node, expected, dflt, nullIsDefault = false) {
|
|
135
|
-
const undefinedArg =
|
|
169
|
+
const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, node);
|
|
136
170
|
if (undefinedArg)
|
|
137
171
|
return defaultAfterUndefined(undefinedArg, dflt);
|
|
138
172
|
if (nullIsDefault && (lowerer.typeOf(node).flags & ts.TypeFlags.Null) !== 0) {
|
|
@@ -502,6 +536,134 @@ export function lowerCreateRequireCall(lowerer, call, loc) {
|
|
|
502
536
|
loc,
|
|
503
537
|
};
|
|
504
538
|
}
|
|
539
|
+
function staticString(node) {
|
|
540
|
+
if (node === undefined)
|
|
541
|
+
return null;
|
|
542
|
+
const value = stripTypeCasts(node);
|
|
543
|
+
return ts.isStringLiteralLike(value) ? value.text : null;
|
|
544
|
+
}
|
|
545
|
+
function runtimeResolveThrow(error, type, loc) {
|
|
546
|
+
return nodeThrowExpr(error.name === "TypeError" ? 1 : 0, error.code, error.message, type, loc);
|
|
547
|
+
}
|
|
548
|
+
/** import.meta.resolve("literal") folds through the runtime-module
|
|
549
|
+
* resolver, never TypeScript's declaration-file resolver. Relative and
|
|
550
|
+
* URL-like names remain valid even when no file exists; bare packages use
|
|
551
|
+
* Node's import-condition exports path. */
|
|
552
|
+
export function lowerImportMetaResolveCall(lowerer, call) {
|
|
553
|
+
const callee = call.expression;
|
|
554
|
+
if (!ts.isPropertyAccessExpression(callee) || callee.questionDotToken !== undefined ||
|
|
555
|
+
callee.name.text !== "resolve" || !ts.isMetaProperty(callee.expression) ||
|
|
556
|
+
callee.expression.keywordToken !== ts.SyntaxKind.ImportKeyword ||
|
|
557
|
+
callee.expression.name.text !== "meta") {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
if (call.questionDotToken !== undefined || call.arguments.length !== 1) {
|
|
561
|
+
lowerer.noLowering("import.meta.resolve with this argument shape", call, "the lowered form is import.meta.resolve(\"<static specifier>\") using the containing module as its parent");
|
|
562
|
+
}
|
|
563
|
+
const specifier = staticString(call.arguments[0]);
|
|
564
|
+
if (specifier === null) {
|
|
565
|
+
lowerer.noLowering("import.meta.resolve with a runtime-computed specifier", call.arguments[0], "a compiled binary has a fixed module graph — pass a string literal");
|
|
566
|
+
}
|
|
567
|
+
const result = resolveImportMetaRuntime(call.getSourceFile().fileName, specifier, lowerer.targetPlatform);
|
|
568
|
+
if (result === null) {
|
|
569
|
+
lowerer.noLowering(`import.meta.resolve of '${specifier}'`, call, "relative paths, URL-like names, builtins, and installed package names are supported; package-import aliases remain unsupported");
|
|
570
|
+
}
|
|
571
|
+
const loc = locOf(call);
|
|
572
|
+
return result.ok
|
|
573
|
+
? { kind: "strLit", value: result.value, type: STRING, loc }
|
|
574
|
+
: runtimeResolveThrow(result.error, STRING, loc);
|
|
575
|
+
}
|
|
576
|
+
function requireResolverBaseFile(lowerer, receiver) {
|
|
577
|
+
const created = createRequireCalleeFileOf(lowerer, receiver);
|
|
578
|
+
if (created !== null)
|
|
579
|
+
return created;
|
|
580
|
+
return lowerer.isStdlibGlobal(receiver, "require") && !isNodeEsmFile(receiver.getSourceFile())
|
|
581
|
+
? receiver.getSourceFile()
|
|
582
|
+
: null;
|
|
583
|
+
}
|
|
584
|
+
function staticResolveOptionPaths(node) {
|
|
585
|
+
if (node === undefined)
|
|
586
|
+
return undefined;
|
|
587
|
+
const value = stripTypeCasts(node);
|
|
588
|
+
if (ts.isIdentifier(value) && value.text === "undefined")
|
|
589
|
+
return undefined;
|
|
590
|
+
if (!ts.isObjectLiteralExpression(value))
|
|
591
|
+
return null;
|
|
592
|
+
let paths;
|
|
593
|
+
for (const prop of value.properties) {
|
|
594
|
+
if (!ts.isPropertyAssignment(prop))
|
|
595
|
+
return null;
|
|
596
|
+
const name = ts.isIdentifier(prop.name) || ts.isStringLiteralLike(prop.name) ? prop.name.text : null;
|
|
597
|
+
if (name !== "paths" || paths !== undefined)
|
|
598
|
+
return null;
|
|
599
|
+
const init = stripTypeCasts(prop.initializer);
|
|
600
|
+
if (!ts.isArrayLiteralExpression(init))
|
|
601
|
+
return null;
|
|
602
|
+
const entries = [];
|
|
603
|
+
for (const item of init.elements) {
|
|
604
|
+
if (!ts.isStringLiteralLike(item))
|
|
605
|
+
return null;
|
|
606
|
+
entries.push(item.text);
|
|
607
|
+
}
|
|
608
|
+
paths = entries;
|
|
609
|
+
}
|
|
610
|
+
return paths;
|
|
611
|
+
}
|
|
612
|
+
/** CommonJS require.resolve and require.resolve.paths for the ambient
|
|
613
|
+
* wrapper or a supported createRequire binding. Results are build-time
|
|
614
|
+
* constants; failures lower to Node's catchable error object. */
|
|
615
|
+
export function lowerRequireResolveCall(lowerer, call) {
|
|
616
|
+
const callee = call.expression;
|
|
617
|
+
if (!ts.isPropertyAccessExpression(callee) || callee.questionDotToken !== undefined)
|
|
618
|
+
return null;
|
|
619
|
+
let receiver;
|
|
620
|
+
let pathsCall = false;
|
|
621
|
+
if (callee.name.text === "resolve") {
|
|
622
|
+
receiver = callee.expression;
|
|
623
|
+
}
|
|
624
|
+
else if (callee.name.text === "paths" && ts.isPropertyAccessExpression(callee.expression) &&
|
|
625
|
+
callee.expression.questionDotToken === undefined && callee.expression.name.text === "resolve") {
|
|
626
|
+
receiver = callee.expression.expression;
|
|
627
|
+
pathsCall = true;
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
const baseFile = requireResolverBaseFile(lowerer, receiver);
|
|
633
|
+
if (baseFile === null)
|
|
634
|
+
return null;
|
|
635
|
+
if (call.questionDotToken !== undefined || call.arguments.length < 1 || call.arguments.length > (pathsCall ? 1 : 2)) {
|
|
636
|
+
lowerer.noLowering(pathsCall ? "require.resolve.paths with this argument shape" : "require.resolve with this argument shape", call);
|
|
637
|
+
}
|
|
638
|
+
const specifier = staticString(call.arguments[0]);
|
|
639
|
+
if (specifier === null) {
|
|
640
|
+
lowerer.noLowering(`${pathsCall ? "require.resolve.paths" : "require.resolve"} with a runtime-computed request`, call.arguments[0], "a compiled binary has a fixed module graph — pass a string literal");
|
|
641
|
+
}
|
|
642
|
+
const loc = locOf(call);
|
|
643
|
+
if (pathsCall) {
|
|
644
|
+
const result = requireResolvePathsRuntime(baseFile.fileName, specifier, lowerer.targetPlatform);
|
|
645
|
+
if (result !== null && !Array.isArray(result)) {
|
|
646
|
+
return runtimeResolveThrow(result, lowerer.irTypeOf(call), loc);
|
|
647
|
+
}
|
|
648
|
+
const raw = result === null
|
|
649
|
+
? { kind: "unitLit", unit: "null", type: NULL_T, loc }
|
|
650
|
+
: {
|
|
651
|
+
kind: "arrayLit",
|
|
652
|
+
elems: result.map((value) => ({ kind: "strLit", value, type: STRING, loc })),
|
|
653
|
+
type: arrayOf(STRING),
|
|
654
|
+
loc,
|
|
655
|
+
};
|
|
656
|
+
return lowerer.coerceInto(call, raw, lowerer.irTypeOf(call));
|
|
657
|
+
}
|
|
658
|
+
const paths = staticResolveOptionPaths(call.arguments[1]);
|
|
659
|
+
if (paths === null) {
|
|
660
|
+
lowerer.noLowering("require.resolve with runtime-computed options", call.arguments[1], "omit options or pass { paths: [\"<static directory>\", ...] }");
|
|
661
|
+
}
|
|
662
|
+
const result = resolveRequireRuntime(baseFile.fileName, specifier, lowerer.targetPlatform, paths);
|
|
663
|
+
return result.ok
|
|
664
|
+
? { kind: "strLit", value: result.value, type: STRING, loc }
|
|
665
|
+
: runtimeResolveThrow(result.error, STRING, loc);
|
|
666
|
+
}
|
|
505
667
|
/** The builtin modules whose `constants` object bakes as literals at
|
|
506
668
|
* every access site (the fs.constants precedent, scaled up): http2's
|
|
507
669
|
* full Node v24 table, and crypto's OpenSSL-constant table. The object
|
|
@@ -1866,13 +2028,12 @@ export function lowerSpawnSyncCall(lowerer, expr, loc) {
|
|
|
1866
2028
|
};
|
|
1867
2029
|
}
|
|
1868
2030
|
/** `spawn(command, args?, options)` → one cp.spawn / cp.spawnOpts
|
|
1869
|
-
* libCall. The options argument must be an object
|
|
1870
|
-
*
|
|
1871
|
-
*
|
|
1872
|
-
* child.stderr
|
|
1873
|
-
*
|
|
1874
|
-
*
|
|
1875
|
-
* output. The other lowered
|
|
2031
|
+
* libCall. The options argument, when present, must be an object
|
|
2032
|
+
* LITERAL. Stdio accepts "ignore", "inherit", or "pipe" as the scalar,
|
|
2033
|
+
* or the 3-tuple whose stdin pipe becomes child.stdin, stdout/stderr
|
|
2034
|
+
* pipes become child.stdout/child.stderr, and output slots may also be
|
|
2035
|
+
* number fds. Omitting stdio uses Node's default: all three piped. The
|
|
2036
|
+
* other lowered
|
|
1876
2037
|
* members: `detached` (a boolean literal, inline or carried by the
|
|
1877
2038
|
* conditional spread `...(c ? { detached: true } : {})` in either
|
|
1878
2039
|
* orientation — POSIX_SPAWN_SETSID, the child gets its own session and
|
|
@@ -1882,11 +2043,28 @@ export function lowerSpawnSyncCall(lowerer, expr, loc) {
|
|
|
1882
2043
|
* keeps its historical cp.spawn lowering. */
|
|
1883
2044
|
export function lowerSpawnCall(lowerer, expr, loc) {
|
|
1884
2045
|
if (expr.arguments.length > 3 || expr.arguments.some(ts.isSpreadElement)) {
|
|
1885
|
-
lowerer.noLowering("spawn with this argument shape", expr, 'the supported form is spawn(command, args?, { stdio: "ignore" | "inherit", detached?, env?, cwd? })');
|
|
2046
|
+
lowerer.noLowering("spawn with this argument shape", expr, 'the supported form is spawn(command, args?, { stdio: "pipe" | "ignore" | "inherit", detached?, env?, cwd? })');
|
|
1886
2047
|
}
|
|
1887
2048
|
const cmd = lowerer.lowerExprExpecting(expr.arguments[0], STRING);
|
|
1888
|
-
|
|
1889
|
-
|
|
2049
|
+
let argsNode;
|
|
2050
|
+
let optsNode;
|
|
2051
|
+
if (expr.arguments.length === 3) {
|
|
2052
|
+
argsNode = expr.arguments[1];
|
|
2053
|
+
optsNode = expr.arguments[2];
|
|
2054
|
+
}
|
|
2055
|
+
else if (expr.arguments.length === 2) {
|
|
2056
|
+
const second = expr.arguments[1];
|
|
2057
|
+
const secondType = lowerer.typeOf(second);
|
|
2058
|
+
const mapped = lowerer.mapTypeOf(secondType);
|
|
2059
|
+
if (lowerer.checker.isArrayType(secondType) || lowerer.checker.isTupleType(secondType) ||
|
|
2060
|
+
(mapped?.kind === "array" && mapped.elem.kind === "string"))
|
|
2061
|
+
argsNode = second;
|
|
2062
|
+
else
|
|
2063
|
+
optsNode = second;
|
|
2064
|
+
}
|
|
2065
|
+
if (optsNode !== undefined && !ts.isObjectLiteralExpression(optsNode)) {
|
|
2066
|
+
lowerer.noLowering("spawn with a non-literal options argument", optsNode, "pass stdio, detached, env, cwd, and windowsHide in an inline object literal");
|
|
2067
|
+
}
|
|
1890
2068
|
const emptyStr = { kind: "strLit", value: "", type: STRING, loc };
|
|
1891
2069
|
// Per-slot stdio modes (scr_child.c: 0 ignore, 1 inherit, 2 fd) and
|
|
1892
2070
|
// the out/err fd expressions for mode 2 (the daemon-log idiom:
|
|
@@ -1900,8 +2078,7 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
1900
2078
|
let envPairs = { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc };
|
|
1901
2079
|
let cwd = emptyStr;
|
|
1902
2080
|
let plain = true; // exactly { stdio: "ignore" }: the historical libCall
|
|
1903
|
-
|
|
1904
|
-
if (optsNode && ts.isObjectLiteralExpression(optsNode) && expr.arguments.length >= 2) {
|
|
2081
|
+
if (optsNode && ts.isObjectLiteralExpression(optsNode)) {
|
|
1905
2082
|
for (const p of optsNode.properties) {
|
|
1906
2083
|
// The conditional-spread idiom `...(isWindows ? {} : { detached:
|
|
1907
2084
|
// true })` (either orientation): the one carried member supported
|
|
@@ -1937,18 +2114,15 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
1937
2114
|
}
|
|
1938
2115
|
switch (m.name) {
|
|
1939
2116
|
case "stdio": {
|
|
1940
|
-
// The 3-tuple form:
|
|
1941
|
-
// stdout/stderr
|
|
1942
|
-
//
|
|
1943
|
-
// openSync result — dup2'd into the child, Node's fd slots).
|
|
2117
|
+
// The 3-tuple form: every slot accepts a literal, including
|
|
2118
|
+
// "pipe" (child.stdin/stdout/stderr); stdout/stderr also accept
|
|
2119
|
+
// a number-typed fd (an openSync result — dup2'd into the child).
|
|
1944
2120
|
if (ts.isArrayLiteralExpression(m.value) && m.value.elements.length === 3) {
|
|
1945
2121
|
const slot = (el, which) => {
|
|
1946
2122
|
const t = lowerer.typeOf(el);
|
|
1947
2123
|
if (t.isStringLiteralType()) {
|
|
1948
|
-
if (t.value === "pipe" && which === 0)
|
|
1949
|
-
pipeFence(el);
|
|
1950
2124
|
if (t.value !== "ignore" && t.value !== "inherit" && t.value !== "pipe") {
|
|
1951
|
-
lowerer.noLowering(`spawn with stdio "${t.value}"`, el, '"ignore", "inherit", "pipe"
|
|
2125
|
+
lowerer.noLowering(`spawn with stdio "${t.value}"`, el, '"ignore", "inherit", "pipe", and number output fds are the supported stdio slots');
|
|
1952
2126
|
}
|
|
1953
2127
|
const mode = t.value === "inherit" ? 1 : t.value === "pipe" ? 3 : 0;
|
|
1954
2128
|
if (which === 0)
|
|
@@ -1962,7 +2136,7 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
1962
2136
|
return;
|
|
1963
2137
|
}
|
|
1964
2138
|
if (which === 0) {
|
|
1965
|
-
lowerer.noLowering("spawn with this stdin slot", el, 'stdin takes "ignore" or "inherit" (fd stdin has no lowering)');
|
|
2139
|
+
lowerer.noLowering("spawn with this stdin slot", el, 'stdin takes "pipe", "ignore", or "inherit" (fd stdin has no lowering)');
|
|
1966
2140
|
}
|
|
1967
2141
|
if (lowerer.mapTypeOf(t)?.kind !== "f64") {
|
|
1968
2142
|
lowerer.noLowering("spawn with this stdio option", el, 'each slot is "ignore", "inherit", or a number fd (an openSync result)');
|
|
@@ -1984,13 +2158,11 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
1984
2158
|
}
|
|
1985
2159
|
const t = lowerer.typeOf(m.value);
|
|
1986
2160
|
const v = t.isStringLiteralType() ? t.value : null;
|
|
1987
|
-
if (v
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
lowerer.noLowering("spawn with this stdio option", m.value, '"ignore" and "inherit" are the supported stdio literals ' +
|
|
1991
|
-
'(or a 3-tuple of those and number fds; "pipe" has no lowering)');
|
|
2161
|
+
if (v !== "pipe" && v !== "ignore" && v !== "inherit") {
|
|
2162
|
+
lowerer.noLowering("spawn with this stdio option", m.value, '"pipe", "ignore", and "inherit" are the supported stdio literals ' +
|
|
2163
|
+
"(or a 3-tuple of those and number output fds)");
|
|
1992
2164
|
}
|
|
1993
|
-
const mode = v === "inherit" ? 1 : 0;
|
|
2165
|
+
const mode = v === "inherit" ? 1 : v === "pipe" ? 3 : 0;
|
|
1994
2166
|
inMode = outMode = errMode = mode;
|
|
1995
2167
|
sawStdio = true;
|
|
1996
2168
|
if (v !== "ignore")
|
|
@@ -2028,7 +2200,8 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2028
2200
|
}
|
|
2029
2201
|
}
|
|
2030
2202
|
if (!sawStdio) {
|
|
2031
|
-
|
|
2203
|
+
inMode = outMode = errMode = 3;
|
|
2204
|
+
plain = false;
|
|
2032
2205
|
}
|
|
2033
2206
|
const argv = lowerer.lowerChildArgsArg(argsNode, loc);
|
|
2034
2207
|
if (plain) {
|
|
@@ -3568,9 +3741,9 @@ export function fencedBuiltinImportOf(lowerer, ident) {
|
|
|
3568
3741
|
/** The composed crypto pattern: `randomBytes(n).toString(enc)` lowers
|
|
3569
3742
|
* as ONE string-producing libCall — the Buffer between the two calls
|
|
3570
3743
|
* never exists at runtime. Only literal "hex"/"base64" encodings lower
|
|
3571
|
-
* (the runtime implements exactly those);
|
|
3572
|
-
*
|
|
3573
|
-
*
|
|
3744
|
+
* (the runtime implements exactly those); wider forms fall through to
|
|
3745
|
+
* the ordinary Buffer lowering. Null when this is not a toString on a
|
|
3746
|
+
* crypto.randomBytes call. */
|
|
3574
3747
|
export function lowerCryptoComposedCall(lowerer, call, access) {
|
|
3575
3748
|
if (call.questionDotToken || access.questionDotToken)
|
|
3576
3749
|
return null;
|
|
@@ -3604,16 +3777,9 @@ export function lowerCryptoComposedCall(lowerer, call, access) {
|
|
|
3604
3777
|
const enc = lowerer.lowerExprExpecting(encNode, STRING);
|
|
3605
3778
|
return { kind: "libCall", fn: "crypto.randomBytesToString", args: [size, enc], type: STRING, loc };
|
|
3606
3779
|
}
|
|
3607
|
-
/**
|
|
3608
|
-
*
|
|
3609
|
-
*
|
|
3610
|
-
* stance. Both import spellings reach here (the named `createHash(...)`
|
|
3611
|
-
* and the namespace `crypto.createHash(...)`). Once the chain is
|
|
3612
|
-
* recognized, the narrow forms FENCE with pointed hints instead of
|
|
3613
|
-
* falling to the generic member fence: sha256 is the lowered algorithm,
|
|
3614
|
-
* one string- or Buffer-typed update, hex digests. Null when the callee
|
|
3615
|
-
* isn't this chain at all (other Hash-typed code lands on the ordinary
|
|
3616
|
-
* Hash.<member> fences). */
|
|
3780
|
+
/** Fast path for the exact single-update hash chain. Wider algorithms,
|
|
3781
|
+
* stored handles, multiple updates, Buffer digests, and input encodings
|
|
3782
|
+
* fall through to the first-class Hash lowering below. */
|
|
3617
3783
|
function lowerHashDigestChain(lowerer, call, access) {
|
|
3618
3784
|
const updateCall = access.expression;
|
|
3619
3785
|
if (!ts.isCallExpression(updateCall) || updateCall.questionDotToken)
|
|
@@ -3637,16 +3803,14 @@ function lowerHashDigestChain(lowerer, call, access) {
|
|
|
3637
3803
|
return null;
|
|
3638
3804
|
const loc = locOf(call);
|
|
3639
3805
|
const algT = chCall.arguments.length === 1 ? lowerer.typeOf(chCall.arguments[0]) : undefined;
|
|
3640
|
-
if (!algT?.isStringLiteralType() || (algT.value !== "sha256" && algT.value !== "sha1"))
|
|
3641
|
-
|
|
3642
|
-
"(sha1 exists for the RFC 6455 Sec-WebSocket-Accept hash)");
|
|
3643
|
-
}
|
|
3806
|
+
if (!algT?.isStringLiteralType() || (algT.value !== "sha256" && algT.value !== "sha1"))
|
|
3807
|
+
return null;
|
|
3644
3808
|
if (updateCall.arguments.length !== 1) {
|
|
3645
|
-
|
|
3809
|
+
return null;
|
|
3646
3810
|
}
|
|
3647
3811
|
const encT = call.arguments.length === 1 ? lowerer.typeOf(call.arguments[0]) : undefined;
|
|
3648
3812
|
if (!encT?.isStringLiteralType() || (encT.value !== "hex" && encT.value !== "base64")) {
|
|
3649
|
-
|
|
3813
|
+
return null;
|
|
3650
3814
|
}
|
|
3651
3815
|
// alg and enc are proven literals (fenced above), so lowering them
|
|
3652
3816
|
// out of source position observes nothing; the data lowers between
|
|
@@ -3667,22 +3831,194 @@ function lowerHashDigestChain(lowerer, call, access) {
|
|
|
3667
3831
|
const enc = lowerer.lowerExprExpecting(call.arguments[0], STRING);
|
|
3668
3832
|
return { kind: "libCall", fn: "crypto.hashDigestStr", args: [alg, data, enc], type: STRING, loc };
|
|
3669
3833
|
}
|
|
3670
|
-
|
|
3671
|
-
}
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3834
|
+
return null;
|
|
3835
|
+
}
|
|
3836
|
+
function cryptoEncoding(lowerer, node, use) {
|
|
3837
|
+
const type = lowerer.typeOf(node);
|
|
3838
|
+
if (!type.isStringLiteralType() || (type.value !== "hex" && type.value !== "base64")) {
|
|
3839
|
+
lowerer.noLowering(`${use} with this encoding`, node, 'the lowered output encodings are "hex" and "base64"');
|
|
3840
|
+
}
|
|
3841
|
+
return lowerer.lowerExprExpecting(node, STRING);
|
|
3842
|
+
}
|
|
3843
|
+
function cryptoAlgorithm(lowerer, node, use) {
|
|
3844
|
+
const type = lowerer.typeOf(node);
|
|
3845
|
+
if (type.isStringLiteralType() && !["md5", "sha1", "sha256"].includes(type.value.toLowerCase())) {
|
|
3846
|
+
lowerer.noLowering(`${use} with algorithm '${type.value}'`, node, "md5, sha1, and sha256 are the lowered digest algorithms");
|
|
3847
|
+
}
|
|
3848
|
+
return lowerer.lowerExprExpecting(node, STRING);
|
|
3849
|
+
}
|
|
3850
|
+
function cryptoInputBytes(lowerer, node, loc) {
|
|
3851
|
+
const value = lowerer.lowerExpr(node);
|
|
3852
|
+
if (value.type.kind === "bytes" && value.type.elem === "u8")
|
|
3853
|
+
return value;
|
|
3854
|
+
if (value.type.kind === "string") {
|
|
3855
|
+
return {
|
|
3856
|
+
kind: "libCall",
|
|
3857
|
+
fn: "buffer.fromStr",
|
|
3858
|
+
args: [value, { kind: "strLit", value: "utf8", type: STRING, loc }],
|
|
3859
|
+
type: BYTES_U8,
|
|
3860
|
+
loc,
|
|
3861
|
+
};
|
|
3862
|
+
}
|
|
3863
|
+
lowerer.noLowering(`crypto byte input of '${lowerer.fmt(value.type)}' values`, node, "string and Buffer/Uint8Array values are supported");
|
|
3864
|
+
}
|
|
3865
|
+
function cryptoBytesCallback(lowerer, node) {
|
|
3866
|
+
let callback = lowerer.lowerExpr(node);
|
|
3867
|
+
if (callback.type.kind === "dyn") {
|
|
3868
|
+
callback = { kind: "dynCheck", value: callback, type: funcOf([DYN, DYN], VOID), loc: locOf(node) };
|
|
3869
|
+
}
|
|
3870
|
+
if (callback.type.kind !== "func" || callback.type.params.length > 2) {
|
|
3871
|
+
lowerer.unsupported("SC1090", node, "crypto callbacks must accept at most (error, buffer)");
|
|
3872
|
+
}
|
|
3873
|
+
const error = callback.type.params[0];
|
|
3874
|
+
if (error !== undefined && error.kind !== "dyn") {
|
|
3875
|
+
if (error.kind !== "union") {
|
|
3876
|
+
lowerer.unsupported("SC1090", node, "crypto callback error parameters must be Error | null");
|
|
3877
|
+
}
|
|
3878
|
+
const def = lowerer.unions.get(error.unionId);
|
|
3879
|
+
const valid = !!def &&
|
|
3880
|
+
def.arms.some((arm) => arm.kind === "nullT") &&
|
|
3881
|
+
def.arms.some((arm) => arm.kind === "object" && arm.className === "%Error") &&
|
|
3882
|
+
def.arms.every((arm) => arm.kind === "nullT" || arm.kind === "undefinedT" || (arm.kind === "object" && arm.className === "%Error"));
|
|
3883
|
+
if (!valid)
|
|
3884
|
+
lowerer.unsupported("SC1090", node, "crypto callback error parameters must be Error | null");
|
|
3885
|
+
}
|
|
3886
|
+
const value = callback.type.params[1];
|
|
3887
|
+
if (value !== undefined && value.kind !== "dyn" && !(value.kind === "bytes" && value.elem === "u8")) {
|
|
3888
|
+
lowerer.unsupported("SC1090", node, "crypto callback result parameters must be Buffer/Uint8Array values");
|
|
3889
|
+
}
|
|
3890
|
+
return voidizedCallback(lowerer, callback, locOf(node));
|
|
3891
|
+
}
|
|
3892
|
+
/** The node:crypto utility and introspection statics. Hash/Hmac are real
|
|
3893
|
+
* native handles; one-shot hashing, constant-time equality, random-fill/
|
|
3894
|
+
* integer, and PBKDF2 share the same runtime primitives as the island.
|
|
3895
|
+
* The name lists remain build-time constants. */
|
|
3683
3896
|
export function lowerCryptoModuleCall(lowerer, expr, bi, loc) {
|
|
3684
3897
|
if (bi.module !== "crypto")
|
|
3685
3898
|
return null;
|
|
3899
|
+
const args = expr.arguments;
|
|
3900
|
+
if (args.some(ts.isSpreadElement)) {
|
|
3901
|
+
lowerer.noLowering(`crypto.${bi.member} with spread arguments`, expr);
|
|
3902
|
+
}
|
|
3903
|
+
if (bi.member === "randomBytes") {
|
|
3904
|
+
if (args.length === 1)
|
|
3905
|
+
return null;
|
|
3906
|
+
if (args.length !== 2) {
|
|
3907
|
+
lowerer.noLowering(`crypto.randomBytes with ${args.length} arguments`, expr, "randomBytes(size) and randomBytes(size, callback) are supported");
|
|
3908
|
+
}
|
|
3909
|
+
return {
|
|
3910
|
+
kind: "libCall",
|
|
3911
|
+
fn: "crypto.randomBytesCb",
|
|
3912
|
+
args: [lowerer.lowerExprExpecting(args[0], F64), cryptoBytesCallback(lowerer, args[1])],
|
|
3913
|
+
type: VOID,
|
|
3914
|
+
loc,
|
|
3915
|
+
};
|
|
3916
|
+
}
|
|
3917
|
+
if (bi.member === "pbkdf2") {
|
|
3918
|
+
if (args.length !== 6) {
|
|
3919
|
+
lowerer.noLowering(`crypto.pbkdf2 with ${args.length} arguments`, expr, "pbkdf2(password, salt, iterations, keylen, digest, callback) is supported");
|
|
3920
|
+
}
|
|
3921
|
+
return {
|
|
3922
|
+
kind: "libCall",
|
|
3923
|
+
fn: "crypto.pbkdf2Cb",
|
|
3924
|
+
args: [
|
|
3925
|
+
cryptoInputBytes(lowerer, args[0], locOf(args[0])),
|
|
3926
|
+
cryptoInputBytes(lowerer, args[1], locOf(args[1])),
|
|
3927
|
+
lowerer.lowerExprExpecting(args[2], F64),
|
|
3928
|
+
lowerer.lowerExprExpecting(args[3], F64),
|
|
3929
|
+
cryptoAlgorithm(lowerer, args[4], "crypto.pbkdf2"),
|
|
3930
|
+
cryptoBytesCallback(lowerer, args[5]),
|
|
3931
|
+
],
|
|
3932
|
+
type: VOID,
|
|
3933
|
+
loc,
|
|
3934
|
+
};
|
|
3935
|
+
}
|
|
3936
|
+
if (bi.member === "createHash") {
|
|
3937
|
+
if (args.length !== 1) {
|
|
3938
|
+
lowerer.noLowering(`crypto.createHash with ${args.length} arguments`, expr, "createHash(algorithm) is supported; options are not yet lowered");
|
|
3939
|
+
}
|
|
3940
|
+
return { kind: "libCall", fn: "crypto.hashNew", args: [cryptoAlgorithm(lowerer, args[0], "crypto.createHash")], type: CRYPTOHASH_T, loc };
|
|
3941
|
+
}
|
|
3942
|
+
if (bi.member === "createHmac") {
|
|
3943
|
+
if (args.length !== 2) {
|
|
3944
|
+
lowerer.noLowering(`crypto.createHmac with ${args.length} arguments`, expr, "createHmac(algorithm, stringOrBufferKey) is supported; options and KeyObject keys are not yet lowered");
|
|
3945
|
+
}
|
|
3946
|
+
const algorithm = cryptoAlgorithm(lowerer, args[0], "crypto.createHmac");
|
|
3947
|
+
const key = lowerer.lowerExpr(args[1]);
|
|
3948
|
+
if (key.type.kind === "string") {
|
|
3949
|
+
return { kind: "libCall", fn: "crypto.hmacNewStr", args: [algorithm, key], type: CRYPTOHMAC_T, loc };
|
|
3950
|
+
}
|
|
3951
|
+
if (key.type.kind === "bytes" && key.type.elem === "u8") {
|
|
3952
|
+
return { kind: "libCall", fn: "crypto.hmacNewBytes", args: [algorithm, key], type: CRYPTOHMAC_T, loc };
|
|
3953
|
+
}
|
|
3954
|
+
lowerer.noLowering(`crypto.createHmac with a '${lowerer.fmt(key.type)}' key`, args[1], "string and Buffer/Uint8Array keys are supported");
|
|
3955
|
+
}
|
|
3956
|
+
if (bi.member === "hash") {
|
|
3957
|
+
if (args.length < 2 || args.length > 3) {
|
|
3958
|
+
lowerer.noLowering(`crypto.hash with ${args.length} arguments`, expr, "hash(algorithm, stringOrBuffer[, \"hex\" | \"base64\"]) is supported");
|
|
3959
|
+
}
|
|
3960
|
+
const algorithm = cryptoAlgorithm(lowerer, args[0], "crypto.hash");
|
|
3961
|
+
const data = lowerer.lowerExpr(args[1]);
|
|
3962
|
+
const encoding = args[2]
|
|
3963
|
+
? cryptoEncoding(lowerer, args[2], "crypto.hash")
|
|
3964
|
+
: { kind: "strLit", value: "hex", type: STRING, loc };
|
|
3965
|
+
if (data.type.kind === "string") {
|
|
3966
|
+
return { kind: "libCall", fn: "crypto.hashDigestStr", args: [algorithm, data, encoding], type: STRING, loc };
|
|
3967
|
+
}
|
|
3968
|
+
if (data.type.kind === "bytes" && data.type.elem === "u8") {
|
|
3969
|
+
return { kind: "libCall", fn: "crypto.hashDigestBytes", args: [algorithm, data, encoding], type: STRING, loc };
|
|
3970
|
+
}
|
|
3971
|
+
lowerer.noLowering(`crypto.hash of '${lowerer.fmt(data.type)}' data`, args[1], "string and Buffer/Uint8Array data are supported");
|
|
3972
|
+
}
|
|
3973
|
+
if (bi.member === "timingSafeEqual") {
|
|
3974
|
+
if (args.length !== 2)
|
|
3975
|
+
lowerer.noLowering(`crypto.timingSafeEqual with ${args.length} arguments`, expr);
|
|
3976
|
+
return {
|
|
3977
|
+
kind: "libCall",
|
|
3978
|
+
fn: "crypto.timingSafeEqual",
|
|
3979
|
+
args: [lowerer.lowerExprExpecting(args[0], BYTES_U8), lowerer.lowerExprExpecting(args[1], BYTES_U8)],
|
|
3980
|
+
type: BOOL,
|
|
3981
|
+
loc,
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
if (bi.member === "randomFillSync") {
|
|
3985
|
+
if (args.length < 1 || args.length > 3) {
|
|
3986
|
+
lowerer.noLowering(`crypto.randomFillSync with ${args.length} arguments`, expr, "randomFillSync(buffer[, offset[, size]]) is supported");
|
|
3987
|
+
}
|
|
3988
|
+
const buffer = lowerer.lowerExprExpecting(args[0], BYTES_U8);
|
|
3989
|
+
const offset = args[1]
|
|
3990
|
+
? lowerer.lowerExprExpecting(args[1], F64)
|
|
3991
|
+
: { kind: "numLit", value: 0, type: F64, loc };
|
|
3992
|
+
if (args[2]) {
|
|
3993
|
+
const size = lowerer.lowerExprExpecting(args[2], F64);
|
|
3994
|
+
return { kind: "libCall", fn: "crypto.randomFill", args: [buffer, offset, size], type: BYTES_U8, loc };
|
|
3995
|
+
}
|
|
3996
|
+
return { kind: "libCall", fn: "crypto.randomFillRest", args: [buffer, offset], type: BYTES_U8, loc };
|
|
3997
|
+
}
|
|
3998
|
+
if (bi.member === "randomInt") {
|
|
3999
|
+
if (args.length !== 1 && args.length !== 2) {
|
|
4000
|
+
lowerer.noLowering(`crypto.randomInt with ${args.length} arguments`, expr, "the synchronous randomInt(max) and randomInt(min, max) forms are supported");
|
|
4001
|
+
}
|
|
4002
|
+
const min = args.length === 1
|
|
4003
|
+
? { kind: "numLit", value: 0, type: F64, loc }
|
|
4004
|
+
: lowerer.lowerExprExpecting(args[0], F64);
|
|
4005
|
+
const max = lowerer.lowerExprExpecting(args[args.length - 1], F64);
|
|
4006
|
+
return { kind: "libCall", fn: "crypto.randomInt", args: [min, max], type: F64, loc };
|
|
4007
|
+
}
|
|
4008
|
+
if (bi.member === "pbkdf2Sync") {
|
|
4009
|
+
if (args.length !== 5) {
|
|
4010
|
+
lowerer.noLowering(`crypto.pbkdf2Sync with ${args.length} arguments`, expr, "pbkdf2Sync(password, salt, iterations, keylen, digest) is supported");
|
|
4011
|
+
}
|
|
4012
|
+
const password = cryptoInputBytes(lowerer, args[0], locOf(args[0]));
|
|
4013
|
+
const salt = cryptoInputBytes(lowerer, args[1], locOf(args[1]));
|
|
4014
|
+
return {
|
|
4015
|
+
kind: "libCall",
|
|
4016
|
+
fn: "crypto.pbkdf2",
|
|
4017
|
+
args: [password, salt, lowerer.lowerExprExpecting(args[2], F64), lowerer.lowerExprExpecting(args[3], F64), cryptoAlgorithm(lowerer, args[4], "crypto.pbkdf2Sync")],
|
|
4018
|
+
type: BYTES_U8,
|
|
4019
|
+
loc,
|
|
4020
|
+
};
|
|
4021
|
+
}
|
|
3686
4022
|
const LISTS = {
|
|
3687
4023
|
getCiphers: CRYPTO_CIPHERS,
|
|
3688
4024
|
getHashes: CRYPTO_HASHES,
|
|
@@ -4094,7 +4430,7 @@ export function lowerFileHandleMethodCall(lowerer, call, access) {
|
|
|
4094
4430
|
const defaultValue = { kind: "numLit", value: dflt, type: F64, loc };
|
|
4095
4431
|
if (!node)
|
|
4096
4432
|
return { value: defaultValue, defaulted: boolLit(true, loc) };
|
|
4097
|
-
const undefinedArg =
|
|
4433
|
+
const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, node);
|
|
4098
4434
|
if (undefinedArg) {
|
|
4099
4435
|
return { value: defaultAfterUndefined(undefinedArg, defaultValue), defaulted: boolLit(true, loc) };
|
|
4100
4436
|
}
|
|
@@ -4303,6 +4639,85 @@ export function lowerStatsMethodCall(lowerer, call, access) {
|
|
|
4303
4639
|
}
|
|
4304
4640
|
lowerer.noLowering(`Stats.${name}`, call, "isFile(), isDirectory(), isSymbolicLink(), size, blocks, nlink, atimeMs, and mtimeMs are the supported Stats members", lowerer.checker.getSymbolAtLocation(access.name));
|
|
4305
4641
|
}
|
|
4642
|
+
/** Calls on native crypto.Hash/Hmac handles. The handle survives locals,
|
|
4643
|
+
* aliases, loops, and returns; update() retains and returns the receiver,
|
|
4644
|
+
* digest() finalizes it, and Hash.copy() snapshots the incremental state. */
|
|
4645
|
+
export function lowerCryptoHashMethodCall(lowerer, call, access) {
|
|
4646
|
+
if (call.questionDotToken || access.questionDotToken)
|
|
4647
|
+
return null;
|
|
4648
|
+
const receiverType = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
4649
|
+
if (receiverType?.kind !== "cryptoHash" && receiverType?.kind !== "cryptoHmac")
|
|
4650
|
+
return null;
|
|
4651
|
+
if (!lowerer.isStdlibMember(access))
|
|
4652
|
+
return null;
|
|
4653
|
+
const name = access.name.text;
|
|
4654
|
+
const loc = locOf(call);
|
|
4655
|
+
const receiver = () => lowerer.lowerExprExpecting(access.expression, receiverType);
|
|
4656
|
+
if (name === "update") {
|
|
4657
|
+
if (call.arguments.length < 1 || call.arguments.length > 2 || call.arguments.some(ts.isSpreadElement)) {
|
|
4658
|
+
lowerer.noLowering(`${receiverType.kind === "cryptoHash" ? "Hash" : "Hmac"}.update with ${call.arguments.length} arguments`, call, "update(stringOrBuffer[, inputEncoding]) is supported");
|
|
4659
|
+
}
|
|
4660
|
+
const dataNode = call.arguments[0];
|
|
4661
|
+
const data = lowerer.lowerExpr(dataNode);
|
|
4662
|
+
const prefix = receiverType.kind === "cryptoHash" ? "crypto.hashUpdate" : "crypto.hmacUpdate";
|
|
4663
|
+
if (data.type.kind === "bytes" && data.type.elem === "u8") {
|
|
4664
|
+
if (call.arguments.length !== 1) {
|
|
4665
|
+
lowerer.noLowering("Hash/Hmac.update with an encoding for Buffer data", call.arguments[1], "input encodings apply only to string data");
|
|
4666
|
+
}
|
|
4667
|
+
return { kind: "libCall", fn: `${prefix}Bytes`, args: [receiver(), data], type: receiverType, loc };
|
|
4668
|
+
}
|
|
4669
|
+
if (data.type.kind === "string") {
|
|
4670
|
+
const encodingNode = call.arguments[1];
|
|
4671
|
+
if (encodingNode === undefined) {
|
|
4672
|
+
return { kind: "libCall", fn: `${prefix}Str`, args: [receiver(), data], type: receiverType, loc };
|
|
4673
|
+
}
|
|
4674
|
+
const encodingType = lowerer.typeOf(encodingNode);
|
|
4675
|
+
if (!encodingType.isStringLiteralType() || !["utf8", "utf-8", "hex", "base64"].includes(encodingType.value)) {
|
|
4676
|
+
lowerer.noLowering("Hash/Hmac.update with this input encoding", encodingNode, 'utf8, hex, and base64 string inputs are supported');
|
|
4677
|
+
}
|
|
4678
|
+
if (encodingType.value === "utf8" || encodingType.value === "utf-8") {
|
|
4679
|
+
lowerer.lowerExprExpecting(encodingNode, STRING);
|
|
4680
|
+
return { kind: "libCall", fn: `${prefix}Str`, args: [receiver(), data], type: receiverType, loc };
|
|
4681
|
+
}
|
|
4682
|
+
const encoded = {
|
|
4683
|
+
kind: "libCall",
|
|
4684
|
+
fn: "buffer.fromStr",
|
|
4685
|
+
args: [data, lowerer.lowerExprExpecting(encodingNode, STRING)],
|
|
4686
|
+
type: BYTES_U8,
|
|
4687
|
+
loc,
|
|
4688
|
+
};
|
|
4689
|
+
return { kind: "libCall", fn: `${prefix}Bytes`, args: [receiver(), encoded], type: receiverType, loc };
|
|
4690
|
+
}
|
|
4691
|
+
lowerer.noLowering(`Hash/Hmac.update of '${lowerer.fmt(data.type)}' values`, dataNode, "string and Buffer/Uint8Array inputs are supported");
|
|
4692
|
+
}
|
|
4693
|
+
if (name === "digest") {
|
|
4694
|
+
if (call.arguments.length > 1 || call.arguments.some(ts.isSpreadElement)) {
|
|
4695
|
+
lowerer.noLowering(`${receiverType.kind === "cryptoHash" ? "Hash" : "Hmac"}.digest with ${call.arguments.length} arguments`, call, 'digest() and digest("hex" | "base64") are supported');
|
|
4696
|
+
}
|
|
4697
|
+
if (call.arguments.length === 0) {
|
|
4698
|
+
return {
|
|
4699
|
+
kind: "libCall",
|
|
4700
|
+
fn: receiverType.kind === "cryptoHash" ? "crypto.hashDigestBuffer" : "crypto.hmacDigestBuffer",
|
|
4701
|
+
args: [receiver()],
|
|
4702
|
+
type: BYTES_U8,
|
|
4703
|
+
loc,
|
|
4704
|
+
};
|
|
4705
|
+
}
|
|
4706
|
+
return {
|
|
4707
|
+
kind: "libCall",
|
|
4708
|
+
fn: receiverType.kind === "cryptoHash" ? "crypto.hashDigestString" : "crypto.hmacDigestString",
|
|
4709
|
+
args: [receiver(), cryptoEncoding(lowerer, call.arguments[0], `${receiverType.kind === "cryptoHash" ? "Hash" : "Hmac"}.digest`)],
|
|
4710
|
+
type: STRING,
|
|
4711
|
+
loc,
|
|
4712
|
+
};
|
|
4713
|
+
}
|
|
4714
|
+
if (name === "copy" && receiverType.kind === "cryptoHash") {
|
|
4715
|
+
if (call.arguments.length !== 0)
|
|
4716
|
+
lowerer.noLowering(`Hash.copy with ${call.arguments.length} arguments`, call, "copy() without options is supported");
|
|
4717
|
+
return { kind: "libCall", fn: "crypto.hashCopy", args: [receiver()], type: CRYPTOHASH_T, loc };
|
|
4718
|
+
}
|
|
4719
|
+
lowerer.noLowering(`${receiverType.kind === "cryptoHash" ? "Hash" : "Hmac"}.${name}`, call, receiverType.kind === "cryptoHash" ? "update(), digest(), and copy() are supported" : "update() and digest() are supported", lowerer.checker.getSymbolAtLocation(access.name));
|
|
4720
|
+
}
|
|
4306
4721
|
/** `Atomics.wait(int32Array, idx, expected, timeoutMs)` — the
|
|
4307
4722
|
* synchronous-sleep idiom (RouteStore's
|
|
4308
4723
|
* `Atomics.wait(sleepBuffer, 0, 0, ms)`): scriptc has no threads, so
|
|
@@ -4358,7 +4773,7 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
|
|
|
4358
4773
|
return { kind: "recordGet", obj: receiver, shapeId: receiver.type.shapeId, field: "%enc", type: STRING, loc: locOf(expr) };
|
|
4359
4774
|
}
|
|
4360
4775
|
const kind = lowerer.mapTypeOf(lowerer.typeOf(expr.expression))?.kind;
|
|
4361
|
-
if (kind !== "stats" && kind !== "fileHandle" && kind !== "spawnRes" && kind !== "child")
|
|
4776
|
+
if (kind !== "stats" && kind !== "fileHandle" && kind !== "spawnRes" && kind !== "child" && kind !== "childWriter")
|
|
4362
4777
|
return null;
|
|
4363
4778
|
if (kind === "child" ? !isChildSurfaceMember(lowerer, expr) : !lowerer.isStdlibMember(expr))
|
|
4364
4779
|
return null;
|
|
@@ -4375,19 +4790,20 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
|
|
|
4375
4790
|
}
|
|
4376
4791
|
lowerer.noLowering(`FileHandle.${name}`, expr, "fd, close(), read(), write(), readFile(), writeFile(), appendFile(), and stat() are the supported FileHandle members", lowerer.checker.getSymbolAtLocation(expr.name));
|
|
4377
4792
|
}
|
|
4378
|
-
// child.stdout
|
|
4379
|
-
//
|
|
4380
|
-
// piped), constructed type-directedly in the backend over the
|
|
4793
|
+
// child.stdin/stdout/stderr — the piped streams: the checker's
|
|
4794
|
+
// `Writable | null` / `Readable | null` (null exactly when the slot
|
|
4795
|
+
// was not piped), constructed type-directedly in the backend over the
|
|
4381
4796
|
// +1-or-NULL runtime pair.
|
|
4382
|
-
if (kind === "child" && (name === "stdout" || name === "stderr")) {
|
|
4797
|
+
if (kind === "child" && (name === "stdin" || name === "stdout" || name === "stderr")) {
|
|
4383
4798
|
const receiver = lowerer.lowerExpr(expr.expression);
|
|
4799
|
+
const streamType = name === "stdin" ? CHILDWRITER_T : CHILDSTREAM_T;
|
|
4384
4800
|
const type = {
|
|
4385
4801
|
kind: "union",
|
|
4386
|
-
unionId: lowerer.unions.intern([
|
|
4802
|
+
unionId: lowerer.unions.intern([streamType, { kind: "nullT" }]),
|
|
4387
4803
|
};
|
|
4388
4804
|
const read = {
|
|
4389
4805
|
kind: "libCall",
|
|
4390
|
-
fn: name === "stdout" ? "child.stdout" : "child.stderr",
|
|
4806
|
+
fn: name === "stdin" ? "child.stdin" : name === "stdout" ? "child.stdout" : "child.stderr",
|
|
4391
4807
|
args: [receiver],
|
|
4392
4808
|
type,
|
|
4393
4809
|
loc,
|
|
@@ -4396,6 +4812,16 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
|
|
|
4396
4812
|
}
|
|
4397
4813
|
if (kind === "child")
|
|
4398
4814
|
return null; // pid/exitCode/killed live in lowerIntrinsicProperty
|
|
4815
|
+
if (kind === "childWriter") {
|
|
4816
|
+
if (name === "writable") {
|
|
4817
|
+
const receiver = lowerer.lowerExprExpecting(expr.expression, CHILDWRITER_T);
|
|
4818
|
+
return { kind: "libCall", fn: "writer.writable", args: [receiver], type: BOOL, loc };
|
|
4819
|
+
}
|
|
4820
|
+
if (name === "write" || name === "end" || name === "destroy" || name === "on" || name === "once") {
|
|
4821
|
+
lowerer.unsupported("SC1090", expr, `child stdin methods as values (call '${name}' directly)`);
|
|
4822
|
+
}
|
|
4823
|
+
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
|
+
}
|
|
4399
4825
|
if (kind === "stats" &&
|
|
4400
4826
|
(name === "blocks" || name === "nlink" || name === "atimeMs" || name === "mtimeMs")) {
|
|
4401
4827
|
const receiver = lowerer.lowerExpr(expr.expression);
|
|
@@ -4579,6 +5005,67 @@ export function lowerChildStreamMethodCall(lowerer, call, access) {
|
|
|
4579
5005
|
}
|
|
4580
5006
|
lowerer.noLowering(`ReadableStream.${name}`, call, 'on/once("data" | "end", cb) are the supported child-stream members', lowerer.checker.getSymbolAtLocation(access.name));
|
|
4581
5007
|
}
|
|
5008
|
+
/** Method calls on a piped child stdin writer. Writes copy one string or
|
|
5009
|
+
* Uint8Array into the nonblocking runtime queue and return the backpressure
|
|
5010
|
+
* signal. end()/destroy() are statement-only, as are drain/finish/error
|
|
5011
|
+
* listener registrations; unsupported callbacks/encodings stay fenced. */
|
|
5012
|
+
export function lowerChildWriterMethodCall(lowerer, call, access) {
|
|
5013
|
+
if (lowerer.chainBlocked(call, access))
|
|
5014
|
+
return null;
|
|
5015
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(access.expression))?.kind !== "childWriter")
|
|
5016
|
+
return null;
|
|
5017
|
+
if (!lowerer.isStdlibMember(access))
|
|
5018
|
+
return null;
|
|
5019
|
+
const name = access.name.text;
|
|
5020
|
+
const loc = locOf(call);
|
|
5021
|
+
if (name === "write") {
|
|
5022
|
+
if (call.arguments.length !== 1 || call.arguments.some(ts.isSpreadElement)) {
|
|
5023
|
+
lowerer.noLowering(`child stdin write with ${call.arguments.length} arguments`, call, "write(string | Uint8Array) with no encoding or callback is supported");
|
|
5024
|
+
}
|
|
5025
|
+
const receiver = lowerer.lowerExprExpecting(access.expression, CHILDWRITER_T);
|
|
5026
|
+
const data = lowerer.lowerExpr(call.arguments[0]);
|
|
5027
|
+
if (data.type.kind === "string") {
|
|
5028
|
+
return { kind: "libCall", fn: "writer.writeString", args: [receiver, data], type: BOOL, loc };
|
|
5029
|
+
}
|
|
5030
|
+
if (data.type.kind === "bytes" && data.type.elem === "u8") {
|
|
5031
|
+
return { kind: "libCall", fn: "writer.writeBytes", args: [receiver, data], type: BOOL, loc };
|
|
5032
|
+
}
|
|
5033
|
+
lowerer.noLowering(`child stdin write of '${lowerer.fmt(data.type)}'`, call.arguments[0], "write one string, Buffer, or Uint8Array value (narrow unions first)");
|
|
5034
|
+
}
|
|
5035
|
+
if ((name === "end" || name === "destroy") && call.arguments.length === 0) {
|
|
5036
|
+
if (!ts.isExpressionStatement(call.parent)) {
|
|
5037
|
+
lowerer.unsupported("SC1090", call, `chaining child.stdin.${name}() (call it as its own statement)`);
|
|
5038
|
+
}
|
|
5039
|
+
const receiver = lowerer.lowerExprExpecting(access.expression, CHILDWRITER_T);
|
|
5040
|
+
return { kind: "libCall", fn: name === "end" ? "writer.end" : "writer.destroy", args: [receiver], type: VOID, loc };
|
|
5041
|
+
}
|
|
5042
|
+
if ((name === "on" || name === "once") && call.arguments.length === 2) {
|
|
5043
|
+
const eventType = lowerer.typeOf(call.arguments[0]);
|
|
5044
|
+
const event = eventType.isStringLiteralType() ? eventType.value : null;
|
|
5045
|
+
if (event !== "drain" && event !== "finish" && event !== "error") {
|
|
5046
|
+
lowerer.noLowering(`child stdin ${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, call.arguments[0], '"drain", "finish", and "error" are the supported child stdin events');
|
|
5047
|
+
}
|
|
5048
|
+
if (!ts.isExpressionStatement(call.parent)) {
|
|
5049
|
+
lowerer.unsupported("SC1090", call, "chaining child stdin listener registration");
|
|
5050
|
+
}
|
|
5051
|
+
const receiver = lowerer.lowerExprExpecting(access.expression, CHILDWRITER_T);
|
|
5052
|
+
const cb = lowerer.lowerExpr(call.arguments[1]);
|
|
5053
|
+
const maxParams = event === "error" ? 1 : 0;
|
|
5054
|
+
if (cb.type.kind !== "func" || cb.type.ret.kind !== "void" || cb.type.params.length > maxParams) {
|
|
5055
|
+
lowerer.unsupported("SC1090", call.arguments[1], event === "error"
|
|
5056
|
+
? "error listeners with at most one Error parameter and no return value"
|
|
5057
|
+
: `${event} listeners with no parameters or return value`);
|
|
5058
|
+
}
|
|
5059
|
+
if (event === "error" && cb.type.params[0] !== undefined &&
|
|
5060
|
+
!(cb.type.params[0].kind === "object" && cb.type.params[0].className === "%Error")) {
|
|
5061
|
+
lowerer.unsupported("SC1090", call.arguments[1], "child stdin error listeners whose parameter is Error");
|
|
5062
|
+
}
|
|
5063
|
+
const once = boolLit(name === "once", loc);
|
|
5064
|
+
const fn = event === "drain" ? "writer.onDrain" : event === "finish" ? "writer.onFinish" : "writer.onError";
|
|
5065
|
+
return { kind: "libCall", fn, args: [receiver, cb, once], type: VOID, loc };
|
|
5066
|
+
}
|
|
5067
|
+
lowerer.noLowering(`Writable.${name}`, call, 'write(string | Uint8Array), end(), destroy(), and on/once("drain" | "finish" | "error", cb) are supported', lowerer.checker.getSymbolAtLocation(access.name));
|
|
5068
|
+
}
|
|
4582
5069
|
/** Method calls on first-class process-stream receivers (procStream —
|
|
4583
5070
|
* a WritableStream-typed value like prefixStream's `output` param):
|
|
4584
5071
|
* write(data) with one string, dispatched at runtime onto the exact
|
|
@@ -4862,6 +5349,15 @@ export function lowerProcessProperty(lowerer, expr) {
|
|
|
4862
5349
|
const fn = expr.name.text === "node" ? "process.versionsNode" : "process.versionsOpenssl";
|
|
4863
5350
|
return { kind: "libCall", fn, args: [], type: STRING, loc: locOf(expr) };
|
|
4864
5351
|
}
|
|
5352
|
+
// process.versions?.electron is the runtime-capability probe used by
|
|
5353
|
+
// CLI packages to select Electron argument conventions. versions
|
|
5354
|
+
// itself always exists, and a native scriptc binary is not Electron,
|
|
5355
|
+
// so both the optional and ordinary property spellings read undefined.
|
|
5356
|
+
if (ts.isPropertyAccessExpression(expr.expression) &&
|
|
5357
|
+
lowerer.stdlibGlobalMember(expr.expression, "process") === "versions" &&
|
|
5358
|
+
expr.name.text === "electron") {
|
|
5359
|
+
return { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc: locOf(expr) };
|
|
5360
|
+
}
|
|
4865
5361
|
// The capability-probe members that honestly DON'T EXIST in a
|
|
4866
5362
|
// compiled binary — each reads undefined (their declared types carry
|
|
4867
5363
|
// the undefined arm), so feature probes take their documented
|
|
@@ -5472,10 +5968,10 @@ export function lowerProcessMethodCall(lowerer, call, access) {
|
|
|
5472
5968
|
const secondNode = args[1];
|
|
5473
5969
|
const thirdNode = args[2];
|
|
5474
5970
|
const secondUndefined = secondNode
|
|
5475
|
-
?
|
|
5971
|
+
? lowerStaticallyUndefinedArgument(lowerer, secondNode)
|
|
5476
5972
|
: null;
|
|
5477
5973
|
const thirdUndefined = thirdNode
|
|
5478
|
-
?
|
|
5974
|
+
? lowerStaticallyUndefinedArgument(lowerer, thirdNode)
|
|
5479
5975
|
: null;
|
|
5480
5976
|
const secondT = secondNode && !secondUndefined ? lowerer.mapTypeOf(lowerer.typeOf(secondNode)) : undefined;
|
|
5481
5977
|
const callbackNode = args.length === 3 && !thirdUndefined
|
|
@@ -5512,7 +6008,7 @@ export function lowerProcessMethodCall(lowerer, call, access) {
|
|
|
5512
6008
|
if (canonical === raw)
|
|
5513
6009
|
return evaluated;
|
|
5514
6010
|
const normalized = { kind: "strLit", value: canonical, type: STRING, loc: locOf(encodingNode) };
|
|
5515
|
-
return
|
|
6011
|
+
return isSafeToDiscard(evaluated)
|
|
5516
6012
|
? normalized
|
|
5517
6013
|
: {
|
|
5518
6014
|
kind: "seqExpr",
|
|
@@ -5577,9 +6073,9 @@ export function lowerProcessMethodCall(lowerer, call, access) {
|
|
|
5577
6073
|
// data and encoding, even though it schedules no callback. Strings
|
|
5578
6074
|
// already evaluate encoding while producing `bytes`; byte chunks
|
|
5579
6075
|
// fold both ignored argument effects into this final ABI slot.
|
|
5580
|
-
if (thirdUndefined && !
|
|
6076
|
+
if (thirdUndefined && !isSafeToDiscard(thirdUndefined)) {
|
|
5581
6077
|
const effects = isBytes
|
|
5582
|
-
? [encoding, thirdUndefined].filter((effect) => !
|
|
6078
|
+
? [encoding, thirdUndefined].filter((effect) => !isSafeToDiscard(effect))
|
|
5583
6079
|
: [thirdUndefined];
|
|
5584
6080
|
runtimeEncoding = {
|
|
5585
6081
|
kind: "seqExpr",
|