@scriptc/compiler 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +13 -0
- package/dist/backend/cc.js +14 -0
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +108 -5
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-types.d.ts +3 -0
- package/dist/backend/emission/emit-types.js +12 -0
- package/dist/backend/emission/emit-types.js.map +1 -1
- package/dist/backend/emission/emit-walkers.d.ts +7 -5
- package/dist/backend/emission/emit-walkers.js +23 -10
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +1 -1
- package/dist/backend/emission/emitter.js +30 -9
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.js +206 -11
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +55 -10
- package/dist/diagnostics/diagnostic.js +82 -20
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +16 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +11 -0
- package/dist/frontend/lowering/lower-builtins.js +111 -3
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +30 -11
- package/dist/frontend/lowering/lower-calls.js +418 -8
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +27 -1
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +4 -2
- package/dist/frontend/lowering/lower-containers.js +193 -43
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-emitter.js +54 -0
- package/dist/frontend/lowering/lower-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +52 -7
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +445 -13
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +28 -2
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +66 -14
- package/dist/frontend/lowering/lowerer.js +273 -28
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +20 -3
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.d.ts +7 -2
- package/dist/frontend/npm-static-rewrite.js +566 -138
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static.js +11 -2
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/program.js +5 -5
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/shared.d.ts +6 -1
- package/dist/frontend/shared.js +46 -1
- package/dist/frontend/shared.js.map +1 -1
- package/dist/frontend/types.js +4 -2
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.js +150 -25
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +131 -7
- package/dist/ir/nodes.js +41 -3
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +58 -8
- package/dist/ir/validate.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +10 -3
|
@@ -8,11 +8,11 @@ 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
13
|
import { lowerImportEquals, nsWritableTarget } from "./lower-namespaces.js";
|
|
14
14
|
import { expandoWritableTarget, lowerExpandoAssignStmt } from "./lower-expando.js";
|
|
15
|
-
import { lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, objectIterOverIndexShape } from "./lower-containers.js";
|
|
15
|
+
import { lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, objectIterOverIndexShape, strCharsCall } from "./lower-containers.js";
|
|
16
16
|
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, implicitLocalFnInfoOf, implicitLocalFnNodeOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
17
17
|
import { isMixinFnBinding, mixinResultBindingClassOf } from "./lower-mixins.js";
|
|
18
18
|
import { genericIfaceBindingKeepsClass } from "./lower-classes.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)));
|
|
@@ -1173,6 +1209,28 @@ export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out) {
|
|
|
1173
1209
|
return;
|
|
1174
1210
|
}
|
|
1175
1211
|
}
|
|
1212
|
+
// STRING sources (`const [a, b] = str`): array destructuring walks
|
|
1213
|
+
// the STRING ITERATOR — code points, astral characters whole —
|
|
1214
|
+
// which is Array.from(string)'s own machinery (%str.chars), so the
|
|
1215
|
+
// source splits once into a hidden chars array and the pattern
|
|
1216
|
+
// lowers as an array pattern over string[]. Positions, holes,
|
|
1217
|
+
// defaults (with the bounds test), rest (the remaining code points
|
|
1218
|
+
// pack fresh — JS's surplus packing builds a new array too), and
|
|
1219
|
+
// nested patterns all ride the array machinery below — which also
|
|
1220
|
+
// means a position past the LAST code point inherits the array
|
|
1221
|
+
// divergence (the read traps where JS binds undefined; a DEFAULTED
|
|
1222
|
+
// position carries its bounds test and fires exactly there).
|
|
1223
|
+
// Callers vetted the source as a checker-typed string: builtin
|
|
1224
|
+
// identity tokens (IR strings the checker types otherwise) fence
|
|
1225
|
+
// before reaching here.
|
|
1226
|
+
if (srcType.kind === "string") {
|
|
1227
|
+
const loc = locOf(pattern);
|
|
1228
|
+
const charsT = arrayOf(STRING);
|
|
1229
|
+
const chars = L.declareHiddenLocal("%dchars", charsT);
|
|
1230
|
+
out.push({ kind: "varDecl", localId: chars.id, init: strCharsCall(L, srcRef(), loc), loc });
|
|
1231
|
+
L.lowerBindingPattern(pattern, () => ({ kind: "varRef", localId: chars.id, type: charsT, loc }), charsT, isLet, out);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1176
1234
|
if (srcType.kind !== "array") {
|
|
1177
1235
|
L.unsupported("SC1031", pattern, `array destructuring of non-array values (the source is ${srcType.kind}-typed)`);
|
|
1178
1236
|
}
|
|
@@ -1359,6 +1417,44 @@ export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out) {
|
|
|
1359
1417
|
return;
|
|
1360
1418
|
}
|
|
1361
1419
|
}
|
|
1420
|
+
// A STRING source under an OBJECT pattern (`const { length } = str`):
|
|
1421
|
+
// JS reads through the wrapper object, whose one own DATA property is
|
|
1422
|
+
// `length` — the strLen intrinsic, exact, renames and (dead) defaults
|
|
1423
|
+
// included (length always exists and is never undefined, so a default
|
|
1424
|
+
// never evaluates — JS's rule). Everything else on the wrapper is a
|
|
1425
|
+
// prototype METHOD (detached, it loses its receiver) or absent (the
|
|
1426
|
+
// read is undefined, which the binding's type cannot hold) — named
|
|
1427
|
+
// fences either way; rest packs the wrapper's own INDICES (JS's
|
|
1428
|
+
// CopyDataProperties copies one property per code UNIT) and fences
|
|
1429
|
+
// naming that. Callers vetted the source as a checker-typed string —
|
|
1430
|
+
// builtin identity tokens fence before reaching here.
|
|
1431
|
+
if (srcType.kind === "string") {
|
|
1432
|
+
for (const el of pattern.elements) {
|
|
1433
|
+
if (el.name === undefined)
|
|
1434
|
+
continue;
|
|
1435
|
+
const loc = locOf(el);
|
|
1436
|
+
if (el.dotDotDotToken) {
|
|
1437
|
+
L.unsupported("SC1031", el, "rest bindings over string sources (JS packs the wrapper's per-code-unit indices — split with [...s] instead)");
|
|
1438
|
+
}
|
|
1439
|
+
const prop = el.propertyName ?? el.name;
|
|
1440
|
+
const propName = ts.isIdentifier(prop) || ts.isComputedPropertyName(prop) || ts.isStringLiteralLike(prop) || ts.isNumericLiteral(prop)
|
|
1441
|
+
? patternKeyNameOf(L, prop)
|
|
1442
|
+
: null;
|
|
1443
|
+
if (propName === null) {
|
|
1444
|
+
L.unsupported("SC1031", el, "destructuring with computed keys that do not fold to one property name");
|
|
1445
|
+
}
|
|
1446
|
+
if (propName !== "length") {
|
|
1447
|
+
L.unsupported("SC1031", el, Object.hasOwn(STR_METHODS, propName)
|
|
1448
|
+
? `destructuring the method '${propName}' of a string (a detached method loses its receiver — call it through the value)`
|
|
1449
|
+
: `destructuring the property '${propName}' strings do not carry as data ('length' is the one own data property)`);
|
|
1450
|
+
}
|
|
1451
|
+
let value = { kind: "strIntrinsic", method: "length", receiver: srcRef(), args: [], type: F64, loc };
|
|
1452
|
+
if (el.initializer)
|
|
1453
|
+
value = applyBindingDefault(L, el, value);
|
|
1454
|
+
L.bindPatternTarget(el.name, value, isLet, out);
|
|
1455
|
+
}
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1362
1458
|
if (srcType.kind !== "record") {
|
|
1363
1459
|
L.unsupported("SC1031", pattern, srcType.kind === "object"
|
|
1364
1460
|
? "object destructuring of class instances (read the fields directly — accessors make the desugar observable)"
|
|
@@ -1485,6 +1581,189 @@ export function patternKeyNameOf(L, prop) {
|
|
|
1485
1581
|
}
|
|
1486
1582
|
return null;
|
|
1487
1583
|
}
|
|
1584
|
+
/** True when the CHECKER types this expression as a string in every arm
|
|
1585
|
+
* (unions of string-likes included). The IR carries builtin IDENTITY
|
|
1586
|
+
* TOKENS as strings too (`globalThis.crypto` taken as a value in a JS
|
|
1587
|
+
* file, or a local that adopted such a value's carrier type), and those
|
|
1588
|
+
* must never reach the string-source destructuring lowerings — the
|
|
1589
|
+
* callers fence with the checker's own type name instead. */
|
|
1590
|
+
function checkerStringSource(L, e) {
|
|
1591
|
+
const stringLike = (t) => {
|
|
1592
|
+
if ((t.flags & ts.TypeFlags.StringLike) !== 0)
|
|
1593
|
+
return true;
|
|
1594
|
+
if (t.isUnionType())
|
|
1595
|
+
return t.getTypes().every(stringLike);
|
|
1596
|
+
return false;
|
|
1597
|
+
};
|
|
1598
|
+
return stringLike(L.typeOf(e));
|
|
1599
|
+
}
|
|
1600
|
+
/** True when a pattern over a STRING source lowers statically whole —
|
|
1601
|
+
* every element a form the chars-array/wrapper-read desugars carry.
|
|
1602
|
+
* Array patterns take positions, holes, defaults, rest (a nested rest
|
|
1603
|
+
* pattern must be an ARRAY pattern — it destructures the packed
|
|
1604
|
+
* string[]), and nested patterns, whose elements are single code
|
|
1605
|
+
* points and recurse through this same test. Object patterns take
|
|
1606
|
+
* `length` bindings — shorthand, renamed, or (dead-)defaulted.
|
|
1607
|
+
* Anything else — wrapper methods, computed keys, rest over the
|
|
1608
|
+
* wrapper — keeps the engine gate (--dynamic runs the real pattern;
|
|
1609
|
+
* a static build reports the dynamic-family choice). */
|
|
1610
|
+
function staticStringPattern(L, pattern) {
|
|
1611
|
+
if (ts.isArrayBindingPattern(pattern)) {
|
|
1612
|
+
return pattern.elements.every((el) => {
|
|
1613
|
+
if (ts.isOmittedExpression(el) || el.name === undefined)
|
|
1614
|
+
return true;
|
|
1615
|
+
if (el.dotDotDotToken) {
|
|
1616
|
+
// The rest binds string[]: identifier targets and nested ARRAY
|
|
1617
|
+
// patterns ride the array machinery; an object pattern over the
|
|
1618
|
+
// packed array keeps its fence.
|
|
1619
|
+
return ts.isIdentifier(el.name) || ts.isArrayBindingPattern(el.name);
|
|
1620
|
+
}
|
|
1621
|
+
if (ts.isIdentifier(el.name))
|
|
1622
|
+
return true;
|
|
1623
|
+
return staticStringPattern(L, el.name);
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
return pattern.elements.every((el) => {
|
|
1627
|
+
if (el.name === undefined)
|
|
1628
|
+
return true;
|
|
1629
|
+
if (el.dotDotDotToken || !ts.isIdentifier(el.name))
|
|
1630
|
+
return false;
|
|
1631
|
+
const prop = el.propertyName ?? el.name;
|
|
1632
|
+
const propName = ts.isIdentifier(prop) || ts.isComputedPropertyName(prop) || ts.isStringLiteralLike(prop) || ts.isNumericLiteral(prop)
|
|
1633
|
+
? patternKeyNameOf(L, prop)
|
|
1634
|
+
: null;
|
|
1635
|
+
return propName === "length";
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
/** The stdlib globals whose members destructure to MEMBER identity
|
|
1639
|
+
* tokens: the OPAQUE globals — no member of theirs has a lowered
|
|
1640
|
+
* surface, so a token binding trades the eager pattern fence for the
|
|
1641
|
+
* per-site token rules and loses nothing. Globals with REAL member
|
|
1642
|
+
* surfaces (Math.max, JSON.stringify, process.env, console.log) stay
|
|
1643
|
+
* OUT: their detached members would ride the token's quiet
|
|
1644
|
+
* own-property-undefined reads where the property spelling works, so
|
|
1645
|
+
* those patterns keep an eager fence naming the global. `console` is
|
|
1646
|
+
* the one split surface: its five CALL members fence by name, while
|
|
1647
|
+
* the rest (`Console` — the suite's constructor-identity probe) have
|
|
1648
|
+
* no surface to lose and bind tokens. */
|
|
1649
|
+
const TOKEN_OPAQUE_GLOBALS = new Set(["crypto"]);
|
|
1650
|
+
const CONSOLE_CALL_MEMBERS = new Set(["log", "info", "debug", "error", "warn"]);
|
|
1651
|
+
/** `const { subtle } = globalThis.crypto`, `const { Console } = console`,
|
|
1652
|
+
* `const { crypto } = globalThis` — an object pattern over a STDLIB
|
|
1653
|
+
* GLOBAL in a JavaScript file (the suite's webcrypto/console
|
|
1654
|
+
* prologues): the identifier chokepoint's identity-token rule, one
|
|
1655
|
+
* member deep. The global taken as a value is an opaque token, and a
|
|
1656
|
+
* member taken off an OPAQUE global is the same kind of value — one
|
|
1657
|
+
* global, one member, one interned string — so plain elements bind
|
|
1658
|
+
* MEMBER tokens (`[builtin crypto.subtle]`), and identity flows agree
|
|
1659
|
+
* across destructures of the same member. A member of GLOBALTHIS that
|
|
1660
|
+
* is itself a canonical global (`const { crypto } = globalThis` —
|
|
1661
|
+
* `declare var` globals are properties of `typeof globalThis`,
|
|
1662
|
+
* stdlib-provenance-checked so user script globals stay out) binds the
|
|
1663
|
+
* global's OWN token, byte-equal to the bare and property spellings'
|
|
1664
|
+
* answers, AND registers in stdlibGlobalAliases — the destructured
|
|
1665
|
+
* twin of stdlibGlobalAliasDecl — so receiver-position uses resolve
|
|
1666
|
+
* through every surface exactly like the bare global (`const { Math }
|
|
1667
|
+
* = globalThis; Math.max(...)` is Math.max). What a token cannot do
|
|
1668
|
+
* meets the per-site rules lazily at each USE — member reads answer
|
|
1669
|
+
* the wrapper's own-property undefined, calls throw Node-shaped
|
|
1670
|
+
* is-not-a-function TypeErrors — at the use's own line, where the
|
|
1671
|
+
* pattern-level fence killed the program at the destructure.
|
|
1672
|
+
*
|
|
1673
|
+
* Everything this rule does NOT claim over a stdlib-global source
|
|
1674
|
+
* fences HERE, naming the global — never the token's carrier string
|
|
1675
|
+
* type: rest (the member set is not statically enumerable), defaults
|
|
1676
|
+
* (member presence is not statically testable), nested patterns,
|
|
1677
|
+
* computed keys, members of the SURFACED globals (detached members
|
|
1678
|
+
* lose their receiver-keyed lowerings), and `var`/TDZ-predeclared/
|
|
1679
|
+
* pre-registered bindings whose slots no token can inhabit. Null only
|
|
1680
|
+
* when the source is not a stdlib global at all, or in TypeScript
|
|
1681
|
+
* files (the identifier chokepoint fences the global there first). */
|
|
1682
|
+
function stdlibGlobalTokenDestructure(L, decl, isLet) {
|
|
1683
|
+
if (decl.initializer === undefined || !isJsSourceFile(decl.getSourceFile()))
|
|
1684
|
+
return null;
|
|
1685
|
+
const globalName = stdlibGlobalNameOf(L, decl.initializer);
|
|
1686
|
+
if (globalName === null)
|
|
1687
|
+
return null;
|
|
1688
|
+
if (!ts.isObjectBindingPattern(decl.name)) {
|
|
1689
|
+
L.unsupported("SC1031", decl.name, `array destructuring of the builtin global '${globalName}' (builtin globals are not iterable)`);
|
|
1690
|
+
}
|
|
1691
|
+
if (isVarDeclared(decl)) {
|
|
1692
|
+
L.unsupported("SC1031", decl.name, `var-declared patterns over the builtin global '${globalName}'`);
|
|
1693
|
+
}
|
|
1694
|
+
const srcT = L.typeOf(decl.initializer);
|
|
1695
|
+
const binds = [];
|
|
1696
|
+
for (const el of decl.name.elements) {
|
|
1697
|
+
if (el.name === undefined)
|
|
1698
|
+
continue;
|
|
1699
|
+
if (el.dotDotDotToken) {
|
|
1700
|
+
L.unsupported("SC1031", el, `rest bindings over the builtin global '${globalName}' (the member set is not statically enumerable)`);
|
|
1701
|
+
}
|
|
1702
|
+
if (el.initializer !== undefined) {
|
|
1703
|
+
L.unsupported("SC1031", el, `binding defaults over the builtin global '${globalName}' (member presence is not statically testable)`);
|
|
1704
|
+
}
|
|
1705
|
+
if (!ts.isIdentifier(el.name)) {
|
|
1706
|
+
L.unsupported("SC1031", el, `nested patterns over the builtin global '${globalName}'`);
|
|
1707
|
+
}
|
|
1708
|
+
const prop = el.propertyName ?? el.name;
|
|
1709
|
+
const propName = ts.isIdentifier(prop) || ts.isComputedPropertyName(prop) || ts.isStringLiteralLike(prop) || ts.isNumericLiteral(prop)
|
|
1710
|
+
? patternKeyNameOf(L, prop)
|
|
1711
|
+
: null;
|
|
1712
|
+
if (propName === null) {
|
|
1713
|
+
L.unsupported("SC1031", el, "destructuring with computed keys that do not fold to one property name");
|
|
1714
|
+
}
|
|
1715
|
+
let token;
|
|
1716
|
+
let alias = null;
|
|
1717
|
+
if (globalName === "globalThis") {
|
|
1718
|
+
// The member must BE a stdlib global (user script vars are
|
|
1719
|
+
// properties of `typeof globalThis` too — their values are real
|
|
1720
|
+
// and this rule has none to give).
|
|
1721
|
+
const member = L.checker.getPropertyOfType(srcT, propName);
|
|
1722
|
+
if (member === undefined || !L.isStdlibSymbol(member)) {
|
|
1723
|
+
L.unsupported("SC1031", el, `destructuring the non-builtin member '${propName}' of 'globalThis'`);
|
|
1724
|
+
}
|
|
1725
|
+
const canonical = member.name === "global" ? "globalThis" : member.name;
|
|
1726
|
+
token = `[builtin ${canonical}]`;
|
|
1727
|
+
alias = canonical;
|
|
1728
|
+
}
|
|
1729
|
+
else if (globalName === "console" && !CONSOLE_CALL_MEMBERS.has(propName)) {
|
|
1730
|
+
token = `[builtin console.${propName}]`;
|
|
1731
|
+
}
|
|
1732
|
+
else if (TOKEN_OPAQUE_GLOBALS.has(globalName)) {
|
|
1733
|
+
token = `[builtin ${globalName}.${propName}]`;
|
|
1734
|
+
}
|
|
1735
|
+
else {
|
|
1736
|
+
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)`);
|
|
1737
|
+
}
|
|
1738
|
+
const symbol = L.checker.getSymbolAtLocation(el.name);
|
|
1739
|
+
if (!symbol || L.tdzPredeclared.has(symbol)) {
|
|
1740
|
+
L.unsupported("SC1031", el, `bindings with predeclared slots over the builtin global '${globalName}'`);
|
|
1741
|
+
}
|
|
1742
|
+
const g = L.globalsBySymbol.get(symbol);
|
|
1743
|
+
if (g !== undefined && g.type.kind !== "string" && g.type.kind !== "dyn") {
|
|
1744
|
+
L.unsupported("SC1031", el, `bindings with '${L.fmt(g.type)}' storage over the builtin global '${globalName}'`);
|
|
1745
|
+
}
|
|
1746
|
+
binds.push({ name: el.name, token, alias, g });
|
|
1747
|
+
}
|
|
1748
|
+
const out = [];
|
|
1749
|
+
for (const b of binds) {
|
|
1750
|
+
const loc = locOf(b.name);
|
|
1751
|
+
const token = { kind: "strLit", value: b.token, type: STRING, loc };
|
|
1752
|
+
if (b.alias !== null) {
|
|
1753
|
+
const symbol = L.checker.getSymbolAtLocation(b.name);
|
|
1754
|
+
if (symbol)
|
|
1755
|
+
L.stdlibGlobalAliases.set(symbol, b.alias);
|
|
1756
|
+
}
|
|
1757
|
+
if (b.g !== undefined) {
|
|
1758
|
+
out.push({ kind: "assign", localId: b.g.id, value: L.coerceInto(b.name, token, b.g.type), loc });
|
|
1759
|
+
}
|
|
1760
|
+
else {
|
|
1761
|
+
const local = L.declareLocal(b.name, b.name.text, STRING, isLet);
|
|
1762
|
+
out.push({ kind: "varDecl", localId: local.id, init: token, loc });
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return out;
|
|
1766
|
+
}
|
|
1488
1767
|
/** `x = dflt` on a pattern position whose source value always EXISTS
|
|
1489
1768
|
* (tuple positions, record fields): JS evaluates the default exactly
|
|
1490
1769
|
* when the value is undefined — the undefined-arm ternary, lazy, so
|
|
@@ -2712,6 +2991,7 @@ export function lowerUnionSwitch(L, stmt, disc) {
|
|
|
2712
2991
|
kind: "unionEq",
|
|
2713
2992
|
unionId: unionType.unionId,
|
|
2714
2993
|
negated: false,
|
|
2994
|
+
sameValue: false,
|
|
2715
2995
|
left: disc,
|
|
2716
2996
|
right: L.coerceInto(clause.expression, test, unionType),
|
|
2717
2997
|
type: BOOL,
|
|
@@ -3415,6 +3695,31 @@ export function lowerExprStatement(L, expr) {
|
|
|
3415
3695
|
};
|
|
3416
3696
|
}
|
|
3417
3697
|
}
|
|
3698
|
+
// `events.defaultMaxListeners = v` in STATEMENT position — the
|
|
3699
|
+
// module-property write Node validates (the lowerBinary
|
|
3700
|
+
// expression twin): the value crosses into the DOM, the runtime
|
|
3701
|
+
// ladder throws Node's exact errors, valid numbers apply.
|
|
3702
|
+
if (!expr.left.questionDotToken && expr.left.name.text === "defaultMaxListeners") {
|
|
3703
|
+
const bi = L.builtinMemberOf(expr.left);
|
|
3704
|
+
if (bi && bi.module === "events" && bi.member === "defaultMaxListeners") {
|
|
3705
|
+
const loc = locOf(expr);
|
|
3706
|
+
const rhs = L.lowerExpr(expr.right);
|
|
3707
|
+
if (rhs.type.kind === "dyn" || rhs.kind === "unitLit" || L.dynConvertible(rhs.type)) {
|
|
3708
|
+
const dynVal = rhs.type.kind === "dyn" ? rhs : { kind: "dynFrom", value: rhs, type: DYN, loc };
|
|
3709
|
+
return {
|
|
3710
|
+
kind: "exprStmt",
|
|
3711
|
+
expr: {
|
|
3712
|
+
kind: "libCall",
|
|
3713
|
+
fn: "emitter.setDefaultMaxChk",
|
|
3714
|
+
args: [dynVal, { kind: "strLit", value: "defaultMaxListeners", type: STRING, loc }],
|
|
3715
|
+
type: VOID,
|
|
3716
|
+
loc,
|
|
3717
|
+
},
|
|
3718
|
+
loc,
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
}
|
|
3418
3723
|
// `r._read = fn` / `w._write = fn` (and the other underscore
|
|
3419
3724
|
// methods) on a runtime-stream-rooted receiver: Node's
|
|
3420
3725
|
// own-property shadow of the prototype method — the runtime
|
|
@@ -3937,6 +4242,31 @@ export function lowerDestructuringAssignParts(L, target, rhs, loc) {
|
|
|
3937
4242
|
* source-spelling TypeError machinery cannot apply), `blame` the node
|
|
3938
4243
|
* fences point at. */
|
|
3939
4244
|
function destructuringAssignInto(L, target, init, rhs, blame, loc) {
|
|
4245
|
+
// IR-string sources in JS files split two ways the carrier type
|
|
4246
|
+
// cannot: a STDLIB GLOBAL (`({ subtle } = globalThis.crypto)`) — the
|
|
4247
|
+
// declaration path binds member identity tokens into FRESH bindings,
|
|
4248
|
+
// but assignment targets carry their own checker-typed slots no token
|
|
4249
|
+
// can honestly inhabit — and a LEAKED token (a local that adopted a
|
|
4250
|
+
// global's carrier type), where the string lowerings below would read
|
|
4251
|
+
// the carrier itself. Both keep fences naming the truth; empty
|
|
4252
|
+
// patterns stay pure (RequireObjectCoercible/GetIterator hold for
|
|
4253
|
+
// globals and strings alike).
|
|
4254
|
+
{
|
|
4255
|
+
const patternEmpty = ts.isObjectLiteralExpression(target)
|
|
4256
|
+
? target.properties.length === 0
|
|
4257
|
+
: target.elements.length === 0;
|
|
4258
|
+
if (!patternEmpty && rhs !== null && isJsSourceFile(rhs.getSourceFile())) {
|
|
4259
|
+
const globalName = stdlibGlobalNameOf(L, rhs);
|
|
4260
|
+
if (globalName !== null) {
|
|
4261
|
+
L.unsupported("SC1031", blame, `destructuring assignment from the builtin global '${globalName}' (bind the members with a const destructuring declaration instead)`);
|
|
4262
|
+
}
|
|
4263
|
+
if (init.type.kind === "string" && !checkerStringSource(L, rhs)) {
|
|
4264
|
+
L.unsupported("SC1031", blame, ts.isArrayLiteralExpression(target)
|
|
4265
|
+
? `array destructuring assignment from non-array values (the source is '${L.checker.typeToString(L.typeOf(rhs))}'-typed)`
|
|
4266
|
+
: `destructuring assignment from non-record values (the source is '${L.checker.typeToString(L.typeOf(rhs))}'-typed)`);
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
3940
4270
|
const tmp = L.declareHiddenLocal("%destr", init.type);
|
|
3941
4271
|
const out = [{ kind: "varDecl", localId: tmp.id, init, loc }];
|
|
3942
4272
|
const tmpRef = () => ({ kind: "varRef", localId: tmp.id, type: init.type, loc });
|
|
@@ -4140,6 +4470,77 @@ function destructuringAssignInto(L, target, init, rhs, blame, loc) {
|
|
|
4140
4470
|
return { stmts: out, value };
|
|
4141
4471
|
}
|
|
4142
4472
|
}
|
|
4473
|
+
// A STRING source under an object ASSIGNMENT pattern (`({ length: n }
|
|
4474
|
+
// = s)`): the declaration path's wrapper rule in assignment position —
|
|
4475
|
+
// `length` is the wrapper's one own data property (the strLen
|
|
4476
|
+
// intrinsic; a default is dead code — length always exists and is
|
|
4477
|
+
// never undefined, so JS never evaluates it either); identifier,
|
|
4478
|
+
// property, and element targets ride the shared target plumbing.
|
|
4479
|
+
// Methods, unknown names, computed keys, spreads, and accessor
|
|
4480
|
+
// properties keep the declaration path's named fences. Callers vetted
|
|
4481
|
+
// the source as a checker-typed string.
|
|
4482
|
+
if (init.type.kind === "string") {
|
|
4483
|
+
for (const prop of target.properties) {
|
|
4484
|
+
const propLoc = locOf(prop);
|
|
4485
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
4486
|
+
L.unsupported("SC1031", prop, "rest bindings over string sources (JS packs the wrapper's per-code-unit indices — split with [...s] instead)");
|
|
4487
|
+
}
|
|
4488
|
+
let fieldName;
|
|
4489
|
+
let bindTo;
|
|
4490
|
+
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
4491
|
+
fieldName = prop.name.text;
|
|
4492
|
+
bindTo = prop.name;
|
|
4493
|
+
}
|
|
4494
|
+
else if (ts.isPropertyAssignment(prop)) {
|
|
4495
|
+
const folded = patternKeyNameOf(L, prop.name);
|
|
4496
|
+
if (folded === null) {
|
|
4497
|
+
L.unsupported("SC1031", prop, "destructuring assignment with computed keys that do not fold to one property name");
|
|
4498
|
+
}
|
|
4499
|
+
fieldName = folded;
|
|
4500
|
+
bindTo = prop.initializer;
|
|
4501
|
+
// `({ length: n = 3 } = s)`: the default is dead (length always
|
|
4502
|
+
// exists) — strip it, exactly the evaluation JS skips.
|
|
4503
|
+
if (ts.isBinaryExpression(bindTo) && bindTo.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
4504
|
+
bindTo = bindTo.left;
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
else {
|
|
4508
|
+
L.unsupported("SC1031", prop, "destructuring assignment with getter/setter or method properties");
|
|
4509
|
+
}
|
|
4510
|
+
if (fieldName !== "length") {
|
|
4511
|
+
L.unsupported("SC1031", prop, Object.hasOwn(STR_METHODS, fieldName)
|
|
4512
|
+
? `destructuring the method '${fieldName}' of a string (a detached method loses its receiver — call it through the value)`
|
|
4513
|
+
: `destructuring the property '${fieldName}' strings do not carry as data ('length' is the one own data property)`);
|
|
4514
|
+
}
|
|
4515
|
+
const readOf = () => ({ kind: "strIntrinsic", method: "length", receiver: tmpRef(), args: [], type: F64, loc: propLoc });
|
|
4516
|
+
while (ts.isParenthesizedExpression(bindTo))
|
|
4517
|
+
bindTo = bindTo.expression;
|
|
4518
|
+
if (!ts.isIdentifier(bindTo)) {
|
|
4519
|
+
lowerAssignTargetInto(L, out, bindTo, prop, null, readOf);
|
|
4520
|
+
continue;
|
|
4521
|
+
}
|
|
4522
|
+
let targetBinding;
|
|
4523
|
+
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
4524
|
+
const valueSymbol = L.checker.getShorthandAssignmentValueSymbol(prop) ?? null;
|
|
4525
|
+
const local = valueSymbol ? L.resolveKey(valueSymbol, bindTo) : null;
|
|
4526
|
+
const g = valueSymbol ? L.globalsBySymbol.get(valueSymbol) : undefined;
|
|
4527
|
+
targetBinding = local ? { id: local.id, type: local.type } : g ? { id: g.id, type: g.type } : null;
|
|
4528
|
+
}
|
|
4529
|
+
else {
|
|
4530
|
+
targetBinding = L.resolveWritable(bindTo);
|
|
4531
|
+
}
|
|
4532
|
+
if (!targetBinding) {
|
|
4533
|
+
L.rejectUnresolved(bindTo, `assignment to '${bindTo.text}' (not a writable local or module global)`);
|
|
4534
|
+
}
|
|
4535
|
+
out.push({
|
|
4536
|
+
kind: "assign",
|
|
4537
|
+
localId: targetBinding.id,
|
|
4538
|
+
value: L.coerceInto(prop, readOf(), targetBinding.type),
|
|
4539
|
+
loc: propLoc,
|
|
4540
|
+
});
|
|
4541
|
+
}
|
|
4542
|
+
return { stmts: out, value };
|
|
4543
|
+
}
|
|
4143
4544
|
if (init.type.kind !== "record") {
|
|
4144
4545
|
L.unsupported("SC1031", blame, init.type.kind === "object"
|
|
4145
4546
|
? "destructuring assignment from class instances (read the fields directly — accessors make the desugar observable)"
|
|
@@ -4473,7 +4874,23 @@ function destructAssignPiece(L, prop, islandSource) {
|
|
|
4473
4874
|
* bounds test (JS reads undefined past the end), rest packs the tail
|
|
4474
4875
|
* fresh (array slices, tuple tails). */
|
|
4475
4876
|
function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
|
|
4476
|
-
|
|
4877
|
+
let srcType = tmpRef().type;
|
|
4878
|
+
// A STRING source (`[a, b] = s`): the declaration path's rule in
|
|
4879
|
+
// assignment position — the string iterator's code-point split
|
|
4880
|
+
// (%str.chars, astral characters whole) into a hidden chars array,
|
|
4881
|
+
// then the pattern proceeds as an array pattern over string[]
|
|
4882
|
+
// (defaults with the bounds test, rest packing the remaining code
|
|
4883
|
+
// points, elisions; positions past the last code point inherit the
|
|
4884
|
+
// array divergence). The EMPTY pattern skips the split — GetIterator
|
|
4885
|
+
// + close, nothing observable — through the iterable early-return
|
|
4886
|
+
// below. Callers vetted the source as a checker-typed string.
|
|
4887
|
+
if (srcType.kind === "string" && target.elements.length > 0) {
|
|
4888
|
+
const charsT = arrayOf(STRING);
|
|
4889
|
+
const chars = L.declareHiddenLocal("%dchars", charsT);
|
|
4890
|
+
out.push({ kind: "varDecl", localId: chars.id, init: strCharsCall(L, tmpRef(), loc), loc });
|
|
4891
|
+
tmpRef = () => ({ kind: "varRef", localId: chars.id, type: charsT, loc });
|
|
4892
|
+
srcType = charsT;
|
|
4893
|
+
}
|
|
4477
4894
|
// A sole leading rest whose operand is ITSELF an array pattern
|
|
4478
4895
|
// (`[...[a, b = 0]] = t`): the rest collects every element, then the
|
|
4479
4896
|
// inner pattern destructures the collection — over arrays/tuples that
|
|
@@ -5296,11 +5713,13 @@ function lowerForOfIsland(L, stmt, iterable, labels) {
|
|
|
5296
5713
|
function lowerForOfString(L, stmt, iterable, labels) {
|
|
5297
5714
|
let exprTarget = null;
|
|
5298
5715
|
let exprTargetNode = null;
|
|
5716
|
+
let exprPattern = null;
|
|
5299
5717
|
if (!ts.isVariableDeclarationList(stmt.initializer)) {
|
|
5300
5718
|
// `for (v of "hello")` over a PRE-DECLARED identifier: assign the
|
|
5301
|
-
// existing binding per pass (the array head's rule).
|
|
5302
|
-
//
|
|
5303
|
-
//
|
|
5719
|
+
// existing binding per pass (the array head's rule). PATTERN heads
|
|
5720
|
+
// (`for ([a] of "xy")`) destructure the per-iteration code point
|
|
5721
|
+
// through the string-source assignment lowerings; member targets
|
|
5722
|
+
// keep the fence.
|
|
5304
5723
|
let target = stmt.initializer;
|
|
5305
5724
|
while (ts.isParenthesizedExpression(target))
|
|
5306
5725
|
target = target.expression;
|
|
@@ -5308,7 +5727,10 @@ function lowerForOfString(L, stmt, iterable, labels) {
|
|
|
5308
5727
|
exprTarget = L.resolveWritable(target);
|
|
5309
5728
|
exprTargetNode = target;
|
|
5310
5729
|
}
|
|
5311
|
-
if (
|
|
5730
|
+
else if (ts.isObjectLiteralExpression(target) || ts.isArrayLiteralExpression(target)) {
|
|
5731
|
+
exprPattern = target;
|
|
5732
|
+
}
|
|
5733
|
+
if (!exprTarget && !exprPattern) {
|
|
5312
5734
|
L.unsupported("SC1090", stmt.initializer, "for-of over a pre-declared variable (declare the loop variable in the loop: for (const x of ...))");
|
|
5313
5735
|
}
|
|
5314
5736
|
}
|
|
@@ -5318,8 +5740,6 @@ function lowerForOfString(L, stmt, iterable, labels) {
|
|
|
5318
5740
|
}
|
|
5319
5741
|
const isLet = list !== null && (list.flags & ts.NodeFlags.Let) !== 0;
|
|
5320
5742
|
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
5743
|
const loc = locOf(stmt);
|
|
5324
5744
|
L.scopes.push(new Map());
|
|
5325
5745
|
try {
|
|
@@ -5331,9 +5751,15 @@ function lowerForOfString(L, stmt, iterable, labels) {
|
|
|
5331
5751
|
// `for (var c of str)`: the character mechanics stay on a hidden
|
|
5332
5752
|
// per-iteration local; the body opens by assigning it into the ONE
|
|
5333
5753
|
// hoisted var binding (see forOfVarTarget). Pre-declared expression
|
|
5334
|
-
// heads assign their existing binding the same way.
|
|
5335
|
-
|
|
5336
|
-
|
|
5754
|
+
// heads assign their existing binding the same way. PATTERN heads
|
|
5755
|
+
// (`for (const [half] of "😀x")`) destructure the per-iteration
|
|
5756
|
+
// code point through the string-source pattern lowerings — each
|
|
5757
|
+
// element is a genuine one-code-point string.
|
|
5758
|
+
const declPattern = decl !== null && !ts.isIdentifier(decl.name)
|
|
5759
|
+
? decl.name
|
|
5760
|
+
: null;
|
|
5761
|
+
const varTarget = exprTarget ?? (decl && !declPattern ? forOfVarTarget(L, decl) : null);
|
|
5762
|
+
const ch = varTarget || declPattern || exprPattern
|
|
5337
5763
|
? L.declareHiddenLocal("%vof", STRING)
|
|
5338
5764
|
: L.declareLocal(decl.name, decl.name.text, STRING, isLet);
|
|
5339
5765
|
const chRef = { kind: "varRef", localId: ch.id, type: STRING, loc };
|
|
@@ -5361,6 +5787,12 @@ function lowerForOfString(L, stmt, iterable, labels) {
|
|
|
5361
5787
|
loc,
|
|
5362
5788
|
},
|
|
5363
5789
|
];
|
|
5790
|
+
if (declPattern) {
|
|
5791
|
+
L.lowerBindingPattern(declPattern, () => chRef, STRING, isLet, head);
|
|
5792
|
+
}
|
|
5793
|
+
if (exprPattern) {
|
|
5794
|
+
head.push(lowerDestructuringAssign(L, exprPattern, chRef, exprPattern, loc));
|
|
5795
|
+
}
|
|
5364
5796
|
const body = L.inCtl("loop", () => L.lowerScopedBlock(stmt.statement), labels);
|
|
5365
5797
|
return {
|
|
5366
5798
|
kind: "block",
|