@scriptc/compiler 0.0.7 → 0.0.9
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 +13 -0
- package/dist/backend/cc.js +14 -0
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +155 -6
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-island.js +9 -1
- package/dist/backend/emission/emit-island.js.map +1 -1
- package/dist/backend/emission/emit-stmts.js +3 -1
- package/dist/backend/emission/emit-stmts.js.map +1 -1
- package/dist/backend/emission/emit-types.d.ts +3 -0
- package/dist/backend/emission/emit-types.js +12 -0
- package/dist/backend/emission/emit-types.js.map +1 -1
- package/dist/backend/emission/emit-walkers.d.ts +7 -5
- package/dist/backend/emission/emit-walkers.js +45 -18
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +1 -1
- package/dist/backend/emission/emitter.js +35 -10
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/emission/may-throw.js +4 -3
- package/dist/backend/emission/may-throw.js.map +1 -1
- package/dist/backend/llvm/emitter.js +300 -18
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +98 -11
- package/dist/diagnostics/diagnostic.js +185 -25
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lib-contract.d.ts +24 -6
- package/dist/frontend/lib-contract.js +125 -44
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +16 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +11 -0
- package/dist/frontend/lowering/lower-builtins.js +127 -4
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +77 -18
- package/dist/frontend/lowering/lower-calls.js +978 -44
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +14 -1
- package/dist/frontend/lowering/lower-classes.js +112 -16
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +4 -2
- package/dist/frontend/lowering/lower-containers.js +236 -44
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-emitter.js +55 -1
- package/dist/frontend/lowering/lower-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +26 -3
- package/dist/frontend/lowering/lower-exprs.js +507 -47
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +7 -0
- package/dist/frontend/lowering/lower-island.js +71 -10
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.d.ts +1 -1
- package/dist/frontend/lowering/lower-mixins.js +1 -1
- package/dist/frontend/lowering/lower-modules.js +95 -4
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +866 -56
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +28 -2
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +85 -17
- package/dist/frontend/lowering/lowerer.js +459 -37
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +20 -3
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.d.ts +7 -2
- package/dist/frontend/npm-static-rewrite.js +566 -138
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static.js +11 -2
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/program.js +5 -5
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/shared.d.ts +6 -1
- package/dist/frontend/shared.js +46 -1
- package/dist/frontend/shared.js.map +1 -1
- package/dist/frontend/types.d.ts +28 -1
- package/dist/frontend/types.js +304 -10
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +181 -26
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +159 -9
- package/dist/ir/nodes.js +76 -6
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +93 -14
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/profile.d.ts +17 -3
- package/dist/library/profile.js +38 -9
- package/dist/library/profile.js.map +1 -1
- package/dist/library/sidecar.js +162 -43
- package/dist/library/sidecar.js.map +1 -1
- package/dist/library/trap-teaching.d.ts +11 -0
- package/dist/library/trap-teaching.js +49 -0
- package/dist/library/trap-teaching.js.map +1 -0
- package/package.json +2 -2
- package/surface-manifest.json +46 -4
|
@@ -388,8 +388,10 @@ export declare function lowerObjectFromEntriesCall(L: Lowerer, call: ts.CallExpr
|
|
|
388
388
|
* every source value (declared fields, and the overflow slot when the
|
|
389
389
|
* source carries one) must enter the target's value slot (identity, a
|
|
390
390
|
* width lift — arm wrap/re-tag/nested reshape, or JSON-safe into a dyn
|
|
391
|
-
* slot), and every target field must be
|
|
392
|
-
*
|
|
391
|
+
* slot), and every target field must either be initialized directly by
|
|
392
|
+
* a same-named source declared field (the rule that admits REQUIRED
|
|
393
|
+
* declared members) or be optional-flavored (the fresh record's
|
|
394
|
+
* default) and writable at runtime. */
|
|
393
395
|
export declare function lowerRecordOvfCaptureHelper(L: Lowerer, fromId: string, toId: string, loc: SrcLoc): string | null;
|
|
394
396
|
/** `Object.assign(target, ...sources)` into an INDEX-SIGNATURE record:
|
|
395
397
|
* the capture helper's keyed-write walk aimed at the EXISTING target —
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
import * as ts from "../ts7/adapter.js";
|
|
6
6
|
import { BOOL, BYTES_U8, CAUGHT, DYN, F64, JSVAL, STRING, UNDEFINED_T, VOID, arrayOf, bytesOf, funcOf, isRefCounted, isSupportedIndexValue, isUnitType, typeEquals } from "../../ir/nodes.js";
|
|
7
7
|
import { ARRAY_METHODS, MAP_METHODS, SET_COMBINE_METHODS, SET_METHODS, STR_METHODS } from "./surfaces.js";
|
|
8
|
-
import { isRequireMainFilename, probeLower, pureReemittable } from "./lower-exprs.js";
|
|
8
|
+
import { isRequireMainFilename, lowerDynObjectLiteral, probeLower, pureReemittable } from "./lower-exprs.js";
|
|
9
9
|
import { forOfVarTarget, lowerDestructuringAssign } from "./lower-stmts.js";
|
|
10
10
|
import { isJsSourceFile, locOf } from "../program.js";
|
|
11
11
|
import { DYN_DISPATCH_METHODS, islandPrimitiveExit } from "./lower-calls.js";
|
|
12
12
|
import { typeKey } from "../types.js";
|
|
13
|
-
import { own } from "./lowerer.js";
|
|
13
|
+
import { dynUndefinedExpr, own } from "./lowerer.js";
|
|
14
14
|
/** Ambient array method calls. `push`/`pop`/`indexOf`/`includes`/`join`
|
|
15
15
|
* lower to arrIntrinsic; the HOF family — `map`/`filter`/`forEach`/
|
|
16
16
|
* `find`/`some`/`every`/`flatMap`/`reduce`/`reduceRight` — desugars to a
|
|
@@ -49,7 +49,7 @@ export function lowerArrayMethodCall(L, call, access) {
|
|
|
49
49
|
return null;
|
|
50
50
|
if (!probedUntyped && !L.isStdlibMember(access))
|
|
51
51
|
return null;
|
|
52
|
-
|
|
52
|
+
let elem = receiverIr.elem;
|
|
53
53
|
const loc = locOf(call);
|
|
54
54
|
// An island handle behind an array-typed .d.ts surface
|
|
55
55
|
// (`parts().join("-")` — arrays never exit eagerly, so the value
|
|
@@ -91,6 +91,19 @@ export function lowerArrayMethodCall(L, call, access) {
|
|
|
91
91
|
}
|
|
92
92
|
L.noLowering(`.${name} on an array value held in a checked-dynamic binding`, call, "assign it to an array-typed binding first (the validated extraction), then call the method");
|
|
93
93
|
}
|
|
94
|
+
// An evolving-`any` array binding under --dynamic whose flow type
|
|
95
|
+
// EVOLVED past `any[]` (`const fns = []; fns.push(() => 1);
|
|
96
|
+
// fns.map(...)` — the binding lowered array<jsval> at its `any[]`
|
|
97
|
+
// declaration, while tsc's evolving-array analysis answers the
|
|
98
|
+
// pushed element type at this site): the VALUE's element type is
|
|
99
|
+
// the truth — ride the explicit-`any[]` handle-element lowering
|
|
100
|
+
// (pushes marshal in, HOF callbacks bind the handle, results exit
|
|
101
|
+
// per the checker type), never a typed intrinsic over a jsval-
|
|
102
|
+
// element array (the validator ICE).
|
|
103
|
+
if (receiver.type.kind === "array" && receiver.type.elem.kind === "jsval" && !typeEquals(receiverIr, receiver.type)) {
|
|
104
|
+
receiverIr = receiver.type;
|
|
105
|
+
elem = receiver.type.elem;
|
|
106
|
+
}
|
|
94
107
|
}
|
|
95
108
|
if (name === "sort")
|
|
96
109
|
return lowerArraySortCall(L, call, access, elem, receiverIr);
|
|
@@ -214,6 +227,20 @@ export function lowerArrayMethodCall(L, call, access) {
|
|
|
214
227
|
args.push(arg);
|
|
215
228
|
continue;
|
|
216
229
|
}
|
|
230
|
+
// A handle-element receiver given an argument whose CHECKER type
|
|
231
|
+
// spells evolved elements while its VALUE is a jsval-element
|
|
232
|
+
// array (`fns.concat(tail)` — both sides of the evolving-`any`
|
|
233
|
+
// adoption): the value's array-ness decides, exactly JS's
|
|
234
|
+
// IsArray — spread it. Non-array values fall through to the
|
|
235
|
+
// element push (the jsvalIn coercion).
|
|
236
|
+
if (elem.kind === "jsval") {
|
|
237
|
+
const arg = L.lowerExpr(argNode);
|
|
238
|
+
if (arg.type.kind === "array" && arg.type.elem.kind === "jsval") {
|
|
239
|
+
shape.push("a");
|
|
240
|
+
args.push(arg);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
217
244
|
// An element value — union elements wrap exactly like a push.
|
|
218
245
|
shape.push("e");
|
|
219
246
|
args.push(L.lowerExprExpecting(argNode, elem));
|
|
@@ -324,6 +351,21 @@ function hofCallbackArg(L, argNode, lead, arrT) {
|
|
|
324
351
|
}
|
|
325
352
|
}
|
|
326
353
|
}
|
|
354
|
+
// A JSVAL-element receiver behind an EVOLVED contextual type (the
|
|
355
|
+
// evolving-`any` array under --dynamic — lowerArrayMethodCall adopted
|
|
356
|
+
// the value's handle element while tsc's evolving analysis types the
|
|
357
|
+
// callback's params by the pushed elements): the lead params BIND the
|
|
358
|
+
// handles the loop passes, whatever the contextual type spelled —
|
|
359
|
+
// paramShape's island-handle early-out, the then-handler rule.
|
|
360
|
+
if (ts.isArrowFunction(argNode) || ts.isFunctionExpression(argNode)) {
|
|
361
|
+
argNode.parameters.forEach((p, i) => {
|
|
362
|
+
if (i >= lead.length || lead[i].kind !== "jsval")
|
|
363
|
+
return;
|
|
364
|
+
if (!ts.isIdentifier(p.name) || p.type || p.initializer || p.dotDotDotToken)
|
|
365
|
+
return;
|
|
366
|
+
L.jsvalParamOverrides.add(p);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
327
369
|
let fnArg;
|
|
328
370
|
try {
|
|
329
371
|
fnArg = L.lowerExpr(argNode);
|
|
@@ -4062,25 +4104,35 @@ export function lowerBytesNew(L, expr, symbol) {
|
|
|
4062
4104
|
const seed = { kind: "arrayLit", elems, type: arrayOf(F64), loc };
|
|
4063
4105
|
return { kind: "bytesNew", source: seed, type, loc };
|
|
4064
4106
|
}
|
|
4065
|
-
// The SYNTACTIC `new T(new
|
|
4066
|
-
//
|
|
4067
|
-
//
|
|
4068
|
-
//
|
|
4069
|
-
//
|
|
4070
|
-
//
|
|
4071
|
-
//
|
|
4072
|
-
//
|
|
4073
|
-
//
|
|
4107
|
+
// The SYNTACTIC `new T(new ArrayBuffer(n))` and `new T(new
|
|
4108
|
+
// SharedArrayBuffer(n))` forms — fresh-buffer construction (the
|
|
4109
|
+
// shared spelling is the Atomics.wait sleep idiom's). The buffer
|
|
4110
|
+
// never exists as a value: n must be a byte-length LITERAL divisible
|
|
4111
|
+
// by the element size (tsc would admit any number; a bad one is
|
|
4112
|
+
// Node's RangeError — rejected at compile time instead of
|
|
4113
|
+
// half-lowering), and the whole expression is a zero-filled typed
|
|
4114
|
+
// array of n/elemSize elements. Erasing the buffer is exact: nothing
|
|
4115
|
+
// else can ever reference it, so neither sharing (scriptc has no
|
|
4116
|
+
// threads) nor aliasing is observable — SEMANTICS.md documents the
|
|
4117
|
+
// stance. The RESIZABLE form (a maxByteLength options bag) fences by
|
|
4118
|
+
// name: resize needs the buffer to exist as a value, and none does.
|
|
4074
4119
|
if (ts.isNewExpression(argNode) &&
|
|
4075
4120
|
ts.isIdentifier(argNode.expression) &&
|
|
4076
|
-
argNode.expression.text === "
|
|
4121
|
+
(argNode.expression.text === "ArrayBuffer" ||
|
|
4122
|
+
argNode.expression.text === "SharedArrayBuffer") &&
|
|
4077
4123
|
L.isStdlibSymbol(L.resolveValueSymbol(argNode.expression) ?? undefined)) {
|
|
4124
|
+
const bufCtor = argNode.expression.text;
|
|
4125
|
+
if ((argNode.arguments?.length ?? 0) > 1) {
|
|
4126
|
+
L.noLowering(`new ${name} over a resizable ${bufCtor}`, argNode, "a maxByteLength options bag makes the buffer resizable, and resizing needs the buffer " +
|
|
4127
|
+
"to exist as a runtime value — no free-standing ArrayBuffer value exists (the buffer here " +
|
|
4128
|
+
"erases into the view): drop the options bag");
|
|
4129
|
+
}
|
|
4078
4130
|
const elemSize = elem === "u8" ? 1 : 4;
|
|
4079
4131
|
const lenArg = argNode.arguments?.length === 1 ? argNode.arguments[0] : undefined;
|
|
4080
4132
|
const lenT = lenArg ? L.typeOf(lenArg) : null;
|
|
4081
4133
|
const byteLen = lenT?.isNumberLiteralType() ? lenT.value : null;
|
|
4082
4134
|
if (byteLen === null || byteLen % elemSize !== 0 || byteLen < 0) {
|
|
4083
|
-
L.noLowering(`new ${name} over this
|
|
4135
|
+
L.noLowering(`new ${name} over this ${bufCtor}`, argNode, `the byte length must be a number literal divisible by ${elemSize} — new ${name}(new ${bufCtor}(${elemSize}))`);
|
|
4084
4136
|
}
|
|
4085
4137
|
const count = { kind: "numLit", value: byteLen / elemSize, type: F64, loc };
|
|
4086
4138
|
return { kind: "bytesNew", source: count, type, loc };
|
|
@@ -4114,8 +4166,37 @@ function lowerDataViewNew(L, expr) {
|
|
|
4114
4166
|
if (!bufNode || args.length > 3 || args.some(ts.isSpreadElement)) {
|
|
4115
4167
|
L.noLowering(`new DataView with ${args.length} arguments`, expr, "supported: new DataView(x.buffer), (x.buffer, byteOffset), or (x.buffer, byteOffset, byteLength)");
|
|
4116
4168
|
}
|
|
4169
|
+
// The FRESH-BUFFER form: `new DataView(new ArrayBuffer(n), ...)` — the
|
|
4170
|
+
// buffer erases into the view (nothing else can ever reference it, so
|
|
4171
|
+
// the aliasing a shared buffer would exhibit is unobservable): the
|
|
4172
|
+
// view's storage is a fresh zero-filled n-byte allocation. n must be a
|
|
4173
|
+
// non-negative integer LITERAL (tsc admits any number; a fractional
|
|
4174
|
+
// one is ToIndex truncation nothing here implements). byteOffset and
|
|
4175
|
+
// byteLength keep their runtime Node-RangeError story. The RESIZABLE
|
|
4176
|
+
// form (a maxByteLength options bag) fences by name.
|
|
4177
|
+
if (ts.isNewExpression(bufNode) &&
|
|
4178
|
+
ts.isIdentifier(bufNode.expression) &&
|
|
4179
|
+
bufNode.expression.text === "ArrayBuffer" &&
|
|
4180
|
+
L.isStdlibSymbol(L.resolveValueSymbol(bufNode.expression) ?? undefined)) {
|
|
4181
|
+
if ((bufNode.arguments?.length ?? 0) > 1) {
|
|
4182
|
+
L.noLowering("new DataView over a resizable ArrayBuffer", bufNode, "a maxByteLength options bag makes the buffer resizable, and resizing needs the buffer " +
|
|
4183
|
+
"to exist as a runtime value — no free-standing ArrayBuffer value exists (the buffer here " +
|
|
4184
|
+
"erases into the view): drop the options bag");
|
|
4185
|
+
}
|
|
4186
|
+
const lenArg = bufNode.arguments?.length === 1 ? bufNode.arguments[0] : undefined;
|
|
4187
|
+
const lenT = lenArg ? L.typeOf(lenArg) : null;
|
|
4188
|
+
const byteLen = lenT?.isNumberLiteralType() ? lenT.value : null;
|
|
4189
|
+
if (byteLen === null || !Number.isInteger(byteLen) || byteLen < 0) {
|
|
4190
|
+
L.noLowering("new DataView over this ArrayBuffer", bufNode, "the byte length must be a non-negative integer literal — new DataView(new ArrayBuffer(8))");
|
|
4191
|
+
}
|
|
4192
|
+
const count = { kind: "numLit", value: byteLen, type: F64, loc };
|
|
4193
|
+
const receiver = { kind: "bytesNew", source: count, type: BYTES_U8, loc };
|
|
4194
|
+
const idxArgs = args.slice(1).map((a) => L.lowerExprExpecting(a, F64));
|
|
4195
|
+
return { kind: "bytesIntrinsic", method: "dataViewNew", receiver, args: idxArgs, type: BYTES_U8, loc };
|
|
4196
|
+
}
|
|
4117
4197
|
const hint = "views compile over a typed array's own storage — new DataView(x.buffer, byteOffset?, byteLength?) " +
|
|
4118
|
-
"where x is a Uint8Array/Uint32Array/Float32Array/Buffer value
|
|
4198
|
+
"where x is a Uint8Array/Uint32Array/Float32Array/Buffer value — or a fresh buffer erased into " +
|
|
4199
|
+
"the view: new DataView(new ArrayBuffer(n), ...); free-standing ArrayBuffers have no representation";
|
|
4119
4200
|
if (!ts.isPropertyAccessExpression(bufNode) || bufNode.name.text !== "buffer" || bufNode.questionDotToken) {
|
|
4120
4201
|
L.noLowering("new DataView over this buffer expression", bufNode, hint);
|
|
4121
4202
|
}
|
|
@@ -4287,6 +4368,27 @@ export function lowerBytesMethodCall(L, call, access) {
|
|
|
4287
4368
|
args.push(L.lowerExprExpecting(call.arguments[1], BOOL));
|
|
4288
4369
|
return { kind: "bytesIntrinsic", method: dvGetter.method, receiver, args, type: F64, loc };
|
|
4289
4370
|
}
|
|
4371
|
+
// DataView setters — the getters' mirror: (byteOffset, value) plus the
|
|
4372
|
+
// optional littleEndian bool on the multi-byte kinds. Void results;
|
|
4373
|
+
// the same constant Node RangeError on a bad offset (may-throw seeds).
|
|
4374
|
+
// setBigUint64/setBigInt64 never lower (bigint ARGUMENTS have no
|
|
4375
|
+
// representation and no composed form exists) — they keep the member
|
|
4376
|
+
// fence, as does setFloat16.
|
|
4377
|
+
const dvSetter = own(DV_SETTERS, name);
|
|
4378
|
+
if (dvSetter !== undefined && receiverIr.elem === "u8") {
|
|
4379
|
+
const maxArgs = dvSetter.le ? 3 : 2;
|
|
4380
|
+
if (nArgs < 2 || nArgs > maxArgs) {
|
|
4381
|
+
L.noLowering(`.${name} with ${nArgs} arguments`, call);
|
|
4382
|
+
}
|
|
4383
|
+
const receiver = L.lowerExpr(access.expression);
|
|
4384
|
+
const args = [
|
|
4385
|
+
L.lowerExprExpecting(call.arguments[0], F64),
|
|
4386
|
+
L.lowerExprExpecting(call.arguments[1], F64),
|
|
4387
|
+
];
|
|
4388
|
+
if (nArgs === 3)
|
|
4389
|
+
args.push(L.lowerExprExpecting(call.arguments[2], BOOL));
|
|
4390
|
+
return { kind: "bytesIntrinsic", method: dvSetter.method, receiver, args, type: VOID, loc };
|
|
4391
|
+
}
|
|
4290
4392
|
return null;
|
|
4291
4393
|
}
|
|
4292
4394
|
/** Node's Buffer encoding names → the runtime's canonical spelling (every
|
|
@@ -4376,20 +4478,59 @@ function lowerBufferInstanceMethod(L, call, access, name, loc) {
|
|
|
4376
4478
|
}
|
|
4377
4479
|
return v;
|
|
4378
4480
|
};
|
|
4481
|
+
// The checked-dynamic crossing for a compare/equals argument slot: dyn
|
|
4482
|
+
// passes through, convertible statics (the invalid-input probes'
|
|
4483
|
+
// literals) wrap in dynFrom, `undefined` literals ride the same wrap.
|
|
4484
|
+
// Fences (never returns) when the value cannot cross — an island jsval.
|
|
4485
|
+
const chkArg = (i) => {
|
|
4486
|
+
// Object literals take the DOM literal path directly (method members
|
|
4487
|
+
// box as dyn functions — the typed record fence never applies).
|
|
4488
|
+
if (ts.isObjectLiteralExpression(args[i])) {
|
|
4489
|
+
return lowerDynObjectLiteral(L, args[i]);
|
|
4490
|
+
}
|
|
4491
|
+
const v = L.lowerExpr(args[i]);
|
|
4492
|
+
if (v.type.kind === "dyn")
|
|
4493
|
+
return v;
|
|
4494
|
+
if (v.kind === "unitLit" || L.dynConvertible(v.type)) {
|
|
4495
|
+
return { kind: "dynFrom", value: v, type: DYN, loc: v.loc };
|
|
4496
|
+
}
|
|
4497
|
+
L.noLowering(`.${name} of '${L.fmt(v.type)}' values`, args[i], "a Buffer/Uint8Array argument is the lowered shape (narrow unions first)");
|
|
4498
|
+
};
|
|
4499
|
+
const argIrKind = (i) => L.mapTypeOf(L.typeOf(args[i]));
|
|
4379
4500
|
if (name === "equals") {
|
|
4380
4501
|
if (nArgs !== 1)
|
|
4381
4502
|
L.noLowering(`.equals with ${nArgs} arguments`, call);
|
|
4382
|
-
|
|
4503
|
+
if (argIrKind(0)?.kind === "bytes") {
|
|
4504
|
+
const other = u8Arg(0);
|
|
4505
|
+
const receiver = L.lowerExpr(access.expression);
|
|
4506
|
+
return { kind: "bytesIntrinsic", method: "equals", receiver, args: [other], type: BOOL, loc };
|
|
4507
|
+
}
|
|
4508
|
+
// Not statically bytes (the invalid-input probes, untyped JS
|
|
4509
|
+
// helpers): Node's "otherBuffer" argument ladder runs at runtime.
|
|
4383
4510
|
const receiver = L.lowerExpr(access.expression);
|
|
4384
|
-
return { kind: "
|
|
4511
|
+
return { kind: "libCall", fn: "bytes.equalsChk", args: [receiver, chkArg(0)], type: BOOL, loc };
|
|
4385
4512
|
}
|
|
4386
4513
|
if (name === "compare") {
|
|
4387
|
-
if (nArgs
|
|
4514
|
+
if (nArgs > 5)
|
|
4388
4515
|
L.noLowering(`.compare with ${nArgs} arguments`, call);
|
|
4389
|
-
const
|
|
4390
|
-
|
|
4516
|
+
const fast = nArgs >= 1 &&
|
|
4517
|
+
argIrKind(0)?.kind === "bytes" &&
|
|
4518
|
+
call.arguments.slice(1).every((a) => L.mapTypeOf(L.typeOf(a))?.kind === "f64");
|
|
4519
|
+
if (fast) {
|
|
4520
|
+
const target = u8Arg(0);
|
|
4521
|
+
const idx = call.arguments.slice(1).map((a) => L.lowerExprExpecting(a, F64));
|
|
4522
|
+
const receiver = L.lowerExpr(access.expression);
|
|
4523
|
+
return { kind: "bytesIntrinsic", method: "compareBuf", receiver, args: [target, ...idx], type: F64, loc };
|
|
4524
|
+
}
|
|
4525
|
+
// An absent/ill-typed target or offset (`a.compare()`, string
|
|
4526
|
+
// offsets, explicit undefined): Node's target/targetStart/targetEnd/
|
|
4527
|
+
// sourceStart/sourceEnd ladder runs at runtime; absent slots pass
|
|
4528
|
+
// the undefined dyn (Node defaults apply there).
|
|
4391
4529
|
const receiver = L.lowerExpr(access.expression);
|
|
4392
|
-
|
|
4530
|
+
const slots = [];
|
|
4531
|
+
for (let i = 0; i < 5; i++)
|
|
4532
|
+
slots.push(i < nArgs ? chkArg(i) : dynUndefinedExpr(loc));
|
|
4533
|
+
return { kind: "libCall", fn: "bytes.compareChk", args: [receiver, ...slots], type: F64, loc };
|
|
4393
4534
|
}
|
|
4394
4535
|
if (name === "indexOf" || name === "lastIndexOf" || name === "includes") {
|
|
4395
4536
|
if (nArgs < 1 || nArgs > 3)
|
|
@@ -4526,6 +4667,16 @@ const DV_GETTERS = {
|
|
|
4526
4667
|
getBigUint64: { method: "dvGetBigUint64Number", le: true },
|
|
4527
4668
|
getBigInt64: { method: "dvGetBigInt64Number", le: true },
|
|
4528
4669
|
};
|
|
4670
|
+
const DV_SETTERS = {
|
|
4671
|
+
setUint8: { method: "dvSetUint8", le: false },
|
|
4672
|
+
setInt8: { method: "dvSetInt8", le: false },
|
|
4673
|
+
setUint16: { method: "dvSetUint16", le: true },
|
|
4674
|
+
setInt16: { method: "dvSetInt16", le: true },
|
|
4675
|
+
setUint32: { method: "dvSetUint32", le: true },
|
|
4676
|
+
setInt32: { method: "dvSetInt32", le: true },
|
|
4677
|
+
setFloat32: { method: "dvSetFloat32", le: true },
|
|
4678
|
+
setFloat64: { method: "dvSetFloat64", le: true },
|
|
4679
|
+
};
|
|
4529
4680
|
/** The Buffer statics — `Buffer.from(...)`, `Buffer.alloc(n)`,
|
|
4530
4681
|
* `Buffer.concat(list)`, `Buffer.isBuffer(x)` — on THE stdlib Buffer
|
|
4531
4682
|
* global (name + provenance; fallback and @types/node alike). Null when
|
|
@@ -4634,14 +4785,22 @@ export function lowerBufferStaticCall(L, call, access) {
|
|
|
4634
4785
|
if (args.length !== 2 || args.some(ts.isSpreadElement)) {
|
|
4635
4786
|
L.noLowering(`Buffer.compare with ${args.length} arguments`, call);
|
|
4636
4787
|
}
|
|
4637
|
-
const sides = args.map((a) =>
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4788
|
+
const sides = args.map((a) => L.lowerExpr(a));
|
|
4789
|
+
if (sides.every((v) => v.type.kind === "bytes" && v.type.elem === "u8")) {
|
|
4790
|
+
return { kind: "bytesIntrinsic", method: "compareBuf", receiver: sides[0], args: [sides[1]], type: F64, loc };
|
|
4791
|
+
}
|
|
4792
|
+
// A side that is not statically bytes (the invalid-input probes,
|
|
4793
|
+
// untyped JS helpers): Node's "buf1"/"buf2" argument ladder runs
|
|
4794
|
+
// at runtime — a well-typed dyn still compares.
|
|
4795
|
+
const dyns = sides.map((v, i) => {
|
|
4796
|
+
if (v.type.kind === "dyn")
|
|
4797
|
+
return v;
|
|
4798
|
+
if (v.kind === "unitLit" || L.dynConvertible(v.type)) {
|
|
4799
|
+
return { kind: "dynFrom", value: v, type: DYN, loc: v.loc };
|
|
4641
4800
|
}
|
|
4642
|
-
|
|
4801
|
+
L.noLowering(`Buffer.compare of '${L.fmt(v.type)}' values`, args[i], "Buffer/Uint8Array values compare (narrow unions first)");
|
|
4643
4802
|
});
|
|
4644
|
-
return { kind: "
|
|
4803
|
+
return { kind: "libCall", fn: "buffer.compareChk", args: dyns, type: F64, loc };
|
|
4645
4804
|
}
|
|
4646
4805
|
if (member === "isBuffer") {
|
|
4647
4806
|
// The type-predicate narrowing test. Lowered where it DECIDES
|
|
@@ -5150,8 +5309,10 @@ function lowerFromEntriesStringRows(L, call, argNode, argIr) {
|
|
|
5150
5309
|
* every source value (declared fields, and the overflow slot when the
|
|
5151
5310
|
* source carries one) must enter the target's value slot (identity, a
|
|
5152
5311
|
* width lift — arm wrap/re-tag/nested reshape, or JSON-safe into a dyn
|
|
5153
|
-
* slot), and every target field must be
|
|
5154
|
-
*
|
|
5312
|
+
* slot), and every target field must either be initialized directly by
|
|
5313
|
+
* a same-named source declared field (the rule that admits REQUIRED
|
|
5314
|
+
* declared members) or be optional-flavored (the fresh record's
|
|
5315
|
+
* default) and writable at runtime. */
|
|
5155
5316
|
export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
5156
5317
|
const from = L.shapes.get(fromId);
|
|
5157
5318
|
const to = L.shapes.get(toId);
|
|
@@ -5172,11 +5333,16 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5172
5333
|
// signature have no overflow to carry over).
|
|
5173
5334
|
if (fIv && slotLift(fIv) === null)
|
|
5174
5335
|
return null;
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
// fields must be DOM-convertible; typed slots write through directly).
|
|
5178
|
-
const defaults = [];
|
|
5336
|
+
const inits = [];
|
|
5337
|
+
const consumed = new Set();
|
|
5179
5338
|
for (const tf of to.fields) {
|
|
5339
|
+
const sf = from.fields.find((f) => f.name === tf.name);
|
|
5340
|
+
const directLift = sf ? L.widthLiftPlan(sf.type, tf.type) : null;
|
|
5341
|
+
if (sf && directLift) {
|
|
5342
|
+
inits.push({ name: tf.name, kind: "direct", src: sf.type, lift: directLift });
|
|
5343
|
+
consumed.add(tf.name);
|
|
5344
|
+
continue;
|
|
5345
|
+
}
|
|
5180
5346
|
if (tf.type.kind !== "union")
|
|
5181
5347
|
return null;
|
|
5182
5348
|
const utag = L.armTag(tf.type.unionId, UNDEFINED_T);
|
|
@@ -5184,7 +5350,7 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5184
5350
|
return null;
|
|
5185
5351
|
if (tIv.kind === "dyn" ? !L.dynConvertible(tf.type) : !typeEquals(tf.type, tIv))
|
|
5186
5352
|
return null;
|
|
5187
|
-
|
|
5353
|
+
inits.push({ name: tf.name, kind: "undef", unionId: tf.type.unionId, utag });
|
|
5188
5354
|
}
|
|
5189
5355
|
// Every source declared field must flow into the keyed-write slot —
|
|
5190
5356
|
// in DECLARATION order (insertion order below; declaredOrder omits
|
|
@@ -5197,11 +5363,30 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5197
5363
|
: from.fields;
|
|
5198
5364
|
const fieldLifts = new Map();
|
|
5199
5365
|
for (const ff of orderedFields) {
|
|
5366
|
+
if (consumed.has(ff.name))
|
|
5367
|
+
continue; // direct-initialized above
|
|
5200
5368
|
const lift = slotLift(ff.type);
|
|
5201
5369
|
if (lift === null)
|
|
5202
5370
|
return null;
|
|
5203
5371
|
fieldLifts.set(ff.name, lift);
|
|
5204
5372
|
}
|
|
5373
|
+
// DISPATCH writes — keys that can hit a declared target slot: the
|
|
5374
|
+
// overflow loop's runtime keys, and a literal source-field name the
|
|
5375
|
+
// target also declares. Non-dyn slots store THROUGH on a collision, so
|
|
5376
|
+
// such writes need every declared field to BE the slot type (the
|
|
5377
|
+
// validator's recordKeySet rule); dyn slots validate per field
|
|
5378
|
+
// (dynCheck), so each field must be DOM-convertible. Writes to
|
|
5379
|
+
// literal names the target does NOT declare are overflowOnly and
|
|
5380
|
+
// exempt — which is what lets a direct-initialized required field
|
|
5381
|
+
// (`id: number` beside a `number | undefined` slot) coexist with a
|
|
5382
|
+
// declared-only source, while an index-signature source declines.
|
|
5383
|
+
const dispatchWrites = fIv !== undefined ||
|
|
5384
|
+
orderedFields.some((ff) => !consumed.has(ff.name) && to.fields.some((f) => f.name === ff.name));
|
|
5385
|
+
if (dispatchWrites) {
|
|
5386
|
+
if (tIv.kind === "dyn" ? !to.fields.every((f) => L.dynConvertible(f.type)) : !to.fields.every((f) => typeEquals(f.type, tIv))) {
|
|
5387
|
+
return null;
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5205
5390
|
const key = `ovf:${fromId}:${toId}`;
|
|
5206
5391
|
const existing = L.widthHelpers.get(key);
|
|
5207
5392
|
if (existing)
|
|
@@ -5223,16 +5408,18 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5223
5408
|
localId: "out.0",
|
|
5224
5409
|
init: {
|
|
5225
5410
|
kind: "recordLit",
|
|
5226
|
-
fields:
|
|
5411
|
+
fields: inits.map((d) => ({
|
|
5227
5412
|
name: d.name,
|
|
5228
|
-
value:
|
|
5229
|
-
kind: "
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5413
|
+
value: d.kind === "direct"
|
|
5414
|
+
? L.applyWidthLift(d.lift, { kind: "recordGet", obj: sRef, shapeId: fromId, field: d.name, type: d.src, loc }, to.fields.find((f) => f.name === d.name).type, loc)
|
|
5415
|
+
: {
|
|
5416
|
+
kind: "unionWrap",
|
|
5417
|
+
unionId: d.unionId,
|
|
5418
|
+
tag: d.utag,
|
|
5419
|
+
value: { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc },
|
|
5420
|
+
type: to.fields.find((f) => f.name === d.name).type,
|
|
5421
|
+
loc,
|
|
5422
|
+
},
|
|
5236
5423
|
})),
|
|
5237
5424
|
type: toT,
|
|
5238
5425
|
loc,
|
|
@@ -5241,8 +5428,10 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5241
5428
|
},
|
|
5242
5429
|
];
|
|
5243
5430
|
// Source declared fields in declaration order, skipping unset
|
|
5244
|
-
// optionals (stance 37).
|
|
5431
|
+
// optionals (stance 37) and the direct-initialized (consumed) names.
|
|
5245
5432
|
for (const ff of orderedFields) {
|
|
5433
|
+
if (consumed.has(ff.name))
|
|
5434
|
+
continue;
|
|
5246
5435
|
const raw = { kind: "recordGet", obj: sRef, shapeId: fromId, field: ff.name, type: ff.type, loc };
|
|
5247
5436
|
const utag = ff.type.kind === "union" ? L.armTag(ff.type.unionId, UNDEFINED_T) : -1;
|
|
5248
5437
|
const write = {
|
|
@@ -5251,6 +5440,9 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5251
5440
|
shapeId: toId,
|
|
5252
5441
|
key: { kind: "strLit", value: ff.name, type: STRING, loc },
|
|
5253
5442
|
value: intoSlot(raw, fieldLifts.get(ff.name)),
|
|
5443
|
+
// A literal name the target does not declare can only land in the
|
|
5444
|
+
// overflow — skip the declared dispatch (and its validator gate).
|
|
5445
|
+
...(to.fields.some((f) => f.name === ff.name) ? {} : { overflowOnly: true }),
|
|
5254
5446
|
loc,
|
|
5255
5447
|
};
|
|
5256
5448
|
body.push(utag >= 0 && ff.type.kind === "union"
|