@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
|
@@ -0,0 +1,1105 @@
|
|
|
1
|
+
import * as ts from "../../ts7/adapter.js";
|
|
2
|
+
import { BIGINT_T, BOOL, BYTES_U8, DYN, F64, STRING, UNDEFINED_T, VOID, arrayOf, bytesOf, typeEquals } from "../../../ir/ir.js";
|
|
3
|
+
import { locOf } from "../../program.js";
|
|
4
|
+
import { dynUndefinedExpr, own } from "../lowerer.js";
|
|
5
|
+
import { buildBytesSortFn } from "../lower-array-sort.js";
|
|
6
|
+
import { isSafeToDiscard } from "../expressions/evaluation-safety.js";
|
|
7
|
+
import { lowerDynObjectLiteral } from "../expressions/object-literals.js";
|
|
8
|
+
import { defaultAfterUndefined, lowerOptionalArgument, lowerStaticallyUndefinedArgument } from "../optional-arguments.js";
|
|
9
|
+
/** Uint8Array.prototype.toSorted. The receiver/comparator expressions are
|
|
10
|
+
* evaluated before entering the helper; the helper snapshots with
|
|
11
|
+
* TypedArray.prototype.slice before its first comparison, then performs
|
|
12
|
+
* the same stable merge walk as Array.toSorted. Uint8Array's default
|
|
13
|
+
* comparator is numeric ascending, so its comparator-less form needs no
|
|
14
|
+
* string-conversion machinery. */
|
|
15
|
+
function lowerBytesToSortedCall(lowerer, call, access, bytesT) {
|
|
16
|
+
const loc = locOf(call);
|
|
17
|
+
if (call.arguments.length > 1 || call.arguments.some(ts.isSpreadElement)) {
|
|
18
|
+
lowerer.noLowering(`.toSorted with ${call.arguments.length} arguments on Uint8Array`, call);
|
|
19
|
+
}
|
|
20
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
21
|
+
const undefinedArg = call.arguments[0]
|
|
22
|
+
? lowerStaticallyUndefinedArgument(lowerer, call.arguments[0])
|
|
23
|
+
: null;
|
|
24
|
+
if (call.arguments.length === 0 || undefinedArg) {
|
|
25
|
+
const key = "bytes.toSorted:u8:default";
|
|
26
|
+
let helper = lowerer.arrHofHelpers.get(key);
|
|
27
|
+
if (!helper) {
|
|
28
|
+
helper = `%bytes.toSorted.${lowerer.arrHofHelpers.size}`;
|
|
29
|
+
lowerer.arrHofHelpers.set(key, helper);
|
|
30
|
+
lowerer.liftedFns.push(buildBytesSortFn(helper, 0, false, loc));
|
|
31
|
+
}
|
|
32
|
+
if (!undefinedArg || isSafeToDiscard(undefinedArg)) {
|
|
33
|
+
return { kind: "call", callee: helper, args: [receiver], type: bytesT, loc };
|
|
34
|
+
}
|
|
35
|
+
// The default helper takes no comparator argument. Snapshot the
|
|
36
|
+
// receiver into a hidden local so the discarded undefined argument
|
|
37
|
+
// still evaluates after the receiver and before the helper call.
|
|
38
|
+
const saved = lowerer.declareHiddenLocal("%bytesSortRecv", bytesT);
|
|
39
|
+
const savedRef = {
|
|
40
|
+
kind: "varRef",
|
|
41
|
+
localId: saved.id,
|
|
42
|
+
type: bytesT,
|
|
43
|
+
loc,
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
kind: "seqExpr",
|
|
47
|
+
stmts: [
|
|
48
|
+
{ kind: "varDecl", localId: saved.id, init: receiver, loc },
|
|
49
|
+
{ kind: "exprStmt", expr: undefinedArg, loc: undefinedArg.loc },
|
|
50
|
+
],
|
|
51
|
+
result: {
|
|
52
|
+
kind: "call",
|
|
53
|
+
callee: helper,
|
|
54
|
+
args: [savedRef],
|
|
55
|
+
type: bytesT,
|
|
56
|
+
loc,
|
|
57
|
+
},
|
|
58
|
+
type: bytesT,
|
|
59
|
+
loc,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const argNode = call.arguments[0];
|
|
63
|
+
const fnArg = lowerer.lowerExpr(argNode);
|
|
64
|
+
if (fnArg.type.kind !== "func" ||
|
|
65
|
+
fnArg.type.params.length > 2 ||
|
|
66
|
+
!fnArg.type.params.every((p) => p.kind === "f64") ||
|
|
67
|
+
fnArg.type.ret.kind !== "f64") {
|
|
68
|
+
lowerer.badType(argNode, lowerer.typeOf(argNode));
|
|
69
|
+
}
|
|
70
|
+
const arity = fnArg.type.params.length;
|
|
71
|
+
const key = `bytes.toSorted:u8:${arity}`;
|
|
72
|
+
let helper = lowerer.arrHofHelpers.get(key);
|
|
73
|
+
if (!helper) {
|
|
74
|
+
helper = `%bytes.toSorted.${lowerer.arrHofHelpers.size}`;
|
|
75
|
+
lowerer.arrHofHelpers.set(key, helper);
|
|
76
|
+
lowerer.liftedFns.push(buildBytesSortFn(helper, arity, true, loc));
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
kind: "call",
|
|
80
|
+
callee: helper,
|
|
81
|
+
args: [receiver, fnArg],
|
|
82
|
+
type: bytesT,
|
|
83
|
+
loc,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/* ── typed arrays / Buffer ─────────────────────────────────────────────── */
|
|
87
|
+
/** The typed-array constructors with a runtime representation, by lib
|
|
88
|
+
* interface name. The other TypedArray flavors (Int8Array, Float64Array,
|
|
89
|
+
* DataView, ...) fall through to the generic stdlib-constructor fence. */
|
|
90
|
+
const BYTES_CTORS = {
|
|
91
|
+
Uint8Array: "u8",
|
|
92
|
+
Uint32Array: "u32",
|
|
93
|
+
Int32Array: "i32",
|
|
94
|
+
Float32Array: "f32",
|
|
95
|
+
};
|
|
96
|
+
/** `new Uint8Array(...)` / `new Uint32Array(...)` / `new Float32Array(...)`
|
|
97
|
+
* (stdlib provenance — a user's own class with the name resolves through
|
|
98
|
+
* classBySymbol). Lowered argument shapes: none (empty), a length
|
|
99
|
+
* (zero-filled; ToIndex at runtime — invalid lengths throw Node's
|
|
100
|
+
* RangeError), a same-kind typed array or Buffer (an independent COPY —
|
|
101
|
+
* the readFile chain's `new Uint8Array(await readFile(p))`), a number[]
|
|
102
|
+
* literal (element-coerced; its contextual type is the lib's
|
|
103
|
+
* ArrayLike/Iterable union, which cannot map — the Set-seed pattern), or
|
|
104
|
+
* a number[]-typed value. ArrayBuffer/view forms are fenced: scriptc
|
|
105
|
+
* typed arrays own their storage. Null when this isn't a stdlib
|
|
106
|
+
* typed-array construction. */
|
|
107
|
+
export function lowerBytesNew(lowerer, expr, symbol) {
|
|
108
|
+
if (symbol && symbol.name === "DataView" && lowerer.isStdlibSymbol(symbol)) {
|
|
109
|
+
return lowerDataViewNew(lowerer, expr);
|
|
110
|
+
}
|
|
111
|
+
const elem = symbol ? own(BYTES_CTORS, symbol.name) : undefined;
|
|
112
|
+
if (!elem || !symbol || !lowerer.isStdlibSymbol(symbol))
|
|
113
|
+
return null;
|
|
114
|
+
const name = symbol.name;
|
|
115
|
+
const type = bytesOf(elem);
|
|
116
|
+
const loc = locOf(expr);
|
|
117
|
+
const args = expr.arguments ?? [];
|
|
118
|
+
if (args.length === 0)
|
|
119
|
+
return { kind: "bytesNew", source: null, type, loc };
|
|
120
|
+
if (args.length === 1 && !ts.isSpreadElement(args[0])) {
|
|
121
|
+
const argNode = args[0];
|
|
122
|
+
if (ts.isArrayLiteralExpression(argNode) && !argNode.elements.some(ts.isSpreadElement)) {
|
|
123
|
+
const elems = argNode.elements.map((el) => lowerer.lowerExprExpecting(el, F64));
|
|
124
|
+
const seed = { kind: "arrayLit", elems, type: arrayOf(F64), loc };
|
|
125
|
+
return { kind: "bytesNew", source: seed, type, loc };
|
|
126
|
+
}
|
|
127
|
+
// The SYNTACTIC `new T(new ArrayBuffer(n))` and `new T(new
|
|
128
|
+
// SharedArrayBuffer(n))` forms — fresh-buffer construction (the
|
|
129
|
+
// shared spelling is the Atomics.wait sleep idiom's). The buffer
|
|
130
|
+
// never exists as a value: n must be a byte-length LITERAL divisible
|
|
131
|
+
// by the element size (tsc would admit any number; a bad one is
|
|
132
|
+
// Node's RangeError — rejected at compile time instead of
|
|
133
|
+
// half-lowering), and the whole expression is a zero-filled typed
|
|
134
|
+
// array of n/elemSize elements. Erasing the buffer is exact: nothing
|
|
135
|
+
// else can ever reference it, so neither sharing (scriptc has no
|
|
136
|
+
// threads) nor aliasing is observable — SEMANTICS.md documents the
|
|
137
|
+
// stance. The RESIZABLE form (a maxByteLength options bag) fences by
|
|
138
|
+
// name: resize needs the buffer to exist as a value, and none does.
|
|
139
|
+
if (ts.isNewExpression(argNode) &&
|
|
140
|
+
ts.isIdentifier(argNode.expression) &&
|
|
141
|
+
(argNode.expression.text === "ArrayBuffer" ||
|
|
142
|
+
argNode.expression.text === "SharedArrayBuffer") &&
|
|
143
|
+
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(argNode.expression) ?? undefined)) {
|
|
144
|
+
const bufCtor = argNode.expression.text;
|
|
145
|
+
if ((argNode.arguments?.length ?? 0) > 1) {
|
|
146
|
+
lowerer.noLowering(`new ${name} over a resizable ${bufCtor}`, argNode, "a maxByteLength options bag makes the buffer resizable, and resizing needs the buffer " +
|
|
147
|
+
"to exist as a runtime value — no free-standing ArrayBuffer value exists (the buffer here " +
|
|
148
|
+
"erases into the view): drop the options bag");
|
|
149
|
+
}
|
|
150
|
+
const elemSize = elem === "u8" ? 1 : 4;
|
|
151
|
+
const lenArg = argNode.arguments?.length === 1 ? argNode.arguments[0] : undefined;
|
|
152
|
+
const lenT = lenArg ? lowerer.typeOf(lenArg) : null;
|
|
153
|
+
const byteLen = lenT?.isNumberLiteralType() ? lenT.value : null;
|
|
154
|
+
if (byteLen === null || byteLen % elemSize !== 0 || byteLen < 0) {
|
|
155
|
+
lowerer.noLowering(`new ${name} over this ${bufCtor}`, argNode, `the byte length must be a number literal divisible by ${elemSize} — new ${name}(new ${bufCtor}(${elemSize}))`);
|
|
156
|
+
}
|
|
157
|
+
const count = { kind: "numLit", value: byteLen / elemSize, type: F64, loc };
|
|
158
|
+
return { kind: "bytesNew", source: count, type, loc };
|
|
159
|
+
}
|
|
160
|
+
const src = lowerer.lowerExpr(argNode);
|
|
161
|
+
if (src.type.kind === "union" && lowerer.armTag(src.type.unionId, UNDEFINED_T) >= 0) {
|
|
162
|
+
const present = lowerer.stripUndefinedArm(src.type);
|
|
163
|
+
if (present.kind === "array" && present.elem.kind === "f64") {
|
|
164
|
+
const undefTag = lowerer.armTag(src.type.unionId, UNDEFINED_T);
|
|
165
|
+
const presentTag = lowerer.armTag(src.type.unionId, present);
|
|
166
|
+
if (undefTag >= 0 && presentTag >= 0) {
|
|
167
|
+
const empty = { kind: "arrayLit", elems: [], type: present, loc };
|
|
168
|
+
const source = {
|
|
169
|
+
kind: "ternary",
|
|
170
|
+
cond: { kind: "unionIsTag", unionId: src.type.unionId, tag: undefTag, negated: false, value: src, type: BOOL, loc },
|
|
171
|
+
then: empty,
|
|
172
|
+
else_: { kind: "unionNarrow", unionId: src.type.unionId, tag: presentTag, value: src, type: present, loc },
|
|
173
|
+
type: present,
|
|
174
|
+
loc,
|
|
175
|
+
};
|
|
176
|
+
return { kind: "bytesNew", source, type, loc };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (src.type.kind === "f64" ||
|
|
181
|
+
typeEquals(src.type, type) ||
|
|
182
|
+
(src.type.kind === "array" && src.type.elem.kind === "f64")) {
|
|
183
|
+
return { kind: "bytesNew", source: src, type, loc };
|
|
184
|
+
}
|
|
185
|
+
if (src.type.kind === "bytes") {
|
|
186
|
+
lowerer.noLowering(`new ${name} over a '${lowerer.fmt(src.type)}'`, argNode, "cross-kind typed-array conversion has no lowering — copy element by element");
|
|
187
|
+
}
|
|
188
|
+
lowerer.noLowering(`new ${name} over '${lowerer.fmt(src.type)}' values`, argNode, `supported: new ${name}(), (length), (typedArray) — always a copy — or (number[]); ` +
|
|
189
|
+
"ArrayBuffers and views do not exist here (narrow unions first)");
|
|
190
|
+
}
|
|
191
|
+
lowerer.noLowering(`new ${name} with ${args.length} arguments`, expr, `supported: new ${name}(), (length), (typedArray), or (number[])`);
|
|
192
|
+
}
|
|
193
|
+
/** `new DataView(x.buffer, byteOffset?, byteLength?)` (stdlib provenance).
|
|
194
|
+
* The first argument must be the SYNTACTIC `.buffer` of a typed-array/
|
|
195
|
+
* Buffer value — `x.buffer` names x's own storage (scriptc typed arrays
|
|
196
|
+
* always own it whole, byteOffset 0), so the view construction takes x
|
|
197
|
+
* itself as the receiver and no ArrayBuffer value ever exists.
|
|
198
|
+
* byteOffset/byteLength are optional f64s (omitted args OMITTED, like
|
|
199
|
+
* slice); bad indices THROW Node's RangeErrors catchably. The result is
|
|
200
|
+
* a true borrowed view: reads and writes through it alias x. */
|
|
201
|
+
function lowerDataViewNew(lowerer, expr) {
|
|
202
|
+
const loc = locOf(expr);
|
|
203
|
+
const args = expr.arguments ?? [];
|
|
204
|
+
const bufNode = args[0];
|
|
205
|
+
if (!bufNode || args.length > 3 || args.some(ts.isSpreadElement)) {
|
|
206
|
+
lowerer.noLowering(`new DataView with ${args.length} arguments`, expr, "supported: new DataView(x.buffer), (x.buffer, byteOffset), or (x.buffer, byteOffset, byteLength)");
|
|
207
|
+
}
|
|
208
|
+
// The FRESH-BUFFER form: `new DataView(new ArrayBuffer(n), ...)` — the
|
|
209
|
+
// buffer erases into the view (nothing else can ever reference it, so
|
|
210
|
+
// the aliasing a shared buffer would exhibit is unobservable): the
|
|
211
|
+
// view's storage is a fresh zero-filled n-byte allocation. n must be a
|
|
212
|
+
// non-negative integer LITERAL (tsc admits any number; a fractional
|
|
213
|
+
// one is ToIndex truncation nothing here implements). byteOffset and
|
|
214
|
+
// byteLength keep their runtime Node-RangeError story. The RESIZABLE
|
|
215
|
+
// form (a maxByteLength options bag) fences by name.
|
|
216
|
+
if (ts.isNewExpression(bufNode) &&
|
|
217
|
+
ts.isIdentifier(bufNode.expression) &&
|
|
218
|
+
bufNode.expression.text === "ArrayBuffer" &&
|
|
219
|
+
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(bufNode.expression) ?? undefined)) {
|
|
220
|
+
if ((bufNode.arguments?.length ?? 0) > 1) {
|
|
221
|
+
lowerer.noLowering("new DataView over a resizable ArrayBuffer", bufNode, "a maxByteLength options bag makes the buffer resizable, and resizing needs the buffer " +
|
|
222
|
+
"to exist as a runtime value — no free-standing ArrayBuffer value exists (the buffer here " +
|
|
223
|
+
"erases into the view): drop the options bag");
|
|
224
|
+
}
|
|
225
|
+
const lenArg = bufNode.arguments?.length === 1 ? bufNode.arguments[0] : undefined;
|
|
226
|
+
const lenT = lenArg ? lowerer.typeOf(lenArg) : null;
|
|
227
|
+
const byteLen = lenT?.isNumberLiteralType() ? lenT.value : null;
|
|
228
|
+
if (byteLen === null || !Number.isInteger(byteLen) || byteLen < 0) {
|
|
229
|
+
lowerer.noLowering("new DataView over this ArrayBuffer", bufNode, "the byte length must be a non-negative integer literal — new DataView(new ArrayBuffer(8))");
|
|
230
|
+
}
|
|
231
|
+
const count = { kind: "numLit", value: byteLen, type: F64, loc };
|
|
232
|
+
const receiver = { kind: "bytesNew", source: count, type: BYTES_U8, loc };
|
|
233
|
+
const idxArgs = args.slice(1).map((a) => lowerer.lowerExprExpecting(a, F64));
|
|
234
|
+
return { kind: "bytesIntrinsic", method: "dataViewNew", receiver, args: idxArgs, type: BYTES_U8, loc };
|
|
235
|
+
}
|
|
236
|
+
const hint = "views compile over a typed array's own storage — new DataView(x.buffer, byteOffset?, byteLength?) " +
|
|
237
|
+
"where x is a Uint8Array/Uint32Array/Float32Array/Buffer value — or a fresh buffer erased into " +
|
|
238
|
+
"the view: new DataView(new ArrayBuffer(n), ...); free-standing ArrayBuffers have no representation";
|
|
239
|
+
if (!ts.isPropertyAccessExpression(bufNode) || bufNode.name.text !== "buffer" || bufNode.questionDotToken) {
|
|
240
|
+
lowerer.noLowering("new DataView over this buffer expression", bufNode, hint);
|
|
241
|
+
}
|
|
242
|
+
const srcIr = lowerer.mapTypeOf(lowerer.typeOf(bufNode.expression));
|
|
243
|
+
if (srcIr?.kind !== "bytes" || !lowerer.isStdlibMember(bufNode)) {
|
|
244
|
+
lowerer.noLowering(`new DataView over '.buffer' of a '${lowerer.checker.typeToString(lowerer.typeOf(bufNode.expression))}'`, bufNode, hint);
|
|
245
|
+
}
|
|
246
|
+
const receiver = lowerer.lowerExpr(bufNode.expression);
|
|
247
|
+
const idxArgs = args.slice(1).map((a) => lowerer.lowerExprExpecting(a, F64));
|
|
248
|
+
return { kind: "bytesIntrinsic", method: "dataViewNew", receiver, args: idxArgs, type: BYTES_U8, loc };
|
|
249
|
+
}
|
|
250
|
+
/** Method calls on typed-array/Buffer receivers: slice and subarray (BOTH
|
|
251
|
+
* copy — subarray's sharing is the documented divergence), set(src,
|
|
252
|
+
* offset?), and the u8-only Buffer surface: toString(enc?) plus the
|
|
253
|
+
* whole numeric read/write family (fixed widths BE/LE and the
|
|
254
|
+
* variable-width read/writeUIntLE quartet — BUF_NUM_METHODS). Everything
|
|
255
|
+
* else the lib declares (fill, indexOf, reverse, ...) falls through to
|
|
256
|
+
* the SC2020 member fence. Null when this isn't a bytes method call. */
|
|
257
|
+
export function lowerBytesMethodCall(lowerer, call, access) {
|
|
258
|
+
if (lowerer.chainBlocked(access, call))
|
|
259
|
+
return null;
|
|
260
|
+
const name = access.name.text;
|
|
261
|
+
const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
262
|
+
if (receiverIr?.kind !== "bytes")
|
|
263
|
+
return null;
|
|
264
|
+
if (!lowerer.isStdlibMember(access))
|
|
265
|
+
return null;
|
|
266
|
+
const loc = locOf(call);
|
|
267
|
+
const nArgs = call.arguments.length;
|
|
268
|
+
if (receiverIr.elem === "u8" && name === "toSorted") {
|
|
269
|
+
return lowerBytesToSortedCall(lowerer, call, access, receiverIr);
|
|
270
|
+
}
|
|
271
|
+
if (receiverIr.elem === "u8" && name === "toReversed") {
|
|
272
|
+
if (nArgs !== 0) {
|
|
273
|
+
lowerer.noLowering(`.toReversed with ${nArgs} arguments on Uint8Array`, call);
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
kind: "bytesIntrinsic",
|
|
277
|
+
method: "toReversed",
|
|
278
|
+
receiver: lowerer.lowerExpr(access.expression),
|
|
279
|
+
args: [],
|
|
280
|
+
type: receiverIr,
|
|
281
|
+
loc,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (receiverIr.elem === "u8" && name === "with") {
|
|
285
|
+
if (nArgs !== 2 || call.arguments.some(ts.isSpreadElement)) {
|
|
286
|
+
lowerer.noLowering(`.with with ${nArgs} arguments on Uint8Array`, call);
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
kind: "bytesIntrinsic",
|
|
290
|
+
method: "with",
|
|
291
|
+
receiver: lowerer.lowerExpr(access.expression),
|
|
292
|
+
args: [
|
|
293
|
+
lowerer.lowerExprExpecting(call.arguments[0], F64),
|
|
294
|
+
lowerer.lowerExprExpecting(call.arguments[1], F64),
|
|
295
|
+
],
|
|
296
|
+
type: receiverIr,
|
|
297
|
+
loc,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
if (receiverIr.elem === "u8" && name === "join") {
|
|
301
|
+
if (nArgs > 1 || call.arguments.some(ts.isSpreadElement)) {
|
|
302
|
+
lowerer.noLowering(`.join with ${nArgs} arguments on Uint8Array`, call);
|
|
303
|
+
}
|
|
304
|
+
const separatorDefault = {
|
|
305
|
+
kind: "strLit",
|
|
306
|
+
value: ",",
|
|
307
|
+
type: STRING,
|
|
308
|
+
loc,
|
|
309
|
+
};
|
|
310
|
+
const separator = call.arguments[0]
|
|
311
|
+
? lowerOptionalArgument(lowerer, call.arguments[0], STRING, separatorDefault)
|
|
312
|
+
: separatorDefault;
|
|
313
|
+
return {
|
|
314
|
+
kind: "bytesIntrinsic",
|
|
315
|
+
method: "join",
|
|
316
|
+
receiver: lowerer.lowerExpr(access.expression),
|
|
317
|
+
args: [separator],
|
|
318
|
+
type: STRING,
|
|
319
|
+
loc,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (name === "slice" || name === "subarray") {
|
|
323
|
+
if (nArgs > 2) {
|
|
324
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments on typed arrays`, call);
|
|
325
|
+
}
|
|
326
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
327
|
+
const args = call.arguments.map((a) => lowerer.lowerExprExpecting(a, F64));
|
|
328
|
+
// subarray is a VIEW (TypedArray.prototype.subarray aliases), and
|
|
329
|
+
// Buffer's slice() is subarray's deprecated Node alias — resolved by
|
|
330
|
+
// where the member is declared, the toString discipline below. Only
|
|
331
|
+
// the plain typed arrays' slice() copies (JS-exact).
|
|
332
|
+
const declaredOnBuffer = name === "slice" &&
|
|
333
|
+
(() => {
|
|
334
|
+
const nameSym = lowerer.checker.getSymbolAtLocation(access.name);
|
|
335
|
+
return nameSym !== undefined && lowerer.checker.declarationsOf(nameSym).some((d) => ts.isInterfaceDeclaration(d.parent) && d.parent.name.text === "Buffer");
|
|
336
|
+
})();
|
|
337
|
+
const method = name === "subarray" || declaredOnBuffer ? "subarray" : "slice";
|
|
338
|
+
return { kind: "bytesIntrinsic", method, receiver, args, type: receiverIr, loc };
|
|
339
|
+
}
|
|
340
|
+
if (name === "fill" && receiverIr.elem !== "u8") {
|
|
341
|
+
// TypedArray.prototype.fill on the non-u8 kinds: per-element value
|
|
342
|
+
// coercion, slice-clamped relative indices, never throws. u8
|
|
343
|
+
// receivers keep the Buffer fill family (string patterns, throwing
|
|
344
|
+
// offset validation) — lowerBufferInstanceMethod's surface.
|
|
345
|
+
if (nArgs < 1 || nArgs > 3) {
|
|
346
|
+
lowerer.noLowering(`.fill with ${nArgs} arguments on typed arrays`, call);
|
|
347
|
+
}
|
|
348
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
349
|
+
const v = lowerer.lowerExprExpecting(call.arguments[0], F64);
|
|
350
|
+
const idx = call.arguments.slice(1).map((a) => lowerer.lowerExprExpecting(a, F64));
|
|
351
|
+
return { kind: "bytesIntrinsic", method: "fillElem", receiver, args: [v, ...idx], type: receiverIr, loc };
|
|
352
|
+
}
|
|
353
|
+
if (name === "set") {
|
|
354
|
+
if (nArgs < 1 || nArgs > 2) {
|
|
355
|
+
lowerer.noLowering(`.set with ${nArgs} arguments on typed arrays`, call);
|
|
356
|
+
}
|
|
357
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
358
|
+
const src = lowerer.lowerExpr(call.arguments[0]);
|
|
359
|
+
if (!typeEquals(src.type, receiverIr)) {
|
|
360
|
+
lowerer.noLowering(`.set from '${lowerer.fmt(src.type)}' values`, call.arguments[0], "only a same-kind typed array copies in (number[] sources have no lowering — narrow unions first)");
|
|
361
|
+
}
|
|
362
|
+
const args = [src];
|
|
363
|
+
if (nArgs === 2)
|
|
364
|
+
args.push(lowerer.lowerExprExpecting(call.arguments[1], F64));
|
|
365
|
+
return { kind: "bytesIntrinsic", method: "setFrom", receiver, args, type: VOID, loc };
|
|
366
|
+
}
|
|
367
|
+
if (name === "toString") {
|
|
368
|
+
// Buffer's toString(encoding?) — utf8 by default. A 0-arg toString
|
|
369
|
+
// resolved against the plain Uint8Array interface is JS's
|
|
370
|
+
// Array-toString (comma join), a different operation: fenced.
|
|
371
|
+
const declaredOnBuffer = (() => {
|
|
372
|
+
const nameSym = lowerer.checker.getSymbolAtLocation(access.name);
|
|
373
|
+
return nameSym !== undefined && lowerer.checker.declarationsOf(nameSym).some((d) => ts.isInterfaceDeclaration(d.parent) && d.parent.name.text === "Buffer");
|
|
374
|
+
})();
|
|
375
|
+
if (receiverIr.elem !== "u8" || !declaredOnBuffer) {
|
|
376
|
+
lowerer.noLowering("typed-array toString", call, 'Buffer.from(x).toString("utf8" | "hex" | "base64") is the lowered string conversion');
|
|
377
|
+
}
|
|
378
|
+
if (nArgs > 3)
|
|
379
|
+
lowerer.noLowering(`.toString with ${nArgs} arguments on Buffers`, call);
|
|
380
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
381
|
+
const encNode = call.arguments[0];
|
|
382
|
+
let method = "toString";
|
|
383
|
+
let enc = { kind: "strLit", value: "utf8", type: STRING, loc };
|
|
384
|
+
if (encNode) {
|
|
385
|
+
const encType = lowerer.typeOf(encNode);
|
|
386
|
+
const encName = encType.isStringLiteralType()
|
|
387
|
+
? knownBufEncoding(encType.value)
|
|
388
|
+
: undefined;
|
|
389
|
+
if (encName !== undefined) {
|
|
390
|
+
// Keep literals on the canonical, non-throwing fast path.
|
|
391
|
+
enc = { kind: "strLit", value: encName, type: STRING, loc };
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, encNode);
|
|
395
|
+
if (undefinedArg) {
|
|
396
|
+
// An explicit undefined is the omitted-encoding default. Keep
|
|
397
|
+
// the canonical, non-throwing path while preserving effects
|
|
398
|
+
// from equivalent spellings such as `void sideEffect()`.
|
|
399
|
+
enc = defaultAfterUndefined(undefinedArg, enc);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
// A BufferEncoding-typed variable selects its decoder at
|
|
403
|
+
// runtime. Optional variables default their undefined arm to
|
|
404
|
+
// utf8; the checked intrinsic canonicalizes every present
|
|
405
|
+
// alias/case and raises Node's ERR_UNKNOWN_ENCODING when a cast
|
|
406
|
+
// lets a bad value through.
|
|
407
|
+
enc = lowerOptionalArgument(lowerer, encNode, STRING, enc);
|
|
408
|
+
method = "toStringVar";
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// The range form toString(enc, start[, end]) decodes the clamped
|
|
413
|
+
// [start, end) byte window (Node's slice-then-decode). An omitted
|
|
414
|
+
// end stays omitted (2 intrinsic args) — the emitter supplies the
|
|
415
|
+
// receiver's length, because EXPLICIT negative ends clamp to empty
|
|
416
|
+
// in Node and no in-band sentinel can represent "omitted" safely.
|
|
417
|
+
if (nArgs > 1) {
|
|
418
|
+
const start = lowerer.lowerExprExpecting(call.arguments[1], F64);
|
|
419
|
+
if (nArgs === 3) {
|
|
420
|
+
const end = lowerer.lowerExprExpecting(call.arguments[2], F64);
|
|
421
|
+
return { kind: "bytesIntrinsic", method, receiver, args: [enc, start, end], type: STRING, loc };
|
|
422
|
+
}
|
|
423
|
+
return { kind: "bytesIntrinsic", method, receiver, args: [enc, start], type: STRING, loc };
|
|
424
|
+
}
|
|
425
|
+
return { kind: "bytesIntrinsic", method, receiver, args: [enc], type: STRING, loc };
|
|
426
|
+
}
|
|
427
|
+
// The Buffer-declared comparison/search/mutation surface. All of
|
|
428
|
+
// these resolve against the Buffer interface (the checker keeps most
|
|
429
|
+
// off plain typed arrays; where the lib DOES declare a same-named
|
|
430
|
+
// TypedArray member — indexOf, includes, fill — the semantics differ,
|
|
431
|
+
// so only Buffer-declared resolutions lower and u8 receivers gate the
|
|
432
|
+
// rest).
|
|
433
|
+
const declOnBuffer = (() => {
|
|
434
|
+
const nameSym = lowerer.checker.getSymbolAtLocation(access.name);
|
|
435
|
+
return nameSym !== undefined && lowerer.checker.declarationsOf(nameSym).some((d) => ts.isInterfaceDeclaration(d.parent) && d.parent.name.text === "Buffer");
|
|
436
|
+
})();
|
|
437
|
+
if (declOnBuffer && receiverIr.elem === "u8") {
|
|
438
|
+
const bufMethod = lowerBufferInstanceMethod(lowerer, call, access, name, loc);
|
|
439
|
+
if (bufMethod)
|
|
440
|
+
return bufMethod;
|
|
441
|
+
}
|
|
442
|
+
const bigKind = own(BUF_BIGINT_METHODS, name);
|
|
443
|
+
if (bigKind !== undefined && declOnBuffer && receiverIr.elem === "u8") {
|
|
444
|
+
const write = name.startsWith("write");
|
|
445
|
+
const required = write ? 1 : 0;
|
|
446
|
+
if (nArgs < required || nArgs > required + 1) {
|
|
447
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
448
|
+
}
|
|
449
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
450
|
+
const offsetNode = call.arguments[required];
|
|
451
|
+
const offset = offsetNode
|
|
452
|
+
? lowerer.lowerExprExpecting(offsetNode, F64)
|
|
453
|
+
: { kind: "numLit", value: 0, type: F64, loc };
|
|
454
|
+
const sign = { kind: "boolLit", value: bigKind.sign, type: BOOL, loc };
|
|
455
|
+
const le = { kind: "boolLit", value: bigKind.le, type: BOOL, loc };
|
|
456
|
+
if (!write) {
|
|
457
|
+
return { kind: "libCall", fn: "bigint.bufferRead", args: [receiver, offset, sign, le], type: BIGINT_T, loc };
|
|
458
|
+
}
|
|
459
|
+
const value = lowerer.lowerExprExpecting(call.arguments[0], BIGINT_T);
|
|
460
|
+
return { kind: "libCall", fn: "bigint.bufferWrite", args: [receiver, value, offset, sign, le], type: F64, loc };
|
|
461
|
+
}
|
|
462
|
+
// The Buffer numeric read/write families — every fixed-width kind in
|
|
463
|
+
// both endiannesses ("Uint" and "UInt" alike: Node aliases both), plus
|
|
464
|
+
// the variable-width read/writeUIntLE quartet. The kind token rides as
|
|
465
|
+
// a strLit args[0]; omitted offsets complete to Node's default 0.
|
|
466
|
+
const numKind = own(BUF_NUM_METHODS, name);
|
|
467
|
+
if (numKind !== undefined) {
|
|
468
|
+
if (receiverIr.elem !== "u8") {
|
|
469
|
+
lowerer.noLowering(`.${name} on a '${lowerer.fmt(receiverIr)}'`, call, "the numeric families read/write Buffer bytes");
|
|
470
|
+
}
|
|
471
|
+
const write = name.startsWith("write");
|
|
472
|
+
const required = write ? 1 : 0; // value; offset defaults to 0
|
|
473
|
+
if (nArgs < required || nArgs > required + 1) {
|
|
474
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
475
|
+
}
|
|
476
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
477
|
+
const kind = { kind: "strLit", value: numKind, type: STRING, loc };
|
|
478
|
+
const args = [kind, ...call.arguments.map((a) => lowerer.lowerExprExpecting(a, F64))];
|
|
479
|
+
if (nArgs === required) {
|
|
480
|
+
args.push({ kind: "numLit", value: 0, type: F64, loc }); // Node's offset default
|
|
481
|
+
}
|
|
482
|
+
return { kind: "bytesIntrinsic", method: write ? "writeNum" : "readNum", receiver, args, type: F64, loc };
|
|
483
|
+
}
|
|
484
|
+
const varKind = own(BUF_NUM_VAR_METHODS, name);
|
|
485
|
+
if (varKind !== undefined) {
|
|
486
|
+
if (receiverIr.elem !== "u8") {
|
|
487
|
+
lowerer.noLowering(`.${name} on a '${lowerer.fmt(receiverIr)}'`, call, "the numeric families read/write Buffer bytes");
|
|
488
|
+
}
|
|
489
|
+
const write = name.startsWith("write");
|
|
490
|
+
const required = write ? 3 : 2; // Node declares offset AND byteLength required
|
|
491
|
+
if (nArgs !== required) {
|
|
492
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
493
|
+
}
|
|
494
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
495
|
+
const kind = { kind: "strLit", value: varKind, type: STRING, loc };
|
|
496
|
+
const args = [kind, ...call.arguments.map((a) => lowerer.lowerExprExpecting(a, F64))];
|
|
497
|
+
return { kind: "bytesIntrinsic", method: write ? "writeNumVar" : "readNumVar", receiver, args, type: F64, loc };
|
|
498
|
+
}
|
|
499
|
+
// DataView getters (DataView maps to bytes<u8>, so the receiver kind
|
|
500
|
+
// and stdlib provenance land here; the checker keeps these names off
|
|
501
|
+
// typed arrays and Buffers). The multi-byte kinds take the optional
|
|
502
|
+
// littleEndian bool (omitted = big-endian, the JS default). All THROW
|
|
503
|
+
// Node's RangeError on a bad offset (may-throw seeds).
|
|
504
|
+
const dvBigGetter = own(DV_BIG_GETTERS, name);
|
|
505
|
+
if (dvBigGetter !== undefined && receiverIr.elem === "u8") {
|
|
506
|
+
if (nArgs < 1 || nArgs > 2)
|
|
507
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
508
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
509
|
+
const offset = lowerer.lowerExprExpecting(call.arguments[0], F64);
|
|
510
|
+
const sign = { kind: "boolLit", value: dvBigGetter.sign, type: BOOL, loc };
|
|
511
|
+
const le = call.arguments[1]
|
|
512
|
+
? lowerer.lowerExprExpecting(call.arguments[1], BOOL)
|
|
513
|
+
: { kind: "boolLit", value: false, type: BOOL, loc };
|
|
514
|
+
return { kind: "libCall", fn: "bigint.dataViewGet", args: [receiver, offset, sign, le], type: BIGINT_T, loc };
|
|
515
|
+
}
|
|
516
|
+
const dvGetter = own(DV_GETTERS, name);
|
|
517
|
+
if (dvGetter !== undefined && receiverIr.elem === "u8") {
|
|
518
|
+
const maxArgs = dvGetter.le ? 2 : 1;
|
|
519
|
+
if (nArgs < 1 || nArgs > maxArgs) {
|
|
520
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
521
|
+
}
|
|
522
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
523
|
+
const args = [lowerer.lowerExprExpecting(call.arguments[0], F64)];
|
|
524
|
+
if (nArgs === 2)
|
|
525
|
+
args.push(lowerer.lowerExprExpecting(call.arguments[1], BOOL));
|
|
526
|
+
return { kind: "bytesIntrinsic", method: dvGetter.method, receiver, args, type: F64, loc };
|
|
527
|
+
}
|
|
528
|
+
// DataView setters — the getters' mirror: (byteOffset, value) plus the
|
|
529
|
+
// optional littleEndian bool on the multi-byte kinds. Void results;
|
|
530
|
+
// the same constant Node RangeError on a bad offset (may-throw seeds).
|
|
531
|
+
// setBigUint64/setBigInt64 never lower (bigint ARGUMENTS have no
|
|
532
|
+
// representation and no composed form exists) — they keep the member
|
|
533
|
+
// fence, as does setFloat16.
|
|
534
|
+
const dvSetter = own(DV_SETTERS, name);
|
|
535
|
+
if ((name === "setBigUint64" || name === "setBigInt64") && receiverIr.elem === "u8") {
|
|
536
|
+
if (nArgs < 2 || nArgs > 3)
|
|
537
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
538
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
539
|
+
const offset = lowerer.lowerExprExpecting(call.arguments[0], F64);
|
|
540
|
+
const value = lowerer.lowerExprExpecting(call.arguments[1], BIGINT_T);
|
|
541
|
+
const le = call.arguments[2]
|
|
542
|
+
? lowerer.lowerExprExpecting(call.arguments[2], BOOL)
|
|
543
|
+
: { kind: "boolLit", value: false, type: BOOL, loc };
|
|
544
|
+
return { kind: "libCall", fn: "bigint.dataViewSet", args: [receiver, offset, value, le], type: VOID, loc };
|
|
545
|
+
}
|
|
546
|
+
if (dvSetter !== undefined && receiverIr.elem === "u8") {
|
|
547
|
+
const maxArgs = dvSetter.le ? 3 : 2;
|
|
548
|
+
if (nArgs < 2 || nArgs > maxArgs) {
|
|
549
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
550
|
+
}
|
|
551
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
552
|
+
const args = [
|
|
553
|
+
lowerer.lowerExprExpecting(call.arguments[0], F64),
|
|
554
|
+
lowerer.lowerExprExpecting(call.arguments[1], F64),
|
|
555
|
+
];
|
|
556
|
+
if (nArgs === 3)
|
|
557
|
+
args.push(lowerer.lowerExprExpecting(call.arguments[2], BOOL));
|
|
558
|
+
return { kind: "bytesIntrinsic", method: dvSetter.method, receiver, args, type: VOID, loc };
|
|
559
|
+
}
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
/** Node's Buffer encoding names → the runtime's canonical spelling (every
|
|
563
|
+
* alias folds at compile time; the runtime sees only canonical names).
|
|
564
|
+
* Shared by toString, Buffer.from, and Buffer.byteLength. */
|
|
565
|
+
const BUF_ENCODINGS = {
|
|
566
|
+
utf8: "utf8",
|
|
567
|
+
"utf-8": "utf8",
|
|
568
|
+
hex: "hex",
|
|
569
|
+
base64: "base64",
|
|
570
|
+
base64url: "base64url",
|
|
571
|
+
latin1: "latin1",
|
|
572
|
+
binary: "latin1",
|
|
573
|
+
ascii: "ascii",
|
|
574
|
+
utf16le: "utf16le",
|
|
575
|
+
"utf-16le": "utf16le",
|
|
576
|
+
ucs2: "utf16le",
|
|
577
|
+
"ucs-2": "utf16le",
|
|
578
|
+
};
|
|
579
|
+
/** A literal encoding's canonical name, or undefined for a spelling Node
|
|
580
|
+
* does not know — the ladder callers (stream options) turn unknown
|
|
581
|
+
* literals into Node's runtime ERR_UNKNOWN_ENCODING throw. */
|
|
582
|
+
export function knownBufEncoding(name) {
|
|
583
|
+
return own(BUF_ENCODINGS, name);
|
|
584
|
+
}
|
|
585
|
+
/** The literal encoding argument of a Buffer surface, normalized — or a
|
|
586
|
+
* fence when it isn't a literal alias Node knows. */
|
|
587
|
+
export function bufEncoding(lowerer, what, encNode) {
|
|
588
|
+
const t = lowerer.typeOf(encNode);
|
|
589
|
+
const v = t.isStringLiteralType() ? own(BUF_ENCODINGS, t.value) : undefined;
|
|
590
|
+
if (v === undefined) {
|
|
591
|
+
lowerer.noLowering(`${what} with this encoding`, encNode, 'a literal "utf8", "hex", "base64", "base64url", "latin1", "binary", "ascii", "utf16le", or "ucs2" spelling is the lowered encoding surface');
|
|
592
|
+
}
|
|
593
|
+
return v;
|
|
594
|
+
}
|
|
595
|
+
/** The fixed-width Buffer numeric methods by source name → the readNum/
|
|
596
|
+
* writeNum kind token. Node declares BOTH capitalizations ("UInt" is the
|
|
597
|
+
* original, "Uint" the aliased spelling) — the tables carry both. */
|
|
598
|
+
const BUF_NUM_METHODS = (() => {
|
|
599
|
+
const out = {};
|
|
600
|
+
for (const rw of ["read", "write"]) {
|
|
601
|
+
for (const u of ["UInt", "Uint"]) {
|
|
602
|
+
out[`${rw}${u}8`] = "u8";
|
|
603
|
+
out[`${rw}${u}16BE`] = "u16be";
|
|
604
|
+
out[`${rw}${u}16LE`] = "u16le";
|
|
605
|
+
out[`${rw}${u}32BE`] = "u32be";
|
|
606
|
+
out[`${rw}${u}32LE`] = "u32le";
|
|
607
|
+
}
|
|
608
|
+
out[`${rw}Int8`] = "i8";
|
|
609
|
+
out[`${rw}Int16BE`] = "i16be";
|
|
610
|
+
out[`${rw}Int16LE`] = "i16le";
|
|
611
|
+
out[`${rw}Int32BE`] = "i32be";
|
|
612
|
+
out[`${rw}Int32LE`] = "i32le";
|
|
613
|
+
out[`${rw}FloatBE`] = "f32be";
|
|
614
|
+
out[`${rw}FloatLE`] = "f32le";
|
|
615
|
+
out[`${rw}DoubleBE`] = "f64be";
|
|
616
|
+
out[`${rw}DoubleLE`] = "f64le";
|
|
617
|
+
}
|
|
618
|
+
return out;
|
|
619
|
+
})();
|
|
620
|
+
const BUF_BIGINT_METHODS = {
|
|
621
|
+
readBigInt64BE: { sign: true, le: false },
|
|
622
|
+
readBigInt64LE: { sign: true, le: true },
|
|
623
|
+
readBigUInt64BE: { sign: false, le: false },
|
|
624
|
+
readBigUInt64LE: { sign: false, le: true },
|
|
625
|
+
readBigUint64BE: { sign: false, le: false },
|
|
626
|
+
readBigUint64LE: { sign: false, le: true },
|
|
627
|
+
writeBigInt64BE: { sign: true, le: false },
|
|
628
|
+
writeBigInt64LE: { sign: true, le: true },
|
|
629
|
+
writeBigUInt64BE: { sign: false, le: false },
|
|
630
|
+
writeBigUInt64LE: { sign: false, le: true },
|
|
631
|
+
writeBigUint64BE: { sign: false, le: false },
|
|
632
|
+
writeBigUint64LE: { sign: false, le: true },
|
|
633
|
+
};
|
|
634
|
+
/** The variable-width quartet (offset + byteLength) → sign/endian token. */
|
|
635
|
+
const BUF_NUM_VAR_METHODS = {
|
|
636
|
+
readUIntBE: "ube",
|
|
637
|
+
readUintBE: "ube",
|
|
638
|
+
readUIntLE: "ule",
|
|
639
|
+
readUintLE: "ule",
|
|
640
|
+
readIntBE: "ibe",
|
|
641
|
+
readIntLE: "ile",
|
|
642
|
+
writeUIntBE: "ube",
|
|
643
|
+
writeUintBE: "ube",
|
|
644
|
+
writeUIntLE: "ule",
|
|
645
|
+
writeUintLE: "ule",
|
|
646
|
+
writeIntBE: "ibe",
|
|
647
|
+
writeIntLE: "ile",
|
|
648
|
+
};
|
|
649
|
+
/** The Buffer-declared comparison/search/mutation methods on a u8
|
|
650
|
+
* receiver: equals, compare, indexOf/lastIndexOf/includes (number,
|
|
651
|
+
* string-with-encoding, and Buffer needles), fill, copy, swap16/32/64,
|
|
652
|
+
* and write. Null when `name` isn't one of them (the numeric families
|
|
653
|
+
* and the generic bytes surface try next). Encodings must be literals
|
|
654
|
+
* (bufEncoding's fence); a trailing string-typed argument is the
|
|
655
|
+
* encoding, exactly Node's overloads. */
|
|
656
|
+
function lowerBufferInstanceMethod(lowerer, call, access, name, loc) {
|
|
657
|
+
const args = call.arguments;
|
|
658
|
+
const nArgs = args.length;
|
|
659
|
+
if (args.some(ts.isSpreadElement))
|
|
660
|
+
return null;
|
|
661
|
+
const isStringArg = (i) => lowerer.mapTypeOf(lowerer.typeOf(args[i]))?.kind === "string";
|
|
662
|
+
const u8Arg = (i) => {
|
|
663
|
+
const v = lowerer.lowerExpr(args[i]);
|
|
664
|
+
if (!(v.type.kind === "bytes" && v.type.elem === "u8")) {
|
|
665
|
+
lowerer.noLowering(`.${name} of '${lowerer.fmt(v.type)}' values`, args[i], "a Buffer/Uint8Array argument is the lowered shape (narrow unions first)");
|
|
666
|
+
}
|
|
667
|
+
return v;
|
|
668
|
+
};
|
|
669
|
+
// The checked-dynamic crossing for a compare/equals argument slot: dyn
|
|
670
|
+
// passes through, convertible statics (the invalid-input probes'
|
|
671
|
+
// literals) wrap in dynFrom, `undefined` literals ride the same wrap.
|
|
672
|
+
// Fences (never returns) when the value cannot cross — an island jsval.
|
|
673
|
+
const chkArg = (i) => {
|
|
674
|
+
// Object literals take the dyn literal path directly (method members
|
|
675
|
+
// box as dyn functions — the typed record fence never applies).
|
|
676
|
+
if (ts.isObjectLiteralExpression(args[i])) {
|
|
677
|
+
return lowerDynObjectLiteral(lowerer, args[i]);
|
|
678
|
+
}
|
|
679
|
+
const v = lowerer.lowerExpr(args[i]);
|
|
680
|
+
if (v.type.kind === "dyn")
|
|
681
|
+
return v;
|
|
682
|
+
if (v.kind === "unitLit" || lowerer.dynConvertible(v.type)) {
|
|
683
|
+
return { kind: "dynFrom", value: v, type: DYN, loc: v.loc };
|
|
684
|
+
}
|
|
685
|
+
lowerer.noLowering(`.${name} of '${lowerer.fmt(v.type)}' values`, args[i], "a Buffer/Uint8Array argument is the lowered shape (narrow unions first)");
|
|
686
|
+
};
|
|
687
|
+
const argIrKind = (i) => lowerer.mapTypeOf(lowerer.typeOf(args[i]));
|
|
688
|
+
if (name === "equals") {
|
|
689
|
+
if (nArgs !== 1)
|
|
690
|
+
lowerer.noLowering(`.equals with ${nArgs} arguments`, call);
|
|
691
|
+
if (argIrKind(0)?.kind === "bytes") {
|
|
692
|
+
const other = u8Arg(0);
|
|
693
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
694
|
+
return { kind: "bytesIntrinsic", method: "equals", receiver, args: [other], type: BOOL, loc };
|
|
695
|
+
}
|
|
696
|
+
// Not statically bytes (the invalid-input probes, untyped JS
|
|
697
|
+
// helpers): Node's "otherBuffer" argument ladder runs at runtime.
|
|
698
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
699
|
+
return { kind: "libCall", fn: "bytes.equalsChk", args: [receiver, chkArg(0)], type: BOOL, loc };
|
|
700
|
+
}
|
|
701
|
+
if (name === "compare") {
|
|
702
|
+
if (nArgs > 5)
|
|
703
|
+
lowerer.noLowering(`.compare with ${nArgs} arguments`, call);
|
|
704
|
+
const fast = nArgs >= 1 &&
|
|
705
|
+
argIrKind(0)?.kind === "bytes" &&
|
|
706
|
+
call.arguments.slice(1).every((a) => lowerer.mapTypeOf(lowerer.typeOf(a))?.kind === "f64");
|
|
707
|
+
if (fast) {
|
|
708
|
+
const target = u8Arg(0);
|
|
709
|
+
const idx = call.arguments.slice(1).map((a) => lowerer.lowerExprExpecting(a, F64));
|
|
710
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
711
|
+
return { kind: "bytesIntrinsic", method: "compareBuf", receiver, args: [target, ...idx], type: F64, loc };
|
|
712
|
+
}
|
|
713
|
+
// An absent/ill-typed target or offset (`a.compare()`, string
|
|
714
|
+
// offsets, explicit undefined): Node's target/targetStart/targetEnd/
|
|
715
|
+
// sourceStart/sourceEnd ladder runs at runtime; absent slots pass
|
|
716
|
+
// the undefined dyn (Node defaults apply there).
|
|
717
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
718
|
+
const slots = [];
|
|
719
|
+
for (let i = 0; i < 5; i++)
|
|
720
|
+
slots.push(i < nArgs ? chkArg(i) : dynUndefinedExpr(loc));
|
|
721
|
+
return { kind: "libCall", fn: "bytes.compareChk", args: [receiver, ...slots], type: F64, loc };
|
|
722
|
+
}
|
|
723
|
+
if (name === "indexOf" || name === "lastIndexOf" || name === "includes") {
|
|
724
|
+
if (nArgs < 1 || nArgs > 3)
|
|
725
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
726
|
+
// The overloads: (v), (v, byteOffset), (v, encoding), (v, byteOffset,
|
|
727
|
+
// encoding) — a trailing string-typed arg is the encoding.
|
|
728
|
+
let encName = "utf8";
|
|
729
|
+
let offNode;
|
|
730
|
+
if (nArgs === 3) {
|
|
731
|
+
offNode = args[1];
|
|
732
|
+
encName = bufEncoding(lowerer, `.${name}`, args[2]);
|
|
733
|
+
}
|
|
734
|
+
else if (nArgs === 2) {
|
|
735
|
+
if (isStringArg(1))
|
|
736
|
+
encName = bufEncoding(lowerer, `.${name}`, args[1]);
|
|
737
|
+
else
|
|
738
|
+
offNode = args[1];
|
|
739
|
+
}
|
|
740
|
+
const resultT = name === "includes" ? BOOL : F64;
|
|
741
|
+
const vT = lowerer.mapTypeOf(lowerer.typeOf(args[0]));
|
|
742
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
743
|
+
if (vT?.kind === "f64") {
|
|
744
|
+
// A number needle wraps & 0xFF at runtime (Buffer semantics; the
|
|
745
|
+
// encoding is irrelevant, like Node).
|
|
746
|
+
const v = lowerer.lowerExprExpecting(args[0], F64);
|
|
747
|
+
const numArgs = [v, ...(offNode ? [lowerer.lowerExprExpecting(offNode, F64)] : [])];
|
|
748
|
+
const method = name === "indexOf" ? "indexOfNum" : name === "lastIndexOf" ? "lastIndexOfNum" : "includesNum";
|
|
749
|
+
return { kind: "bytesIntrinsic", method, receiver, args: numArgs, type: resultT, loc };
|
|
750
|
+
}
|
|
751
|
+
let needle;
|
|
752
|
+
let align = 1;
|
|
753
|
+
if (vT?.kind === "string") {
|
|
754
|
+
const s = lowerer.lowerExprExpecting(args[0], STRING);
|
|
755
|
+
const enc = { kind: "strLit", value: encName, type: STRING, loc };
|
|
756
|
+
needle = { kind: "libCall", fn: "buffer.fromStr", args: [s, enc], type: BYTES_U8, loc };
|
|
757
|
+
if (encName === "utf16le")
|
|
758
|
+
align = 2;
|
|
759
|
+
}
|
|
760
|
+
else if (vT?.kind === "bytes" && vT.elem === "u8") {
|
|
761
|
+
needle = lowerer.lowerExpr(args[0]);
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
lowerer.noLowering(`.${name} of '${lowerer.checker.typeToString(lowerer.typeOf(args[0]))}' values`, args[0], "string, number, and Buffer/Uint8Array needles search (narrow unions first)");
|
|
765
|
+
}
|
|
766
|
+
const alignLit = { kind: "numLit", value: align, type: F64, loc };
|
|
767
|
+
const searchArgs = [needle, alignLit, ...(offNode ? [lowerer.lowerExprExpecting(offNode, F64)] : [])];
|
|
768
|
+
return { kind: "bytesIntrinsic", method: name, receiver, args: searchArgs, type: resultT, loc };
|
|
769
|
+
}
|
|
770
|
+
if (name === "fill") {
|
|
771
|
+
if (nArgs < 1 || nArgs > 4)
|
|
772
|
+
lowerer.noLowering(`.fill with ${nArgs} arguments`, call);
|
|
773
|
+
// A trailing string-typed arg past the value is the encoding.
|
|
774
|
+
let encNode;
|
|
775
|
+
let idxNodes = call.arguments.slice(1);
|
|
776
|
+
if (idxNodes.length > 0 && isStringArg(nArgs - 1)) {
|
|
777
|
+
encNode = idxNodes[idxNodes.length - 1];
|
|
778
|
+
idxNodes = idxNodes.slice(0, -1);
|
|
779
|
+
}
|
|
780
|
+
if (idxNodes.length > 2)
|
|
781
|
+
lowerer.noLowering(`.fill with ${nArgs} arguments`, call);
|
|
782
|
+
const idx = idxNodes.map((a) => lowerer.lowerExprExpecting(a, F64));
|
|
783
|
+
const receiverT = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
784
|
+
if (receiverT?.kind !== "bytes")
|
|
785
|
+
lowerer.badType(access.expression, lowerer.typeOf(access.expression));
|
|
786
|
+
const vT = lowerer.mapTypeOf(lowerer.typeOf(args[0]));
|
|
787
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
788
|
+
if (vT?.kind === "string") {
|
|
789
|
+
const encName = encNode ? bufEncoding(lowerer, ".fill", encNode) : "utf8";
|
|
790
|
+
const s = lowerer.lowerExprExpecting(args[0], STRING);
|
|
791
|
+
const enc = { kind: "strLit", value: encName, type: STRING, loc };
|
|
792
|
+
return { kind: "bytesIntrinsic", method: "fillStr", receiver, args: [s, enc, ...idx], type: BYTES_U8, loc };
|
|
793
|
+
}
|
|
794
|
+
if (encNode)
|
|
795
|
+
lowerer.noLowering(".fill with an encoding on a non-string value", encNode);
|
|
796
|
+
if (vT?.kind === "f64") {
|
|
797
|
+
const v = lowerer.lowerExprExpecting(args[0], F64);
|
|
798
|
+
return { kind: "bytesIntrinsic", method: "fillNum", receiver, args: [v, ...idx], type: BYTES_U8, loc };
|
|
799
|
+
}
|
|
800
|
+
const pattern = u8Arg(0);
|
|
801
|
+
return { kind: "bytesIntrinsic", method: "fill", receiver, args: [pattern, ...idx], type: BYTES_U8, loc };
|
|
802
|
+
}
|
|
803
|
+
if (name === "copy") {
|
|
804
|
+
if (nArgs < 1 || nArgs > 4)
|
|
805
|
+
lowerer.noLowering(`.copy with ${nArgs} arguments`, call);
|
|
806
|
+
const target = u8Arg(0);
|
|
807
|
+
const idx = call.arguments.slice(1).map((a) => lowerer.lowerExprExpecting(a, F64));
|
|
808
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
809
|
+
return { kind: "bytesIntrinsic", method: "copy", receiver, args: [target, ...idx], type: F64, loc };
|
|
810
|
+
}
|
|
811
|
+
if (name === "swap16" || name === "swap32" || name === "swap64") {
|
|
812
|
+
if (nArgs !== 0)
|
|
813
|
+
lowerer.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
814
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
815
|
+
return { kind: "bytesIntrinsic", method: name, receiver, args: [], type: BYTES_U8, loc };
|
|
816
|
+
}
|
|
817
|
+
if (name === "write") {
|
|
818
|
+
if (nArgs < 1 || nArgs > 4)
|
|
819
|
+
lowerer.noLowering(`.write with ${nArgs} arguments`, call);
|
|
820
|
+
// (str), (str, enc), (str, offset), (str, offset, enc),
|
|
821
|
+
// (str, offset, length), (str, offset, length, enc).
|
|
822
|
+
let encNode;
|
|
823
|
+
let idxNodes = call.arguments.slice(1);
|
|
824
|
+
if (idxNodes.length > 0 && isStringArg(nArgs - 1)) {
|
|
825
|
+
encNode = idxNodes[idxNodes.length - 1];
|
|
826
|
+
idxNodes = idxNodes.slice(0, -1);
|
|
827
|
+
}
|
|
828
|
+
if (idxNodes.length > 2)
|
|
829
|
+
lowerer.noLowering(`.write with ${nArgs} arguments`, call);
|
|
830
|
+
const encName = encNode ? bufEncoding(lowerer, "Buffer.write", encNode) : "utf8";
|
|
831
|
+
const s = lowerer.lowerExprExpecting(args[0], STRING);
|
|
832
|
+
const enc = { kind: "strLit", value: encName, type: STRING, loc };
|
|
833
|
+
const offset = idxNodes[0]
|
|
834
|
+
? lowerer.lowerExprExpecting(idxNodes[0], F64)
|
|
835
|
+
: { kind: "numLit", value: 0, type: F64, loc };
|
|
836
|
+
const writeArgs = [s, enc, offset, ...(idxNodes[1] ? [lowerer.lowerExprExpecting(idxNodes[1], F64)] : [])];
|
|
837
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
838
|
+
return { kind: "bytesIntrinsic", method: "writeStr", receiver, args: writeArgs, type: F64, loc };
|
|
839
|
+
}
|
|
840
|
+
return null;
|
|
841
|
+
}
|
|
842
|
+
const DV_BIG_GETTERS = {
|
|
843
|
+
getBigInt64: { sign: true },
|
|
844
|
+
getBigUint64: { sign: false },
|
|
845
|
+
};
|
|
846
|
+
/** The number-returning DataView getter surface by source name. `le` marks
|
|
847
|
+
* the multi-byte kinds whose signature declares optional littleEndian;
|
|
848
|
+
* the BigInt pair routes through DV_BIG_GETTERS above. */
|
|
849
|
+
const DV_GETTERS = {
|
|
850
|
+
getUint8: { method: "dvGetUint8", le: false },
|
|
851
|
+
getInt8: { method: "dvGetInt8", le: false },
|
|
852
|
+
getUint16: { method: "dvGetUint16", le: true },
|
|
853
|
+
getInt16: { method: "dvGetInt16", le: true },
|
|
854
|
+
getUint32: { method: "dvGetUint32", le: true },
|
|
855
|
+
getInt32: { method: "dvGetInt32", le: true },
|
|
856
|
+
getFloat32: { method: "dvGetFloat32", le: true },
|
|
857
|
+
getFloat64: { method: "dvGetFloat64", le: true },
|
|
858
|
+
};
|
|
859
|
+
const DV_SETTERS = {
|
|
860
|
+
setUint8: { method: "dvSetUint8", le: false },
|
|
861
|
+
setInt8: { method: "dvSetInt8", le: false },
|
|
862
|
+
setUint16: { method: "dvSetUint16", le: true },
|
|
863
|
+
setInt16: { method: "dvSetInt16", le: true },
|
|
864
|
+
setUint32: { method: "dvSetUint32", le: true },
|
|
865
|
+
setInt32: { method: "dvSetInt32", le: true },
|
|
866
|
+
setFloat32: { method: "dvSetFloat32", le: true },
|
|
867
|
+
setFloat64: { method: "dvSetFloat64", le: true },
|
|
868
|
+
};
|
|
869
|
+
/** The Buffer statics — `Buffer.from(...)`, `Buffer.alloc(n)`,
|
|
870
|
+
* `Buffer.concat(list)`, `Buffer.isBuffer(x)` — on THE stdlib Buffer
|
|
871
|
+
* global (name + provenance; fallback and @types/node alike). Null when
|
|
872
|
+
* the callee isn't a Buffer-static access. */
|
|
873
|
+
function knownBufferProducer(lowerer, source, seen = new Set()) {
|
|
874
|
+
let node = source;
|
|
875
|
+
while (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isTypeAssertion(node))
|
|
876
|
+
node = node.expression;
|
|
877
|
+
if (ts.isIdentifier(node)) {
|
|
878
|
+
const symbol = lowerer.checker.getSymbolAtLocation(node);
|
|
879
|
+
if (!symbol || seen.has(symbol))
|
|
880
|
+
return false;
|
|
881
|
+
seen.add(symbol);
|
|
882
|
+
return lowerer.checker.declarationsOf(symbol).some((decl) => ts.isVariableDeclaration(decl) &&
|
|
883
|
+
ts.isVariableDeclarationList(decl.parent) &&
|
|
884
|
+
(decl.parent.flags & ts.NodeFlags.Const) !== 0 &&
|
|
885
|
+
decl.initializer !== undefined &&
|
|
886
|
+
knownBufferProducer(lowerer, decl.initializer, seen));
|
|
887
|
+
}
|
|
888
|
+
if (!ts.isCallExpression(node))
|
|
889
|
+
return false;
|
|
890
|
+
const callee = node.expression;
|
|
891
|
+
const bi = ts.isIdentifier(callee)
|
|
892
|
+
? lowerer.builtinImportOf(callee)
|
|
893
|
+
: ts.isPropertyAccessExpression(callee)
|
|
894
|
+
? lowerer.builtinMemberOf(callee)
|
|
895
|
+
: null;
|
|
896
|
+
if (bi?.module === "crypto" && (bi.member === "randomBytes" || bi.member === "pbkdf2Sync"))
|
|
897
|
+
return true;
|
|
898
|
+
if (!ts.isPropertyAccessExpression(callee) || callee.name.text !== "digest" || node.arguments.length !== 0)
|
|
899
|
+
return false;
|
|
900
|
+
const type = lowerer.mapTypeOf(lowerer.typeOf(callee.expression));
|
|
901
|
+
return type?.kind === "cryptoHash" || type?.kind === "cryptoHmac";
|
|
902
|
+
}
|
|
903
|
+
export function lowerBufferStaticCall(lowerer, call, access) {
|
|
904
|
+
if (call.questionDotToken || access.questionDotToken)
|
|
905
|
+
return null;
|
|
906
|
+
if (!lowerer.isStdlibGlobal(access.expression, "Buffer"))
|
|
907
|
+
return null;
|
|
908
|
+
const member = access.name.text;
|
|
909
|
+
const loc = locOf(call);
|
|
910
|
+
const args = call.arguments;
|
|
911
|
+
if (member === "from") {
|
|
912
|
+
// Buffer.from(x.buffer[, byteOffset[, length]]): a u8 VIEW sharing
|
|
913
|
+
// x's storage (Node shares the ArrayBuffer; length is in BYTES).
|
|
914
|
+
// The first argument must be the SYNTACTIC `.buffer` of a typed
|
|
915
|
+
// array — the DataView peel — and the construction rides the same
|
|
916
|
+
// dataViewNew intrinsic (offset/length validation throws Node's
|
|
917
|
+
// DataView-shaped RangeErrors; valid indices behave exactly).
|
|
918
|
+
if (args.length >= 1 && args.length <= 3 && !args.some(ts.isSpreadElement) &&
|
|
919
|
+
ts.isPropertyAccessExpression(args[0]) && args[0].name.text === "buffer") {
|
|
920
|
+
const srcNode = args[0].expression;
|
|
921
|
+
const srcIr = lowerer.mapTypeOf(lowerer.typeOf(srcNode));
|
|
922
|
+
if (srcIr?.kind === "bytes") {
|
|
923
|
+
const receiver = lowerer.lowerExpr(srcNode);
|
|
924
|
+
const idxArgs = args.slice(1).map((a) => lowerer.lowerExprExpecting(a, F64));
|
|
925
|
+
return { kind: "bytesIntrinsic", method: "dataViewNew", receiver, args: idxArgs, type: BYTES_U8, loc };
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
if (args.length >= 1 && args.length <= 2 && !ts.isSpreadElement(args[0])) {
|
|
929
|
+
const argNode = args[0];
|
|
930
|
+
if (ts.isArrayLiteralExpression(argNode) && !argNode.elements.some(ts.isSpreadElement)) {
|
|
931
|
+
// A number[] literal (contextually typed by the lib's readonly
|
|
932
|
+
// number[] slot — build it element-wise, the Set-seed pattern).
|
|
933
|
+
if (args.length === 1) {
|
|
934
|
+
const elems = argNode.elements.map((el) => lowerer.lowerExprExpecting(el, F64));
|
|
935
|
+
const seed = { kind: "arrayLit", elems, type: arrayOf(F64), loc };
|
|
936
|
+
return { kind: "bytesNew", source: seed, type: BYTES_U8, loc };
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
else {
|
|
940
|
+
const srcIr = lowerer.mapTypeOf(lowerer.typeOf(argNode));
|
|
941
|
+
if (srcIr?.kind === "string") {
|
|
942
|
+
const encNode = args[1];
|
|
943
|
+
const encName = encNode ? bufEncoding(lowerer, "Buffer.from", encNode) : "utf8";
|
|
944
|
+
const s = lowerer.lowerExprExpecting(argNode, STRING);
|
|
945
|
+
const enc = { kind: "strLit", value: encName, type: STRING, loc };
|
|
946
|
+
return { kind: "libCall", fn: "buffer.fromStr", args: [s, enc], type: BYTES_U8, loc };
|
|
947
|
+
}
|
|
948
|
+
if (args.length === 1 && srcIr?.kind === "bytes" && srcIr.elem === "u8") {
|
|
949
|
+
return { kind: "bytesNew", source: lowerer.lowerExpr(argNode), type: BYTES_U8, loc };
|
|
950
|
+
}
|
|
951
|
+
if (args.length === 1 && srcIr?.kind === "array" && srcIr.elem.kind === "f64") {
|
|
952
|
+
return { kind: "bytesNew", source: lowerer.lowerExpr(argNode), type: BYTES_U8, loc };
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
lowerer.noLowering("Buffer.from with this argument shape", call, "supported: Buffer.from(string, encoding?) with a literal encoding, Buffer.from(u8Array) — a copy — " +
|
|
957
|
+
"Buffer.from(number[]), or Buffer.from(x.buffer, byteOffset?, length?) — a view sharing x's " +
|
|
958
|
+
"storage (no free-standing ArrayBuffer value exists; narrow unions first)");
|
|
959
|
+
}
|
|
960
|
+
if (member === "alloc" || member === "allocUnsafe") {
|
|
961
|
+
const maxArgs = member === "alloc" ? 3 : 1;
|
|
962
|
+
if (args.length < 1 || args.length > maxArgs || args.some(ts.isSpreadElement)) {
|
|
963
|
+
lowerer.noLowering(`Buffer.${member} with ${args.length} arguments`, call);
|
|
964
|
+
}
|
|
965
|
+
// allocUnsafe's contents are UNSPECIFIED in Node (uninitialized pool
|
|
966
|
+
// memory); a zero-filled buffer is a valid instance of unspecified,
|
|
967
|
+
// and the deterministic choice — a program observing the difference
|
|
968
|
+
// is depending on garbage.
|
|
969
|
+
const size = lowerer.lowerExprExpecting(args[0], F64);
|
|
970
|
+
const fresh = { kind: "bytesNew", source: size, type: BYTES_U8, loc };
|
|
971
|
+
if (args.length === 1)
|
|
972
|
+
return fresh;
|
|
973
|
+
// alloc(size, fill, encoding?): the fill semantics ARE fill()'s —
|
|
974
|
+
// the fresh buffer is the receiver of a whole-range fill.
|
|
975
|
+
const fillT = lowerer.mapTypeOf(lowerer.typeOf(args[1]));
|
|
976
|
+
if (fillT?.kind === "string") {
|
|
977
|
+
const encName = args[2] ? bufEncoding(lowerer, "Buffer.alloc", args[2]) : "utf8";
|
|
978
|
+
const s = lowerer.lowerExprExpecting(args[1], STRING);
|
|
979
|
+
const enc = { kind: "strLit", value: encName, type: STRING, loc };
|
|
980
|
+
return { kind: "bytesIntrinsic", method: "fillStr", receiver: fresh, args: [s, enc], type: BYTES_U8, loc };
|
|
981
|
+
}
|
|
982
|
+
if (args.length > 2) {
|
|
983
|
+
lowerer.noLowering("Buffer.alloc with an encoding on a non-string fill", args[2]);
|
|
984
|
+
}
|
|
985
|
+
if (fillT?.kind === "f64") {
|
|
986
|
+
const v = lowerer.lowerExprExpecting(args[1], F64);
|
|
987
|
+
return { kind: "bytesIntrinsic", method: "fillNum", receiver: fresh, args: [v], type: BYTES_U8, loc };
|
|
988
|
+
}
|
|
989
|
+
if (fillT?.kind === "bytes" && fillT.elem === "u8") {
|
|
990
|
+
const pattern = lowerer.lowerExpr(args[1]);
|
|
991
|
+
return { kind: "bytesIntrinsic", method: "fill", receiver: fresh, args: [pattern], type: BYTES_U8, loc };
|
|
992
|
+
}
|
|
993
|
+
lowerer.noLowering(`Buffer.alloc with a '${lowerer.checker.typeToString(lowerer.typeOf(args[1]))}' fill`, args[1], "number, string, and Buffer/Uint8Array fills are the lowered shapes");
|
|
994
|
+
}
|
|
995
|
+
if (member === "concat") {
|
|
996
|
+
if (args.length < 1 || args.length > 2 || args.some(ts.isSpreadElement)) {
|
|
997
|
+
lowerer.noLowering(`Buffer.concat with ${args.length} arguments`, call);
|
|
998
|
+
}
|
|
999
|
+
const argNode = args[0];
|
|
1000
|
+
let list;
|
|
1001
|
+
if (ts.isArrayLiteralExpression(argNode) && !argNode.elements.some(ts.isSpreadElement)) {
|
|
1002
|
+
const elems = argNode.elements.map((el) => lowerer.lowerExprExpecting(el, BYTES_U8));
|
|
1003
|
+
list = { kind: "arrayLit", elems, type: arrayOf(BYTES_U8), loc };
|
|
1004
|
+
}
|
|
1005
|
+
else {
|
|
1006
|
+
list = lowerer.lowerExpr(argNode);
|
|
1007
|
+
if (!typeEquals(list.type, arrayOf(BYTES_U8))) {
|
|
1008
|
+
lowerer.noLowering(`Buffer.concat of '${lowerer.fmt(list.type)}' values`, argNode, "one Uint8Array[]/Buffer[] value (or literal) is the lowered list shape");
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (args.length === 2) {
|
|
1012
|
+
// The totalLength form truncates or zero-pads (and THROWS Node's
|
|
1013
|
+
// 'length' RangeError on bad totals).
|
|
1014
|
+
const total = lowerer.lowerExprExpecting(args[1], F64);
|
|
1015
|
+
return { kind: "libCall", fn: "buffer.concatLen", args: [list, total], type: BYTES_U8, loc };
|
|
1016
|
+
}
|
|
1017
|
+
return { kind: "libCall", fn: "buffer.concat", args: [list], type: BYTES_U8, loc };
|
|
1018
|
+
}
|
|
1019
|
+
if (member === "compare") {
|
|
1020
|
+
// The static form: Buffer.compare(a, b) IS a.compare(b).
|
|
1021
|
+
if (args.length !== 2 || args.some(ts.isSpreadElement)) {
|
|
1022
|
+
lowerer.noLowering(`Buffer.compare with ${args.length} arguments`, call);
|
|
1023
|
+
}
|
|
1024
|
+
const sides = args.map((a) => lowerer.lowerExpr(a));
|
|
1025
|
+
if (sides.every((v) => v.type.kind === "bytes" && v.type.elem === "u8")) {
|
|
1026
|
+
return { kind: "bytesIntrinsic", method: "compareBuf", receiver: sides[0], args: [sides[1]], type: F64, loc };
|
|
1027
|
+
}
|
|
1028
|
+
// A side that is not statically bytes (the invalid-input probes,
|
|
1029
|
+
// untyped JS helpers): Node's "buf1"/"buf2" argument ladder runs
|
|
1030
|
+
// at runtime — a well-typed dyn still compares.
|
|
1031
|
+
const dyns = sides.map((v, i) => {
|
|
1032
|
+
if (v.type.kind === "dyn")
|
|
1033
|
+
return v;
|
|
1034
|
+
if (v.kind === "unitLit" || lowerer.dynConvertible(v.type)) {
|
|
1035
|
+
return { kind: "dynFrom", value: v, type: DYN, loc: v.loc };
|
|
1036
|
+
}
|
|
1037
|
+
lowerer.noLowering(`Buffer.compare of '${lowerer.fmt(v.type)}' values`, args[i], "Buffer/Uint8Array values compare (narrow unions first)");
|
|
1038
|
+
});
|
|
1039
|
+
return { kind: "libCall", fn: "buffer.compareChk", args: dyns, type: F64, loc };
|
|
1040
|
+
}
|
|
1041
|
+
if (member === "isBuffer") {
|
|
1042
|
+
// The type-predicate narrowing test. Lowered where it DECIDES
|
|
1043
|
+
// something: a union-typed argument with a Buffer arm becomes a
|
|
1044
|
+
// runtime tag test (tsc's narrowing then types the branches, the
|
|
1045
|
+
// discriminated-union machinery). Statically-decided arguments stay
|
|
1046
|
+
// fenced except for standard-library calls (and const aliases of
|
|
1047
|
+
// them) proven to return a real Buffer; those preserve evaluation and
|
|
1048
|
+
// fold true even though Buffer and Uint8Array share one IR storage.
|
|
1049
|
+
if (args.length === 1 && !ts.isSpreadElement(args[0])) {
|
|
1050
|
+
const source = args[0];
|
|
1051
|
+
const v = lowerer.lowerExpr(args[0]);
|
|
1052
|
+
// The bytes IR intentionally unifies Buffer and Uint8Array storage,
|
|
1053
|
+
// but these standard-library producers are known to return a real
|
|
1054
|
+
// Node Buffer. Preserve source evaluation and fold the predicate.
|
|
1055
|
+
if (knownBufferProducer(lowerer, source)) {
|
|
1056
|
+
return {
|
|
1057
|
+
kind: "seqExpr",
|
|
1058
|
+
stmts: [{ kind: "exprStmt", expr: v, loc: v.loc }],
|
|
1059
|
+
result: { kind: "boolLit", value: true, type: BOOL, loc },
|
|
1060
|
+
type: BOOL,
|
|
1061
|
+
loc,
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
if (v.type.kind === "union") {
|
|
1065
|
+
const def = lowerer.unions.get(v.type.unionId);
|
|
1066
|
+
const tag = def ? def.arms.findIndex((a) => a.kind === "bytes" && a.elem === "u8") : -1;
|
|
1067
|
+
if (tag >= 0) {
|
|
1068
|
+
return { kind: "unionIsTag", unionId: v.type.unionId, tag, negated: false, value: v, type: BOOL, loc };
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
lowerer.noLowering(`Buffer.isBuffer of '${lowerer.fmt(v.type)}' values`, args[0], "the check lowers as a runtime tag test on unions with a Buffer arm — here the answer is static");
|
|
1072
|
+
}
|
|
1073
|
+
lowerer.noLowering("Buffer.isBuffer with this argument shape", call);
|
|
1074
|
+
}
|
|
1075
|
+
if (member === "byteLength") {
|
|
1076
|
+
// Buffer.byteLength(string, enc?) — the UTF-16-aware per-encoding
|
|
1077
|
+
// count — or of a typed array/Buffer (its byte length, a property
|
|
1078
|
+
// read at heart).
|
|
1079
|
+
if (args.length >= 1 && args.length <= 2 && !ts.isSpreadElement(args[0])) {
|
|
1080
|
+
const srcIr = lowerer.mapTypeOf(lowerer.typeOf(args[0]));
|
|
1081
|
+
if (srcIr?.kind === "string") {
|
|
1082
|
+
const encName = args[1] ? bufEncoding(lowerer, "Buffer.byteLength", args[1]) : "utf8";
|
|
1083
|
+
const s = lowerer.lowerExprExpecting(args[0], STRING);
|
|
1084
|
+
const enc = { kind: "strLit", value: encName, type: STRING, loc };
|
|
1085
|
+
return { kind: "libCall", fn: "buffer.byteLenStr", args: [s, enc], type: F64, loc };
|
|
1086
|
+
}
|
|
1087
|
+
if (srcIr?.kind === "bytes" && args.length === 1) {
|
|
1088
|
+
const receiver = lowerer.lowerExpr(args[0]);
|
|
1089
|
+
return { kind: "bytesIntrinsic", method: "byteLength", receiver, args: [], type: F64, loc };
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
lowerer.noLowering("Buffer.byteLength with this argument shape", call, "supported: Buffer.byteLength(string, literalEncoding?) or Buffer.byteLength(typedArray)");
|
|
1093
|
+
}
|
|
1094
|
+
if (member === "isEncoding") {
|
|
1095
|
+
// The runtime alias-set test — a plain bool over any string value
|
|
1096
|
+
// (Node's case-insensitive normalizeEncoding check).
|
|
1097
|
+
if (args.length === 1 && !ts.isSpreadElement(args[0]) && lowerer.mapTypeOf(lowerer.typeOf(args[0]))?.kind === "string") {
|
|
1098
|
+
const s = lowerer.lowerExprExpecting(args[0], STRING);
|
|
1099
|
+
return { kind: "libCall", fn: "buffer.isEncoding", args: [s], type: BOOL, loc };
|
|
1100
|
+
}
|
|
1101
|
+
lowerer.noLowering("Buffer.isEncoding with this argument shape", call, "one string-typed argument is the lowered form");
|
|
1102
|
+
}
|
|
1103
|
+
return null; // of, copyBytesFrom, ... → the SC2020 member fence
|
|
1104
|
+
}
|
|
1105
|
+
//# sourceMappingURL=bytes.js.map
|