@scriptc/compiler 0.0.8 → 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 +108 -5
- package/dist/backend/emission/emit-exprs.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 +23 -10
- 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 +30 -9
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.js +206 -11
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +55 -10
- package/dist/diagnostics/diagnostic.js +82 -20
- package/dist/diagnostics/diagnostic.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 +111 -3
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +30 -11
- package/dist/frontend/lowering/lower-calls.js +418 -8
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +27 -1
- 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 +193 -43
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-emitter.js +54 -0
- package/dist/frontend/lowering/lower-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +52 -7
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +445 -13
- 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 +66 -14
- package/dist/frontend/lowering/lowerer.js +273 -28
- 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.js +4 -2
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.js +150 -25
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +131 -7
- package/dist/ir/nodes.js +41 -3
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +58 -8
- package/dist/ir/validate.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +10 -3
|
@@ -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
|
|
@@ -4104,25 +4104,35 @@ export function lowerBytesNew(L, expr, symbol) {
|
|
|
4104
4104
|
const seed = { kind: "arrayLit", elems, type: arrayOf(F64), loc };
|
|
4105
4105
|
return { kind: "bytesNew", source: seed, type, loc };
|
|
4106
4106
|
}
|
|
4107
|
-
// The SYNTACTIC `new T(new
|
|
4108
|
-
//
|
|
4109
|
-
//
|
|
4110
|
-
//
|
|
4111
|
-
//
|
|
4112
|
-
//
|
|
4113
|
-
//
|
|
4114
|
-
//
|
|
4115
|
-
//
|
|
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.
|
|
4116
4119
|
if (ts.isNewExpression(argNode) &&
|
|
4117
4120
|
ts.isIdentifier(argNode.expression) &&
|
|
4118
|
-
argNode.expression.text === "
|
|
4121
|
+
(argNode.expression.text === "ArrayBuffer" ||
|
|
4122
|
+
argNode.expression.text === "SharedArrayBuffer") &&
|
|
4119
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
|
+
}
|
|
4120
4130
|
const elemSize = elem === "u8" ? 1 : 4;
|
|
4121
4131
|
const lenArg = argNode.arguments?.length === 1 ? argNode.arguments[0] : undefined;
|
|
4122
4132
|
const lenT = lenArg ? L.typeOf(lenArg) : null;
|
|
4123
4133
|
const byteLen = lenT?.isNumberLiteralType() ? lenT.value : null;
|
|
4124
4134
|
if (byteLen === null || byteLen % elemSize !== 0 || byteLen < 0) {
|
|
4125
|
-
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}))`);
|
|
4126
4136
|
}
|
|
4127
4137
|
const count = { kind: "numLit", value: byteLen / elemSize, type: F64, loc };
|
|
4128
4138
|
return { kind: "bytesNew", source: count, type, loc };
|
|
@@ -4156,8 +4166,37 @@ function lowerDataViewNew(L, expr) {
|
|
|
4156
4166
|
if (!bufNode || args.length > 3 || args.some(ts.isSpreadElement)) {
|
|
4157
4167
|
L.noLowering(`new DataView with ${args.length} arguments`, expr, "supported: new DataView(x.buffer), (x.buffer, byteOffset), or (x.buffer, byteOffset, byteLength)");
|
|
4158
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
|
+
}
|
|
4159
4197
|
const hint = "views compile over a typed array's own storage — new DataView(x.buffer, byteOffset?, byteLength?) " +
|
|
4160
|
-
"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";
|
|
4161
4200
|
if (!ts.isPropertyAccessExpression(bufNode) || bufNode.name.text !== "buffer" || bufNode.questionDotToken) {
|
|
4162
4201
|
L.noLowering("new DataView over this buffer expression", bufNode, hint);
|
|
4163
4202
|
}
|
|
@@ -4329,6 +4368,27 @@ export function lowerBytesMethodCall(L, call, access) {
|
|
|
4329
4368
|
args.push(L.lowerExprExpecting(call.arguments[1], BOOL));
|
|
4330
4369
|
return { kind: "bytesIntrinsic", method: dvGetter.method, receiver, args, type: F64, loc };
|
|
4331
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
|
+
}
|
|
4332
4392
|
return null;
|
|
4333
4393
|
}
|
|
4334
4394
|
/** Node's Buffer encoding names → the runtime's canonical spelling (every
|
|
@@ -4418,20 +4478,59 @@ function lowerBufferInstanceMethod(L, call, access, name, loc) {
|
|
|
4418
4478
|
}
|
|
4419
4479
|
return v;
|
|
4420
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]));
|
|
4421
4500
|
if (name === "equals") {
|
|
4422
4501
|
if (nArgs !== 1)
|
|
4423
4502
|
L.noLowering(`.equals with ${nArgs} arguments`, call);
|
|
4424
|
-
|
|
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.
|
|
4425
4510
|
const receiver = L.lowerExpr(access.expression);
|
|
4426
|
-
return { kind: "
|
|
4511
|
+
return { kind: "libCall", fn: "bytes.equalsChk", args: [receiver, chkArg(0)], type: BOOL, loc };
|
|
4427
4512
|
}
|
|
4428
4513
|
if (name === "compare") {
|
|
4429
|
-
if (nArgs
|
|
4514
|
+
if (nArgs > 5)
|
|
4430
4515
|
L.noLowering(`.compare with ${nArgs} arguments`, call);
|
|
4431
|
-
const
|
|
4432
|
-
|
|
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).
|
|
4433
4529
|
const receiver = L.lowerExpr(access.expression);
|
|
4434
|
-
|
|
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 };
|
|
4435
4534
|
}
|
|
4436
4535
|
if (name === "indexOf" || name === "lastIndexOf" || name === "includes") {
|
|
4437
4536
|
if (nArgs < 1 || nArgs > 3)
|
|
@@ -4568,6 +4667,16 @@ const DV_GETTERS = {
|
|
|
4568
4667
|
getBigUint64: { method: "dvGetBigUint64Number", le: true },
|
|
4569
4668
|
getBigInt64: { method: "dvGetBigInt64Number", le: true },
|
|
4570
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
|
+
};
|
|
4571
4680
|
/** The Buffer statics — `Buffer.from(...)`, `Buffer.alloc(n)`,
|
|
4572
4681
|
* `Buffer.concat(list)`, `Buffer.isBuffer(x)` — on THE stdlib Buffer
|
|
4573
4682
|
* global (name + provenance; fallback and @types/node alike). Null when
|
|
@@ -4676,14 +4785,22 @@ export function lowerBufferStaticCall(L, call, access) {
|
|
|
4676
4785
|
if (args.length !== 2 || args.some(ts.isSpreadElement)) {
|
|
4677
4786
|
L.noLowering(`Buffer.compare with ${args.length} arguments`, call);
|
|
4678
4787
|
}
|
|
4679
|
-
const sides = args.map((a) =>
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
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 };
|
|
4683
4800
|
}
|
|
4684
|
-
|
|
4801
|
+
L.noLowering(`Buffer.compare of '${L.fmt(v.type)}' values`, args[i], "Buffer/Uint8Array values compare (narrow unions first)");
|
|
4685
4802
|
});
|
|
4686
|
-
return { kind: "
|
|
4803
|
+
return { kind: "libCall", fn: "buffer.compareChk", args: dyns, type: F64, loc };
|
|
4687
4804
|
}
|
|
4688
4805
|
if (member === "isBuffer") {
|
|
4689
4806
|
// The type-predicate narrowing test. Lowered where it DECIDES
|
|
@@ -5192,8 +5309,10 @@ function lowerFromEntriesStringRows(L, call, argNode, argIr) {
|
|
|
5192
5309
|
* every source value (declared fields, and the overflow slot when the
|
|
5193
5310
|
* source carries one) must enter the target's value slot (identity, a
|
|
5194
5311
|
* width lift — arm wrap/re-tag/nested reshape, or JSON-safe into a dyn
|
|
5195
|
-
* slot), and every target field must be
|
|
5196
|
-
*
|
|
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. */
|
|
5197
5316
|
export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
5198
5317
|
const from = L.shapes.get(fromId);
|
|
5199
5318
|
const to = L.shapes.get(toId);
|
|
@@ -5214,11 +5333,16 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5214
5333
|
// signature have no overflow to carry over).
|
|
5215
5334
|
if (fIv && slotLift(fIv) === null)
|
|
5216
5335
|
return null;
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
// fields must be DOM-convertible; typed slots write through directly).
|
|
5220
|
-
const defaults = [];
|
|
5336
|
+
const inits = [];
|
|
5337
|
+
const consumed = new Set();
|
|
5221
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
|
+
}
|
|
5222
5346
|
if (tf.type.kind !== "union")
|
|
5223
5347
|
return null;
|
|
5224
5348
|
const utag = L.armTag(tf.type.unionId, UNDEFINED_T);
|
|
@@ -5226,7 +5350,7 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5226
5350
|
return null;
|
|
5227
5351
|
if (tIv.kind === "dyn" ? !L.dynConvertible(tf.type) : !typeEquals(tf.type, tIv))
|
|
5228
5352
|
return null;
|
|
5229
|
-
|
|
5353
|
+
inits.push({ name: tf.name, kind: "undef", unionId: tf.type.unionId, utag });
|
|
5230
5354
|
}
|
|
5231
5355
|
// Every source declared field must flow into the keyed-write slot —
|
|
5232
5356
|
// in DECLARATION order (insertion order below; declaredOrder omits
|
|
@@ -5239,11 +5363,30 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5239
5363
|
: from.fields;
|
|
5240
5364
|
const fieldLifts = new Map();
|
|
5241
5365
|
for (const ff of orderedFields) {
|
|
5366
|
+
if (consumed.has(ff.name))
|
|
5367
|
+
continue; // direct-initialized above
|
|
5242
5368
|
const lift = slotLift(ff.type);
|
|
5243
5369
|
if (lift === null)
|
|
5244
5370
|
return null;
|
|
5245
5371
|
fieldLifts.set(ff.name, lift);
|
|
5246
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
|
+
}
|
|
5247
5390
|
const key = `ovf:${fromId}:${toId}`;
|
|
5248
5391
|
const existing = L.widthHelpers.get(key);
|
|
5249
5392
|
if (existing)
|
|
@@ -5265,16 +5408,18 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5265
5408
|
localId: "out.0",
|
|
5266
5409
|
init: {
|
|
5267
5410
|
kind: "recordLit",
|
|
5268
|
-
fields:
|
|
5411
|
+
fields: inits.map((d) => ({
|
|
5269
5412
|
name: d.name,
|
|
5270
|
-
value:
|
|
5271
|
-
kind: "
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
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
|
+
},
|
|
5278
5423
|
})),
|
|
5279
5424
|
type: toT,
|
|
5280
5425
|
loc,
|
|
@@ -5283,8 +5428,10 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5283
5428
|
},
|
|
5284
5429
|
];
|
|
5285
5430
|
// Source declared fields in declaration order, skipping unset
|
|
5286
|
-
// optionals (stance 37).
|
|
5431
|
+
// optionals (stance 37) and the direct-initialized (consumed) names.
|
|
5287
5432
|
for (const ff of orderedFields) {
|
|
5433
|
+
if (consumed.has(ff.name))
|
|
5434
|
+
continue;
|
|
5288
5435
|
const raw = { kind: "recordGet", obj: sRef, shapeId: fromId, field: ff.name, type: ff.type, loc };
|
|
5289
5436
|
const utag = ff.type.kind === "union" ? L.armTag(ff.type.unionId, UNDEFINED_T) : -1;
|
|
5290
5437
|
const write = {
|
|
@@ -5293,6 +5440,9 @@ export function lowerRecordOvfCaptureHelper(L, fromId, toId, loc) {
|
|
|
5293
5440
|
shapeId: toId,
|
|
5294
5441
|
key: { kind: "strLit", value: ff.name, type: STRING, loc },
|
|
5295
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 }),
|
|
5296
5446
|
loc,
|
|
5297
5447
|
};
|
|
5298
5448
|
body.push(utag >= 0 && ff.type.kind === "union"
|