@scriptc/compiler 0.0.8 → 0.0.10
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 +244 -17
- 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 +52 -12
- 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/dyn.d.ts +1 -0
- package/dist/backend/llvm/dyn.js +83 -1
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +357 -20
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +73 -14
- package/dist/diagnostics/diagnostic.js +112 -31
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/render.js +8 -0
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/frontend/lowering/lib-boundary.js +11 -1
- package/dist/frontend/lowering/lib-boundary.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 +24 -0
- package/dist/frontend/lowering/lower-builtins.js +256 -3
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +71 -11
- package/dist/frontend/lowering/lower-calls.js +942 -18
- 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 +21 -2
- package/dist/frontend/lowering/lower-containers.js +386 -53
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-dgram.js +92 -17
- package/dist/frontend/lowering/lower-dgram.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 +231 -17
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +33 -2
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.d.ts +25 -11
- package/dist/frontend/lowering/lower-namespaces.js +50 -15
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-server.js +211 -4
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +1 -5
- package/dist/frontend/lowering/lower-stmts.js +660 -19
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +92 -5
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +102 -15
- package/dist/frontend/lowering/lowerer.js +378 -30
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +20 -3
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.d.ts +7 -2
- package/dist/frontend/npm-static-rewrite.js +566 -138
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static.js +11 -2
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/program.js +5 -5
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/shared.d.ts +6 -1
- package/dist/frontend/shared.js +46 -1
- package/dist/frontend/shared.js.map +1 -1
- package/dist/frontend/types.d.ts +13 -0
- package/dist/frontend/types.js +88 -9
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.js +169 -32
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +230 -12
- package/dist/ir/nodes.js +85 -6
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +109 -9
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/fence-eval.d.ts +77 -0
- package/dist/library/fence-eval.js +385 -0
- package/dist/library/fence-eval.js.map +1 -0
- package/dist/library/profile.d.ts +11 -1
- package/dist/library/profile.js +102 -18
- package/dist/library/profile.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +10 -3
|
@@ -8,12 +8,12 @@ import { BOOL, BYTES_U8, CAUGHT, DYN, F64, JSVAL, STRING, UNDEFINED_T, VOID, arr
|
|
|
8
8
|
import { PoisonError, boundIdentifiersOf, dynFallbackType, dynUndefinedExpr, importCallHandleType, neverTaintedJsType, stmtUsesIsland, uncheckedOverloadHandleCall } from "./lowerer.js";
|
|
9
9
|
import { enforceLibBoundary } from "./lib-boundary.js";
|
|
10
10
|
import { cjsExportAssignmentOf, cjsExportDiscardReason, cjsExportTargetLiteral, isCjsJsFile, isJsSourceFile, locOf, requireSpecOf } from "../program.js";
|
|
11
|
-
import { COMPOUND_ASSIGN_OPS, UNSUPPORTED_STMT, isStdlibMember, sideEffectFreeOptionValue, stdlibGlobalAliasDecl, stdlibGlobalNameOf } from "./surfaces.js";
|
|
11
|
+
import { COMPOUND_ASSIGN_OPS, STR_METHODS, UNSUPPORTED_STMT, isStdlibMember, sideEffectFreeOptionValue, stdlibGlobalAliasDecl, stdlibGlobalNameOf } from "./surfaces.js";
|
|
12
12
|
import { isProvenanceSourceFile } from "../provenance-registry.js";
|
|
13
|
-
import { lowerImportEquals, nsWritableTarget } from "./lower-namespaces.js";
|
|
13
|
+
import { ambientUndefVarRootOf, lowerImportEquals, nsUndefRead, nsWritableTarget } from "./lower-namespaces.js";
|
|
14
14
|
import { expandoWritableTarget, lowerExpandoAssignStmt } from "./lower-expando.js";
|
|
15
|
-
import { lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, objectIterOverIndexShape } from "./lower-containers.js";
|
|
16
|
-
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, implicitLocalFnInfoOf, implicitLocalFnNodeOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
15
|
+
import { lowerForOfArrayIter, lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, objectIterOverIndexShape, strCharsCall } from "./lower-containers.js";
|
|
16
|
+
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, nullishExprUnitOf, nullishGenericBindingUnitOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
17
17
|
import { isMixinFnBinding, mixinResultBindingClassOf } from "./lower-mixins.js";
|
|
18
18
|
import { genericIfaceBindingKeepsClass } from "./lower-classes.js";
|
|
19
19
|
import { lowerStreamUnderscoreAssign, streamClassAliasDecl, streamSidesOf } from "./lower-stream.js";
|
|
@@ -1002,6 +1002,19 @@ export function lowerDestructuringDecl(L, decl, isLet) {
|
|
|
1002
1002
|
// tsc already rejects this (TS1182); defensive.
|
|
1003
1003
|
L.unsupported("SC1031", decl);
|
|
1004
1004
|
}
|
|
1005
|
+
// A STDLIB-GLOBAL source in a JavaScript file (`const { subtle } =
|
|
1006
|
+
// globalThis.crypto`, `const { Console } = console` — the suite's
|
|
1007
|
+
// webcrypto/console prologues): each element binds a member IDENTITY
|
|
1008
|
+
// TOKEN, the identifier chokepoint's rule one member deep — see
|
|
1009
|
+
// stdlibGlobalTokenDestructure. Checked before the initializer
|
|
1010
|
+
// lowers: the whole-global spelling would answer the global's own
|
|
1011
|
+
// token (a string), and the pattern would meet the string fences
|
|
1012
|
+
// below blaming the token's carrier type instead of the global.
|
|
1013
|
+
{
|
|
1014
|
+
const tokenBound = stdlibGlobalTokenDestructure(L, decl, isLet);
|
|
1015
|
+
if (tokenBound !== null)
|
|
1016
|
+
return tokenBound;
|
|
1017
|
+
}
|
|
1005
1018
|
let init = L.lowerExpr(decl.initializer);
|
|
1006
1019
|
// A TS `any`-origin source whose lowered value is NOT a destructurable
|
|
1007
1020
|
// shape (`var { x } = <any>0` — the cast erases to the f64; a dyn
|
|
@@ -1016,6 +1029,20 @@ export function lowerDestructuringDecl(L, decl, isLet) {
|
|
|
1016
1029
|
init.type.kind !== "record" && init.type.kind !== "array" && init.type.kind !== "jsval") {
|
|
1017
1030
|
L.anyOpFence("destructuring", decl);
|
|
1018
1031
|
}
|
|
1032
|
+
// An IR-string source the CHECKER does not type as a string is a
|
|
1033
|
+
// builtin identity token that leaked through a JS local (`const c =
|
|
1034
|
+
// globalThis.crypto; const { x } = c` — the local adopted the token's
|
|
1035
|
+
// carrier type): the value is a token, not a string — the string
|
|
1036
|
+
// lowerings below would read the CARRIER (its length, its code
|
|
1037
|
+
// points), numbers and characters Node never sees. The fence names
|
|
1038
|
+
// the checker's own type instead of the carrier.
|
|
1039
|
+
if (init.type.kind === "string" &&
|
|
1040
|
+
isJsSourceFile(decl.getSourceFile()) &&
|
|
1041
|
+
!checkerStringSource(L, decl.initializer)) {
|
|
1042
|
+
L.unsupported("SC1031", decl.name, ts.isArrayBindingPattern(decl.name)
|
|
1043
|
+
? `array destructuring of non-array values (the source is '${L.checker.typeToString(L.typeOf(decl.initializer))}'-typed)`
|
|
1044
|
+
: `object destructuring of non-record values (the source is '${L.checker.typeToString(L.typeOf(decl.initializer))}'-typed)`);
|
|
1045
|
+
}
|
|
1019
1046
|
// PRIMITIVE and unit sources (`let { toString } = 1`, `const [c] =
|
|
1020
1047
|
// "xy"`, `var [] = null`): JS destructures any value by reading
|
|
1021
1048
|
// through its wrapper object — prototype members included — or throws
|
|
@@ -1023,9 +1050,18 @@ export function lowerDestructuringDecl(L, decl, isLet) {
|
|
|
1023
1050
|
// pattern has an engine form, --dynamic marshals the value in and
|
|
1024
1051
|
// runs the REAL pattern (lowerJsvalBindingPattern); a static build
|
|
1025
1052
|
// reports the dynamic-family choice instead of the dead-end type
|
|
1026
|
-
// recitation.
|
|
1053
|
+
// recitation. STRING sources whose pattern the static path claims
|
|
1054
|
+
// whole (staticStringPattern — code-point positions, `length`
|
|
1055
|
+
// bindings) skip the gate in STATIC builds: lowerBindingPattern's
|
|
1056
|
+
// string branches are exact there, and the diagnostic would report a
|
|
1057
|
+
// dynamic-engine dependency the program does not have. --dynamic
|
|
1058
|
+
// keeps the engine route for every string pattern — the engine binds
|
|
1059
|
+
// undefined past the last code point where the static chars array
|
|
1060
|
+
// inherits the array divergence's trap.
|
|
1027
1061
|
if ((init.type.kind === "f64" || init.type.kind === "bool" || init.type.kind === "string" || isUnitType(init.type)) &&
|
|
1028
1062
|
!isJsSourceFile(decl.getSourceFile()) &&
|
|
1063
|
+
!(!L.dynamic && init.type.kind === "string" &&
|
|
1064
|
+
staticStringPattern(L, decl.name)) &&
|
|
1029
1065
|
enginePatternSpec(L, decl.name) !== null) {
|
|
1030
1066
|
if (!L.dynamic) {
|
|
1031
1067
|
L.pushDiag(requiresDynamicDiag(`destructuring '${L.fmt(init.type)}' values (the engine reads through JS's wrapper coercion)`, locOf(decl)));
|
|
@@ -1069,12 +1105,24 @@ export function lowerDestructuringDecl(L, decl, isLet) {
|
|
|
1069
1105
|
}
|
|
1070
1106
|
const tmp = L.declareHiddenLocal("%destr", init.type);
|
|
1071
1107
|
out.push({ kind: "varDecl", localId: tmp.id, init, loc });
|
|
1072
|
-
|
|
1108
|
+
// V8's destructuring TypeError spells STATIC sources: an identifier
|
|
1109
|
+
// source reads "<name> is not iterable", an identifier-callee call
|
|
1110
|
+
// "<name> is not a function or its return value is not iterable";
|
|
1111
|
+
// everything else (property reads, method calls, parameters) gets the
|
|
1112
|
+
// runtime kind wording — exactly V8's CallPrinter behavior. Only the
|
|
1113
|
+
// DYN pattern arm consumes this.
|
|
1114
|
+
const src = decl.initializer;
|
|
1115
|
+
const dynSpell = ts.isIdentifier(src)
|
|
1116
|
+
? `${src.text} is not iterable`
|
|
1117
|
+
: ts.isCallExpression(src) && ts.isIdentifier(src.expression)
|
|
1118
|
+
? `${src.expression.text} is not a function or its return value is not iterable`
|
|
1119
|
+
: undefined;
|
|
1120
|
+
L.lowerBindingPattern(decl.name, () => ({ kind: "varRef", localId: tmp.id, type: init.type, loc }), init.type, isLet, out, dynSpell);
|
|
1073
1121
|
return out;
|
|
1074
1122
|
}
|
|
1075
1123
|
/** One pattern level: emits a declaration (or global assignment) per
|
|
1076
1124
|
* bound name, recursing through hidden temps for nested patterns. */
|
|
1077
|
-
export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out) {
|
|
1125
|
+
export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out, dynSpell) {
|
|
1078
1126
|
if (ts.isArrayBindingPattern(pattern)) {
|
|
1079
1127
|
// Tuple sources: each position is a field read of the tuple's record
|
|
1080
1128
|
// shape — the positional twin of object destructuring below.
|
|
@@ -1173,6 +1221,90 @@ export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out) {
|
|
|
1173
1221
|
return;
|
|
1174
1222
|
}
|
|
1175
1223
|
}
|
|
1224
|
+
// STRING sources (`const [a, b] = str`): array destructuring walks
|
|
1225
|
+
// the STRING ITERATOR — code points, astral characters whole —
|
|
1226
|
+
// which is Array.from(string)'s own machinery (%str.chars), so the
|
|
1227
|
+
// source splits once into a hidden chars array and the pattern
|
|
1228
|
+
// lowers as an array pattern over string[]. Positions, holes,
|
|
1229
|
+
// defaults (with the bounds test), rest (the remaining code points
|
|
1230
|
+
// pack fresh — JS's surplus packing builds a new array too), and
|
|
1231
|
+
// nested patterns all ride the array machinery below — which also
|
|
1232
|
+
// means a position past the LAST code point inherits the array
|
|
1233
|
+
// divergence (the read traps where JS binds undefined; a DEFAULTED
|
|
1234
|
+
// position carries its bounds test and fires exactly there).
|
|
1235
|
+
// Callers vetted the source as a checker-typed string: builtin
|
|
1236
|
+
// identity tokens (IR strings the checker types otherwise) fence
|
|
1237
|
+
// before reaching here.
|
|
1238
|
+
if (srcType.kind === "string") {
|
|
1239
|
+
const loc = locOf(pattern);
|
|
1240
|
+
const charsT = arrayOf(STRING);
|
|
1241
|
+
const chars = L.declareHiddenLocal("%dchars", charsT);
|
|
1242
|
+
out.push({ kind: "varDecl", localId: chars.id, init: strCharsCall(L, srcRef(), loc), loc });
|
|
1243
|
+
L.lowerBindingPattern(pattern, () => ({ kind: "varRef", localId: chars.id, type: charsT, loc }), charsT, isLet, out);
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
// DYN sources (`const [a, b] = d` over a DOM value, `([_, code]) =>`
|
|
1247
|
+
// params destructuring dyn callback arguments — the suite harness's
|
|
1248
|
+
// _expectWarning shapes): the source packs ONCE through the spread
|
|
1249
|
+
// walk (dyn.iterPack — arrays element-by-element, strings by code
|
|
1250
|
+
// point, bytes by byte; every other kind throws V8's destructuring
|
|
1251
|
+
// TypeError, the compile-time spelling when the source has one),
|
|
1252
|
+
// then positions bind the pack's index keys as dyn values — reads
|
|
1253
|
+
// past the end bind undefined, exactly JS. Defaults fire on the DOM
|
|
1254
|
+
// undefined (JS's rule); rest elements keep the fence (surplus
|
|
1255
|
+
// packing over the DOM needs a slice the IR does not spell yet).
|
|
1256
|
+
if (srcType.kind === "dyn") {
|
|
1257
|
+
const loc = locOf(pattern);
|
|
1258
|
+
const pack = L.declareHiddenLocal("%dpack", DYN);
|
|
1259
|
+
out.push({
|
|
1260
|
+
kind: "varDecl",
|
|
1261
|
+
localId: pack.id,
|
|
1262
|
+
init: {
|
|
1263
|
+
kind: "libCall",
|
|
1264
|
+
fn: "dyn.iterPack",
|
|
1265
|
+
args: [srcRef(), { kind: "strLit", value: dynSpell ?? "", type: STRING, loc }],
|
|
1266
|
+
type: DYN,
|
|
1267
|
+
loc,
|
|
1268
|
+
},
|
|
1269
|
+
loc,
|
|
1270
|
+
});
|
|
1271
|
+
pattern.elements.forEach((el, i) => {
|
|
1272
|
+
if (ts.isOmittedExpression(el) || el.name === undefined)
|
|
1273
|
+
return; // hole: the position skips
|
|
1274
|
+
const elLoc = locOf(el);
|
|
1275
|
+
if (el.dotDotDotToken) {
|
|
1276
|
+
L.unsupported("SC1031", el, "rest elements over checked-dynamic sources");
|
|
1277
|
+
}
|
|
1278
|
+
let value = {
|
|
1279
|
+
kind: "dynKeyGet",
|
|
1280
|
+
key: { kind: "strLit", value: String(i), type: STRING, loc: elLoc },
|
|
1281
|
+
value: { kind: "varRef", localId: pack.id, type: DYN, loc: elLoc },
|
|
1282
|
+
type: DYN,
|
|
1283
|
+
loc: elLoc,
|
|
1284
|
+
};
|
|
1285
|
+
if (el.initializer) {
|
|
1286
|
+
// The default fires exactly on the DOM undefined (JS's rule),
|
|
1287
|
+
// its value converting into the DOM like any dyn-slot value.
|
|
1288
|
+
const rl = L.declareHiddenLocal("%delem", DYN);
|
|
1289
|
+
out.push({ kind: "varDecl", localId: rl.id, init: value, loc: elLoc });
|
|
1290
|
+
const ref = () => ({ kind: "varRef", localId: rl.id, type: DYN, loc: elLoc });
|
|
1291
|
+
const dflt = L.coerceToExpected(L.lowerExpr(el.initializer), DYN);
|
|
1292
|
+
if (dflt.type.kind !== "dyn") {
|
|
1293
|
+
L.unsupported("SC1031", el.initializer, `defaults of '${L.fmt(dflt.type)}' type over checked-dynamic sources (the value cannot convert into the DOM)`);
|
|
1294
|
+
}
|
|
1295
|
+
value = {
|
|
1296
|
+
kind: "ternary",
|
|
1297
|
+
cond: { kind: "dynTest", test: "undefined", value: ref(), type: BOOL, loc: elLoc },
|
|
1298
|
+
then: dflt,
|
|
1299
|
+
else_: ref(),
|
|
1300
|
+
type: DYN,
|
|
1301
|
+
loc: elLoc,
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
L.bindPatternTarget(el.name, value, isLet, out);
|
|
1305
|
+
});
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1176
1308
|
if (srcType.kind !== "array") {
|
|
1177
1309
|
L.unsupported("SC1031", pattern, `array destructuring of non-array values (the source is ${srcType.kind}-typed)`);
|
|
1178
1310
|
}
|
|
@@ -1359,6 +1491,44 @@ export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out) {
|
|
|
1359
1491
|
return;
|
|
1360
1492
|
}
|
|
1361
1493
|
}
|
|
1494
|
+
// A STRING source under an OBJECT pattern (`const { length } = str`):
|
|
1495
|
+
// JS reads through the wrapper object, whose one own DATA property is
|
|
1496
|
+
// `length` — the strLen intrinsic, exact, renames and (dead) defaults
|
|
1497
|
+
// included (length always exists and is never undefined, so a default
|
|
1498
|
+
// never evaluates — JS's rule). Everything else on the wrapper is a
|
|
1499
|
+
// prototype METHOD (detached, it loses its receiver) or absent (the
|
|
1500
|
+
// read is undefined, which the binding's type cannot hold) — named
|
|
1501
|
+
// fences either way; rest packs the wrapper's own INDICES (JS's
|
|
1502
|
+
// CopyDataProperties copies one property per code UNIT) and fences
|
|
1503
|
+
// naming that. Callers vetted the source as a checker-typed string —
|
|
1504
|
+
// builtin identity tokens fence before reaching here.
|
|
1505
|
+
if (srcType.kind === "string") {
|
|
1506
|
+
for (const el of pattern.elements) {
|
|
1507
|
+
if (el.name === undefined)
|
|
1508
|
+
continue;
|
|
1509
|
+
const loc = locOf(el);
|
|
1510
|
+
if (el.dotDotDotToken) {
|
|
1511
|
+
L.unsupported("SC1031", el, "rest bindings over string sources (JS packs the wrapper's per-code-unit indices — split with [...s] instead)");
|
|
1512
|
+
}
|
|
1513
|
+
const prop = el.propertyName ?? el.name;
|
|
1514
|
+
const propName = ts.isIdentifier(prop) || ts.isComputedPropertyName(prop) || ts.isStringLiteralLike(prop) || ts.isNumericLiteral(prop)
|
|
1515
|
+
? patternKeyNameOf(L, prop)
|
|
1516
|
+
: null;
|
|
1517
|
+
if (propName === null) {
|
|
1518
|
+
L.unsupported("SC1031", el, "destructuring with computed keys that do not fold to one property name");
|
|
1519
|
+
}
|
|
1520
|
+
if (propName !== "length") {
|
|
1521
|
+
L.unsupported("SC1031", el, Object.hasOwn(STR_METHODS, propName)
|
|
1522
|
+
? `destructuring the method '${propName}' of a string (a detached method loses its receiver — call it through the value)`
|
|
1523
|
+
: `destructuring the property '${propName}' strings do not carry as data ('length' is the one own data property)`);
|
|
1524
|
+
}
|
|
1525
|
+
let value = { kind: "strIntrinsic", method: "length", receiver: srcRef(), args: [], type: F64, loc };
|
|
1526
|
+
if (el.initializer)
|
|
1527
|
+
value = applyBindingDefault(L, el, value);
|
|
1528
|
+
L.bindPatternTarget(el.name, value, isLet, out);
|
|
1529
|
+
}
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1362
1532
|
if (srcType.kind !== "record") {
|
|
1363
1533
|
L.unsupported("SC1031", pattern, srcType.kind === "object"
|
|
1364
1534
|
? "object destructuring of class instances (read the fields directly — accessors make the desugar observable)"
|
|
@@ -1485,6 +1655,189 @@ export function patternKeyNameOf(L, prop) {
|
|
|
1485
1655
|
}
|
|
1486
1656
|
return null;
|
|
1487
1657
|
}
|
|
1658
|
+
/** True when the CHECKER types this expression as a string in every arm
|
|
1659
|
+
* (unions of string-likes included). The IR carries builtin IDENTITY
|
|
1660
|
+
* TOKENS as strings too (`globalThis.crypto` taken as a value in a JS
|
|
1661
|
+
* file, or a local that adopted such a value's carrier type), and those
|
|
1662
|
+
* must never reach the string-source destructuring lowerings — the
|
|
1663
|
+
* callers fence with the checker's own type name instead. */
|
|
1664
|
+
function checkerStringSource(L, e) {
|
|
1665
|
+
const stringLike = (t) => {
|
|
1666
|
+
if ((t.flags & ts.TypeFlags.StringLike) !== 0)
|
|
1667
|
+
return true;
|
|
1668
|
+
if (t.isUnionType())
|
|
1669
|
+
return t.getTypes().every(stringLike);
|
|
1670
|
+
return false;
|
|
1671
|
+
};
|
|
1672
|
+
return stringLike(L.typeOf(e));
|
|
1673
|
+
}
|
|
1674
|
+
/** True when a pattern over a STRING source lowers statically whole —
|
|
1675
|
+
* every element a form the chars-array/wrapper-read desugars carry.
|
|
1676
|
+
* Array patterns take positions, holes, defaults, rest (a nested rest
|
|
1677
|
+
* pattern must be an ARRAY pattern — it destructures the packed
|
|
1678
|
+
* string[]), and nested patterns, whose elements are single code
|
|
1679
|
+
* points and recurse through this same test. Object patterns take
|
|
1680
|
+
* `length` bindings — shorthand, renamed, or (dead-)defaulted.
|
|
1681
|
+
* Anything else — wrapper methods, computed keys, rest over the
|
|
1682
|
+
* wrapper — keeps the engine gate (--dynamic runs the real pattern;
|
|
1683
|
+
* a static build reports the dynamic-family choice). */
|
|
1684
|
+
function staticStringPattern(L, pattern) {
|
|
1685
|
+
if (ts.isArrayBindingPattern(pattern)) {
|
|
1686
|
+
return pattern.elements.every((el) => {
|
|
1687
|
+
if (ts.isOmittedExpression(el) || el.name === undefined)
|
|
1688
|
+
return true;
|
|
1689
|
+
if (el.dotDotDotToken) {
|
|
1690
|
+
// The rest binds string[]: identifier targets and nested ARRAY
|
|
1691
|
+
// patterns ride the array machinery; an object pattern over the
|
|
1692
|
+
// packed array keeps its fence.
|
|
1693
|
+
return ts.isIdentifier(el.name) || ts.isArrayBindingPattern(el.name);
|
|
1694
|
+
}
|
|
1695
|
+
if (ts.isIdentifier(el.name))
|
|
1696
|
+
return true;
|
|
1697
|
+
return staticStringPattern(L, el.name);
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1700
|
+
return pattern.elements.every((el) => {
|
|
1701
|
+
if (el.name === undefined)
|
|
1702
|
+
return true;
|
|
1703
|
+
if (el.dotDotDotToken || !ts.isIdentifier(el.name))
|
|
1704
|
+
return false;
|
|
1705
|
+
const prop = el.propertyName ?? el.name;
|
|
1706
|
+
const propName = ts.isIdentifier(prop) || ts.isComputedPropertyName(prop) || ts.isStringLiteralLike(prop) || ts.isNumericLiteral(prop)
|
|
1707
|
+
? patternKeyNameOf(L, prop)
|
|
1708
|
+
: null;
|
|
1709
|
+
return propName === "length";
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
/** The stdlib globals whose members destructure to MEMBER identity
|
|
1713
|
+
* tokens: the OPAQUE globals — no member of theirs has a lowered
|
|
1714
|
+
* surface, so a token binding trades the eager pattern fence for the
|
|
1715
|
+
* per-site token rules and loses nothing. Globals with REAL member
|
|
1716
|
+
* surfaces (Math.max, JSON.stringify, process.env, console.log) stay
|
|
1717
|
+
* OUT: their detached members would ride the token's quiet
|
|
1718
|
+
* own-property-undefined reads where the property spelling works, so
|
|
1719
|
+
* those patterns keep an eager fence naming the global. `console` is
|
|
1720
|
+
* the one split surface: its five CALL members fence by name, while
|
|
1721
|
+
* the rest (`Console` — the suite's constructor-identity probe) have
|
|
1722
|
+
* no surface to lose and bind tokens. */
|
|
1723
|
+
const TOKEN_OPAQUE_GLOBALS = new Set(["crypto"]);
|
|
1724
|
+
const CONSOLE_CALL_MEMBERS = new Set(["log", "info", "debug", "error", "warn"]);
|
|
1725
|
+
/** `const { subtle } = globalThis.crypto`, `const { Console } = console`,
|
|
1726
|
+
* `const { crypto } = globalThis` — an object pattern over a STDLIB
|
|
1727
|
+
* GLOBAL in a JavaScript file (the suite's webcrypto/console
|
|
1728
|
+
* prologues): the identifier chokepoint's identity-token rule, one
|
|
1729
|
+
* member deep. The global taken as a value is an opaque token, and a
|
|
1730
|
+
* member taken off an OPAQUE global is the same kind of value — one
|
|
1731
|
+
* global, one member, one interned string — so plain elements bind
|
|
1732
|
+
* MEMBER tokens (`[builtin crypto.subtle]`), and identity flows agree
|
|
1733
|
+
* across destructures of the same member. A member of GLOBALTHIS that
|
|
1734
|
+
* is itself a canonical global (`const { crypto } = globalThis` —
|
|
1735
|
+
* `declare var` globals are properties of `typeof globalThis`,
|
|
1736
|
+
* stdlib-provenance-checked so user script globals stay out) binds the
|
|
1737
|
+
* global's OWN token, byte-equal to the bare and property spellings'
|
|
1738
|
+
* answers, AND registers in stdlibGlobalAliases — the destructured
|
|
1739
|
+
* twin of stdlibGlobalAliasDecl — so receiver-position uses resolve
|
|
1740
|
+
* through every surface exactly like the bare global (`const { Math }
|
|
1741
|
+
* = globalThis; Math.max(...)` is Math.max). What a token cannot do
|
|
1742
|
+
* meets the per-site rules lazily at each USE — member reads answer
|
|
1743
|
+
* the wrapper's own-property undefined, calls throw Node-shaped
|
|
1744
|
+
* is-not-a-function TypeErrors — at the use's own line, where the
|
|
1745
|
+
* pattern-level fence killed the program at the destructure.
|
|
1746
|
+
*
|
|
1747
|
+
* Everything this rule does NOT claim over a stdlib-global source
|
|
1748
|
+
* fences HERE, naming the global — never the token's carrier string
|
|
1749
|
+
* type: rest (the member set is not statically enumerable), defaults
|
|
1750
|
+
* (member presence is not statically testable), nested patterns,
|
|
1751
|
+
* computed keys, members of the SURFACED globals (detached members
|
|
1752
|
+
* lose their receiver-keyed lowerings), and `var`/TDZ-predeclared/
|
|
1753
|
+
* pre-registered bindings whose slots no token can inhabit. Null only
|
|
1754
|
+
* when the source is not a stdlib global at all, or in TypeScript
|
|
1755
|
+
* files (the identifier chokepoint fences the global there first). */
|
|
1756
|
+
function stdlibGlobalTokenDestructure(L, decl, isLet) {
|
|
1757
|
+
if (decl.initializer === undefined || !isJsSourceFile(decl.getSourceFile()))
|
|
1758
|
+
return null;
|
|
1759
|
+
const globalName = stdlibGlobalNameOf(L, decl.initializer);
|
|
1760
|
+
if (globalName === null)
|
|
1761
|
+
return null;
|
|
1762
|
+
if (!ts.isObjectBindingPattern(decl.name)) {
|
|
1763
|
+
L.unsupported("SC1031", decl.name, `array destructuring of the builtin global '${globalName}' (builtin globals are not iterable)`);
|
|
1764
|
+
}
|
|
1765
|
+
if (isVarDeclared(decl)) {
|
|
1766
|
+
L.unsupported("SC1031", decl.name, `var-declared patterns over the builtin global '${globalName}'`);
|
|
1767
|
+
}
|
|
1768
|
+
const srcT = L.typeOf(decl.initializer);
|
|
1769
|
+
const binds = [];
|
|
1770
|
+
for (const el of decl.name.elements) {
|
|
1771
|
+
if (el.name === undefined)
|
|
1772
|
+
continue;
|
|
1773
|
+
if (el.dotDotDotToken) {
|
|
1774
|
+
L.unsupported("SC1031", el, `rest bindings over the builtin global '${globalName}' (the member set is not statically enumerable)`);
|
|
1775
|
+
}
|
|
1776
|
+
if (el.initializer !== undefined) {
|
|
1777
|
+
L.unsupported("SC1031", el, `binding defaults over the builtin global '${globalName}' (member presence is not statically testable)`);
|
|
1778
|
+
}
|
|
1779
|
+
if (!ts.isIdentifier(el.name)) {
|
|
1780
|
+
L.unsupported("SC1031", el, `nested patterns over the builtin global '${globalName}'`);
|
|
1781
|
+
}
|
|
1782
|
+
const prop = el.propertyName ?? el.name;
|
|
1783
|
+
const propName = ts.isIdentifier(prop) || ts.isComputedPropertyName(prop) || ts.isStringLiteralLike(prop) || ts.isNumericLiteral(prop)
|
|
1784
|
+
? patternKeyNameOf(L, prop)
|
|
1785
|
+
: null;
|
|
1786
|
+
if (propName === null) {
|
|
1787
|
+
L.unsupported("SC1031", el, "destructuring with computed keys that do not fold to one property name");
|
|
1788
|
+
}
|
|
1789
|
+
let token;
|
|
1790
|
+
let alias = null;
|
|
1791
|
+
if (globalName === "globalThis") {
|
|
1792
|
+
// The member must BE a stdlib global (user script vars are
|
|
1793
|
+
// properties of `typeof globalThis` too — their values are real
|
|
1794
|
+
// and this rule has none to give).
|
|
1795
|
+
const member = L.checker.getPropertyOfType(srcT, propName);
|
|
1796
|
+
if (member === undefined || !L.isStdlibSymbol(member)) {
|
|
1797
|
+
L.unsupported("SC1031", el, `destructuring the non-builtin member '${propName}' of 'globalThis'`);
|
|
1798
|
+
}
|
|
1799
|
+
const canonical = member.name === "global" ? "globalThis" : member.name;
|
|
1800
|
+
token = `[builtin ${canonical}]`;
|
|
1801
|
+
alias = canonical;
|
|
1802
|
+
}
|
|
1803
|
+
else if (globalName === "console" && !CONSOLE_CALL_MEMBERS.has(propName)) {
|
|
1804
|
+
token = `[builtin console.${propName}]`;
|
|
1805
|
+
}
|
|
1806
|
+
else if (TOKEN_OPAQUE_GLOBALS.has(globalName)) {
|
|
1807
|
+
token = `[builtin ${globalName}.${propName}]`;
|
|
1808
|
+
}
|
|
1809
|
+
else {
|
|
1810
|
+
L.unsupported("SC1031", el, `destructuring the member '${propName}' of the builtin global '${globalName}' (a detached member loses its receiver-keyed lowering — call it through the global)`);
|
|
1811
|
+
}
|
|
1812
|
+
const symbol = L.checker.getSymbolAtLocation(el.name);
|
|
1813
|
+
if (!symbol || L.tdzPredeclared.has(symbol)) {
|
|
1814
|
+
L.unsupported("SC1031", el, `bindings with predeclared slots over the builtin global '${globalName}'`);
|
|
1815
|
+
}
|
|
1816
|
+
const g = L.globalsBySymbol.get(symbol);
|
|
1817
|
+
if (g !== undefined && g.type.kind !== "string" && g.type.kind !== "dyn") {
|
|
1818
|
+
L.unsupported("SC1031", el, `bindings with '${L.fmt(g.type)}' storage over the builtin global '${globalName}'`);
|
|
1819
|
+
}
|
|
1820
|
+
binds.push({ name: el.name, token, alias, g });
|
|
1821
|
+
}
|
|
1822
|
+
const out = [];
|
|
1823
|
+
for (const b of binds) {
|
|
1824
|
+
const loc = locOf(b.name);
|
|
1825
|
+
const token = { kind: "strLit", value: b.token, type: STRING, loc };
|
|
1826
|
+
if (b.alias !== null) {
|
|
1827
|
+
const symbol = L.checker.getSymbolAtLocation(b.name);
|
|
1828
|
+
if (symbol)
|
|
1829
|
+
L.stdlibGlobalAliases.set(symbol, b.alias);
|
|
1830
|
+
}
|
|
1831
|
+
if (b.g !== undefined) {
|
|
1832
|
+
out.push({ kind: "assign", localId: b.g.id, value: L.coerceInto(b.name, token, b.g.type), loc });
|
|
1833
|
+
}
|
|
1834
|
+
else {
|
|
1835
|
+
const local = L.declareLocal(b.name, b.name.text, STRING, isLet);
|
|
1836
|
+
out.push({ kind: "varDecl", localId: local.id, init: token, loc });
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
return out;
|
|
1840
|
+
}
|
|
1488
1841
|
/** `x = dflt` on a pattern position whose source value always EXISTS
|
|
1489
1842
|
* (tuple positions, record fields): JS evaluates the default exactly
|
|
1490
1843
|
* when the value is undefined — the undefined-arm ternary, lazy, so
|
|
@@ -2097,6 +2450,13 @@ export function lowerJsvalBindingPattern(L, pattern, srcRef, isLet, out) {
|
|
|
2097
2450
|
* lowerVarDecl (module-global assignment for pre-registered file-scope
|
|
2098
2451
|
* names, declareLocal otherwise), or a nested pattern through its own
|
|
2099
2452
|
* hidden temp. */
|
|
2453
|
+
/** An island-world ('any'-flavored) checker spelling: bare jsval or an
|
|
2454
|
+
* array of jsvals (`(string | object)[]` absorbed by its jsval arm) —
|
|
2455
|
+
* the shapes the runtime-world local rule keeps DYN when the value
|
|
2456
|
+
* lowered checked-dynamic. */
|
|
2457
|
+
function jsvalFlavoredType(t) {
|
|
2458
|
+
return t.kind === "jsval" || (t.kind === "array" && t.elem.kind === "jsval");
|
|
2459
|
+
}
|
|
2100
2460
|
export function bindPatternTarget(L, name, value, isLet, out) {
|
|
2101
2461
|
const loc = value.loc;
|
|
2102
2462
|
if (ts.isIdentifier(name)) {
|
|
@@ -2117,7 +2477,21 @@ export function bindPatternTarget(L, name, value, isLet, out) {
|
|
|
2117
2477
|
out.push({ kind: "assign", localId: hoisted.id, value: L.coerceInto(name, value, hoisted.type), loc });
|
|
2118
2478
|
return;
|
|
2119
2479
|
}
|
|
2120
|
-
|
|
2480
|
+
// The runtime-world local rule, dyn side (383(d)'s dispatch stance):
|
|
2481
|
+
// a pattern binding whose VALUE lowered checked-dynamic stays dyn
|
|
2482
|
+
// where the checker spells an island-world type (`function f({
|
|
2483
|
+
// plugins = [] } = {})` over a JSDoc '(string | object)[]=' member —
|
|
2484
|
+
// the read is a DOM keyed read whose members may be wrapped island
|
|
2485
|
+
// values) or a type with no mapping at all (the varDecl rule's
|
|
2486
|
+
// unmappable-declared-type stance, `string | object` bindings
|
|
2487
|
+
// included). Marshaling here would fence or deep-copy; the value's
|
|
2488
|
+
// world is the honest dispatch.
|
|
2489
|
+
const dynStays = value.type.kind === "dyn" &&
|
|
2490
|
+
(() => {
|
|
2491
|
+
const mapped = L.mapTypeOf(L.typeOf(name));
|
|
2492
|
+
return mapped === null || (L.dynamic && jsvalFlavoredType(mapped));
|
|
2493
|
+
})();
|
|
2494
|
+
const type = dynStays ? DYN : L.irTypeOf(name);
|
|
2121
2495
|
if (type.kind === "void")
|
|
2122
2496
|
L.badType(name, L.typeOf(name));
|
|
2123
2497
|
const coerced = L.coerceInto(name, value, type);
|
|
@@ -2184,8 +2558,48 @@ export function lowerVarDecl(L, decl, isLet) {
|
|
|
2184
2558
|
// put its fences on the build.
|
|
2185
2559
|
if (provenanceElidedConstDecl(L, decl))
|
|
2186
2560
|
return null;
|
|
2561
|
+
// A TRAP declaration — the initializer's chain roots at an
|
|
2562
|
+
// ambient-undefined name (`declare function factory...; const make =
|
|
2563
|
+
// factory<T>()`): Node throws the root's ReferenceError while
|
|
2564
|
+
// evaluating the initializer, module init unwinds THERE, and nothing
|
|
2565
|
+
// after — including every reference to this binding — ever runs. The
|
|
2566
|
+
// statement lowers to exactly that throw; the binding registers as a
|
|
2567
|
+
// trap (references lower to the same never-reached shape), and no
|
|
2568
|
+
// storage or type mapping is needed for a value that never exists.
|
|
2569
|
+
if (decl.initializer !== undefined) {
|
|
2570
|
+
const root = ambientUndefVarRootOf(L, decl.initializer);
|
|
2571
|
+
if (root !== null) {
|
|
2572
|
+
for (const nameNode of boundIdentifiersOf(decl.name)) {
|
|
2573
|
+
const sym = L.checker.getSymbolAtLocation(nameNode);
|
|
2574
|
+
if (sym)
|
|
2575
|
+
L.trapBindings.add(sym);
|
|
2576
|
+
}
|
|
2577
|
+
return {
|
|
2578
|
+
kind: "exprStmt",
|
|
2579
|
+
expr: nsUndefRead(L, root.text, decl.initializer, F64),
|
|
2580
|
+
loc: locOf(decl),
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2187
2584
|
if (!ts.isIdentifier(decl.name))
|
|
2188
2585
|
L.unsupported("SC1031", decl.name);
|
|
2586
|
+
// A NULLISH generic binding (`const i: I<A & B> = null as any` — the
|
|
2587
|
+
// declared type has no mapping, the value is provably null/undefined
|
|
2588
|
+
// forever): no storage exists; reads know the value (member reads and
|
|
2589
|
+
// method calls lower to Node's exact TypeError, nullish-to-nullish
|
|
2590
|
+
// flows to nothing), so the declaration emits nothing.
|
|
2591
|
+
if (nullishGenericBindingUnitOf(L, L.checker.getSymbolAtLocation(decl.name) ?? null) !== null) {
|
|
2592
|
+
return null;
|
|
2593
|
+
}
|
|
2594
|
+
// A DEAD unmappable binding (`var xs2: typeof Array;`, the write-only
|
|
2595
|
+
// `var f2: { <T, U>(x: T, y: U): T }`): never read anywhere in the
|
|
2596
|
+
// program, its initializer (if any) a side-effect-free value — Node
|
|
2597
|
+
// materializes the value and drops it, zero observable effect — so
|
|
2598
|
+
// the declaration emits nothing instead of fencing on a type the
|
|
2599
|
+
// program never consumes.
|
|
2600
|
+
if (deadUnmappableBinding(L, L.checker.getSymbolAtLocation(decl.name) ?? null, decl)) {
|
|
2601
|
+
return null;
|
|
2602
|
+
}
|
|
2189
2603
|
// An initializer-LESS declaration named `module` or `exports` at the
|
|
2190
2604
|
// top level of an import/export-free TS file shadows the CommonJS
|
|
2191
2605
|
// wrapper binding: Node hosts such a file as CJS, where
|
|
@@ -2407,6 +2821,16 @@ export function lowerVarDecl(L, decl, isLet) {
|
|
|
2407
2821
|
// uncheckedOverloadHandleCall.
|
|
2408
2822
|
(init.type.kind === "jsval" && uncheckedOverloadHandleCall(L, decl.initializer) ? JSVAL : null))
|
|
2409
2823
|
: null) ??
|
|
2824
|
+
// The runtime-world local rule, dyn side (383(d)'s dispatch stance —
|
|
2825
|
+
// the island-HANDLE local rule's mirror): a binding whose
|
|
2826
|
+
// INITIALIZER lowered checked-dynamic stays dyn even where the
|
|
2827
|
+
// checker spells 'any' (`const parsers = options.parsers ?? {}` over
|
|
2828
|
+
// an 'object'-typed bag — the read is a DOM keyed read). Marshaling
|
|
2829
|
+
// into the engine here would deep-copy data and sever aliasing; the
|
|
2830
|
+
// value's world is the honest dispatch, and every use site already
|
|
2831
|
+
// handles dyn (validated exits, routed engine ops for wrapped
|
|
2832
|
+
// island members).
|
|
2833
|
+
(L.dynamic && init.type.kind === "dyn" && jsvalFlavoredType(L.mapTypeOf(L.typeOf(decl.name)) ?? DYN) ? DYN : null) ??
|
|
2410
2834
|
(bindingTainted ? null : L.mapTypeOf(L.typeOf(decl.name))) ??
|
|
2411
2835
|
(init.type.kind === "dyn" ? DYN : null);
|
|
2412
2836
|
// A JS `let x = {}`: TS's empty-object-literal type admits ANY later
|
|
@@ -2712,6 +3136,7 @@ export function lowerUnionSwitch(L, stmt, disc) {
|
|
|
2712
3136
|
kind: "unionEq",
|
|
2713
3137
|
unionId: unionType.unionId,
|
|
2714
3138
|
negated: false,
|
|
3139
|
+
sameValue: false,
|
|
2715
3140
|
left: disc,
|
|
2716
3141
|
right: L.coerceInto(clause.expression, test, unionType),
|
|
2717
3142
|
type: BOOL,
|
|
@@ -3157,6 +3582,37 @@ export function lowerExprStatement(L, expr) {
|
|
|
3157
3582
|
const stmtNode = expr.parent;
|
|
3158
3583
|
while (ts.isParenthesizedExpression(expr))
|
|
3159
3584
|
expr = expr.expression;
|
|
3585
|
+
// Assignment statements over the no-storage binding families:
|
|
3586
|
+
// - `f1 = f2` where the RHS roots at an ambient-undefined name:
|
|
3587
|
+
// Node evaluates the RHS first and dies on the root's
|
|
3588
|
+
// ReferenceError before any binding is touched — the statement IS
|
|
3589
|
+
// that throw;
|
|
3590
|
+
// - `a = b` between NULLISH generic bindings: the value is known
|
|
3591
|
+
// and the target has no storage — nothing happens;
|
|
3592
|
+
// - a write to a DEAD binding (registration proved every write's
|
|
3593
|
+
// RHS side-effect-free): Node builds the value and drops it —
|
|
3594
|
+
// nothing happens.
|
|
3595
|
+
if (ts.isBinaryExpression(expr) &&
|
|
3596
|
+
expr.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
3597
|
+
ts.isIdentifier(expr.left)) {
|
|
3598
|
+
const rhsRoot = ambientUndefVarRootOf(L, expr.right);
|
|
3599
|
+
if (rhsRoot !== null) {
|
|
3600
|
+
return {
|
|
3601
|
+
kind: "exprStmt",
|
|
3602
|
+
expr: nsUndefRead(L, rhsRoot.text, expr.right, F64),
|
|
3603
|
+
loc: locOf(expr),
|
|
3604
|
+
};
|
|
3605
|
+
}
|
|
3606
|
+
const targetSym = L.resolveValueSymbol(expr.left);
|
|
3607
|
+
if (targetSym !== null) {
|
|
3608
|
+
if (nullishGenericBindingUnitOf(L, targetSym) !== null && nullishExprUnitOf(L, expr.right) !== null) {
|
|
3609
|
+
return { kind: "block", body: [], loc: locOf(expr) };
|
|
3610
|
+
}
|
|
3611
|
+
if (L.deadBindings.has(targetSym)) {
|
|
3612
|
+
return { kind: "block", body: [], loc: locOf(expr) };
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3160
3616
|
// A bare module-namespace binding in STATEMENT position (`ns;` for
|
|
3161
3617
|
// `import * as ns from ...` — the corpus's canonical "the import
|
|
3162
3618
|
// linked" assertion): Node evaluates an initialized binding (namespace
|
|
@@ -3314,8 +3770,25 @@ export function lowerExprStatement(L, expr) {
|
|
|
3314
3770
|
// o.x = v on an island receiver: engine property write; the
|
|
3315
3771
|
// value marshals in (a static RHS crosses by value/copy).
|
|
3316
3772
|
const obj = L.lowerExpr(expr.left.expression);
|
|
3317
|
-
const value = L.jsvalIn(L.lowerExpr(expr.right), expr.right);
|
|
3318
3773
|
const loc = locOf(expr);
|
|
3774
|
+
// Dispatch follows the RUNTIME world (383(d)): a checker-'any'
|
|
3775
|
+
// receiver whose value LOWERED checked-dynamic (`bag.nested.x
|
|
3776
|
+
// = v` behind an 'object'-typed bag) takes the DOM keyed
|
|
3777
|
+
// write — the routed JSVAL arm lands it on the real engine
|
|
3778
|
+
// object.
|
|
3779
|
+
if (obj.type.kind === "dyn") {
|
|
3780
|
+
const key = { kind: "strLit", value: expr.left.name.text, type: STRING, loc: locOf(expr.left.name) };
|
|
3781
|
+
const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
|
|
3782
|
+
if (value.type.kind !== "dyn") {
|
|
3783
|
+
L.unsupported("SC1100", expr.right, `assigning '${L.fmt(value.type)}' values through 'unknown' receivers`);
|
|
3784
|
+
}
|
|
3785
|
+
return {
|
|
3786
|
+
kind: "exprStmt",
|
|
3787
|
+
expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc },
|
|
3788
|
+
loc,
|
|
3789
|
+
};
|
|
3790
|
+
}
|
|
3791
|
+
const value = L.jsvalIn(L.lowerExpr(expr.right), expr.right);
|
|
3319
3792
|
return {
|
|
3320
3793
|
kind: "exprStmt",
|
|
3321
3794
|
expr: { kind: "jsOp", op: "setProp", name: expr.left.name.text, args: [obj, value], type: VOID, loc },
|
|
@@ -3415,6 +3888,31 @@ export function lowerExprStatement(L, expr) {
|
|
|
3415
3888
|
};
|
|
3416
3889
|
}
|
|
3417
3890
|
}
|
|
3891
|
+
// `events.defaultMaxListeners = v` in STATEMENT position — the
|
|
3892
|
+
// module-property write Node validates (the lowerBinary
|
|
3893
|
+
// expression twin): the value crosses into the DOM, the runtime
|
|
3894
|
+
// ladder throws Node's exact errors, valid numbers apply.
|
|
3895
|
+
if (!expr.left.questionDotToken && expr.left.name.text === "defaultMaxListeners") {
|
|
3896
|
+
const bi = L.builtinMemberOf(expr.left);
|
|
3897
|
+
if (bi && bi.module === "events" && bi.member === "defaultMaxListeners") {
|
|
3898
|
+
const loc = locOf(expr);
|
|
3899
|
+
const rhs = L.lowerExpr(expr.right);
|
|
3900
|
+
if (rhs.type.kind === "dyn" || rhs.kind === "unitLit" || L.dynConvertible(rhs.type)) {
|
|
3901
|
+
const dynVal = rhs.type.kind === "dyn" ? rhs : { kind: "dynFrom", value: rhs, type: DYN, loc };
|
|
3902
|
+
return {
|
|
3903
|
+
kind: "exprStmt",
|
|
3904
|
+
expr: {
|
|
3905
|
+
kind: "libCall",
|
|
3906
|
+
fn: "emitter.setDefaultMaxChk",
|
|
3907
|
+
args: [dynVal, { kind: "strLit", value: "defaultMaxListeners", type: STRING, loc }],
|
|
3908
|
+
type: VOID,
|
|
3909
|
+
loc,
|
|
3910
|
+
},
|
|
3911
|
+
loc,
|
|
3912
|
+
};
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3418
3916
|
// `r._read = fn` / `w._write = fn` (and the other underscore
|
|
3419
3917
|
// methods) on a runtime-stream-rooted receiver: Node's
|
|
3420
3918
|
// own-property shadow of the prototype method — the runtime
|
|
@@ -3937,6 +4435,31 @@ export function lowerDestructuringAssignParts(L, target, rhs, loc) {
|
|
|
3937
4435
|
* source-spelling TypeError machinery cannot apply), `blame` the node
|
|
3938
4436
|
* fences point at. */
|
|
3939
4437
|
function destructuringAssignInto(L, target, init, rhs, blame, loc) {
|
|
4438
|
+
// IR-string sources in JS files split two ways the carrier type
|
|
4439
|
+
// cannot: a STDLIB GLOBAL (`({ subtle } = globalThis.crypto)`) — the
|
|
4440
|
+
// declaration path binds member identity tokens into FRESH bindings,
|
|
4441
|
+
// but assignment targets carry their own checker-typed slots no token
|
|
4442
|
+
// can honestly inhabit — and a LEAKED token (a local that adopted a
|
|
4443
|
+
// global's carrier type), where the string lowerings below would read
|
|
4444
|
+
// the carrier itself. Both keep fences naming the truth; empty
|
|
4445
|
+
// patterns stay pure (RequireObjectCoercible/GetIterator hold for
|
|
4446
|
+
// globals and strings alike).
|
|
4447
|
+
{
|
|
4448
|
+
const patternEmpty = ts.isObjectLiteralExpression(target)
|
|
4449
|
+
? target.properties.length === 0
|
|
4450
|
+
: target.elements.length === 0;
|
|
4451
|
+
if (!patternEmpty && rhs !== null && isJsSourceFile(rhs.getSourceFile())) {
|
|
4452
|
+
const globalName = stdlibGlobalNameOf(L, rhs);
|
|
4453
|
+
if (globalName !== null) {
|
|
4454
|
+
L.unsupported("SC1031", blame, `destructuring assignment from the builtin global '${globalName}' (bind the members with a const destructuring declaration instead)`);
|
|
4455
|
+
}
|
|
4456
|
+
if (init.type.kind === "string" && !checkerStringSource(L, rhs)) {
|
|
4457
|
+
L.unsupported("SC1031", blame, ts.isArrayLiteralExpression(target)
|
|
4458
|
+
? `array destructuring assignment from non-array values (the source is '${L.checker.typeToString(L.typeOf(rhs))}'-typed)`
|
|
4459
|
+
: `destructuring assignment from non-record values (the source is '${L.checker.typeToString(L.typeOf(rhs))}'-typed)`);
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
3940
4463
|
const tmp = L.declareHiddenLocal("%destr", init.type);
|
|
3941
4464
|
const out = [{ kind: "varDecl", localId: tmp.id, init, loc }];
|
|
3942
4465
|
const tmpRef = () => ({ kind: "varRef", localId: tmp.id, type: init.type, loc });
|
|
@@ -4140,6 +4663,77 @@ function destructuringAssignInto(L, target, init, rhs, blame, loc) {
|
|
|
4140
4663
|
return { stmts: out, value };
|
|
4141
4664
|
}
|
|
4142
4665
|
}
|
|
4666
|
+
// A STRING source under an object ASSIGNMENT pattern (`({ length: n }
|
|
4667
|
+
// = s)`): the declaration path's wrapper rule in assignment position —
|
|
4668
|
+
// `length` is the wrapper's one own data property (the strLen
|
|
4669
|
+
// intrinsic; a default is dead code — length always exists and is
|
|
4670
|
+
// never undefined, so JS never evaluates it either); identifier,
|
|
4671
|
+
// property, and element targets ride the shared target plumbing.
|
|
4672
|
+
// Methods, unknown names, computed keys, spreads, and accessor
|
|
4673
|
+
// properties keep the declaration path's named fences. Callers vetted
|
|
4674
|
+
// the source as a checker-typed string.
|
|
4675
|
+
if (init.type.kind === "string") {
|
|
4676
|
+
for (const prop of target.properties) {
|
|
4677
|
+
const propLoc = locOf(prop);
|
|
4678
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
4679
|
+
L.unsupported("SC1031", prop, "rest bindings over string sources (JS packs the wrapper's per-code-unit indices — split with [...s] instead)");
|
|
4680
|
+
}
|
|
4681
|
+
let fieldName;
|
|
4682
|
+
let bindTo;
|
|
4683
|
+
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
4684
|
+
fieldName = prop.name.text;
|
|
4685
|
+
bindTo = prop.name;
|
|
4686
|
+
}
|
|
4687
|
+
else if (ts.isPropertyAssignment(prop)) {
|
|
4688
|
+
const folded = patternKeyNameOf(L, prop.name);
|
|
4689
|
+
if (folded === null) {
|
|
4690
|
+
L.unsupported("SC1031", prop, "destructuring assignment with computed keys that do not fold to one property name");
|
|
4691
|
+
}
|
|
4692
|
+
fieldName = folded;
|
|
4693
|
+
bindTo = prop.initializer;
|
|
4694
|
+
// `({ length: n = 3 } = s)`: the default is dead (length always
|
|
4695
|
+
// exists) — strip it, exactly the evaluation JS skips.
|
|
4696
|
+
if (ts.isBinaryExpression(bindTo) && bindTo.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
4697
|
+
bindTo = bindTo.left;
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
else {
|
|
4701
|
+
L.unsupported("SC1031", prop, "destructuring assignment with getter/setter or method properties");
|
|
4702
|
+
}
|
|
4703
|
+
if (fieldName !== "length") {
|
|
4704
|
+
L.unsupported("SC1031", prop, Object.hasOwn(STR_METHODS, fieldName)
|
|
4705
|
+
? `destructuring the method '${fieldName}' of a string (a detached method loses its receiver — call it through the value)`
|
|
4706
|
+
: `destructuring the property '${fieldName}' strings do not carry as data ('length' is the one own data property)`);
|
|
4707
|
+
}
|
|
4708
|
+
const readOf = () => ({ kind: "strIntrinsic", method: "length", receiver: tmpRef(), args: [], type: F64, loc: propLoc });
|
|
4709
|
+
while (ts.isParenthesizedExpression(bindTo))
|
|
4710
|
+
bindTo = bindTo.expression;
|
|
4711
|
+
if (!ts.isIdentifier(bindTo)) {
|
|
4712
|
+
lowerAssignTargetInto(L, out, bindTo, prop, null, readOf);
|
|
4713
|
+
continue;
|
|
4714
|
+
}
|
|
4715
|
+
let targetBinding;
|
|
4716
|
+
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
4717
|
+
const valueSymbol = L.checker.getShorthandAssignmentValueSymbol(prop) ?? null;
|
|
4718
|
+
const local = valueSymbol ? L.resolveKey(valueSymbol, bindTo) : null;
|
|
4719
|
+
const g = valueSymbol ? L.globalsBySymbol.get(valueSymbol) : undefined;
|
|
4720
|
+
targetBinding = local ? { id: local.id, type: local.type } : g ? { id: g.id, type: g.type } : null;
|
|
4721
|
+
}
|
|
4722
|
+
else {
|
|
4723
|
+
targetBinding = L.resolveWritable(bindTo);
|
|
4724
|
+
}
|
|
4725
|
+
if (!targetBinding) {
|
|
4726
|
+
L.rejectUnresolved(bindTo, `assignment to '${bindTo.text}' (not a writable local or module global)`);
|
|
4727
|
+
}
|
|
4728
|
+
out.push({
|
|
4729
|
+
kind: "assign",
|
|
4730
|
+
localId: targetBinding.id,
|
|
4731
|
+
value: L.coerceInto(prop, readOf(), targetBinding.type),
|
|
4732
|
+
loc: propLoc,
|
|
4733
|
+
});
|
|
4734
|
+
}
|
|
4735
|
+
return { stmts: out, value };
|
|
4736
|
+
}
|
|
4143
4737
|
if (init.type.kind !== "record") {
|
|
4144
4738
|
L.unsupported("SC1031", blame, init.type.kind === "object"
|
|
4145
4739
|
? "destructuring assignment from class instances (read the fields directly — accessors make the desugar observable)"
|
|
@@ -4473,7 +5067,23 @@ function destructAssignPiece(L, prop, islandSource) {
|
|
|
4473
5067
|
* bounds test (JS reads undefined past the end), rest packs the tail
|
|
4474
5068
|
* fresh (array slices, tuple tails). */
|
|
4475
5069
|
function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
|
|
4476
|
-
|
|
5070
|
+
let srcType = tmpRef().type;
|
|
5071
|
+
// A STRING source (`[a, b] = s`): the declaration path's rule in
|
|
5072
|
+
// assignment position — the string iterator's code-point split
|
|
5073
|
+
// (%str.chars, astral characters whole) into a hidden chars array,
|
|
5074
|
+
// then the pattern proceeds as an array pattern over string[]
|
|
5075
|
+
// (defaults with the bounds test, rest packing the remaining code
|
|
5076
|
+
// points, elisions; positions past the last code point inherit the
|
|
5077
|
+
// array divergence). The EMPTY pattern skips the split — GetIterator
|
|
5078
|
+
// + close, nothing observable — through the iterable early-return
|
|
5079
|
+
// below. Callers vetted the source as a checker-typed string.
|
|
5080
|
+
if (srcType.kind === "string" && target.elements.length > 0) {
|
|
5081
|
+
const charsT = arrayOf(STRING);
|
|
5082
|
+
const chars = L.declareHiddenLocal("%dchars", charsT);
|
|
5083
|
+
out.push({ kind: "varDecl", localId: chars.id, init: strCharsCall(L, tmpRef(), loc), loc });
|
|
5084
|
+
tmpRef = () => ({ kind: "varRef", localId: chars.id, type: charsT, loc });
|
|
5085
|
+
srcType = charsT;
|
|
5086
|
+
}
|
|
4477
5087
|
// A sole leading rest whose operand is ITSELF an array pattern
|
|
4478
5088
|
// (`[...[a, b = 0]] = t`): the rest collects every element, then the
|
|
4479
5089
|
// inner pattern destructures the collection — over arrays/tuples that
|
|
@@ -4694,6 +5304,15 @@ export function lowerForOf(L, stmt) {
|
|
|
4694
5304
|
if (recv?.kind === "searchParams") {
|
|
4695
5305
|
return lowerForOfSearchParams(L, stmt, L.lowerExpr(src.expression.expression), proj);
|
|
4696
5306
|
}
|
|
5307
|
+
// ARRAY keys()/entries() projections: the live index walk yielding
|
|
5308
|
+
// the index or the [index, element] pair (lower-containers).
|
|
5309
|
+
// `values` falls through to the receiver unwrap below.
|
|
5310
|
+
if (recv?.kind === "array" && (proj === "keys" || proj === "entries")) {
|
|
5311
|
+
const container = L.lowerExpr(src.expression.expression);
|
|
5312
|
+
if (container.type.kind === "array") {
|
|
5313
|
+
return lowerForOfArrayIter(L, stmt, container, proj);
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
4697
5316
|
}
|
|
4698
5317
|
}
|
|
4699
5318
|
// `for (const m of s.matchAll(re))` — direct call or a stored-const
|
|
@@ -4857,6 +5476,13 @@ export function lowerForOf(L, stmt) {
|
|
|
4857
5476
|
if (iterable.type.kind === "jsval") {
|
|
4858
5477
|
return lowerForOfIsland(L, stmt, iterable, labels);
|
|
4859
5478
|
}
|
|
5479
|
+
// A CHECKED-DYNAMIC iterable (`unknown[]` and the collapsed
|
|
5480
|
+
// `(string | object)[]` are DOM values now, so the TYPE maps —
|
|
5481
|
+
// badType would tell a stale component story): the OPERATION is
|
|
5482
|
+
// the gap — name it, with the validated-cast escape hatch.
|
|
5483
|
+
if (iterable.type.kind === "dyn") {
|
|
5484
|
+
L.unsupported("SC1090", stmt.expression, "for-of over checked-dynamic ('unknown') values (validate into a typed array first — `u as string[]` — or index with a counted loop: length and element reads compile)");
|
|
5485
|
+
}
|
|
4860
5486
|
if (iterable.type.kind !== "array") {
|
|
4861
5487
|
L.badType(stmt.expression, L.typeOf(stmt.expression));
|
|
4862
5488
|
}
|
|
@@ -5296,11 +5922,13 @@ function lowerForOfIsland(L, stmt, iterable, labels) {
|
|
|
5296
5922
|
function lowerForOfString(L, stmt, iterable, labels) {
|
|
5297
5923
|
let exprTarget = null;
|
|
5298
5924
|
let exprTargetNode = null;
|
|
5925
|
+
let exprPattern = null;
|
|
5299
5926
|
if (!ts.isVariableDeclarationList(stmt.initializer)) {
|
|
5300
5927
|
// `for (v of "hello")` over a PRE-DECLARED identifier: assign the
|
|
5301
|
-
// existing binding per pass (the array head's rule).
|
|
5302
|
-
//
|
|
5303
|
-
//
|
|
5928
|
+
// existing binding per pass (the array head's rule). PATTERN heads
|
|
5929
|
+
// (`for ([a] of "xy")`) destructure the per-iteration code point
|
|
5930
|
+
// through the string-source assignment lowerings; member targets
|
|
5931
|
+
// keep the fence.
|
|
5304
5932
|
let target = stmt.initializer;
|
|
5305
5933
|
while (ts.isParenthesizedExpression(target))
|
|
5306
5934
|
target = target.expression;
|
|
@@ -5308,7 +5936,10 @@ function lowerForOfString(L, stmt, iterable, labels) {
|
|
|
5308
5936
|
exprTarget = L.resolveWritable(target);
|
|
5309
5937
|
exprTargetNode = target;
|
|
5310
5938
|
}
|
|
5311
|
-
if (
|
|
5939
|
+
else if (ts.isObjectLiteralExpression(target) || ts.isArrayLiteralExpression(target)) {
|
|
5940
|
+
exprPattern = target;
|
|
5941
|
+
}
|
|
5942
|
+
if (!exprTarget && !exprPattern) {
|
|
5312
5943
|
L.unsupported("SC1090", stmt.initializer, "for-of over a pre-declared variable (declare the loop variable in the loop: for (const x of ...))");
|
|
5313
5944
|
}
|
|
5314
5945
|
}
|
|
@@ -5318,8 +5949,6 @@ function lowerForOfString(L, stmt, iterable, labels) {
|
|
|
5318
5949
|
}
|
|
5319
5950
|
const isLet = list !== null && (list.flags & ts.NodeFlags.Let) !== 0;
|
|
5320
5951
|
const decl = list ? list.declarations[0] : null; // the grammar allows exactly one
|
|
5321
|
-
if (decl && !ts.isIdentifier(decl.name))
|
|
5322
|
-
L.unsupported("SC1031", decl.name);
|
|
5323
5952
|
const loc = locOf(stmt);
|
|
5324
5953
|
L.scopes.push(new Map());
|
|
5325
5954
|
try {
|
|
@@ -5331,9 +5960,15 @@ function lowerForOfString(L, stmt, iterable, labels) {
|
|
|
5331
5960
|
// `for (var c of str)`: the character mechanics stay on a hidden
|
|
5332
5961
|
// per-iteration local; the body opens by assigning it into the ONE
|
|
5333
5962
|
// hoisted var binding (see forOfVarTarget). Pre-declared expression
|
|
5334
|
-
// heads assign their existing binding the same way.
|
|
5335
|
-
|
|
5336
|
-
|
|
5963
|
+
// heads assign their existing binding the same way. PATTERN heads
|
|
5964
|
+
// (`for (const [half] of "😀x")`) destructure the per-iteration
|
|
5965
|
+
// code point through the string-source pattern lowerings — each
|
|
5966
|
+
// element is a genuine one-code-point string.
|
|
5967
|
+
const declPattern = decl !== null && !ts.isIdentifier(decl.name)
|
|
5968
|
+
? decl.name
|
|
5969
|
+
: null;
|
|
5970
|
+
const varTarget = exprTarget ?? (decl && !declPattern ? forOfVarTarget(L, decl) : null);
|
|
5971
|
+
const ch = varTarget || declPattern || exprPattern
|
|
5337
5972
|
? L.declareHiddenLocal("%vof", STRING)
|
|
5338
5973
|
: L.declareLocal(decl.name, decl.name.text, STRING, isLet);
|
|
5339
5974
|
const chRef = { kind: "varRef", localId: ch.id, type: STRING, loc };
|
|
@@ -5361,6 +5996,12 @@ function lowerForOfString(L, stmt, iterable, labels) {
|
|
|
5361
5996
|
loc,
|
|
5362
5997
|
},
|
|
5363
5998
|
];
|
|
5999
|
+
if (declPattern) {
|
|
6000
|
+
L.lowerBindingPattern(declPattern, () => chRef, STRING, isLet, head);
|
|
6001
|
+
}
|
|
6002
|
+
if (exprPattern) {
|
|
6003
|
+
head.push(lowerDestructuringAssign(L, exprPattern, chRef, exprPattern, loc));
|
|
6004
|
+
}
|
|
5364
6005
|
const body = L.inCtl("loop", () => L.lowerScopedBlock(stmt.statement), labels);
|
|
5365
6006
|
return {
|
|
5366
6007
|
kind: "block",
|