@scriptc/compiler 0.0.11 → 0.0.13
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 +153 -24
- package/ambient/scriptc.d.ts +2 -2
- package/dist/backend/cc.d.ts +10 -2
- package/dist/backend/cc.js +12 -0
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-async.js +1 -1
- package/dist/backend/emission/emit-exprs.js +235 -64
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-island.js +1 -1
- package/dist/backend/emission/emit-shapes.js +1 -1
- package/dist/backend/emission/emit-walkers.d.ts +14 -14
- package/dist/backend/emission/emit-walkers.js +44 -44
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +3 -3
- package/dist/backend/emission/emitter.js +8 -8
- package/dist/backend/emission/may-throw.js +1 -1
- package/dist/backend/llvm/dyn.d.ts +10 -10
- package/dist/backend/llvm/dyn.js +26 -26
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +140 -49
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.js +1 -1
- package/dist/backend/llvm/walkers.js +2 -2
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +1 -1
- package/dist/diagnostics/diagnostic.js +4 -3
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lowering/crypto-tables.d.ts +7 -0
- package/dist/frontend/lowering/crypto-tables.js +139 -0
- package/dist/frontend/lowering/crypto-tables.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.js +5 -5
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +12 -12
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +45 -27
- package/dist/frontend/lowering/lower-builtins.js +203 -70
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +4 -4
- package/dist/frontend/lowering/lower-calls.js +74 -66
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +41 -7
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +5 -5
- package/dist/frontend/lowering/lower-containers.js +19 -19
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-emitter.js +2 -2
- package/dist/frontend/lowering/lower-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +7 -7
- package/dist/frontend/lowering/lower-exprs.js +227 -119
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +5 -5
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +12 -3
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +17 -17
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.d.ts +11 -1
- package/dist/frontend/lowering/lower-namespaces.js +41 -6
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +21 -0
- package/dist/frontend/lowering/lower-server.js +278 -23
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +1 -1
- package/dist/frontend/lowering/lower-stmts.js +195 -34
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +67 -23
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +31 -13
- package/dist/frontend/lowering/lowerer.js +99 -45
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +6 -0
- package/dist/frontend/lowering/surfaces.js +78 -2
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/program.js +1 -1
- package/dist/frontend/shared.js +7 -7
- package/dist/frontend/shared.js.map +1 -1
- package/dist/frontend/types.d.ts +2 -2
- package/dist/frontend/types.js +27 -15
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.js +44 -8
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +208 -106
- package/dist/ir/nodes.js +100 -22
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +96 -38
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/int-infer.js +13 -0
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/profile.d.ts +8 -6
- package/dist/library/profile.js.map +1 -1
- package/dist/library/sidecar-validate.js +1 -1
- package/dist/library/sidecar-validate.js.map +1 -1
- package/dist/library/sidecar.d.ts +1 -1
- package/dist/library/sidecar.js +20 -7
- package/dist/library/sidecar.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +289 -4
|
@@ -17,7 +17,7 @@ import { unsupportedModuleFeatureOf } from "../shared.js";
|
|
|
17
17
|
import { fenceEnumObjectValue, lowerEnumAccess } from "./lower-enums.js";
|
|
18
18
|
import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUndefinedFnSymbolOf, contextualUndefReadType, fenceEarlyAliasUse, fenceEarlyNsMemberRef, lowerNsIdentifierValue, nsMemberIdentOf, nsUndefRead, nsWritableTarget } from "./lower-namespaces.js";
|
|
19
19
|
import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
|
|
20
|
-
import { lowerSocketInstanceOf } from "./lower-server.js";
|
|
20
|
+
import { lowerSocketInstanceOf, lowerTlsRootCertificates } from "./lower-server.js";
|
|
21
21
|
import { findGenericMethodOn, lowerStaticFieldRead } from "./lower-classes.js";
|
|
22
22
|
import { bindingNeverReassigned, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
|
|
23
23
|
import { mixinFnOfCallee } from "./lower-mixins.js";
|
|
@@ -222,9 +222,9 @@ function lowerExprInner(L, expr) {
|
|
|
222
222
|
// A JS variable INITIALIZER whose BINDING registered as a
|
|
223
223
|
// checked-dynamic module global (the typedef-annotated
|
|
224
224
|
// doc-builder consts whose JSDoc types degraded — DYN slots):
|
|
225
|
-
// the island build could never land (no engine→
|
|
225
|
+
// the island build could never land (no engine→dyn crossing
|
|
226
226
|
// exists), so the literal takes its own world's path instead —
|
|
227
|
-
// the
|
|
227
|
+
// the dyn literal, or a static record the slot converts.
|
|
228
228
|
if (isJsSourceFile(expr.getSourceFile()) &&
|
|
229
229
|
ts.isVariableDeclaration(expr.parent) &&
|
|
230
230
|
expr.parent.initializer === expr &&
|
|
@@ -239,7 +239,7 @@ function lowerExprInner(L, expr) {
|
|
|
239
239
|
// reference degraded to checker-`any` — the tsgo multi-file
|
|
240
240
|
// value-as-type residue) is not an npm slot: when the literal's
|
|
241
241
|
// OWN type maps statically, it builds that way — the binding's
|
|
242
|
-
// checked-dynamic slot takes the
|
|
242
|
+
// checked-dynamic slot takes the dyn conversion. Genuinely
|
|
243
243
|
// island slots (npm .d.ts provenance, plain `any`) keep the
|
|
244
244
|
// native build.
|
|
245
245
|
if (ctxTs !== undefined && (ctxTs.flags & ts.TypeFlags.Any) === 0) {
|
|
@@ -274,13 +274,22 @@ function lowerExprInner(L, expr) {
|
|
|
274
274
|
const argsWith = [];
|
|
275
275
|
const getters = [];
|
|
276
276
|
let spread = null;
|
|
277
|
-
// A MEMBER a JS file cannot lower or marshal
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
|
|
277
|
+
// A MEMBER a JS file cannot lower or marshal: defer like a
|
|
278
|
+
// statement fence, shaped by what the member IS. A FUNCTION-shaped
|
|
279
|
+
// member (a generic function as a value, a signature with
|
|
280
|
+
// checked-dynamic parameters — the doc-builder public aggregate's
|
|
281
|
+
// degraded pieces) becomes a host closure that THROWS the captured
|
|
282
|
+
// diagnostic when invoked — building the aggregate compiles and
|
|
283
|
+
// only a CALL through the island stops the run. A DATA-shaped
|
|
284
|
+
// member must NOT become a callable (the retired fence box's
|
|
285
|
+
// silent wrong answers: typeof said "function", downstream errors
|
|
286
|
+
// blamed the wrong thing — the withPlugins `plugins:` slot), so it
|
|
287
|
+
// defines through the engine's getter machinery instead: READING
|
|
288
|
+
// the member throws the diagnostic — the honest granularity, since
|
|
289
|
+
// using the value is exactly what cannot be answered. `getterName`
|
|
290
|
+
// null keeps the closure shape (function-shaped members, and the
|
|
291
|
+
// conditional-spread arms where getters cannot combine).
|
|
292
|
+
const islandMemberFence = (diagsBefore, err, valueNode, getterName = null) => {
|
|
284
293
|
if (!(err instanceof PoisonError) ||
|
|
285
294
|
!isJsSourceFile(expr.getSourceFile()) ||
|
|
286
295
|
L.diagSink !== null ||
|
|
@@ -310,8 +319,33 @@ function lowerExprInner(L, expr) {
|
|
|
310
319
|
loc: locOf(valueNode),
|
|
311
320
|
});
|
|
312
321
|
const fence = { kind: "closure", fnName, captures: [], type: funcOf([], VOID), loc: locOf(valueNode) };
|
|
322
|
+
if (getterName !== null) {
|
|
323
|
+
getters.push({ name: getterName, fn: L.jsvalIn(fence, valueNode), loc: locOf(valueNode) });
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
313
326
|
return L.jsvalIn(fence, valueNode);
|
|
314
327
|
};
|
|
328
|
+
// The member's SHAPE decides the fence's granularity: syntactic
|
|
329
|
+
// functions and checker-callable values keep the call-time
|
|
330
|
+
// closure; everything else (call results, awaits, data reads —
|
|
331
|
+
// the withPlugins `plugins:` shape) fences at the READ.
|
|
332
|
+
const funcShapedMember = (p) => {
|
|
333
|
+
if (ts.isMethodDeclaration(p))
|
|
334
|
+
return true;
|
|
335
|
+
const src = ts.isPropertyAssignment(p)
|
|
336
|
+
? p.initializer
|
|
337
|
+
: ts.isShorthandPropertyAssignment(p)
|
|
338
|
+
? p.name
|
|
339
|
+
: null;
|
|
340
|
+
if (!src || !ts.isExpression(src))
|
|
341
|
+
return true; // unknown form: keep the closure shape
|
|
342
|
+
let inner = src;
|
|
343
|
+
while (ts.isParenthesizedExpression(inner))
|
|
344
|
+
inner = inner.expression;
|
|
345
|
+
if (ts.isArrowFunction(inner) || ts.isFunctionExpression(inner))
|
|
346
|
+
return true;
|
|
347
|
+
return L.checker.getCallSignatures(L.typeOf(src)).length > 0;
|
|
348
|
+
};
|
|
315
349
|
const pushProp = (name, value, valueNode, into) => {
|
|
316
350
|
const diagsBefore = L.diags.length;
|
|
317
351
|
let marshaled;
|
|
@@ -319,7 +353,15 @@ function lowerExprInner(L, expr) {
|
|
|
319
353
|
marshaled = L.jsvalIn(value, valueNode);
|
|
320
354
|
}
|
|
321
355
|
catch (err) {
|
|
322
|
-
|
|
356
|
+
// A value that LOWERED but cannot cross (a promise, a class
|
|
357
|
+
// instance): func-typed values keep the call-time closure;
|
|
358
|
+
// data-shaped values fence at the read (the getter), unless a
|
|
359
|
+
// conditional spread owns the literal (getters cannot combine).
|
|
360
|
+
const asGetter = value.type.kind !== "func" && spread === null;
|
|
361
|
+
const fence = islandMemberFence(diagsBefore, err, valueNode, asGetter ? name.text : null);
|
|
362
|
+
if (fence === null)
|
|
363
|
+
return; // registered as a fence getter — no data property
|
|
364
|
+
marshaled = fence;
|
|
323
365
|
}
|
|
324
366
|
for (const args of into) {
|
|
325
367
|
args.push({
|
|
@@ -403,7 +445,11 @@ function lowerExprInner(L, expr) {
|
|
|
403
445
|
: null;
|
|
404
446
|
}
|
|
405
447
|
catch (err) {
|
|
406
|
-
|
|
448
|
+
const nameText = name && (ts.isIdentifier(name) || ts.isStringLiteral(name)) ? name.text : null;
|
|
449
|
+
const asGetter = nameText !== null && spread === null && !funcShapedMember(prop);
|
|
450
|
+
value = islandMemberFence(valueDiagsBefore, err, prop, asGetter ? nameText : null);
|
|
451
|
+
if (value === null)
|
|
452
|
+
continue; // registered as a fence getter — no data property
|
|
407
453
|
}
|
|
408
454
|
if (value && name && (ts.isIdentifier(name) || ts.isStringLiteral(name))) {
|
|
409
455
|
pushProp(name, value, prop, [argsWithout, argsWith]);
|
|
@@ -494,7 +540,7 @@ function lowerExprInner(L, expr) {
|
|
|
494
540
|
}
|
|
495
541
|
// A checker-narrowed dyn read arrives as a VALIDATED extraction
|
|
496
542
|
// (maybeNarrow's dynCheck bridge). typeof needs no extraction — the
|
|
497
|
-
//
|
|
543
|
+
// dyn kind table answers the question directly, and answers it even
|
|
498
544
|
// where the flow type lied (the extraction would throw instead) —
|
|
499
545
|
// so unwrap back to the dyn value and take the dyn.typeof path.
|
|
500
546
|
if (operand.kind === "dynCheck" && operand.value.type.kind === "dyn") {
|
|
@@ -539,7 +585,7 @@ function lowerExprInner(L, expr) {
|
|
|
539
585
|
if (operand.type.kind === "dyn") {
|
|
540
586
|
// Bare typeof on a dyn value: the runtime's kind→string table
|
|
541
587
|
// (null answers "object", boxed closures "function" — JS-exact
|
|
542
|
-
// for every
|
|
588
|
+
// for every dyn kind; "bigint"/"symbol" have no producers).
|
|
543
589
|
return { kind: "libCall", fn: "dyn.typeof", args: [operand], type: STRING, loc };
|
|
544
590
|
}
|
|
545
591
|
L.unsupported("SC1090", expr, "typeof expressions on statically-typed values");
|
|
@@ -549,7 +595,7 @@ function lowerExprInner(L, expr) {
|
|
|
549
595
|
return { kind: "selfRef", type: L.ctx.selfType, loc };
|
|
550
596
|
}
|
|
551
597
|
// `arguments` in a variadic JS function (the rest-marked form): the
|
|
552
|
-
// synthetic trailing
|
|
598
|
+
// synthetic trailing dyn-array param — lambdaSignature marked the
|
|
553
599
|
// type and lowerLambda declared the local.
|
|
554
600
|
if (expr.text === "arguments" && L.ctx.argumentsLocal) {
|
|
555
601
|
return { kind: "varRef", localId: L.ctx.argumentsLocal.id, type: DYN, loc };
|
|
@@ -618,7 +664,7 @@ function lowerExprInner(L, expr) {
|
|
|
618
664
|
// optional/default/rest declarations pass the value fence first.
|
|
619
665
|
L.noteEdge(sig.name);
|
|
620
666
|
// dynRest slots stay out of the VALUE type's param list (fn.length
|
|
621
|
-
// semantics); the rest marker carries the trailing
|
|
667
|
+
// semantics); the rest marker carries the trailing dyn-array ABI.
|
|
622
668
|
const funcType = {
|
|
623
669
|
kind: "func",
|
|
624
670
|
params: sig.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
|
|
@@ -647,10 +693,11 @@ function lowerExprInner(L, expr) {
|
|
|
647
693
|
if (acc)
|
|
648
694
|
return acc;
|
|
649
695
|
}
|
|
650
|
-
// A binding destructured from
|
|
651
|
-
// (
|
|
696
|
+
// A binding destructured from a baked constants object
|
|
697
|
+
// (http2.constants / crypto.constants): the literal — the
|
|
698
|
+
// declaration emitted nothing (builtinConstantsDestructureDecl).
|
|
652
699
|
{
|
|
653
|
-
const h2c = L.
|
|
700
|
+
const h2c = L.builtinConstantBindingOf(expr);
|
|
654
701
|
if (h2c)
|
|
655
702
|
return h2c;
|
|
656
703
|
}
|
|
@@ -669,6 +716,14 @@ function lowerExprInner(L, expr) {
|
|
|
669
716
|
if (bi.module === "module" && bi.member === "builtinModules") {
|
|
670
717
|
return builtinModulesArrayLit(loc);
|
|
671
718
|
}
|
|
719
|
+
// tls.rootCertificates: a runtime-valued module constant (the
|
|
720
|
+
// cached bundled-CA array) — the one member read that lowers
|
|
721
|
+
// to a libCall instead of a baked literal.
|
|
722
|
+
{
|
|
723
|
+
const roots = lowerTlsRootCertificates(L, bi, loc);
|
|
724
|
+
if (roots)
|
|
725
|
+
return roots;
|
|
726
|
+
}
|
|
672
727
|
// JavaScript sources: a builtin member taken as a bare VALUE is
|
|
673
728
|
// the same identity-token story as stdlib globals above (the
|
|
674
729
|
// harness adds worker_threads.Worker to its identity Set).
|
|
@@ -999,7 +1054,7 @@ function lowerExprInner(L, expr) {
|
|
|
999
1054
|
if (value.type.kind !== "promise") {
|
|
1000
1055
|
// A CHECKED-DYNAMIC operand (`await v` where v rode an untyped
|
|
1001
1056
|
// binding — a destructured helper's return, a dyn call result):
|
|
1002
|
-
// the runtime decides — a
|
|
1057
|
+
// the runtime decides — a dyn promise adopts (rejections
|
|
1003
1058
|
// re-throw), anything else takes JS's one-hop non-thenable await
|
|
1004
1059
|
// and answers itself. Thenable adoption stays unmodeled
|
|
1005
1060
|
// (SEMANTICS.md).
|
|
@@ -1157,9 +1212,9 @@ function lowerExprInner(L, expr) {
|
|
|
1157
1212
|
// bound handle).
|
|
1158
1213
|
if (L.isIslandExpr(expr.expression)) {
|
|
1159
1214
|
const receiver = L.lowerExpr(expr.expression);
|
|
1160
|
-
// The checker said 'any' but the VALUE lives in the
|
|
1215
|
+
// The checker said 'any' but the VALUE lives in the checked-dynamic tree (`this`
|
|
1161
1216
|
// in a plain JS function — dyn.this — or a checked-dynamic local
|
|
1162
|
-
// behind an any-typed spelling): the property read is the
|
|
1217
|
+
// behind an any-typed spelling): the property read is the checked-dynamic tree's
|
|
1163
1218
|
// own keyed read, dyn results and dyn chains exactly like every
|
|
1164
1219
|
// checked-dynamic member access (a nullish receiver throws V8's
|
|
1165
1220
|
// catchable TypeError, exactly Node). Never a jsOp over a dyn —
|
|
@@ -1401,7 +1456,7 @@ function lowerExprInner(L, expr) {
|
|
|
1401
1456
|
L.lowerFsConstantsProperty(expr) ??
|
|
1402
1457
|
// http2.constants.NGHTTP2_CANCEL (any constants-object spelling):
|
|
1403
1458
|
// the baked-literal table.
|
|
1404
|
-
L.
|
|
1459
|
+
L.lowerBuiltinConstantsProperty(expr) ??
|
|
1405
1460
|
// Builtin namespace imports (`path.sep`, `os.EOL`,
|
|
1406
1461
|
// `fs.constants.R_OK` where the root is `import * as ...`): the
|
|
1407
1462
|
// same constants and per-member fences as named builtin imports.
|
|
@@ -1443,14 +1498,14 @@ function lowerExprInner(L, expr) {
|
|
|
1443
1498
|
// .length` where the binding is an evolving any[] living as a
|
|
1444
1499
|
// checked-dynamic global): the stdlib member fence below would blame
|
|
1445
1500
|
// the unmappable checker type (`any[].length`), but the VALUE is a
|
|
1446
|
-
//
|
|
1501
|
+
// dyn node — lower the receiver first and read through the dyn keyed
|
|
1447
1502
|
// read (ARR answers length; everything else JS's own-property
|
|
1448
1503
|
// answer). Mapped receivers keep the fence-first order: their
|
|
1449
1504
|
// members' gaps are real surface gaps ([1,2].entries), not
|
|
1450
1505
|
// representation artifacts. `.length` on a receiver whose checker
|
|
1451
1506
|
// ARRAY type lowers to the checked-dynamic representation —
|
|
1452
1507
|
// `unknown[]`, the collapsed `(string | object)[]`, and the `any[]`
|
|
1453
|
-
// an Array.isArray guard narrows a collapsed union to — is the
|
|
1508
|
+
// an Array.isArray guard narrows a collapsed union to — is the checked-dynamic tree
|
|
1454
1509
|
// array's OWN length (a keyed read the ARR kind answers exactly),
|
|
1455
1510
|
// never an `Array.prototype` surface gap; both source languages.
|
|
1456
1511
|
// Prototype-method VALUE reads (`ps.map` unparenthesized) keep the
|
|
@@ -1460,7 +1515,7 @@ function lowerExprInner(L, expr) {
|
|
|
1460
1515
|
if ((isJsSourceFile(expr.getSourceFile()) &&
|
|
1461
1516
|
(L.mapTypeOf(L.typeOf(expr.expression)) === null ||
|
|
1462
1517
|
// A never-tainted receiver type maps (never rides as f64) but
|
|
1463
|
-
// its VALUE lowered checked-dynamic — same
|
|
1518
|
+
// its VALUE lowered checked-dynamic — same dyn read.
|
|
1464
1519
|
neverTaintedJsType(L, expr.expression, L.typeOf(expr.expression)))) ||
|
|
1465
1520
|
(expr.name.text === "length" &&
|
|
1466
1521
|
(L.checkerAnyArray(expr.expression) ||
|
|
@@ -1571,7 +1626,7 @@ function lowerExprInner(L, expr) {
|
|
|
1571
1626
|
// the property and the receiver's type instead of "property access".
|
|
1572
1627
|
const recvLowered = L.lowerExpr(expr.expression);
|
|
1573
1628
|
// A dyn receiver — JSON.parse's `any`, or `unknown` the checker
|
|
1574
|
-
// narrowed to `object` — reads through the
|
|
1629
|
+
// narrowed to `object` — reads through the dyn keyed read: member
|
|
1575
1630
|
// or undefined (JS's own-property answer), throwing JS's TypeError
|
|
1576
1631
|
// on undefined/null receivers unless an earlier `?.` guards the
|
|
1577
1632
|
// chain. Scalar-narrowed occurrences bridge via maybeNarrow's
|
|
@@ -1812,16 +1867,17 @@ export function lowerTernary(L, expr, expected) {
|
|
|
1812
1867
|
// tagged element type the checker's own type doesn't spell).
|
|
1813
1868
|
const own = L.mapTypeOf(L.typeOf(expr));
|
|
1814
1869
|
const useCtx = (ctxMapped?.kind === "record" || ctxMapped?.kind === "union" || ctxMapped?.kind === "object") &&
|
|
1815
|
-
(own === null ||
|
|
1870
|
+
(own === null ||
|
|
1871
|
+
(own.kind === "union" && !typeEquals(own, ctxMapped) && !L.inLogicalLeftPosition(expr)));
|
|
1816
1872
|
// Mixed dyn/unit arms under an unmappable own type (`typeof pkg.name
|
|
1817
1873
|
// === "string" ? pkg.name : null` — the lowering world types the
|
|
1818
1874
|
// unknown-receiver read `any`, which a static build cannot hold):
|
|
1819
1875
|
// dyn represents null and undefined directly, so the ternary stays
|
|
1820
|
-
// dyn — a unit arm converts to the
|
|
1876
|
+
// dyn — a unit arm converts to the dyn unit value (the dynFrom form
|
|
1821
1877
|
// the index-signature lowerings already use). A FRESH literal arm
|
|
1822
1878
|
// (`... ? pkg.scripts : {}` — the default-object idiom) converts the
|
|
1823
1879
|
// same way when it is JSON-safe: nothing else aliases a literal, so
|
|
1824
|
-
// the
|
|
1880
|
+
// the dyn copy is unobservable.
|
|
1825
1881
|
const dynish = (e) => e.type.kind === "dyn" ||
|
|
1826
1882
|
e.kind === "unitLit" ||
|
|
1827
1883
|
((e.kind === "recordLit" || e.kind === "arrayLit") && L.dynConvertible(e.type));
|
|
@@ -1907,13 +1963,13 @@ export function maybeNarrow(L, expr, node) {
|
|
|
1907
1963
|
(narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string")) {
|
|
1908
1964
|
return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
|
|
1909
1965
|
}
|
|
1910
|
-
// An `instanceof Uint8Array` narrow: the
|
|
1966
|
+
// An `instanceof Uint8Array` narrow: the checked-dynamic tree's bytes kind, extracted
|
|
1911
1967
|
// with the same validated copy the checked cast uses (a Buffer that
|
|
1912
1968
|
// crossed in rides the kind too — it IS a Uint8Array in Node).
|
|
1913
1969
|
if (narrowed?.kind === "bytes" && narrowed.elem === "u8") {
|
|
1914
1970
|
return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
|
|
1915
1971
|
}
|
|
1916
|
-
// An `instanceof Error` narrow: the
|
|
1972
|
+
// An `instanceof Error` narrow: the checked-dynamic tree's error encoding rebuilds a
|
|
1917
1973
|
// fresh %Error (name/message/code from the marker object — a COPY,
|
|
1918
1974
|
// the unknown boundary's stance; SEMANTICS.md 67), validated like
|
|
1919
1975
|
// every dyn extraction.
|
|
@@ -2089,7 +2145,7 @@ export function lowerLooseNullCompare(L, expr, loc) {
|
|
|
2089
2145
|
return { kind: "boolLit", value: !negated, type: BOOL, loc };
|
|
2090
2146
|
}
|
|
2091
2147
|
if (other.type.kind === "dyn") {
|
|
2092
|
-
// `v != null` on unknown: one
|
|
2148
|
+
// `v != null` on unknown: one dyn kind test covers both units.
|
|
2093
2149
|
return {
|
|
2094
2150
|
kind: "dynTest",
|
|
2095
2151
|
test: "nullish",
|
|
@@ -2248,7 +2304,7 @@ export function lowerNullishCoalesce(L, expr, loc) {
|
|
|
2248
2304
|
// runtime kind (scr_dyn_is_nullish — UNDEF/NULL take the default;
|
|
2249
2305
|
// a wrapped island value routes to the engine's test, defensively:
|
|
2250
2306
|
// the wrap constructor scalar-normalizes engine null/undefined
|
|
2251
|
-
// away). Both sides live in the
|
|
2307
|
+
// away). Both sides live in the checked-dynamic tree — the right converts through
|
|
2252
2308
|
// the usual boundary and evaluates lazily in its branch; a default
|
|
2253
2309
|
// with no dyn representation keeps the fence.
|
|
2254
2310
|
const right = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
|
|
@@ -2530,7 +2586,7 @@ export function lowerOptionalChain(L, expr) {
|
|
|
2530
2586
|
}
|
|
2531
2587
|
// NUMBER-typed indices (`entries?.[0]`): the property key is
|
|
2532
2588
|
// ToString(i), exactly JS — the canonical number text answers
|
|
2533
|
-
// array indices in the
|
|
2589
|
+
// array indices in the dyn helper, anything else (fractions,
|
|
2534
2590
|
// negatives, NaN) reads as an absent key.
|
|
2535
2591
|
if (key.type.kind === "f64") {
|
|
2536
2592
|
const skey = { kind: "toString", operand: key, type: STRING, loc: key.loc };
|
|
@@ -2538,12 +2594,12 @@ export function lowerOptionalChain(L, expr) {
|
|
|
2538
2594
|
}
|
|
2539
2595
|
}
|
|
2540
2596
|
// The METHOD-call step (`rawName?.match(re)` on a dyn value): a
|
|
2541
|
-
// nullish receiver short-circuits to the undefined
|
|
2597
|
+
// nullish receiver short-circuits to the undefined dyn singleton
|
|
2542
2598
|
// (dyn represents undefined directly); anything else runs the
|
|
2543
2599
|
// VALIDATED dynamic dispatch — the same one the truthy-guarded
|
|
2544
2600
|
// spelling (`v ? v.match(...) : null`) compiles to, Node-shaped
|
|
2545
2601
|
// TypeError on a kind mismatch included. The dispatch's static
|
|
2546
|
-
// result converts back into the
|
|
2602
|
+
// result converts back into the checked-dynamic tree (the chain's checker type is
|
|
2547
2603
|
// the error-any world's `any`, so dyn IS its representation).
|
|
2548
2604
|
if (ts.isCallExpression(expr) &&
|
|
2549
2605
|
ts.isPropertyAccessExpression(dotNode) &&
|
|
@@ -2714,7 +2770,7 @@ export function finishOptionalChain(L, expr, id, receiver, body, loc) {
|
|
|
2714
2770
|
}
|
|
2715
2771
|
// A dyn body (`pricing?.[key]` — an unknown-valued index-signature
|
|
2716
2772
|
// read) stays dyn: `unknown | undefined` IS unknown, and dyn represents
|
|
2717
|
-
// undefined directly (the unit path yields the undefined
|
|
2773
|
+
// undefined directly (the unit path yields the undefined dyn value).
|
|
2718
2774
|
if (body.type.kind === "dyn") {
|
|
2719
2775
|
return { kind: "optChain", id, receiver, body, type: DYN, loc };
|
|
2720
2776
|
}
|
|
@@ -2770,8 +2826,8 @@ export function ensureBool(L, e, node) {
|
|
|
2770
2826
|
return { kind: "toBool", operand: e, type: BOOL, loc: e.loc };
|
|
2771
2827
|
}
|
|
2772
2828
|
if (e.type.kind === "dyn") {
|
|
2773
|
-
// ToBoolean over the
|
|
2774
|
-
//
|
|
2829
|
+
// ToBoolean over the checked-dynamic tree (`if (pkg)` on a JSON.parse result): every
|
|
2830
|
+
// dyn kind has a JS-exact answer — the truthy dynTest reads the kind
|
|
2775
2831
|
// tag (plus the scalar payload for number/string).
|
|
2776
2832
|
return { kind: "dynTest", test: "truthy", value: e, type: BOOL, loc: e.loc };
|
|
2777
2833
|
}
|
|
@@ -2797,7 +2853,7 @@ export function ensureBool(L, e, node) {
|
|
|
2797
2853
|
L.badType(node, L.typeOf(node));
|
|
2798
2854
|
}
|
|
2799
2855
|
/** Truthiness needs a ToBoolean per arm: dyn/caught arms have none (a
|
|
2800
|
-
* dynamic ToBoolean over the
|
|
2856
|
+
* dynamic ToBoolean over the checked-dynamic tree / the snapshot box) — fence those
|
|
2801
2857
|
* unions; every other arm kind is answerable (units false, scalars and
|
|
2802
2858
|
* strings by value, refs true, jsval by the engine). */
|
|
2803
2859
|
export function requireTruthyUnion(L, unionId, node) {
|
|
@@ -2826,7 +2882,7 @@ export function lowerIntrinsicProperty(L, expr) {
|
|
|
2826
2882
|
return null;
|
|
2827
2883
|
// A never-tainted JS receiver type lowered checked-dynamic
|
|
2828
2884
|
// (neverTaintedJsType — `cmd.length` on `const cmd = ['pwd', []]`):
|
|
2829
|
-
// stand down so the
|
|
2885
|
+
// stand down so the dyn keyed read below the chain answers, instead
|
|
2830
2886
|
// of an array libCall over a dyn receiver hitting the boundary fence.
|
|
2831
2887
|
let kind = neverTaintedJsType(L, expr.expression, L.typeOf(expr.expression))
|
|
2832
2888
|
? undefined
|
|
@@ -3090,8 +3146,8 @@ export function lowerIntrinsicProperty(L, expr) {
|
|
|
3090
3146
|
return { kind: "jsExit", value: read, type: F64, loc: locOf(expr) };
|
|
3091
3147
|
}
|
|
3092
3148
|
// A CHECKED-DYNAMIC value behind an array-typed checker spelling
|
|
3093
|
-
// (`Object.keys(u).length` — the dyn walk answers a
|
|
3094
|
-
// tsc spells string[]): the runtime-world dispatch — a
|
|
3149
|
+
// (`Object.keys(u).length` — the dyn walk answers a dyn array while
|
|
3150
|
+
// tsc spells string[]): the runtime-world dispatch — a dyn keyed
|
|
3095
3151
|
// read validated into the declared number (a lying length throws
|
|
3096
3152
|
// the catchable TypeError, the dynCheck stance).
|
|
3097
3153
|
if (receiver.type.kind === "dyn") {
|
|
@@ -3134,7 +3190,7 @@ export function lowerArrayLiteral(L, expr, expected) {
|
|
|
3134
3190
|
// A JS literal whose OWN inferred type is never-tainted
|
|
3135
3191
|
// (neverTaintedJsType — the evolving `const gb = []`, the mixed
|
|
3136
3192
|
// command tuple `['pwd', []]`) carries no element information: route
|
|
3137
|
-
// it to the
|
|
3193
|
+
// it to the checked-dynamic tree fallback below rather than letting never's f64
|
|
3138
3194
|
// representation build a static number array (a later dyn push would
|
|
3139
3195
|
// throw "expected number at $, got string"; the tuple's union arm
|
|
3140
3196
|
// would re-tag as number[] and fence). A REAL slot still wins:
|
|
@@ -3264,14 +3320,14 @@ export function lowerArrayLiteral(L, expr, expected) {
|
|
|
3264
3320
|
if (!mapped || mapped.kind !== "array") {
|
|
3265
3321
|
// The JS declaration fallback, literal-side: an element type with no
|
|
3266
3322
|
// static home (a string | string[] mixed command tuple, an evolving
|
|
3267
|
-
// []) builds as a
|
|
3323
|
+
// []) builds as a dyn ARRAY — one dyn value whose elements each
|
|
3268
3324
|
// convert through the usual boundary (dynFrom's JSON-safe domain);
|
|
3269
3325
|
// an element that cannot convert fences per element. length/index
|
|
3270
|
-
// reads and dynamic consumers ride the keyed-
|
|
3326
|
+
// reads and dynamic consumers ride the keyed-dyn paths. TS literals
|
|
3271
3327
|
// take the same build when the slot ITSELF is checked-dynamic — an
|
|
3272
3328
|
// `unknown[]` annotation or a collapsed `(string | object)[]` maps
|
|
3273
3329
|
// to DYN wholesale now (mapType's dyn-element array rule), so the
|
|
3274
|
-
// literal IS the
|
|
3330
|
+
// literal IS the dyn array.
|
|
3275
3331
|
if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn") {
|
|
3276
3332
|
const elems = expr.elements.map((el) => {
|
|
3277
3333
|
if (ts.isSpreadElement(el)) {
|
|
@@ -3557,12 +3613,12 @@ function propNameText(L, name) {
|
|
|
3557
3613
|
return name.text;
|
|
3558
3614
|
}
|
|
3559
3615
|
/** The runtime-keyed JS object literal (a computed key that doesn't fold):
|
|
3560
|
-
* builds a
|
|
3616
|
+
* builds a dyn object member-by-member. Keys evaluate before their values,
|
|
3561
3617
|
* properties in source order — JS's object-literal evaluation exactly.
|
|
3562
3618
|
* Identifier/string keys are compile-time strings; numeric keys take
|
|
3563
3619
|
* their canonical number string (`{ 0x10: v }` stores "16" — JS's
|
|
3564
3620
|
* ToPropertyKey); computed keys evaluate and pass through ToString (the
|
|
3565
|
-
*
|
|
3621
|
+
* dyn's String() for dyn operands — `{ [field]: v }` where field is a
|
|
3566
3622
|
* checked-dynamic param). Values convert through the usual dyn boundary
|
|
3567
3623
|
* (dynFrom's domain, functions box); a value with no dyn representation
|
|
3568
3624
|
* fences per property. Spreads, accessors, and methods stay fenced —
|
|
@@ -3655,7 +3711,7 @@ export function lowerDynObjectLiteral(L, expr) {
|
|
|
3655
3711
|
L.unsupported("SC1101", valueExpr, `holding '${L.fmt(v.type)}' values in a runtime-keyed (computed-key) object literal`);
|
|
3656
3712
|
}
|
|
3657
3713
|
catch (err) {
|
|
3658
|
-
// A member value the
|
|
3714
|
+
// A member value the checked-dynamic tree cannot hold — a func whose signature
|
|
3659
3715
|
// cannot box, an island ('any') handle, a record carrying func
|
|
3660
3716
|
// fields (the export aggregate's typed utilities and npm-handle
|
|
3661
3717
|
// members): the same call-time fence deferral as an unlowerable
|
|
@@ -3725,7 +3781,7 @@ function fenceClosureProbe(L, node, slotType, attempt) {
|
|
|
3725
3781
|
return null;
|
|
3726
3782
|
}
|
|
3727
3783
|
// The trap's ABI: the field's func slot when one exists; otherwise (the
|
|
3728
|
-
//
|
|
3784
|
+
// dyn-object literal path — the value boxes into dyn) the all-dyn
|
|
3729
3785
|
// signature of the lambda's own arity, which canBoxFuncIntoDyn always
|
|
3730
3786
|
// admits.
|
|
3731
3787
|
const fieldType = slotType !== undefined && slotType.kind === "func"
|
|
@@ -3868,7 +3924,7 @@ export function lowerObjectLiteral(L, expr) {
|
|
|
3868
3924
|
const loc = locOf(expr);
|
|
3869
3925
|
// The RUNTIME-KEYED literal (JS): a computed key that doesn't fold to a
|
|
3870
3926
|
// compile-time string means the literal's shape is not a compile-time
|
|
3871
|
-
// fact — no record shape can hold it. The whole literal builds as a
|
|
3927
|
+
// fact — no record shape can hold it. The whole literal builds as a dyn
|
|
3872
3928
|
// object instead (`{ [field]: criteria, actual: 0, ... }` —
|
|
3873
3929
|
// test/common's _mustCallInner context), where keys are runtime string
|
|
3874
3930
|
// values. TypeScript keeps the record world and its fence: this shape
|
|
@@ -4117,13 +4173,13 @@ export function lowerObjectLiteral(L, expr) {
|
|
|
4117
4173
|
const shapeId = L.shapes.intern(fields.map((f) => ({ name: f.name, type: f.value.type })));
|
|
4118
4174
|
return { kind: "recordLit", fields, type: { kind: "record", shapeId }, loc };
|
|
4119
4175
|
}
|
|
4120
|
-
// The JS declaration fallback, literal-side (the
|
|
4176
|
+
// The JS declaration fallback, literal-side (the checked-dynamic tree-array rule's
|
|
4121
4177
|
// object form): a literal whose shape has no static home — an
|
|
4122
4178
|
// unmappable contextual type over unmappable own fields (the
|
|
4123
4179
|
// PropertyDescriptorMap argument of Object.defineProperties, nested
|
|
4124
|
-
// descriptor records with `any` values) — builds as a
|
|
4180
|
+
// descriptor records with `any` values) — builds as a dyn OBJECT:
|
|
4125
4181
|
// each field converts through the usual dyn boundary, and dynamic
|
|
4126
|
-
// consumers ride the keyed-
|
|
4182
|
+
// consumers ride the keyed-dyn paths. TypeScript keeps the fence.
|
|
4127
4183
|
if ((!mapped || mapped.kind === "dyn") && isJsSourceFile(expr.getSourceFile())) {
|
|
4128
4184
|
return lowerDynObjectLiteral(L, expr);
|
|
4129
4185
|
}
|
|
@@ -4389,8 +4445,8 @@ export function lowerObjectLiteral(L, expr) {
|
|
|
4389
4445
|
const recArm = recArms[0];
|
|
4390
4446
|
const recTag = def.arms.indexOf(recArm);
|
|
4391
4447
|
// A checked-dynamic VALUE under the union-mapped checker type
|
|
4392
|
-
// (a JS
|
|
4393
|
-
// union tags a
|
|
4448
|
+
// (a JS dyn-holding binding): the present/absent desugar tests
|
|
4449
|
+
// union tags a dyn box does not carry — fence honestly instead
|
|
4394
4450
|
// of the validator's ICE.
|
|
4395
4451
|
const probedSrc = srcLowered ?? probeLower(L, srcNode);
|
|
4396
4452
|
if (probedSrc?.type.kind === "dyn") {
|
|
@@ -4572,11 +4628,11 @@ export function lowerObjectLiteral(L, expr) {
|
|
|
4572
4628
|
throw new PoisonError();
|
|
4573
4629
|
}
|
|
4574
4630
|
const obj = srcLowered ?? L.lowerExpr(srcNode);
|
|
4575
|
-
// A record-mapped CHECKER type whose VALUE lives in the
|
|
4631
|
+
// A record-mapped CHECKER type whose VALUE lives in the checked-dynamic tree (a
|
|
4576
4632
|
// JS file-scope object-literal global): read each field from
|
|
4577
|
-
// the
|
|
4633
|
+
// the checked-dynamic tree (dynKeyGet) and VALIDATE it into the source shape's
|
|
4578
4634
|
// field type (dynCheck) — the checked-dynamic member-read
|
|
4579
|
-
// discipline. A missing key answers the
|
|
4635
|
+
// discipline. A missing key answers the dyn undefined, exactly
|
|
4580
4636
|
// the undefined-armed optional's absent case; a mismatched
|
|
4581
4637
|
// runtime value throws the catchable TypeError, never a silent
|
|
4582
4638
|
// wrong copy. Runtime-ADDED keys drop — width subtyping in
|
|
@@ -4584,7 +4640,7 @@ export function lowerObjectLiteral(L, expr) {
|
|
|
4584
4640
|
let value;
|
|
4585
4641
|
if (obj.type.kind === "dyn") {
|
|
4586
4642
|
if (!canDynCheckTo(f.type, (id) => L.shapes.get(id), (id) => L.unions.get(id))) {
|
|
4587
|
-
L.unsupported("SC1100", prop, `object spread of a checked-dynamic source whose field '${f.name}' ('${L.fmt(f.type)}') cannot validate out of the
|
|
4643
|
+
L.unsupported("SC1100", prop, `object spread of a checked-dynamic source whose field '${f.name}' ('${L.fmt(f.type)}') cannot validate out of the checked-dynamic tree (copy the fields explicitly)`);
|
|
4588
4644
|
}
|
|
4589
4645
|
value = {
|
|
4590
4646
|
kind: "dynCheck",
|
|
@@ -4723,7 +4779,7 @@ export function lowerObjectLiteral(L, expr) {
|
|
|
4723
4779
|
// lowering: a union field with one array-family arm builds the
|
|
4724
4780
|
// literal AS that arm (lowerExprExpecting's IR-directed rule —
|
|
4725
4781
|
// the option-table `default: [{ value: [] }]` shape), where the
|
|
4726
|
-
// bare lowering would take the JS
|
|
4782
|
+
// bare lowering would take the JS dyn fallback and fence.
|
|
4727
4783
|
let init = prop.initializer;
|
|
4728
4784
|
while (ts.isParenthesizedExpression(init))
|
|
4729
4785
|
init = init.expression;
|
|
@@ -4768,7 +4824,7 @@ export function lowerObjectLiteral(L, expr) {
|
|
|
4768
4824
|
}
|
|
4769
4825
|
if (!fieldType) {
|
|
4770
4826
|
// Overflow entry: the value flows into the index signature's value
|
|
4771
|
-
// slot — dyn slots take a
|
|
4827
|
+
// slot — dyn slots take a dyn conversion (dynFrom), typed slots the
|
|
4772
4828
|
// ordinary coercion path. Later duplicates override (map semantics
|
|
4773
4829
|
// are last-write-wins already; a duplicate literal key is a tsc
|
|
4774
4830
|
// error anyway).
|
|
@@ -4810,7 +4866,7 @@ export function lowerObjectLiteral(L, expr) {
|
|
|
4810
4866
|
for (const f of shape.fields) {
|
|
4811
4867
|
if (provided.has(f.name))
|
|
4812
4868
|
continue;
|
|
4813
|
-
// 'unknown' fields complete with the
|
|
4869
|
+
// 'unknown' fields complete with the dyn undefined — the absent
|
|
4814
4870
|
// property reads as undefined in Node, and a dyn slot holds
|
|
4815
4871
|
// exactly that (the options-record call shape against
|
|
4816
4872
|
// `{ plugins: unknown, ... }` — a JS caller the checker admits).
|
|
@@ -5171,7 +5227,7 @@ export function lowerElementAccess(L, expr) {
|
|
|
5171
5227
|
// A never-tainted JS receiver type (neverTaintedJsType — `cmd[1]` on
|
|
5172
5228
|
// `const cmd = ['pwd', []]`, whose binding lowered checked-dynamic)
|
|
5173
5229
|
// dispatches as unmapped so the dyn-receiver branch below reads
|
|
5174
|
-
// through the
|
|
5230
|
+
// through the checked-dynamic tree instead of dynChecking into never's f64 residue.
|
|
5175
5231
|
const receiverIr = neverTaintedJsType(L, expr.expression, L.typeOf(expr.expression))
|
|
5176
5232
|
? null
|
|
5177
5233
|
: L.mapTypeOf(L.typeOf(expr.expression));
|
|
@@ -5179,7 +5235,7 @@ export function lowerElementAccess(L, expr) {
|
|
|
5179
5235
|
// Dispatch follows the RUNTIME world (383(d)): a checker-'any'
|
|
5180
5236
|
// receiver whose value LOWERED checked-dynamic (`bag.list[0]` where
|
|
5181
5237
|
// bag.list is a routed keyed read off a wrapped island value) takes
|
|
5182
|
-
// the
|
|
5238
|
+
// the dyn keyed read below — the routed JSVAL arm reads the real
|
|
5183
5239
|
// engine element; a jsval-lowered receiver keeps the island read.
|
|
5184
5240
|
const recv = L.lowerExpr(expr.expression);
|
|
5185
5241
|
if (recv.type.kind !== "dyn")
|
|
@@ -5258,7 +5314,7 @@ export function lowerElementAccess(L, expr) {
|
|
|
5258
5314
|
}
|
|
5259
5315
|
}
|
|
5260
5316
|
// `pkg["k"]` / `scripts[name]` / `scopeMatch[1]` on a dyn receiver (a
|
|
5261
|
-
// JSON.parse result): the
|
|
5317
|
+
// JSON.parse result): the dyn keyed read, exactly the dot form.
|
|
5262
5318
|
// NUMBER-typed indices convert through ToString first — JS property
|
|
5263
5319
|
// keys are strings, and the canonical number text answers array
|
|
5264
5320
|
// indices in the helper (fractions, negatives, and NaN read as
|
|
@@ -5368,7 +5424,7 @@ export function lowerElementAccess(L, expr) {
|
|
|
5368
5424
|
return islandElementRead(L, expr, arr);
|
|
5369
5425
|
// The checker sees an array but the VALUE lowered checked-dynamic (a
|
|
5370
5426
|
// jsdoc-typed default export from a .js module — signature 14): the
|
|
5371
|
-
// read rides the dynCheck boundary when the array shape is one the
|
|
5427
|
+
// read rides the dynCheck boundary when the array shape is one the checked-dynamic tree
|
|
5372
5428
|
// can validate, and fences by name when it is not — an arrayGet over a
|
|
5373
5429
|
// dyn receiver is never emitted (the validator ICE).
|
|
5374
5430
|
if (arr.type.kind === "dyn") {
|
|
@@ -5444,7 +5500,7 @@ function islandElementRead(L, expr, obj) {
|
|
|
5444
5500
|
* the index signature's value type (dyn for `unknown`), or its
|
|
5445
5501
|
* undefined-armed union under noUncheckedIndexedAccess. Every declared
|
|
5446
5502
|
* field must be able to SURFACE as that type (equal, an arm of it, or —
|
|
5447
|
-
* for dyn results — JSON-safe for the
|
|
5503
|
+
* for dyn results — JSON-safe for the dyn conversion); shapes mixing in
|
|
5448
5504
|
* fields outside that stay fenced. Declared-only shapes support reads
|
|
5449
5505
|
* whose key type proves membership (tsc's keyof check): all fields must
|
|
5450
5506
|
* share the result type, and a smuggled miss traps. */
|
|
@@ -5463,16 +5519,19 @@ export function lowerRecordKeyRead(L, expr, shapeId, shape) {
|
|
|
5463
5519
|
// The receiver lowers FIRST (both branches below read it, and JS
|
|
5464
5520
|
// evaluates the receiver before the key).
|
|
5465
5521
|
const obj = L.lowerExpr(expr.expression);
|
|
5466
|
-
// A record-mapped CHECKER type over a VALUE living in the
|
|
5522
|
+
// A record-mapped CHECKER type over a VALUE living in the checked-dynamic tree (a JS
|
|
5467
5523
|
// file-scope object-literal global): the checked-dynamic keyed read —
|
|
5468
|
-
// dynKeyGet against the runtime keys (a missing key answers the
|
|
5524
|
+
// dynKeyGet against the runtime keys (a missing key answers the checked-dynamic tree
|
|
5469
5525
|
// undefined, exactly JS); consumers validate (dynCheck) where a
|
|
5470
5526
|
// static type is required, the member-read discipline.
|
|
5471
5527
|
if (obj.type.kind === "dyn") {
|
|
5472
5528
|
let dk = litKey !== null
|
|
5473
5529
|
? { kind: "strLit", value: litKey, type: STRING, loc: locOf(keyNode) }
|
|
5474
5530
|
: L.lowerExpr(keyNode);
|
|
5475
|
-
|
|
5531
|
+
// Number AND checked-dynamic keys ride the JS-exact formatter —
|
|
5532
|
+
// property keys ARE strings (o[k] is o[String(k)] in JS), and a dyn
|
|
5533
|
+
// key (agent.sockets[agent.getName(...)]) stringifies the same way.
|
|
5534
|
+
if (dk.type.kind === "f64" || dk.type.kind === "dyn")
|
|
5476
5535
|
dk = L.ensureString(dk, keyNode);
|
|
5477
5536
|
if (dk.type.kind !== "string") {
|
|
5478
5537
|
L.unsupported("SC1090", keyNode, "indexing records with non-string or non-number keys");
|
|
@@ -5574,8 +5633,8 @@ export function recordKeyTypeLiteralText(L, node) {
|
|
|
5574
5633
|
}
|
|
5575
5634
|
/** Can every value a dynamic key can reach (declared fields + the
|
|
5576
5635
|
* overflow) surface as the read's result type? dyn results need
|
|
5577
|
-
*
|
|
5578
|
-
* the top — an optional field's undefined becomes the undefined
|
|
5636
|
+
* dyn-convertible fields (JSON-safe, with the undefined arm allowed at
|
|
5637
|
+
* the top — an optional field's undefined becomes the undefined dyn
|
|
5579
5638
|
* value, exactly what the missing-key path produces); typed results need
|
|
5580
5639
|
* each field to be the type itself or one of its arms, and the overflow
|
|
5581
5640
|
* value to be the type or wrappable into it. */
|
|
@@ -5643,7 +5702,7 @@ export function lowerElementWrite(L, expr) {
|
|
|
5643
5702
|
if (receiverIr?.kind === "jsval") {
|
|
5644
5703
|
const obj = L.lowerExpr(target.expression);
|
|
5645
5704
|
// Dispatch follows the RUNTIME world (383(d)): a checker-'any'
|
|
5646
|
-
// receiver whose value LOWERED checked-dynamic takes the
|
|
5705
|
+
// receiver whose value LOWERED checked-dynamic takes the dyn keyed
|
|
5647
5706
|
// write — the routed JSVAL arm lands it on the real engine object.
|
|
5648
5707
|
if (obj.type.kind === "dyn") {
|
|
5649
5708
|
const loc = locOf(expr);
|
|
@@ -5670,12 +5729,12 @@ export function lowerElementWrite(L, expr) {
|
|
|
5670
5729
|
return { kind: "exprStmt", expr: { kind: "jsOp", op: "setIdx", args: [obj, key, value], type: VOID, loc }, loc };
|
|
5671
5730
|
}
|
|
5672
5731
|
// A checked-dynamic receiver TYPE (`unknown[]` and the collapsed
|
|
5673
|
-
// `(string | object)[]` map to DYN wholesale now): the
|
|
5732
|
+
// `(string | object)[]` map to DYN wholesale now): the dyn keyed
|
|
5674
5733
|
// write — dyn.keySet. Number keys canonicalize through the JS-exact
|
|
5675
5734
|
// formatter; ARR receivers take canonical index keys as element
|
|
5676
5735
|
// set/extend (undefined-hole padding, JS's length growth exactly),
|
|
5677
5736
|
// OBJ receivers set the member, and the runtime throws Node's
|
|
5678
|
-
// TypeErrors on every other kind. Values convert into the
|
|
5737
|
+
// TypeErrors on every other kind. Values convert into the checked-dynamic tree;
|
|
5679
5738
|
// unconvertible ones fence per site (the dynFrom stance).
|
|
5680
5739
|
if (receiverIr?.kind === "dyn") {
|
|
5681
5740
|
const obj = L.lowerExpr(target.expression);
|
|
@@ -5689,7 +5748,7 @@ export function lowerElementWrite(L, expr) {
|
|
|
5689
5748
|
}
|
|
5690
5749
|
const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
|
|
5691
5750
|
if (value.type.kind !== "dyn") {
|
|
5692
|
-
L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic array (the value cannot convert into the
|
|
5751
|
+
L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic array (the value cannot convert into the checked-dynamic tree)`);
|
|
5693
5752
|
}
|
|
5694
5753
|
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
|
|
5695
5754
|
}
|
|
@@ -5729,11 +5788,11 @@ export function lowerElementWrite(L, expr) {
|
|
|
5729
5788
|
const loc = locOf(expr);
|
|
5730
5789
|
return { kind: "exprStmt", expr: { kind: "jsOp", op: "setIdx", args: [obj, key, value], type: VOID, loc }, loc };
|
|
5731
5790
|
}
|
|
5732
|
-
// A record-mapped CHECKER type over a VALUE living in the
|
|
5791
|
+
// A record-mapped CHECKER type over a VALUE living in the checked-dynamic tree (a
|
|
5733
5792
|
// JS file-scope object-literal global): the checked-dynamic keyed
|
|
5734
5793
|
// write — dyn.keySet (later writes win, insertion order; Node's
|
|
5735
5794
|
// TypeErrors on non-object receivers), the value converting into
|
|
5736
|
-
// the
|
|
5795
|
+
// the checked-dynamic tree. Literal and runtime keys alike: the checked-dynamic tree's key set is
|
|
5737
5796
|
// open, so no declared-field collision analysis applies.
|
|
5738
5797
|
if (obj.type.kind === "dyn") {
|
|
5739
5798
|
const loc = locOf(expr);
|
|
@@ -5748,7 +5807,7 @@ export function lowerElementWrite(L, expr) {
|
|
|
5748
5807
|
}
|
|
5749
5808
|
const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
|
|
5750
5809
|
if (value.type.kind !== "dyn") {
|
|
5751
|
-
L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the
|
|
5810
|
+
L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the checked-dynamic tree)`);
|
|
5752
5811
|
}
|
|
5753
5812
|
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
|
|
5754
5813
|
}
|
|
@@ -5836,7 +5895,7 @@ export function lowerElementWrite(L, expr) {
|
|
|
5836
5895
|
// A LITERAL key naming no declared field is a pure overflow insert
|
|
5837
5896
|
// — no collision to validate. Runtime keys must be able to collide
|
|
5838
5897
|
// with every declared field: dyn slots validate through the
|
|
5839
|
-
// dynCheck walker (fields must be
|
|
5898
|
+
// dynCheck walker (fields must be dyn-convertible — the same
|
|
5840
5899
|
// convertibility the read needs); typed slots write through
|
|
5841
5900
|
// directly, so every field must BE the index-value type.
|
|
5842
5901
|
const overflowOnly = litKey !== null;
|
|
@@ -5875,7 +5934,7 @@ export function lowerElementWrite(L, expr) {
|
|
|
5875
5934
|
// `result[key] = value`): dyn.keySet, exactly the record-mapped dyn
|
|
5876
5935
|
// arm above — later writes win in insertion order, number keys
|
|
5877
5936
|
// canonicalize through the JS-exact formatter (ToPropertyKey's string
|
|
5878
|
-
// side), index keys on
|
|
5937
|
+
// side), index keys on dyn arrays set/extend elements, and non-object
|
|
5879
5938
|
// receivers throw Node's TypeErrors at runtime. An UNMAPPED checker
|
|
5880
5939
|
// type (static `any` — mapTypeOf answers null without --dynamic)
|
|
5881
5940
|
// probes the receiver's own lowered world: a dyn value takes the same
|
|
@@ -5898,7 +5957,7 @@ export function lowerElementWrite(L, expr) {
|
|
|
5898
5957
|
}
|
|
5899
5958
|
const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
|
|
5900
5959
|
if (value.type.kind !== "dyn") {
|
|
5901
|
-
L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the
|
|
5960
|
+
L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the checked-dynamic tree)`);
|
|
5902
5961
|
}
|
|
5903
5962
|
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
|
|
5904
5963
|
}
|
|
@@ -5932,7 +5991,7 @@ export function ensureString(L, e, node) {
|
|
|
5932
5991
|
if (e.type.kind === "string")
|
|
5933
5992
|
return e;
|
|
5934
5993
|
if (e.type.kind === "dyn") {
|
|
5935
|
-
// String(u) / `${u}`: a runtime dispatch over the
|
|
5994
|
+
// String(u) / `${u}`: a runtime dispatch over the dyn kind — Node's
|
|
5936
5995
|
// String() exactly (undefined/null texts, JS number formatting,
|
|
5937
5996
|
// strings verbatim, arrays via join, objects as "[object Object]").
|
|
5938
5997
|
return { kind: "toString", operand: e, type: STRING, loc: e.loc };
|
|
@@ -6058,7 +6117,7 @@ export function lowerTemplate(L, expr) {
|
|
|
6058
6117
|
* documented divergence: a lying cast throws instead of corrupting
|
|
6059
6118
|
* memory);
|
|
6060
6119
|
* - dyn → anything else (closures, class instances, void) is rejected:
|
|
6061
|
-
* those types cannot be found inside a JSON
|
|
6120
|
+
* those types cannot be found inside a JSON dyn. */
|
|
6062
6121
|
/** `e as T` and the old-style assertion `<T>e` — one node shape (both
|
|
6063
6122
|
* carry `.type` and `.expression`), one lowering. */
|
|
6064
6123
|
export function lowerAsExpression(L, expr) {
|
|
@@ -6162,7 +6221,7 @@ export function lowerAsExpression(L, expr) {
|
|
|
6162
6221
|
return inner; // `as unknown`: erasure
|
|
6163
6222
|
if (target.kind === "void" || !L.jsonSafe(target)) {
|
|
6164
6223
|
// Bare undefined-armed targets pass when every OTHER arm is
|
|
6165
|
-
// JSON-safe: the
|
|
6224
|
+
// JSON-safe: the checked-dynamic tree holds a first-class undefined value now
|
|
6166
6225
|
// (index-signature overflow reads produce it for missing keys), and
|
|
6167
6226
|
// the undefined arm matches exactly it — `p[key] as string |
|
|
6168
6227
|
// undefined` is the missing-key idiom. Parsed JSON still never
|
|
@@ -6171,7 +6230,7 @@ export function lowerAsExpression(L, expr) {
|
|
|
6171
6230
|
if (target.kind === "union" && L.dynConvertible(target)) {
|
|
6172
6231
|
return { kind: "dynCheck", value: inner, type: target, loc: locOf(expr) };
|
|
6173
6232
|
}
|
|
6174
|
-
// Uint8Array targets: the
|
|
6233
|
+
// Uint8Array targets: the checked-dynamic tree carries a bytes kind now (converted
|
|
6175
6234
|
// stdin chunks) — the extraction validates the kind and copies out.
|
|
6176
6235
|
if (target.kind === "bytes" && target.elem === "u8") {
|
|
6177
6236
|
return { kind: "dynCheck", value: inner, type: target, loc: locOf(expr) };
|
|
@@ -6194,7 +6253,7 @@ export function lowerAsExpression(L, expr) {
|
|
|
6194
6253
|
// — the errno-probing idiom): there is nothing to validate (every
|
|
6195
6254
|
// field is unknown, exactly what the dyn value already answers) and
|
|
6196
6255
|
// nothing to build — the cast is pure typing, so it ERASES and the
|
|
6197
|
-
// reads ride the
|
|
6256
|
+
// reads ride the dyn keyed read.
|
|
6198
6257
|
if (target.kind === "record") {
|
|
6199
6258
|
const shape = L.shapes.get(target.shapeId);
|
|
6200
6259
|
if (shape &&
|
|
@@ -6355,7 +6414,7 @@ export function lowerBinary(L, expr) {
|
|
|
6355
6414
|
}
|
|
6356
6415
|
// `events.defaultMaxListeners = v` — the module-property write
|
|
6357
6416
|
// Node validates (validateNumber(n, 'defaultMaxListeners', 0)):
|
|
6358
|
-
// the value crosses into the
|
|
6417
|
+
// the value crosses into the checked-dynamic tree and the runtime ladder throws
|
|
6359
6418
|
// ERR_INVALID_ARG_TYPE / ERR_OUT_OF_RANGE with Node's exact slot
|
|
6360
6419
|
// name; valid numbers apply. The expression's value is the RHS
|
|
6361
6420
|
// (JS's assignment yield).
|
|
@@ -6528,8 +6587,8 @@ export function lowerBinary(L, expr) {
|
|
|
6528
6587
|
}
|
|
6529
6588
|
if (left.type.kind === "dyn" || right.type.kind === "dyn") {
|
|
6530
6589
|
// A checked-dynamic operand (`fn.name || '<anonymous>'` —
|
|
6531
|
-
// test/common's _mustCallInner): both sides live in the
|
|
6532
|
-
// the deciding test is ToBoolean over the
|
|
6590
|
+
// test/common's _mustCallInner): both sides live in the checked-dynamic tree and
|
|
6591
|
+
// the deciding test is ToBoolean over the dyn kind
|
|
6533
6592
|
// (scr_dyn_truthy) — JS value semantics exactly, result dyn. The
|
|
6534
6593
|
// non-dyn side converts through the usual boundary; a value with
|
|
6535
6594
|
// no dyn representation keeps the fence.
|
|
@@ -6566,6 +6625,30 @@ export function lowerBinary(L, expr) {
|
|
|
6566
6625
|
// shapes lower through lowerCondition's bool descent instead.
|
|
6567
6626
|
const target = L.mapTypeOf(L.typeOf(expr));
|
|
6568
6627
|
if (target?.kind === "union") {
|
|
6628
|
+
// `||` whose left does NOT fit the result union: the checker built
|
|
6629
|
+
// that result by DROPPING the left's falsy arms (`process.env.X ||
|
|
6630
|
+
// null` is `string | null`, `|| 3000` is `string | number` — the
|
|
6631
|
+
// `undefined` is gone from both), so coercing the left eagerly, as
|
|
6632
|
+
// the shared shape below does, retags an arm the test is about to
|
|
6633
|
+
// rule out and throws where Node yields the default. Single-eval
|
|
6634
|
+
// instead: test the left in its OWN union and retag only on the
|
|
6635
|
+
// truthy side, where the dropped arms are unreachable. `&&` keeps
|
|
6636
|
+
// the eager shape — there the falsy left IS the result, so the
|
|
6637
|
+
// checker keeps those arms in the target and they must coerce.
|
|
6638
|
+
if (op === ts.SyntaxKind.BarBarToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
|
|
6639
|
+
const retag = L.unionRetagHelper(left.type.unionId, target.unionId, loc);
|
|
6640
|
+
if (retag) {
|
|
6641
|
+
L.requireTruthyUnion(left.type.unionId, expr);
|
|
6642
|
+
return {
|
|
6643
|
+
kind: "orDefault",
|
|
6644
|
+
left,
|
|
6645
|
+
right: L.coerceInto(expr.right, right, target),
|
|
6646
|
+
retag,
|
|
6647
|
+
type: target,
|
|
6648
|
+
loc,
|
|
6649
|
+
};
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6569
6652
|
L.requireTruthyUnion(target.unionId, expr);
|
|
6570
6653
|
return {
|
|
6571
6654
|
kind: "logical",
|
|
@@ -6665,7 +6748,7 @@ export function lowerBinary(L, expr) {
|
|
|
6665
6748
|
const right = L.lowerExpr(expr.right);
|
|
6666
6749
|
if (left.type.kind === "dyn" || right.type.kind === "dyn") {
|
|
6667
6750
|
// The narrowing unit comparisons ARE answerable on unknown: `v ===
|
|
6668
|
-
// undefined` / `v !== null` test the
|
|
6751
|
+
// undefined` / `v !== null` test the dyn node's kind directly, and
|
|
6669
6752
|
// tsc's control flow narrows the branches (reads then bridge through
|
|
6670
6753
|
// maybeNarrow's validated extraction).
|
|
6671
6754
|
if (op === ts.SyntaxKind.EqualsEqualsEqualsToken ||
|
|
@@ -6685,14 +6768,14 @@ export function lowerBinary(L, expr) {
|
|
|
6685
6768
|
}
|
|
6686
6769
|
// dyn vs SCALAR strict equality (`value !== ""`, `u === 5` — the
|
|
6687
6770
|
// normalizePricing filter): a guarded kind test + payload compare,
|
|
6688
|
-
// JS-exact (strict equality never coerces; a non-matching
|
|
6771
|
+
// JS-exact (strict equality never coerces; a non-matching dyn kind
|
|
6689
6772
|
// answers false). dyn vs dyn keeps the fence below.
|
|
6690
6773
|
const dynSide = left.type.kind === "dyn" ? left : right;
|
|
6691
6774
|
const scalarSide = dynSide === left ? right : left;
|
|
6692
6775
|
if (dynSide.type.kind === "dyn" &&
|
|
6693
6776
|
(scalarSide.type.kind === "f64" || scalarSide.type.kind === "string" || scalarSide.type.kind === "bool" ||
|
|
6694
6777
|
// dyn vs dyn (`context.actual !== context.exact` —
|
|
6695
|
-
// test/common's exit accounting): the runtime's whole-
|
|
6778
|
+
// test/common's exit accounting): the runtime's whole-dyn
|
|
6696
6779
|
// strict equality — scalars by value, units by kind,
|
|
6697
6780
|
// reference kinds by node identity (scr_dyn_strict_eq).
|
|
6698
6781
|
scalarSide.type.kind === "dyn")) {
|
|
@@ -6747,7 +6830,7 @@ export function lowerBinary(L, expr) {
|
|
|
6747
6830
|
}
|
|
6748
6831
|
if (other.type.kind === "string") {
|
|
6749
6832
|
// String-context `+`: JS's answer is String(unknown) — the
|
|
6750
|
-
// JS-exact
|
|
6833
|
+
// JS-exact dyn walker (numbers format, arrays join, objects
|
|
6751
6834
|
// print [object Object], handles the same) — never a checked
|
|
6752
6835
|
// cast: `'status ' + res.statusCode` concatenates like Node.
|
|
6753
6836
|
const strOf = (e) => e.type.kind === "dyn" ? { kind: "toString", operand: e, type: STRING, loc: e.loc } : e;
|
|
@@ -6765,7 +6848,7 @@ export function lowerBinary(L, expr) {
|
|
|
6765
6848
|
L.anyOpFence(`the '${ts.tokenToString(op) ?? ts.SyntaxKind[op]}' operator`, expr);
|
|
6766
6849
|
}
|
|
6767
6850
|
// tsc allows ===/!== on unknown (arithmetic/comparisons it rejects
|
|
6768
|
-
// itself); a dynamic equality would need a
|
|
6851
|
+
// itself); a dynamic equality would need a dyn walk — validate first.
|
|
6769
6852
|
L.unsupported("SC1100", expr, "operators on 'unknown' values");
|
|
6770
6853
|
}
|
|
6771
6854
|
// Operators over 'any' execute in the island with JS-exact semantics
|
|
@@ -6932,6 +7015,12 @@ export function lowerBinary(L, expr) {
|
|
|
6932
7015
|
typeEquals(idLeft.type, idRight.type)) {
|
|
6933
7016
|
return { kind: "bin", op: negated ? "!==" : "===", left: idLeft, right: idRight, type: BOOL, loc };
|
|
6934
7017
|
}
|
|
7018
|
+
// Runtime HANDLES are objects to === too: one handle per socket/
|
|
7019
|
+
// request/response, so pointer identity IS JS's object equality
|
|
7020
|
+
// (`c.pause() === c` — Node's chaining assertions). */
|
|
7021
|
+
if (DYN_HANDLE_KINDS.has(idLeft.type.kind) && typeEquals(idLeft.type, idRight.type)) {
|
|
7022
|
+
return { kind: "bin", op: negated ? "!==" : "===", left: idLeft, right: idRight, type: BOOL, loc };
|
|
7023
|
+
}
|
|
6935
7024
|
L.unsupported("SC1043", expr);
|
|
6936
7025
|
break;
|
|
6937
7026
|
}
|
|
@@ -7303,7 +7392,7 @@ export function lowerUnionTypeofTest(L, expr, loc) {
|
|
|
7303
7392
|
return null;
|
|
7304
7393
|
}
|
|
7305
7394
|
/** `typeof v === "lit"` / `typeof v !== "lit"` where v is `unknown`-typed
|
|
7306
|
-
* (dyn): the runtime kind test over the
|
|
7395
|
+
* (dyn): the runtime kind test over the dyn node's tag. Only the kinds a
|
|
7307
7396
|
* later read can bridge narrow ("string"/"number"/"boolean") plus
|
|
7308
7397
|
* "undefined" (no payload to read); "object"/"function" tests keep the
|
|
7309
7398
|
* fence — an object-narrowed `unknown` read has no lowering anyway (a
|
|
@@ -7387,7 +7476,7 @@ export function lowerDynTypeofTest(L, expr, loc) {
|
|
|
7387
7476
|
loc,
|
|
7388
7477
|
};
|
|
7389
7478
|
}
|
|
7390
|
-
// `typeof v === "object"`: the
|
|
7479
|
+
// `typeof v === "object"`: the dyn answers exactly (object, array,
|
|
7391
7480
|
// bytes, and null kinds — JS's oldest wart preserved). tsc narrows
|
|
7392
7481
|
// the branch to `object | null`; reads past the narrow take the
|
|
7393
7482
|
// checked-cast path per site.
|
|
@@ -7401,7 +7490,7 @@ export function lowerDynTypeofTest(L, expr, loc) {
|
|
|
7401
7490
|
loc,
|
|
7402
7491
|
};
|
|
7403
7492
|
}
|
|
7404
|
-
// `typeof v === "function"`: a REAL runtime test since the
|
|
7493
|
+
// `typeof v === "function"`: a REAL runtime test since the checked-dynamic tree's
|
|
7405
7494
|
// function kind exists (boxed closures — the mustCall guard
|
|
7406
7495
|
// `if (typeof fn !== 'function') throw` answers honestly).
|
|
7407
7496
|
if (b.text === "function") {
|
|
@@ -7564,7 +7653,7 @@ export function probeLower(L, node) {
|
|
|
7564
7653
|
* over a hierarchy class, `typeof` over a primitive), the read bridges
|
|
7565
7654
|
* with a kind-unchecked `caughtNarrow` extraction (trust-the-checker,
|
|
7566
7655
|
* exactly like unionNarrow). Every OTHER read is the narrowness fence:
|
|
7567
|
-
* the binding's payload is a typed exception snapshot, not a dyn
|
|
7656
|
+
* the binding's payload is a typed exception snapshot, not a dyn, so
|
|
7568
7657
|
* un-narrowed uses have nothing sound to lower to. */
|
|
7569
7658
|
export function caughtRead(L, node, local, loc) {
|
|
7570
7659
|
const ref = { kind: "varRef", localId: local.id, type: CAUGHT, loc };
|
|
@@ -7580,10 +7669,10 @@ export function caughtRead(L, node, local, loc) {
|
|
|
7580
7669
|
}
|
|
7581
7670
|
}
|
|
7582
7671
|
// An UN-narrowed use — the occurrence still types `unknown` — converts
|
|
7583
|
-
// to a dyn
|
|
7672
|
+
// to a dyn value (`options.onError?.(error)` — the caught snapshot
|
|
7584
7673
|
// flowing into an unknown slot): the typed→unknown deep-copy stance
|
|
7585
7674
|
// extended to exception payloads. Error payloads keep their
|
|
7586
|
-
// observability (instanceof Error / .message / String() — the
|
|
7675
|
+
// observability (instanceof Error / .message / String() — the checked-dynamic tree's
|
|
7587
7676
|
// error encoding); other object payloads are type-erased at runtime and
|
|
7588
7677
|
// convert to the "[object Object]" approximation — SEMANTICS.md 67.
|
|
7589
7678
|
if (narrowed?.kind === "dyn") {
|
|
@@ -7689,14 +7778,14 @@ export function lowerInstanceOf(L, expr, loc) {
|
|
|
7689
7778
|
L.builtinStreamInfoOf(rhsMemberSymbol) ??
|
|
7690
7779
|
undefined;
|
|
7691
7780
|
if (!target) {
|
|
7692
|
-
// `u instanceof Uint8Array` on an `unknown` value: the
|
|
7781
|
+
// `u instanceof Uint8Array` on an `unknown` value: the checked-dynamic tree carries a
|
|
7693
7782
|
// bytes kind — one runtime tag test, and tsc's narrowing types the
|
|
7694
7783
|
// true branch (reads bridge through maybeNarrow's validated
|
|
7695
7784
|
// extraction, like the typeof tests). Node's Buffer IS a Uint8Array
|
|
7696
7785
|
// subclass and rides the same bytes kind, so both worlds answer true
|
|
7697
7786
|
// for Buffer payloads — Node-exact (the bytes kind's other
|
|
7698
7787
|
// divergences are SEMANTICS.md 45). Catch bindings stay out (their
|
|
7699
|
-
// payload is a typed snapshot, not a
|
|
7788
|
+
// payload is a typed snapshot, not a dyn).
|
|
7700
7789
|
if (ts.isIdentifier(expr.right) &&
|
|
7701
7790
|
L.isStdlibGlobal(expr.right, "Uint8Array") &&
|
|
7702
7791
|
!L.caughtLocalOf(expr.left)) {
|
|
@@ -7787,7 +7876,7 @@ export function lowerInstanceOf(L, expr, loc) {
|
|
|
7787
7876
|
};
|
|
7788
7877
|
}
|
|
7789
7878
|
const left = L.lowerExpr(expr.left);
|
|
7790
|
-
// `u instanceof Error` on an `unknown` value: the
|
|
7879
|
+
// `u instanceof Error` on an `unknown` value: the checked-dynamic tree's error encoding
|
|
7791
7880
|
// (the shape caughtToDyn builds for Error payloads — the reserved
|
|
7792
7881
|
// "%error" marker) answers the test, so a caught Error passed through
|
|
7793
7882
|
// an unknown slot narrows like Node (`error instanceof Error ?
|
|
@@ -7800,7 +7889,7 @@ export function lowerInstanceOf(L, expr, loc) {
|
|
|
7800
7889
|
return { kind: "dynTest", test: "error", value: left, type: BOOL, loc };
|
|
7801
7890
|
}
|
|
7802
7891
|
// `u instanceof TypeError` (and the other BUILTIN error classes) on a
|
|
7803
|
-
// dyn value: the from_error cache holds the
|
|
7892
|
+
// dyn value: the from_error cache holds the checked-dynamic tree↔error identity edge,
|
|
7804
7893
|
// so the runtime resolves the encoding back to its error and asks the
|
|
7805
7894
|
// vtable's stamped interval — exact for every error that crossed the
|
|
7806
7895
|
// boundary (a hand-built {%error} literal answers false: subclass
|
|
@@ -8031,6 +8120,25 @@ export function lowerInExpression(L, expr, loc) {
|
|
|
8031
8120
|
const rIn = lowerRuntimeKeyIn(L, expr, loc);
|
|
8032
8121
|
if (rIn)
|
|
8033
8122
|
return rIn;
|
|
8123
|
+
// A runtime key over a CHECKED-DYNAMIC receiver (`name in
|
|
8124
|
+
// agent.sockets` — both sides computed; the checker may type the
|
|
8125
|
+
// receiver as a Dict while the VALUE lives in the checked-dynamic tree, so the
|
|
8126
|
+
// LOWERED type decides): the dyn presence answer, with the key
|
|
8127
|
+
// stringified like every property key (o[k] is o[String(k)] in JS;
|
|
8128
|
+
// `in` shares the coercion).
|
|
8129
|
+
{
|
|
8130
|
+
const probed = probeLower(L, expr.right);
|
|
8131
|
+
if (probed?.type.kind === "dyn") {
|
|
8132
|
+
let k = L.lowerExpr(expr.left); // JS order: the key evaluates first
|
|
8133
|
+
if (k.type.kind === "f64" || k.type.kind === "string" || k.type.kind === "dyn") {
|
|
8134
|
+
k = L.ensureString(k, expr.left);
|
|
8135
|
+
const recvD = L.lowerExpr(expr.right);
|
|
8136
|
+
if (recvD.type.kind === "dyn") {
|
|
8137
|
+
return { kind: "libCall", fn: "dyn.hasKey", args: [recvD, k], type: BOOL, loc };
|
|
8138
|
+
}
|
|
8139
|
+
}
|
|
8140
|
+
}
|
|
8141
|
+
}
|
|
8034
8142
|
L.unsupported("SC1090", expr.left, "'in' with computed (non-literal) keys (a const whose type is one string literal folds; a string-typed key answers over index-signature record receivers)");
|
|
8035
8143
|
}
|
|
8036
8144
|
// A receiver re-read (the static folds below) is safe for plain reads
|
|
@@ -8093,7 +8201,7 @@ export function lowerInExpression(L, expr, loc) {
|
|
|
8093
8201
|
}
|
|
8094
8202
|
}
|
|
8095
8203
|
// A dyn receiver (`"portless" in pkg` after the `typeof pkg ===
|
|
8096
|
-
// "object"` guard): own-member presence on the
|
|
8204
|
+
// "object"` guard): own-member presence on the checked-dynamic tree — tsc admits `in`
|
|
8097
8205
|
// only on object-typed operands, so unit receivers are unreachable.
|
|
8098
8206
|
if (recv.type.kind === "dyn") {
|
|
8099
8207
|
return { kind: "dynHasKey", key, value: recv, type: BOOL, loc };
|
|
@@ -8831,8 +8939,8 @@ export function lowerUnionProperty(L, expr) {
|
|
|
8831
8939
|
// fallback's discipline.
|
|
8832
8940
|
// A checker-union receiver whose VALUE lowered checked-dynamic (a
|
|
8833
8941
|
// never-tainted JS chain — `cmd[1].length` on `const cmd = ['pwd',
|
|
8834
|
-
// []]`, where the element read stayed a
|
|
8835
|
-
//
|
|
8942
|
+
// []]`, where the element read stayed a dyn node): read through the
|
|
8943
|
+
// dyn keyed read like the unmappable-receiver path below the chain.
|
|
8836
8944
|
if (value.type.kind === "dyn") {
|
|
8837
8945
|
const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
|
|
8838
8946
|
return { kind: "dynKeyGet", key, value, type: DYN, loc: locOf(expr) };
|
|
@@ -9219,9 +9327,9 @@ export function symbolFieldTarget(L, expr) {
|
|
|
9219
9327
|
* tsc-clean (the property types as the setter's param), but Node yields
|
|
9220
9328
|
* undefined, which these property types cannot represent. */
|
|
9221
9329
|
export function fieldGetExpr(L, target, loc, blame) {
|
|
9222
|
-
// A record-shaped CHECKER target whose receiver VALUE lives in the
|
|
9330
|
+
// A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree
|
|
9223
9331
|
// (a JS file-scope object-literal global): the checked-dynamic keyed
|
|
9224
|
-
// read — dynKeyGet (a missing key answers the
|
|
9332
|
+
// read — dynKeyGet (a missing key answers the dyn undefined, exactly
|
|
9225
9333
|
// JS); consumers validate (dynCheck) where a static type is required.
|
|
9226
9334
|
if ((target.container === "record" || target.container === "recordOvf") &&
|
|
9227
9335
|
target.obj.type.kind === "dyn") {
|
|
@@ -9302,15 +9410,15 @@ export function fieldGetExpr(L, target, loc, blame) {
|
|
|
9302
9410
|
* call). A write to a getter-only property never gets here in a clean
|
|
9303
9411
|
* program (tsc's TS2540 is the fence); the rejection is the backstop. */
|
|
9304
9412
|
export function fieldSetStmt(L, target, value, loc, blame) {
|
|
9305
|
-
// A record-shaped CHECKER target whose receiver VALUE lives in the
|
|
9413
|
+
// A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree:
|
|
9306
9414
|
// the checked-dynamic keyed write — dyn.keySet (later writes win,
|
|
9307
9415
|
// insertion order; Node's TypeErrors on non-object receivers), the
|
|
9308
|
-
// value converting into the
|
|
9416
|
+
// value converting into the checked-dynamic tree.
|
|
9309
9417
|
if ((target.container === "record" || target.container === "recordOvf") &&
|
|
9310
9418
|
target.obj.type.kind === "dyn") {
|
|
9311
9419
|
const v = L.coerceToExpected(value, DYN);
|
|
9312
9420
|
if (v.type.kind !== "dyn") {
|
|
9313
|
-
L.unsupported("SC1101", blame, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the
|
|
9421
|
+
L.unsupported("SC1101", blame, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the checked-dynamic tree)`);
|
|
9314
9422
|
}
|
|
9315
9423
|
return {
|
|
9316
9424
|
kind: "exprStmt",
|
|
@@ -9398,7 +9506,7 @@ export function lowerPromiseRejectCall(L, call, access) {
|
|
|
9398
9506
|
const type = ctxT ? L.mapTypeOf(ctxT) : null;
|
|
9399
9507
|
const reasonNode = call.arguments[0];
|
|
9400
9508
|
// A CHECKED-DYNAMIC reason (`Promise.reject(value)` where value rode
|
|
9401
|
-
// an untyped binding — the tracingChannel suite's shape): the
|
|
9509
|
+
// an untyped binding — the tracingChannel suite's shape): the checked-dynamic tree
|
|
9402
9510
|
// value IS the rejection payload (the thrown-dyn representation, so
|
|
9403
9511
|
// identity survives to catch/unhandledRejection observers), and the
|
|
9404
9512
|
// result is promise<dyn> when no context names a concrete one.
|
|
@@ -9541,7 +9649,7 @@ export function lowerFieldCompound(L, access, op, rhsNode, loc) {
|
|
|
9541
9649
|
const concat = {
|
|
9542
9650
|
kind: "strConcat",
|
|
9543
9651
|
// String-context read: JS's String(unknown) over the field —
|
|
9544
|
-
// the JS-exact
|
|
9652
|
+
// the JS-exact dyn walker, never a checked cast.
|
|
9545
9653
|
left: { kind: "toString", operand: read, type: STRING, loc },
|
|
9546
9654
|
right: rhs,
|
|
9547
9655
|
type: STRING,
|