@scriptc/compiler 0.1.2 → 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 +59 -14
- package/dist/backend/c/async.d.ts +5 -0
- package/dist/backend/c/async.js +57 -0
- package/dist/backend/c/async.js.map +1 -1
- package/dist/backend/c/c-emitter.d.ts +3 -0
- package/dist/backend/c/c-emitter.js +15 -8
- 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 +152 -9
- package/dist/backend/c/exprs.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/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 +32 -11
- package/dist/backend/c/walkers.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/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 +24 -13
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +35 -7
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +4 -0
- package/dist/backend/llvm/expr-dynamic.js +9 -0
- package/dist/backend/llvm/expr-dynamic.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 +2 -1
- 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 +22 -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 +46 -0
- package/dist/backend/llvm/lib-shared.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/walkers.js +7 -5
- package/dist/backend/llvm/walkers.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.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/coverage/surface-manifest.js +1 -0
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +3 -3
- package/dist/diagnostics/diagnostic.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/frontend/lowering/containers/bytes.d.ts +0 -4
- package/dist/frontend/lowering/containers/bytes.js +112 -28
- package/dist/frontend/lowering/containers/bytes.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-builtins.d.ts +22 -21
- package/dist/frontend/lowering/lower-builtins.js +392 -67
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +8 -6
- package/dist/frontend/lowering/lower-calls.js +110 -12
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +12 -8
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +104 -5
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +7 -0
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +8 -0
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +5 -4
- package/dist/frontend/lowering/lower-stmts.js +23 -6
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +4 -4
- package/dist/frontend/lowering/lowerer.js +5 -5
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- 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.js +27 -14
- package/dist/frontend/npm-static-declarations.js.map +1 -1
- package/dist/frontend/npm-static-declarations.test.js +10 -0
- package/dist/frontend/npm-static-declarations.test.js.map +1 -1
- package/dist/frontend/program.js +18 -2
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.d.ts +22 -7
- package/dist/frontend/resolve.js +83 -22
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/resolve.test.js +21 -1
- package/dist/frontend/resolve.test.js.map +1 -1
- package/dist/frontend/type-mapper.js +52 -10
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.js +6 -0
- 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 +67 -24
- package/dist/ir/ir.js +81 -3
- 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 +77 -5
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/int-infer.test.js +4 -4
- 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/package.json +17 -17
- package/surface-manifest.json +32 -17
|
@@ -24,7 +24,7 @@ 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 { 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";
|
|
28
28
|
import { boolLit, countedFor, numLit, strLit, varRef } from "../../ir/build.js";
|
|
29
29
|
function optionalStringTags(lowerer, type) {
|
|
30
30
|
if (type.kind !== "union")
|
|
@@ -2028,13 +2028,12 @@ export function lowerSpawnSyncCall(lowerer, expr, loc) {
|
|
|
2028
2028
|
};
|
|
2029
2029
|
}
|
|
2030
2030
|
/** `spawn(command, args?, options)` → one cp.spawn / cp.spawnOpts
|
|
2031
|
-
* libCall. The options argument must be an object
|
|
2032
|
-
*
|
|
2033
|
-
*
|
|
2034
|
-
* child.stderr
|
|
2035
|
-
*
|
|
2036
|
-
*
|
|
2037
|
-
* 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
|
|
2038
2037
|
* members: `detached` (a boolean literal, inline or carried by the
|
|
2039
2038
|
* conditional spread `...(c ? { detached: true } : {})` in either
|
|
2040
2039
|
* orientation — POSIX_SPAWN_SETSID, the child gets its own session and
|
|
@@ -2044,11 +2043,28 @@ export function lowerSpawnSyncCall(lowerer, expr, loc) {
|
|
|
2044
2043
|
* keeps its historical cp.spawn lowering. */
|
|
2045
2044
|
export function lowerSpawnCall(lowerer, expr, loc) {
|
|
2046
2045
|
if (expr.arguments.length > 3 || expr.arguments.some(ts.isSpreadElement)) {
|
|
2047
|
-
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? })');
|
|
2048
2047
|
}
|
|
2049
2048
|
const cmd = lowerer.lowerExprExpecting(expr.arguments[0], STRING);
|
|
2050
|
-
|
|
2051
|
-
|
|
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
|
+
}
|
|
2052
2068
|
const emptyStr = { kind: "strLit", value: "", type: STRING, loc };
|
|
2053
2069
|
// Per-slot stdio modes (scr_child.c: 0 ignore, 1 inherit, 2 fd) and
|
|
2054
2070
|
// the out/err fd expressions for mode 2 (the daemon-log idiom:
|
|
@@ -2062,8 +2078,7 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2062
2078
|
let envPairs = { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc };
|
|
2063
2079
|
let cwd = emptyStr;
|
|
2064
2080
|
let plain = true; // exactly { stdio: "ignore" }: the historical libCall
|
|
2065
|
-
|
|
2066
|
-
if (optsNode && ts.isObjectLiteralExpression(optsNode) && expr.arguments.length >= 2) {
|
|
2081
|
+
if (optsNode && ts.isObjectLiteralExpression(optsNode)) {
|
|
2067
2082
|
for (const p of optsNode.properties) {
|
|
2068
2083
|
// The conditional-spread idiom `...(isWindows ? {} : { detached:
|
|
2069
2084
|
// true })` (either orientation): the one carried member supported
|
|
@@ -2099,18 +2114,15 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2099
2114
|
}
|
|
2100
2115
|
switch (m.name) {
|
|
2101
2116
|
case "stdio": {
|
|
2102
|
-
// The 3-tuple form:
|
|
2103
|
-
// stdout/stderr
|
|
2104
|
-
//
|
|
2105
|
-
// 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).
|
|
2106
2120
|
if (ts.isArrayLiteralExpression(m.value) && m.value.elements.length === 3) {
|
|
2107
2121
|
const slot = (el, which) => {
|
|
2108
2122
|
const t = lowerer.typeOf(el);
|
|
2109
2123
|
if (t.isStringLiteralType()) {
|
|
2110
|
-
if (t.value === "pipe" && which === 0)
|
|
2111
|
-
pipeFence(el);
|
|
2112
2124
|
if (t.value !== "ignore" && t.value !== "inherit" && t.value !== "pipe") {
|
|
2113
|
-
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');
|
|
2114
2126
|
}
|
|
2115
2127
|
const mode = t.value === "inherit" ? 1 : t.value === "pipe" ? 3 : 0;
|
|
2116
2128
|
if (which === 0)
|
|
@@ -2124,7 +2136,7 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2124
2136
|
return;
|
|
2125
2137
|
}
|
|
2126
2138
|
if (which === 0) {
|
|
2127
|
-
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)');
|
|
2128
2140
|
}
|
|
2129
2141
|
if (lowerer.mapTypeOf(t)?.kind !== "f64") {
|
|
2130
2142
|
lowerer.noLowering("spawn with this stdio option", el, 'each slot is "ignore", "inherit", or a number fd (an openSync result)');
|
|
@@ -2146,13 +2158,11 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2146
2158
|
}
|
|
2147
2159
|
const t = lowerer.typeOf(m.value);
|
|
2148
2160
|
const v = t.isStringLiteralType() ? t.value : null;
|
|
2149
|
-
if (v
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
lowerer.noLowering("spawn with this stdio option", m.value, '"ignore" and "inherit" are the supported stdio literals ' +
|
|
2153
|
-
'(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)");
|
|
2154
2164
|
}
|
|
2155
|
-
const mode = v === "inherit" ? 1 : 0;
|
|
2165
|
+
const mode = v === "inherit" ? 1 : v === "pipe" ? 3 : 0;
|
|
2156
2166
|
inMode = outMode = errMode = mode;
|
|
2157
2167
|
sawStdio = true;
|
|
2158
2168
|
if (v !== "ignore")
|
|
@@ -2190,7 +2200,8 @@ export function lowerSpawnCall(lowerer, expr, loc) {
|
|
|
2190
2200
|
}
|
|
2191
2201
|
}
|
|
2192
2202
|
if (!sawStdio) {
|
|
2193
|
-
|
|
2203
|
+
inMode = outMode = errMode = 3;
|
|
2204
|
+
plain = false;
|
|
2194
2205
|
}
|
|
2195
2206
|
const argv = lowerer.lowerChildArgsArg(argsNode, loc);
|
|
2196
2207
|
if (plain) {
|
|
@@ -3730,9 +3741,9 @@ export function fencedBuiltinImportOf(lowerer, ident) {
|
|
|
3730
3741
|
/** The composed crypto pattern: `randomBytes(n).toString(enc)` lowers
|
|
3731
3742
|
* as ONE string-producing libCall — the Buffer between the two calls
|
|
3732
3743
|
* never exists at runtime. Only literal "hex"/"base64" encodings lower
|
|
3733
|
-
* (the runtime implements exactly those);
|
|
3734
|
-
*
|
|
3735
|
-
*
|
|
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. */
|
|
3736
3747
|
export function lowerCryptoComposedCall(lowerer, call, access) {
|
|
3737
3748
|
if (call.questionDotToken || access.questionDotToken)
|
|
3738
3749
|
return null;
|
|
@@ -3766,16 +3777,9 @@ export function lowerCryptoComposedCall(lowerer, call, access) {
|
|
|
3766
3777
|
const enc = lowerer.lowerExprExpecting(encNode, STRING);
|
|
3767
3778
|
return { kind: "libCall", fn: "crypto.randomBytesToString", args: [size, enc], type: STRING, loc };
|
|
3768
3779
|
}
|
|
3769
|
-
/**
|
|
3770
|
-
*
|
|
3771
|
-
*
|
|
3772
|
-
* stance. Both import spellings reach here (the named `createHash(...)`
|
|
3773
|
-
* and the namespace `crypto.createHash(...)`). Once the chain is
|
|
3774
|
-
* recognized, the narrow forms FENCE with pointed hints instead of
|
|
3775
|
-
* falling to the generic member fence: sha256 is the lowered algorithm,
|
|
3776
|
-
* one string- or Buffer-typed update, hex digests. Null when the callee
|
|
3777
|
-
* isn't this chain at all (other Hash-typed code lands on the ordinary
|
|
3778
|
-
* 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. */
|
|
3779
3783
|
function lowerHashDigestChain(lowerer, call, access) {
|
|
3780
3784
|
const updateCall = access.expression;
|
|
3781
3785
|
if (!ts.isCallExpression(updateCall) || updateCall.questionDotToken)
|
|
@@ -3799,16 +3803,14 @@ function lowerHashDigestChain(lowerer, call, access) {
|
|
|
3799
3803
|
return null;
|
|
3800
3804
|
const loc = locOf(call);
|
|
3801
3805
|
const algT = chCall.arguments.length === 1 ? lowerer.typeOf(chCall.arguments[0]) : undefined;
|
|
3802
|
-
if (!algT?.isStringLiteralType() || (algT.value !== "sha256" && algT.value !== "sha1"))
|
|
3803
|
-
|
|
3804
|
-
"(sha1 exists for the RFC 6455 Sec-WebSocket-Accept hash)");
|
|
3805
|
-
}
|
|
3806
|
+
if (!algT?.isStringLiteralType() || (algT.value !== "sha256" && algT.value !== "sha1"))
|
|
3807
|
+
return null;
|
|
3806
3808
|
if (updateCall.arguments.length !== 1) {
|
|
3807
|
-
|
|
3809
|
+
return null;
|
|
3808
3810
|
}
|
|
3809
3811
|
const encT = call.arguments.length === 1 ? lowerer.typeOf(call.arguments[0]) : undefined;
|
|
3810
3812
|
if (!encT?.isStringLiteralType() || (encT.value !== "hex" && encT.value !== "base64")) {
|
|
3811
|
-
|
|
3813
|
+
return null;
|
|
3812
3814
|
}
|
|
3813
3815
|
// alg and enc are proven literals (fenced above), so lowering them
|
|
3814
3816
|
// out of source position observes nothing; the data lowers between
|
|
@@ -3829,22 +3831,194 @@ function lowerHashDigestChain(lowerer, call, access) {
|
|
|
3829
3831
|
const enc = lowerer.lowerExprExpecting(call.arguments[0], STRING);
|
|
3830
3832
|
return { kind: "libCall", fn: "crypto.hashDigestStr", args: [alg, data, enc], type: STRING, loc };
|
|
3831
3833
|
}
|
|
3832
|
-
|
|
3833
|
-
}
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
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. */
|
|
3845
3896
|
export function lowerCryptoModuleCall(lowerer, expr, bi, loc) {
|
|
3846
3897
|
if (bi.module !== "crypto")
|
|
3847
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
|
+
}
|
|
3848
4022
|
const LISTS = {
|
|
3849
4023
|
getCiphers: CRYPTO_CIPHERS,
|
|
3850
4024
|
getHashes: CRYPTO_HASHES,
|
|
@@ -4465,6 +4639,85 @@ export function lowerStatsMethodCall(lowerer, call, access) {
|
|
|
4465
4639
|
}
|
|
4466
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));
|
|
4467
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
|
+
}
|
|
4468
4721
|
/** `Atomics.wait(int32Array, idx, expected, timeoutMs)` — the
|
|
4469
4722
|
* synchronous-sleep idiom (RouteStore's
|
|
4470
4723
|
* `Atomics.wait(sleepBuffer, 0, 0, ms)`): scriptc has no threads, so
|
|
@@ -4520,7 +4773,7 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
|
|
|
4520
4773
|
return { kind: "recordGet", obj: receiver, shapeId: receiver.type.shapeId, field: "%enc", type: STRING, loc: locOf(expr) };
|
|
4521
4774
|
}
|
|
4522
4775
|
const kind = lowerer.mapTypeOf(lowerer.typeOf(expr.expression))?.kind;
|
|
4523
|
-
if (kind !== "stats" && kind !== "fileHandle" && kind !== "spawnRes" && kind !== "child")
|
|
4776
|
+
if (kind !== "stats" && kind !== "fileHandle" && kind !== "spawnRes" && kind !== "child" && kind !== "childWriter")
|
|
4524
4777
|
return null;
|
|
4525
4778
|
if (kind === "child" ? !isChildSurfaceMember(lowerer, expr) : !lowerer.isStdlibMember(expr))
|
|
4526
4779
|
return null;
|
|
@@ -4537,19 +4790,20 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
|
|
|
4537
4790
|
}
|
|
4538
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));
|
|
4539
4792
|
}
|
|
4540
|
-
// child.stdout
|
|
4541
|
-
//
|
|
4542
|
-
// 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
|
|
4543
4796
|
// +1-or-NULL runtime pair.
|
|
4544
|
-
if (kind === "child" && (name === "stdout" || name === "stderr")) {
|
|
4797
|
+
if (kind === "child" && (name === "stdin" || name === "stdout" || name === "stderr")) {
|
|
4545
4798
|
const receiver = lowerer.lowerExpr(expr.expression);
|
|
4799
|
+
const streamType = name === "stdin" ? CHILDWRITER_T : CHILDSTREAM_T;
|
|
4546
4800
|
const type = {
|
|
4547
4801
|
kind: "union",
|
|
4548
|
-
unionId: lowerer.unions.intern([
|
|
4802
|
+
unionId: lowerer.unions.intern([streamType, { kind: "nullT" }]),
|
|
4549
4803
|
};
|
|
4550
4804
|
const read = {
|
|
4551
4805
|
kind: "libCall",
|
|
4552
|
-
fn: name === "stdout" ? "child.stdout" : "child.stderr",
|
|
4806
|
+
fn: name === "stdin" ? "child.stdin" : name === "stdout" ? "child.stdout" : "child.stderr",
|
|
4553
4807
|
args: [receiver],
|
|
4554
4808
|
type,
|
|
4555
4809
|
loc,
|
|
@@ -4558,6 +4812,16 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
|
|
|
4558
4812
|
}
|
|
4559
4813
|
if (kind === "child")
|
|
4560
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
|
+
}
|
|
4561
4825
|
if (kind === "stats" &&
|
|
4562
4826
|
(name === "blocks" || name === "nlink" || name === "atimeMs" || name === "mtimeMs")) {
|
|
4563
4827
|
const receiver = lowerer.lowerExpr(expr.expression);
|
|
@@ -4741,6 +5005,67 @@ export function lowerChildStreamMethodCall(lowerer, call, access) {
|
|
|
4741
5005
|
}
|
|
4742
5006
|
lowerer.noLowering(`ReadableStream.${name}`, call, 'on/once("data" | "end", cb) are the supported child-stream members', lowerer.checker.getSymbolAtLocation(access.name));
|
|
4743
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
|
+
}
|
|
4744
5069
|
/** Method calls on first-class process-stream receivers (procStream —
|
|
4745
5070
|
* a WritableStream-typed value like prefixStream's `output` param):
|
|
4746
5071
|
* write(data) with one string, dispatched at runtime onto the exact
|