@scriptc/compiler 0.0.0 → 0.0.2
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/LICENSE +202 -0
- package/ambient/package.json +3 -0
- package/ambient/scriptc-node-fallback.d.ts +3001 -0
- package/ambient/scriptc-overrides.d.ts +95 -0
- package/ambient/scriptc.d.ts +74 -0
- package/dist/backend/cc.d.ts +169 -0
- package/dist/backend/cc.js +897 -0
- package/dist/backend/cc.js.map +1 -0
- package/dist/backend/emission/emit-async.d.ts +145 -0
- package/dist/backend/emission/emit-async.js +996 -0
- package/dist/backend/emission/emit-async.js.map +1 -0
- package/dist/backend/emission/emit-exprs.d.ts +3 -0
- package/dist/backend/emission/emit-exprs.js +5949 -0
- package/dist/backend/emission/emit-exprs.js.map +1 -0
- package/dist/backend/emission/emit-island.d.ts +45 -0
- package/dist/backend/emission/emit-island.js +271 -0
- package/dist/backend/emission/emit-island.js.map +1 -0
- package/dist/backend/emission/emit-shapes.d.ts +142 -0
- package/dist/backend/emission/emit-shapes.js +575 -0
- package/dist/backend/emission/emit-shapes.js.map +1 -0
- package/dist/backend/emission/emit-stmts.d.ts +78 -0
- package/dist/backend/emission/emit-stmts.js +960 -0
- package/dist/backend/emission/emit-stmts.js.map +1 -0
- package/dist/backend/emission/emit-types.d.ts +65 -0
- package/dist/backend/emission/emit-types.js +652 -0
- package/dist/backend/emission/emit-types.js.map +1 -0
- package/dist/backend/emission/emit-walkers.d.ts +154 -0
- package/dist/backend/emission/emit-walkers.js +1587 -0
- package/dist/backend/emission/emit-walkers.js.map +1 -0
- package/dist/backend/emission/emitter.d.ts +452 -0
- package/dist/backend/emission/emitter.js +1249 -0
- package/dist/backend/emission/emitter.js.map +1 -0
- package/dist/backend/emission/may-throw.d.ts +15 -0
- package/dist/backend/emission/may-throw.js +250 -0
- package/dist/backend/emission/may-throw.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +21 -0
- package/dist/backend/llvm/blocks.js +68 -0
- package/dist/backend/llvm/blocks.js.map +1 -0
- package/dist/backend/llvm/classes.d.ts +84 -0
- package/dist/backend/llvm/classes.js +438 -0
- package/dist/backend/llvm/classes.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +123 -0
- package/dist/backend/llvm/dyn.js +2624 -0
- package/dist/backend/llvm/dyn.js.map +1 -0
- package/dist/backend/llvm/emitter.d.ts +3 -0
- package/dist/backend/llvm/emitter.js +10102 -0
- package/dist/backend/llvm/emitter.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +78 -0
- package/dist/backend/llvm/shapes.js +754 -0
- package/dist/backend/llvm/shapes.js.map +1 -0
- package/dist/backend/llvm/unsupported.d.ts +6 -0
- package/dist/backend/llvm/unsupported.js +12 -0
- package/dist/backend/llvm/unsupported.js.map +1 -0
- package/dist/backend/llvm/walkers.d.ts +58 -0
- package/dist/backend/llvm/walkers.js +793 -0
- package/dist/backend/llvm/walkers.js.map +1 -0
- package/dist/backend/mangle.d.ts +124 -0
- package/dist/backend/mangle.js +232 -0
- package/dist/backend/mangle.js.map +1 -0
- package/dist/coverage/report.d.ts +65 -0
- package/dist/coverage/report.js +238 -0
- package/dist/coverage/report.js.map +1 -0
- package/dist/diagnostics/diagnostic.d.ts +140 -0
- package/dist/diagnostics/diagnostic.js +458 -0
- package/dist/diagnostics/diagnostic.js.map +1 -0
- package/dist/diagnostics/render.d.ts +11 -0
- package/dist/diagnostics/render.js +58 -0
- package/dist/diagnostics/render.js.map +1 -0
- package/dist/frontend/cjs-lexer.d.ts +20 -0
- package/dist/frontend/cjs-lexer.js +813 -0
- package/dist/frontend/cjs-lexer.js.map +1 -0
- package/dist/frontend/lowering/http2-constants.d.ts +1 -0
- package/dist/frontend/lowering/http2-constants.js +251 -0
- package/dist/frontend/lowering/http2-constants.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.d.ts +6 -0
- package/dist/frontend/lowering/lib-boundary.js +143 -0
- package/dist/frontend/lowering/lib-boundary.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.d.ts +18 -0
- package/dist/frontend/lowering/lower-assert.js +1269 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +511 -0
- package/dist/frontend/lowering/lower-builtins.js +5249 -0
- package/dist/frontend/lowering/lower-builtins.js.map +1 -0
- package/dist/frontend/lowering/lower-calls.d.ts +581 -0
- package/dist/frontend/lowering/lower-calls.js +6286 -0
- package/dist/frontend/lowering/lower-calls.js.map +1 -0
- package/dist/frontend/lowering/lower-classes.d.ts +727 -0
- package/dist/frontend/lowering/lower-classes.js +4701 -0
- package/dist/frontend/lowering/lower-classes.js.map +1 -0
- package/dist/frontend/lowering/lower-comptime.d.ts +53 -0
- package/dist/frontend/lowering/lower-comptime.js +244 -0
- package/dist/frontend/lowering/lower-comptime.js.map +1 -0
- package/dist/frontend/lowering/lower-containers.d.ts +486 -0
- package/dist/frontend/lowering/lower-containers.js +6345 -0
- package/dist/frontend/lowering/lower-containers.js.map +1 -0
- package/dist/frontend/lowering/lower-dgram.d.ts +20 -0
- package/dist/frontend/lowering/lower-dgram.js +319 -0
- package/dist/frontend/lowering/lower-dgram.js.map +1 -0
- package/dist/frontend/lowering/lower-emitter.d.ts +10 -0
- package/dist/frontend/lowering/lower-emitter.js +688 -0
- package/dist/frontend/lowering/lower-emitter.js.map +1 -0
- package/dist/frontend/lowering/lower-enums.d.ts +22 -0
- package/dist/frontend/lowering/lower-enums.js +235 -0
- package/dist/frontend/lowering/lower-enums.js.map +1 -0
- package/dist/frontend/lowering/lower-expando.d.ts +35 -0
- package/dist/frontend/lowering/lower-expando.js +276 -0
- package/dist/frontend/lowering/lower-expando.js.map +1 -0
- package/dist/frontend/lowering/lower-exprs.d.ts +524 -0
- package/dist/frontend/lowering/lower-exprs.js +8111 -0
- package/dist/frontend/lowering/lower-exprs.js.map +1 -0
- package/dist/frontend/lowering/lower-generators.d.ts +50 -0
- package/dist/frontend/lowering/lower-generators.js +421 -0
- package/dist/frontend/lowering/lower-generators.js.map +1 -0
- package/dist/frontend/lowering/lower-inspect.d.ts +20 -0
- package/dist/frontend/lowering/lower-inspect.js +1074 -0
- package/dist/frontend/lowering/lower-inspect.js.map +1 -0
- package/dist/frontend/lowering/lower-island.d.ts +108 -0
- package/dist/frontend/lowering/lower-island.js +650 -0
- package/dist/frontend/lowering/lower-island.js.map +1 -0
- package/dist/frontend/lowering/lower-mixins.d.ts +96 -0
- package/dist/frontend/lowering/lower-mixins.js +534 -0
- package/dist/frontend/lowering/lower-mixins.js.map +1 -0
- package/dist/frontend/lowering/lower-modules.d.ts +135 -0
- package/dist/frontend/lowering/lower-modules.js +1613 -0
- package/dist/frontend/lowering/lower-modules.js.map +1 -0
- package/dist/frontend/lowering/lower-namespaces.d.ts +168 -0
- package/dist/frontend/lowering/lower-namespaces.js +706 -0
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -0
- package/dist/frontend/lowering/lower-server.d.ts +90 -0
- package/dist/frontend/lowering/lower-server.js +3471 -0
- package/dist/frontend/lowering/lower-server.js.map +1 -0
- package/dist/frontend/lowering/lower-stmts.d.ts +361 -0
- package/dist/frontend/lowering/lower-stmts.js +5341 -0
- package/dist/frontend/lowering/lower-stmts.js.map +1 -0
- package/dist/frontend/lowering/lower-stream.d.ts +88 -0
- package/dist/frontend/lowering/lower-stream.js +1533 -0
- package/dist/frontend/lowering/lower-stream.js.map +1 -0
- package/dist/frontend/lowering/lower-test.d.ts +27 -0
- package/dist/frontend/lowering/lower-test.js +353 -0
- package/dist/frontend/lowering/lower-test.js.map +1 -0
- package/dist/frontend/lowering/lowerer.d.ts +1748 -0
- package/dist/frontend/lowering/lowerer.js +6257 -0
- package/dist/frontend/lowering/lowerer.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +272 -0
- package/dist/frontend/lowering/surfaces.js +1096 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +55 -0
- package/dist/frontend/npm-static.js +300 -0
- package/dist/frontend/npm-static.js.map +1 -0
- package/dist/frontend/npm.d.ts +330 -0
- package/dist/frontend/npm.js +1259 -0
- package/dist/frontend/npm.js.map +1 -0
- package/dist/frontend/program.d.ts +186 -0
- package/dist/frontend/program.js +2255 -0
- package/dist/frontend/program.js.map +1 -0
- package/dist/frontend/provenance-registry.d.ts +48 -0
- package/dist/frontend/provenance-registry.js +87 -0
- package/dist/frontend/provenance-registry.js.map +1 -0
- package/dist/frontend/provenance.d.ts +6 -0
- package/dist/frontend/provenance.js +459 -0
- package/dist/frontend/provenance.js.map +1 -0
- package/dist/frontend/resolve.d.ts +56 -0
- package/dist/frontend/resolve.js +682 -0
- package/dist/frontend/resolve.js.map +1 -0
- package/dist/frontend/shared.d.ts +67 -0
- package/dist/frontend/shared.js +241 -0
- package/dist/frontend/shared.js.map +1 -0
- package/dist/frontend/ts7/adapter.d.ts +7 -0
- package/dist/frontend/ts7/adapter.js +54 -0
- package/dist/frontend/ts7/adapter.js.map +1 -0
- package/dist/frontend/ts7/ast.d.ts +50 -0
- package/dist/frontend/ts7/ast.js +211 -0
- package/dist/frontend/ts7/ast.js.map +1 -0
- package/dist/frontend/ts7/census-check.d.ts +1 -0
- package/dist/frontend/ts7/census-check.js +12 -0
- package/dist/frontend/ts7/census-check.js.map +1 -0
- package/dist/frontend/ts7/checker.d.ts +140 -0
- package/dist/frontend/ts7/checker.js +544 -0
- package/dist/frontend/ts7/checker.js.map +1 -0
- package/dist/frontend/ts7/enums.d.ts +18 -0
- package/dist/frontend/ts7/enums.js +43 -0
- package/dist/frontend/ts7/enums.js.map +1 -0
- package/dist/frontend/ts7/program.d.ts +99 -0
- package/dist/frontend/ts7/program.js +278 -0
- package/dist/frontend/ts7/program.js.map +1 -0
- package/dist/frontend/ts7/world-check.d.ts +3 -0
- package/dist/frontend/ts7/world-check.js +34 -0
- package/dist/frontend/ts7/world-check.js.map +1 -0
- package/dist/frontend/types.d.ts +205 -0
- package/dist/frontend/types.js +2485 -0
- package/dist/frontend/types.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +429 -0
- package/dist/index.js.map +1 -0
- package/dist/ir/nodes.d.ts +4334 -0
- package/dist/ir/nodes.js +1906 -0
- package/dist/ir/nodes.js.map +1 -0
- package/dist/ir/serialize.d.ts +4 -0
- package/dist/ir/serialize.js +34 -0
- package/dist/ir/serialize.js.map +1 -0
- package/dist/ir/validate.d.ts +36 -0
- package/dist/ir/validate.js +4885 -0
- package/dist/ir/validate.js.map +1 -0
- package/package.json +30 -6
- package/README.md +0 -3
- package/index.js +0 -2
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
/* Namespace lowering (`namespace N { ... }` / legacy `module N { ... }`).
|
|
2
|
+
*
|
|
3
|
+
* A namespace is a STATIC shape: its members are compile-time-known
|
|
4
|
+
* declarations, so the lowering flattens each instantiated namespace body
|
|
5
|
+
* into the enclosing file's parts — functions and classes hoist into the
|
|
6
|
+
* ordinary collection lists under NAMESPACE-QUALIFIED names, and every
|
|
7
|
+
* other statement (member variables, side-effecting statements, nested
|
|
8
|
+
* blocks' statements) stays in the file's init body AT ITS SOURCE
|
|
9
|
+
* POSITION, so declaration-time side effects run exactly when Node runs
|
|
10
|
+
* them. Qualified references (`N.x`, `A.B.f()`, `new N.C()`) resolve
|
|
11
|
+
* through the checker's member symbols to the same registries bare
|
|
12
|
+
* identifiers use; the namespace OBJECT itself has no runtime value and
|
|
13
|
+
* every first-class use of it fences by name.
|
|
14
|
+
*
|
|
15
|
+
* Zero-runtime namespaces stay zero-runtime: ambient declarations
|
|
16
|
+
* (`declare namespace`/`declare module`, `declare global`, string-named
|
|
17
|
+
* modules) and non-instantiated bodies (only interfaces/type aliases/
|
|
18
|
+
* import= aliases inside) lower to NOTHING — reads of ambient namespace
|
|
19
|
+
* members compile to Node's exact catchable ReferenceError at the access
|
|
20
|
+
* (the namespace object never exists at runtime, same stance as ambient
|
|
21
|
+
* `declare const` reads).
|
|
22
|
+
*
|
|
23
|
+
* Honesty guards (Node evaluates namespace bodies in source order, and a
|
|
24
|
+
* namespace member does not exist before its declaration statement ran):
|
|
25
|
+
* - a qualified reference in INIT-EXECUTING position (top level, not
|
|
26
|
+
* inside a function body) textually above the member's declaration
|
|
27
|
+
* fences — Node would observe an uninitialized binding where the static
|
|
28
|
+
* resolution would read the final one;
|
|
29
|
+
* - `import x = N.y` aliases are pure plumbing for IMMUTABLE targets
|
|
30
|
+
* (namespaces, functions, classes, consts); a mutable (`let`) target
|
|
31
|
+
* would need snapshot-at-alias semantics and fences;
|
|
32
|
+
* - alias segments in init-position reference chains carry the same
|
|
33
|
+
* source-order guards (the alias captures its target at the alias
|
|
34
|
+
* statement).
|
|
35
|
+
* References from FUNCTION BODIES take the module-globals stance the rest
|
|
36
|
+
* of the compiler already takes (a body running before the declaring
|
|
37
|
+
* statement is not modeled — same as top-level const reads through early
|
|
38
|
+
* calls). */
|
|
39
|
+
import * as ts from "../ts7/adapter.js";
|
|
40
|
+
import { locOf, resolveImport } from "../program.js";
|
|
41
|
+
import { F64, STRING } from "../../ir/nodes.js";
|
|
42
|
+
/** True when this module declaration produces NO runtime construct at all:
|
|
43
|
+
* ambient (`declare namespace/module`, `declare global`, string-named
|
|
44
|
+
* modules — .d.ts surface or user-file declares) or a non-instantiated
|
|
45
|
+
* body (only type-world statements inside). */
|
|
46
|
+
export function nsZeroRuntime(decl) {
|
|
47
|
+
if (isAmbientModuleDecl(decl))
|
|
48
|
+
return true;
|
|
49
|
+
if (!ts.isIdentifier(decl.name))
|
|
50
|
+
return true; // string-named: ambient by grammar
|
|
51
|
+
return nsTypeOnlyBody(decl);
|
|
52
|
+
}
|
|
53
|
+
/** Ambient module declaration: carries `declare` itself, sits in an
|
|
54
|
+
* ambient ancestor, or is the `declare global` augmentation. */
|
|
55
|
+
export function isAmbientModuleDecl(decl) {
|
|
56
|
+
if (decl.flags & ts.NodeFlags.Ambient)
|
|
57
|
+
return true;
|
|
58
|
+
for (let p = decl; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
|
|
59
|
+
if (ts.isModuleDeclaration(p)) {
|
|
60
|
+
if (ts.getCombinedModifierFlags(p) & ts.ModifierFlags.Ambient)
|
|
61
|
+
return true;
|
|
62
|
+
if (ts.isStringLiteral(p.name))
|
|
63
|
+
return true;
|
|
64
|
+
if (ts.isIdentifier(p.name) && p.name.text === "global")
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/** Non-instantiated body: only interfaces, type aliases, and recursively
|
|
71
|
+
* non-instantiated namespaces. Such a namespace is pure type world — no
|
|
72
|
+
* emit of Node's transform materializes it. import= aliases COUNT as
|
|
73
|
+
* instantiating (tsc's isInstantiatedModule rule, and Node's transform
|
|
74
|
+
* emits their `var x = ...` unconditionally). */
|
|
75
|
+
function nsTypeOnlyBody(decl) {
|
|
76
|
+
let body = decl.body;
|
|
77
|
+
while (body !== undefined && ts.isModuleDeclaration(body))
|
|
78
|
+
body = body.body;
|
|
79
|
+
if (body === undefined)
|
|
80
|
+
return true;
|
|
81
|
+
if (!ts.isModuleBlock(body))
|
|
82
|
+
return true;
|
|
83
|
+
return body.statements.every((s) => ts.isInterfaceDeclaration(s) ||
|
|
84
|
+
ts.isTypeAliasDeclaration(s) ||
|
|
85
|
+
(ts.isModuleDeclaration(s) && nsTypeOnlyBody(s)));
|
|
86
|
+
}
|
|
87
|
+
/** A namespace SYMBOL's runtime state: "instantiated" when any merged
|
|
88
|
+
* declaration's block flattened (the object exists once a block ran;
|
|
89
|
+
* merged ambient declarations don't subtract), "typeOnly"/"ambient" when
|
|
90
|
+
* none did. Null for non-namespace symbols. */
|
|
91
|
+
export function nsSymbolRuntimeKind(L, sym) {
|
|
92
|
+
if (!(sym.flags & (ts.SymbolFlags.ValueModule | ts.SymbolFlags.NamespaceModule)))
|
|
93
|
+
return null;
|
|
94
|
+
let sawTypeOnly = false;
|
|
95
|
+
let sawNamespace = false;
|
|
96
|
+
for (const d of L.checker.declarationsOf(sym)) {
|
|
97
|
+
if (!ts.isModuleDeclaration(d))
|
|
98
|
+
continue;
|
|
99
|
+
sawNamespace = true;
|
|
100
|
+
let body = d.body;
|
|
101
|
+
while (body !== undefined && ts.isModuleDeclaration(body))
|
|
102
|
+
body = body.body;
|
|
103
|
+
const kind = body !== undefined ? L.nsBlocks.get(body) : undefined;
|
|
104
|
+
if (kind === "flattened")
|
|
105
|
+
return "instantiated";
|
|
106
|
+
if (kind === "typeOnly")
|
|
107
|
+
sawTypeOnly = true;
|
|
108
|
+
}
|
|
109
|
+
if (!sawNamespace)
|
|
110
|
+
return null;
|
|
111
|
+
return sawTypeOnly ? "typeOnly" : "ambient";
|
|
112
|
+
}
|
|
113
|
+
/** splitFiles' namespace hook: classify one top-level (or nested)
|
|
114
|
+
* ModuleDeclaration and either skip it (zero runtime — its blocks register
|
|
115
|
+
* as "typeOnly" so import= alias members still resolve statically) or
|
|
116
|
+
* FLATTEN its body into the file's parts: functions/classes hoist into the
|
|
117
|
+
* collection lists, everything else joins the init body in source order.
|
|
118
|
+
* Nested namespaces recurse; dotted declarations (`namespace A.B.C`)
|
|
119
|
+
* descend to the innermost block. */
|
|
120
|
+
export function collectNamespaceStmt(L, decl, fp) {
|
|
121
|
+
if (nsZeroRuntime(decl)) {
|
|
122
|
+
if (!isAmbientModuleDecl(decl))
|
|
123
|
+
registerTypeOnlyBlocks(L, decl);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
let body = decl.body;
|
|
127
|
+
while (body !== undefined && ts.isModuleDeclaration(body))
|
|
128
|
+
body = body.body;
|
|
129
|
+
if (body === undefined || !ts.isModuleBlock(body))
|
|
130
|
+
return; // bodyless non-ambient: tsc-rejected
|
|
131
|
+
L.nsBlocks.set(body, "flattened");
|
|
132
|
+
for (const s of body.statements) {
|
|
133
|
+
if (ts.isFunctionDeclaration(s))
|
|
134
|
+
fp.fnDecls.push(s);
|
|
135
|
+
else if (ts.isClassDeclaration(s))
|
|
136
|
+
fp.classDecls.push(s);
|
|
137
|
+
else if (ts.isInterfaceDeclaration(s) || ts.isTypeAliasDeclaration(s))
|
|
138
|
+
continue;
|
|
139
|
+
else if (ts.isModuleDeclaration(s))
|
|
140
|
+
collectNamespaceStmt(L, s, fp);
|
|
141
|
+
// Everything else — member variables, side-effecting statements,
|
|
142
|
+
// import= aliases (lowerStmt handles them), enums (their own fence) —
|
|
143
|
+
// runs in the init body at its source position.
|
|
144
|
+
else
|
|
145
|
+
fp.topStmts.push(s);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function registerTypeOnlyBlocks(L, decl) {
|
|
149
|
+
let body = decl.body;
|
|
150
|
+
while (body !== undefined && ts.isModuleDeclaration(body))
|
|
151
|
+
body = body.body;
|
|
152
|
+
if (body === undefined || !ts.isModuleBlock(body))
|
|
153
|
+
return;
|
|
154
|
+
L.nsBlocks.set(body, "typeOnly");
|
|
155
|
+
for (const s of body.statements) {
|
|
156
|
+
if (ts.isModuleDeclaration(s))
|
|
157
|
+
registerTypeOnlyBlocks(L, s);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** The namespace-path prefix of a declaration's emitted name ("A%B." for
|
|
161
|
+
* an exported member of namespace A.B; "" at real top level). Segments
|
|
162
|
+
* join with '%' — a character no user identifier can contain, so a
|
|
163
|
+
* namespaced name can never collide with a plain top-level one after
|
|
164
|
+
* mangling. NON-exported members (and non-exported nested namespace
|
|
165
|
+
* segments) append the declaring block's source position: they are
|
|
166
|
+
* block-local bindings — two blocks of the same namespace may each declare
|
|
167
|
+
* one under the same spelling, and the position keeps their storage
|
|
168
|
+
* distinct (resolution is by symbol; names only need uniqueness and
|
|
169
|
+
* determinism). `modifierDecl` carries the declaration whose modifiers
|
|
170
|
+
* decide exportedness (the first declarator of a variable statement). */
|
|
171
|
+
export function nsPathPrefix(node, modifierDecl) {
|
|
172
|
+
const parent = node.parent;
|
|
173
|
+
if (parent === undefined || !ts.isModuleBlock(parent))
|
|
174
|
+
return "";
|
|
175
|
+
const flagsHost = modifierDecl ?? node;
|
|
176
|
+
const exported = (ts.getCombinedModifierFlags(flagsHost) & ts.ModifierFlags.Export) !== 0;
|
|
177
|
+
const segs = [];
|
|
178
|
+
for (let p = parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
|
|
179
|
+
if (ts.isModuleDeclaration(p) && ts.isIdentifier(p.name)) {
|
|
180
|
+
// A nested namespace STATEMENT without `export` is block-local;
|
|
181
|
+
// dotted continuations (parent is a ModuleDeclaration) and top-level
|
|
182
|
+
// declarations merge by name and take the plain segment.
|
|
183
|
+
const nested = p.parent !== undefined && ts.isModuleBlock(p.parent);
|
|
184
|
+
const nonExported = nested && (ts.getCombinedModifierFlags(p) & ts.ModifierFlags.Export) === 0;
|
|
185
|
+
segs.unshift(p.name.text + (nonExported ? `%${p.getStart()}` : ""));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return `${segs.join("%")}${exported ? "" : `%${parent.getStart()}`}.`;
|
|
189
|
+
}
|
|
190
|
+
/** The registered namespace-block kind a symbol's declaration sits in
|
|
191
|
+
* ("flattened" — a lowered value namespace; "typeOnly" — a skipped
|
|
192
|
+
* non-instantiated one, whose only value members are import= aliases), or
|
|
193
|
+
* null. The walk stops at class/function boundaries: a static member of a
|
|
194
|
+
* class INSIDE a namespace is the class's business, not the namespace's. */
|
|
195
|
+
export function nsBlockKindOfSymbol(L, sym) {
|
|
196
|
+
for (const d of L.checker.declarationsOf(sym)) {
|
|
197
|
+
// TYPE declarations don't make a member a namespace VALUE: a class
|
|
198
|
+
// static `C.B` whose name also carries `namespace C { export
|
|
199
|
+
// interface B }` is the static's business — value references consult
|
|
200
|
+
// value declarations only. AMBIENT member declarations inside a real
|
|
201
|
+
// block (`export declare const x` — nothing defines the property, so
|
|
202
|
+
// Node reads undefined off the live object) stay unclaimed too: the
|
|
203
|
+
// receiver-path fences own them, never a wrong static read.
|
|
204
|
+
if (ts.isInterfaceDeclaration(d) || ts.isTypeAliasDeclaration(d))
|
|
205
|
+
continue;
|
|
206
|
+
if (ts.getCombinedModifierFlags(d) & ts.ModifierFlags.Ambient)
|
|
207
|
+
continue;
|
|
208
|
+
let blocked = false;
|
|
209
|
+
for (let p = d.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
|
|
210
|
+
const kind = L.nsBlocks.get(p);
|
|
211
|
+
if (kind !== undefined) {
|
|
212
|
+
if (!blocked)
|
|
213
|
+
return kind;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
if (ts.isClassDeclaration(p) || ts.isClassExpression(p) || ts.isFunctionLike(p) ||
|
|
217
|
+
ts.isInterfaceDeclaration(p) || ts.isObjectLiteralExpression(p) || ts.isEnumDeclaration(p)) {
|
|
218
|
+
blocked = true; // the member belongs to that construct, not the namespace
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
/** The program SOURCE FILE a module-namespace expression denotes: the
|
|
225
|
+
* receiver is an identifier (an `import * as ns` / `export * as ns`
|
|
226
|
+
* binding — re-export chains resolve through getAliasedSymbol) or a
|
|
227
|
+
* property access whose member itself names a module (`agg.ns` where agg
|
|
228
|
+
* re-exports `* as ns`), and the alias chain lands on a module symbol
|
|
229
|
+
* whose declaration is a NON-DECLARATION source file of this program.
|
|
230
|
+
* Builtin/stdlib module namespaces (string-named ambient modules in
|
|
231
|
+
* .d.ts files) and npm packages answer null — their own chokepoints and
|
|
232
|
+
* fences keep ownership. */
|
|
233
|
+
export function moduleNsSourceFileOf(L, e) {
|
|
234
|
+
let sym;
|
|
235
|
+
if (ts.isIdentifier(e)) {
|
|
236
|
+
sym = L.checker.getSymbolAtLocation(e);
|
|
237
|
+
}
|
|
238
|
+
else if (ts.isPropertyAccessExpression(e) &&
|
|
239
|
+
e.questionDotToken === undefined &&
|
|
240
|
+
ts.isIdentifier(e.name)) {
|
|
241
|
+
sym = L.checker.getSymbolAtLocation(e.name);
|
|
242
|
+
}
|
|
243
|
+
if (!sym)
|
|
244
|
+
return null;
|
|
245
|
+
if (sym.flags & ts.SymbolFlags.Alias)
|
|
246
|
+
sym = L.checker.getAliasedSymbol(sym);
|
|
247
|
+
for (const d of L.checker.declarationsOf(sym)) {
|
|
248
|
+
if (sym.flags & ts.SymbolFlags.ValueModule &&
|
|
249
|
+
ts.isSourceFile(d) && !d.isDeclarationFile && L.fileTag.has(d)) {
|
|
250
|
+
return d;
|
|
251
|
+
}
|
|
252
|
+
// A symbol MERGED with type-world declarations (`export type Drink =
|
|
253
|
+
// ...; export * as Drink from "./constants"`) does not alias-resolve
|
|
254
|
+
// to the bare module symbol — but its namespace-import/export
|
|
255
|
+
// declaration still names the module: resolve through the statement's
|
|
256
|
+
// own specifier.
|
|
257
|
+
if (ts.isNamespaceImport(d) || ts.isNamespaceExport(d)) {
|
|
258
|
+
const stmt = ts.isImportClause(d.parent) ? d.parent.parent : d.parent;
|
|
259
|
+
if ((ts.isImportDeclaration(stmt) || ts.isExportDeclaration(stmt)) &&
|
|
260
|
+
stmt.moduleSpecifier !== undefined &&
|
|
261
|
+
ts.isStringLiteral(stmt.moduleSpecifier)) {
|
|
262
|
+
const dep = resolveImport(L.program, d.getSourceFile(), stmt.moduleSpecifier.text);
|
|
263
|
+
if (dep && !dep.isDeclarationFile && L.fileTag.has(dep))
|
|
264
|
+
return dep;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
/** The member identifier of a qualified namespace reference, when `access`
|
|
271
|
+
* reads a member declared in a lowered (or type-only) namespace block —
|
|
272
|
+
* or an EXPORT of a program module read through its namespace-import
|
|
273
|
+
* binding (`import * as ns from "./m"; ns.f`): the checker binds the
|
|
274
|
+
* member name to the exporter's own symbol either way. Callers delegate
|
|
275
|
+
* the actual lowering to the identifier paths — the member NAME resolves
|
|
276
|
+
* through resolveValueSymbol exactly like a bare reference (globals,
|
|
277
|
+
* function values, classes, generic functions). Module-namespace member
|
|
278
|
+
* reads are LIVE bindings in Node (the namespace object's properties
|
|
279
|
+
* alias the exporter's storage), which is exactly what resolving to the
|
|
280
|
+
* exporter's module global gives. */
|
|
281
|
+
export function nsMemberIdentOf(L, access) {
|
|
282
|
+
if (access.questionDotToken !== undefined)
|
|
283
|
+
return null;
|
|
284
|
+
if (!ts.isIdentifier(access.name))
|
|
285
|
+
return null;
|
|
286
|
+
const memberSym = L.checker.getSymbolAtLocation(access.name);
|
|
287
|
+
if (!memberSym)
|
|
288
|
+
return null;
|
|
289
|
+
if (nsBlockKindOfSymbol(L, memberSym) !== null)
|
|
290
|
+
return access.name;
|
|
291
|
+
if (moduleNsSourceFileOf(L, access.expression) !== null)
|
|
292
|
+
return access.name;
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
/** The ROOT identifier of a qualifier chain that denotes a user-file
|
|
296
|
+
* AMBIENT namespace (every value declaration an ambient identifier-named
|
|
297
|
+
* ModuleDeclaration outside the stdlib/@types surface), or null. Reads
|
|
298
|
+
* through it compile to Node's ReferenceError naming the root — the
|
|
299
|
+
* ambient namespace object never exists at runtime. */
|
|
300
|
+
export function ambientNsRootOf(L, e) {
|
|
301
|
+
let root = e;
|
|
302
|
+
while (ts.isPropertyAccessExpression(root) && root.questionDotToken === undefined) {
|
|
303
|
+
root = root.expression;
|
|
304
|
+
}
|
|
305
|
+
if (!ts.isIdentifier(root))
|
|
306
|
+
return null;
|
|
307
|
+
let sym = L.checker.getSymbolAtLocation(root);
|
|
308
|
+
if (sym && sym.flags & ts.SymbolFlags.Alias)
|
|
309
|
+
sym = L.checker.getAliasedSymbol(sym);
|
|
310
|
+
if (!sym || !(sym.flags & (ts.SymbolFlags.ValueModule | ts.SymbolFlags.NamespaceModule)))
|
|
311
|
+
return null;
|
|
312
|
+
if (L.isStdlibSymbol(sym))
|
|
313
|
+
return null;
|
|
314
|
+
const decls = L.checker.declarationsOf(sym);
|
|
315
|
+
if (decls.length === 0)
|
|
316
|
+
return null;
|
|
317
|
+
for (const d of decls) {
|
|
318
|
+
// A namespace declaration, or the fundule/clazzdule merge partners
|
|
319
|
+
// (`declare function B(): ...; declare namespace B { ... }`) — every
|
|
320
|
+
// declaration must be AMBIENT in a user program file for the root to
|
|
321
|
+
// be a nothing-defines-it name.
|
|
322
|
+
if (!ts.isModuleDeclaration(d) && !ts.isFunctionDeclaration(d) && !ts.isClassDeclaration(d)) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
if (ts.isModuleDeclaration(d) && !ts.isIdentifier(d.name))
|
|
326
|
+
return null;
|
|
327
|
+
if (d.getSourceFile().isDeclarationFile)
|
|
328
|
+
return null; // lib/@types keep their own fences
|
|
329
|
+
if (ts.isModuleDeclaration(d) ? !isAmbientModuleDecl(d) : !(ts.getCombinedModifierFlags(d) & ts.ModifierFlags.Ambient)) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return root;
|
|
334
|
+
}
|
|
335
|
+
/** The ROOT identifier of a property/element-access chain that names an
|
|
336
|
+
* initializer-less AMBIENT variable (`declare const x: T;`, `declare var`)
|
|
337
|
+
* in a user program file — the `declare const __VERSION__` stance's CHAIN
|
|
338
|
+
* form. Node erases the declaration entirely, so the ROOT read throws the
|
|
339
|
+
* catchable ReferenceError "<name> is not defined" BEFORE any member or
|
|
340
|
+
* element key matters; callers lower the whole chain to that throw, typed
|
|
341
|
+
* by the use site. Mappable roots never need this (their bare-read
|
|
342
|
+
* undefRead composes through ordinary member lowerings); this serves the
|
|
343
|
+
* roots whose declared type has no mapping. Null for stdlib/@types roots
|
|
344
|
+
* (their own chokepoints stand), optional chains (conservative — the
|
|
345
|
+
* ordinary fences keep them), and anything declared with a value. */
|
|
346
|
+
export function ambientUndefVarRootOf(L, e) {
|
|
347
|
+
let root = e;
|
|
348
|
+
for (;;) {
|
|
349
|
+
if (ts.isParenthesizedExpression(root) || ts.isNonNullExpression(root)) {
|
|
350
|
+
root = root.expression;
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if ((ts.isPropertyAccessExpression(root) || ts.isElementAccessExpression(root)) &&
|
|
354
|
+
root.questionDotToken === undefined) {
|
|
355
|
+
root = root.expression;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
if (!ts.isIdentifier(root))
|
|
361
|
+
return null;
|
|
362
|
+
const sym = L.resolveValueSymbol(root);
|
|
363
|
+
if (!sym || L.isStdlibSymbol(sym))
|
|
364
|
+
return null;
|
|
365
|
+
const decls = L.checker.declarationsOf(sym);
|
|
366
|
+
if (decls.length === 0)
|
|
367
|
+
return null;
|
|
368
|
+
for (const d of decls) {
|
|
369
|
+
if (!ts.isVariableDeclaration(d) || d.initializer !== undefined)
|
|
370
|
+
return null;
|
|
371
|
+
if (d.getSourceFile().isDeclarationFile)
|
|
372
|
+
return null; // lib/@types keep their own fences
|
|
373
|
+
if (!(ts.getCombinedModifierFlags(d) & ts.ModifierFlags.Ambient))
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
return root;
|
|
377
|
+
}
|
|
378
|
+
/** ambientUndefReadType's CONTEXTUAL fallback: when the use site's own
|
|
379
|
+
* type has no mapping (an `any`-typed member off a narrowed ambient), the
|
|
380
|
+
* type the value flows INTO types the throw instead — sound because the
|
|
381
|
+
* read never returns, so the typed dummy is never observed. */
|
|
382
|
+
export function contextualUndefReadType(L, node) {
|
|
383
|
+
const ctx = L.checker.getContextualType(node);
|
|
384
|
+
const mapped = ctx ? L.mapTypeOf(ctx) : null;
|
|
385
|
+
if (mapped && mapped.kind !== "void" && !L.typeNamesUnregisteredClass(mapped))
|
|
386
|
+
return mapped;
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
/** The IR type an ambient-namespace undefRead carries at `node`: the use
|
|
390
|
+
* site's mapped type when it has one, the F64 dummy when the value is
|
|
391
|
+
* DISCARDED anyway (expression-statement position — the bare `Foo.a;`
|
|
392
|
+
* reads conformance tests are full of), null otherwise (the callers fall
|
|
393
|
+
* through to their ordinary fences). The read always throws, so the typed
|
|
394
|
+
* dummy is never observed. */
|
|
395
|
+
export function ambientUndefReadType(L, node) {
|
|
396
|
+
const declared = L.mapTypeOf(L.typeOf(node));
|
|
397
|
+
// A type naming an UNREGISTERED class (an ambient class's instance —
|
|
398
|
+
// nothing collected a shape) would make the emitter name a struct that
|
|
399
|
+
// does not exist; the F64 dummy serves discarded positions instead.
|
|
400
|
+
if (declared && declared.kind !== "void" && !L.typeNamesUnregisteredClass(declared)) {
|
|
401
|
+
return declared;
|
|
402
|
+
}
|
|
403
|
+
const p = node.parent;
|
|
404
|
+
if (p !== undefined && ts.isExpressionStatement(p))
|
|
405
|
+
return F64;
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
/** The symbol behind `ident` when it names an ambient `declare function`
|
|
409
|
+
* NOTHING defines: every function declaration on the symbol is body-less
|
|
410
|
+
* and ambient in a USER program file (merge partners — the fundule's
|
|
411
|
+
* ambient namespace, an ambient class — may ride along; type-world
|
|
412
|
+
* declarations don't count). Node erases the declaration entirely, so a
|
|
413
|
+
* reference throws the catchable ReferenceError "<name> is not defined"
|
|
414
|
+
* at the use site — the `declare const` / ambient-namespace undefRead
|
|
415
|
+
* stance. Null for stdlib/@types surfaces (their own chokepoints stand),
|
|
416
|
+
* for overload signatures (an implementation exists), and for anything
|
|
417
|
+
* declared in a .d.ts. */
|
|
418
|
+
export function ambientUndefinedFnSymbolOf(L, ident) {
|
|
419
|
+
const sym = L.resolveValueSymbol(ident);
|
|
420
|
+
if (!sym || !(sym.flags & ts.SymbolFlags.Function))
|
|
421
|
+
return null;
|
|
422
|
+
if (L.isStdlibSymbol(sym))
|
|
423
|
+
return null;
|
|
424
|
+
let sawFn = false;
|
|
425
|
+
for (const d of L.checker.declarationsOf(sym)) {
|
|
426
|
+
if (ts.isInterfaceDeclaration(d) || ts.isTypeAliasDeclaration(d))
|
|
427
|
+
continue; // type-world merge partners
|
|
428
|
+
if (ts.isFunctionDeclaration(d)) {
|
|
429
|
+
if (d.body)
|
|
430
|
+
return null; // an implementation exists — not ambient-undefined
|
|
431
|
+
sawFn = true;
|
|
432
|
+
}
|
|
433
|
+
else if (!ts.isModuleDeclaration(d) && !ts.isClassDeclaration(d)) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
if (d.getSourceFile().isDeclarationFile)
|
|
437
|
+
return null;
|
|
438
|
+
if (!(ts.getCombinedModifierFlags(d) & ts.ModifierFlags.Ambient))
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
return sawFn ? sym : null;
|
|
442
|
+
}
|
|
443
|
+
/** Node's ReferenceError at an ambient namespace access — the undefRead
|
|
444
|
+
* libCall the ambient `declare const` path already uses, typed by the use
|
|
445
|
+
* site (it always throws; the typed dummy is abandoned by the unwind). */
|
|
446
|
+
export function nsUndefRead(L, rootName, node, type) {
|
|
447
|
+
const loc = locOf(node);
|
|
448
|
+
return {
|
|
449
|
+
kind: "libCall",
|
|
450
|
+
fn: "global.undefRead",
|
|
451
|
+
args: [{ kind: "strLit", value: rootName, type: STRING, loc }],
|
|
452
|
+
type,
|
|
453
|
+
loc,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
/** True when `node` executes during module INIT (top-level statement
|
|
457
|
+
* position — namespace bodies included — not inside any function-like
|
|
458
|
+
* body). Class static blocks and field initializers count as init-time:
|
|
459
|
+
* static blocks literally are, and over-fencing an early instance-field
|
|
460
|
+
* read is conservative, never wrong output. */
|
|
461
|
+
function initExecutingPosition(node) {
|
|
462
|
+
for (let p = node.parent; p !== undefined; p = p.parent) {
|
|
463
|
+
if (ts.isFunctionLike(p))
|
|
464
|
+
return false;
|
|
465
|
+
if (ts.isSourceFile(p))
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
470
|
+
function earliestSameFileDeclStart(L, sym, sf) {
|
|
471
|
+
let earliest = Infinity;
|
|
472
|
+
for (const d of L.checker.declarationsOf(sym)) {
|
|
473
|
+
if (d.getSourceFile() === sf)
|
|
474
|
+
earliest = Math.min(earliest, d.getStart());
|
|
475
|
+
}
|
|
476
|
+
return earliest;
|
|
477
|
+
}
|
|
478
|
+
/** The source-order guard for one import=-alias segment (or a bare alias
|
|
479
|
+
* use): the alias statement captures its target's CURRENT value, so an
|
|
480
|
+
* init-position use above the alias — or an alias above its target — would
|
|
481
|
+
* observe what Node leaves uninitialized. Fences instead of diverging. */
|
|
482
|
+
export function fenceEarlyAliasUse(L, ident, refNode) {
|
|
483
|
+
const sym = L.checker.getSymbolAtLocation(ident);
|
|
484
|
+
if (!sym || !(sym.flags & ts.SymbolFlags.Alias))
|
|
485
|
+
return;
|
|
486
|
+
const aliasDecl = L.checker.declarationsOf(sym).find(ts.isImportEqualsDeclaration);
|
|
487
|
+
if (!aliasDecl || ts.isExternalModuleReference(aliasDecl.moduleReference))
|
|
488
|
+
return;
|
|
489
|
+
if (!initExecutingPosition(refNode))
|
|
490
|
+
return;
|
|
491
|
+
const sf = refNode.getSourceFile();
|
|
492
|
+
if (aliasDecl.getSourceFile() !== sf)
|
|
493
|
+
return; // cross-file: module init order applies
|
|
494
|
+
const aliasStart = aliasDecl.getStart();
|
|
495
|
+
if (refNode.getStart() < aliasStart) {
|
|
496
|
+
L.unsupported("SC1090", refNode, `reading '${ident.text}' above its \`import ${ident.text} = ...\` alias (Node binds the alias at its statement — this read would observe an uninitialized binding; move it below the alias)`);
|
|
497
|
+
}
|
|
498
|
+
// The alias itself must run AFTER its target exists: a namespace target
|
|
499
|
+
// needs a contributing block above the alias, a value target its
|
|
500
|
+
// declaration.
|
|
501
|
+
const target = L.checker.getAliasedSymbol(sym);
|
|
502
|
+
const targetStart = earliestSameFileDeclStart(L, target, sf);
|
|
503
|
+
if (targetStart !== Infinity && aliasStart < targetStart) {
|
|
504
|
+
L.unsupported("SC1090", refNode, `the \`import ${ident.text} = ...\` alias above its target's declaration (Node binds the alias at its statement, when the target is still uninitialized; move the alias below the target)`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
/** The source-order guard for a qualified namespace-member reference in
|
|
508
|
+
* init-executing position: the member's declaration (and every import=
|
|
509
|
+
* alias segment in the qualifier chain) must precede the reference —
|
|
510
|
+
* Node's namespace bodies run in source order, and `N.x` above the
|
|
511
|
+
* declaring statement observes an uninitialized binding (undefined member
|
|
512
|
+
* reads, TypeErrors on calls) that static resolution cannot reproduce.
|
|
513
|
+
* Function-body references are exempt: they take the module-globals stance
|
|
514
|
+
* the compiler already takes for top-level consts read through early
|
|
515
|
+
* calls. */
|
|
516
|
+
export function fenceEarlyNsMemberRef(L, access, memberSym) {
|
|
517
|
+
if (!initExecutingPosition(access))
|
|
518
|
+
return;
|
|
519
|
+
const sf = access.getSourceFile();
|
|
520
|
+
const refStart = access.getStart();
|
|
521
|
+
const memberStart = earliestSameFileDeclStart(L, memberSym, sf);
|
|
522
|
+
if (memberStart !== Infinity && refStart < memberStart) {
|
|
523
|
+
L.unsupported("SC1090", access, `reading namespace member '${access.name.text}' above its declaration (Node evaluates namespace bodies in source order — this read would observe an uninitialized binding; move it below the declaring block)`);
|
|
524
|
+
}
|
|
525
|
+
// Alias segments in the qualifier chain (`C.a.Origin` where `a` is
|
|
526
|
+
// `import a = A`): each captures its target at ITS statement.
|
|
527
|
+
for (let e = access.expression;;) {
|
|
528
|
+
if (ts.isPropertyAccessExpression(e) && ts.isIdentifier(e.name)) {
|
|
529
|
+
fenceEarlyAliasUse(L, e.name, access);
|
|
530
|
+
e = e.expression;
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
if (ts.isIdentifier(e))
|
|
534
|
+
fenceEarlyAliasUse(L, e, access);
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/** The cross-block bare-reference fence. tsc's checker puts every
|
|
539
|
+
* exported member of EVERY block of a merged namespace in scope inside
|
|
540
|
+
* every other block, and tsc's own emit rewrites such bare references to
|
|
541
|
+
* the qualified form — but Node's transform (the oracle) does NOT:
|
|
542
|
+
* a bare `f()` in one block referring to `export function f` in a SIBLING
|
|
543
|
+
* block throws ReferenceError at runtime under
|
|
544
|
+
* `--experimental-transform-types`. Static resolution would silently
|
|
545
|
+
* print what Node crashes on, so those references fence with the fix in
|
|
546
|
+
* the message. References to members of the SAME block or an ENCLOSING
|
|
547
|
+
* namespace's block are lexically bound in the emitted nesting and lower
|
|
548
|
+
* fine; alias bindings (import=/ESM imports) are exempt here — the alias
|
|
549
|
+
* machinery carries its own guards. Called from resolveValueSymbol with
|
|
550
|
+
* the PRE-alias symbol; property NAMES (qualified reads — runtime
|
|
551
|
+
* property lookups, always fine) never reach it. */
|
|
552
|
+
export function fenceCrossBlockNsRef(L, ident, symbol) {
|
|
553
|
+
if (L.nsBlocks.size === 0 || L.collecting)
|
|
554
|
+
return;
|
|
555
|
+
if (symbol.flags & ts.SymbolFlags.Alias)
|
|
556
|
+
return;
|
|
557
|
+
const p = ident.parent;
|
|
558
|
+
if (p !== undefined && ts.isPropertyAccessExpression(p) && p.name === ident)
|
|
559
|
+
return;
|
|
560
|
+
// The registered blocks enclosing the REFERENCE, innermost first.
|
|
561
|
+
const refBlocks = [];
|
|
562
|
+
for (let q = ident.parent; q !== undefined && !ts.isSourceFile(q); q = q.parent) {
|
|
563
|
+
if (L.nsBlocks.has(q))
|
|
564
|
+
refBlocks.push(q);
|
|
565
|
+
}
|
|
566
|
+
for (const d of L.checker.declarationsOf(symbol)) {
|
|
567
|
+
for (let q = d.parent; q !== undefined && !ts.isSourceFile(q); q = q.parent) {
|
|
568
|
+
if (!L.nsBlocks.has(q))
|
|
569
|
+
continue;
|
|
570
|
+
// Declared in a registered block: fine when that block encloses the
|
|
571
|
+
// reference too (same block, or an enclosing namespace's block).
|
|
572
|
+
if (refBlocks.includes(q))
|
|
573
|
+
return;
|
|
574
|
+
const nsName = (() => {
|
|
575
|
+
const m = q.parent;
|
|
576
|
+
return m !== undefined && ts.isModuleDeclaration(m) && ts.isIdentifier(m.name) ? m.name.text : "N";
|
|
577
|
+
})();
|
|
578
|
+
L.unsupported("SC1090", ident, `the bare reference to '${ident.text}' from a different block of the merged namespace (Node's TypeScript transform does not bind exported members across namespace blocks — it throws ReferenceError where tsc's emit would qualify; write ${nsName}.${ident.text})`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
/** Assignment-target resolution for namespace-qualified writes
|
|
583
|
+
* (`N.x = v`, `N.x += v`, `N.x++`): the member's module global — exported
|
|
584
|
+
* `let` members are ordinary mutable globals (tsc already rejected writes
|
|
585
|
+
* to consts/functions/classes through the qualifier). Null when `access`
|
|
586
|
+
* is not a lowered-namespace member at all; a member whose declaration
|
|
587
|
+
* never registered storage takes the blocked-binding cascade. */
|
|
588
|
+
export function nsWritableTarget(L, access) {
|
|
589
|
+
const nsMember = nsMemberIdentOf(L, access);
|
|
590
|
+
if (!nsMember)
|
|
591
|
+
return null;
|
|
592
|
+
const memberSym = L.checker.getSymbolAtLocation(nsMember);
|
|
593
|
+
if (memberSym)
|
|
594
|
+
fenceEarlyNsMemberRef(L, access, memberSym);
|
|
595
|
+
const resolved = L.resolveValueSymbol(nsMember);
|
|
596
|
+
const g = resolved ? L.globalsBySymbol.get(resolved) : undefined;
|
|
597
|
+
if (!g) {
|
|
598
|
+
L.rejectUnresolved(nsMember, `assignment to namespace member '${nsMember.text}' (not a writable module global)`);
|
|
599
|
+
}
|
|
600
|
+
return { id: g.id, type: g.type };
|
|
601
|
+
}
|
|
602
|
+
/** lowerStmt's ImportEqualsDeclaration case. The RUNTIME require form
|
|
603
|
+
* keeps its fence (preflight owns the top-level sites; namespace-internal
|
|
604
|
+
* ones land here); `import type x = require(...)` is pure type surface and
|
|
605
|
+
* lowers to nothing. The entity form is pure alias plumbing — references
|
|
606
|
+
* resolve through the checker to the target's own registrations and the
|
|
607
|
+
* statement emits nothing — EXCEPT aliases of MUTABLE bindings: Node's
|
|
608
|
+
* transform emits `var x = N.y` (a snapshot at the alias statement), so
|
|
609
|
+
* collection registered the alias its own const global (keyed by the
|
|
610
|
+
* pre-alias symbol — resolveValueSymbol stops there) and this statement
|
|
611
|
+
* assigns it from the target's storage, exactly when Node does. */
|
|
612
|
+
export function lowerImportEquals(L, stmt) {
|
|
613
|
+
if (stmt.isTypeOnly)
|
|
614
|
+
return null;
|
|
615
|
+
if (ts.isExternalModuleReference(stmt.moduleReference)) {
|
|
616
|
+
L.unsupported("SC1013", stmt, "import = require(...) assignments");
|
|
617
|
+
}
|
|
618
|
+
// A BARE-identifier entity target (`import g = f` inside a block):
|
|
619
|
+
// Node emits `var g = f` — the same cross-block bare-reference hole as
|
|
620
|
+
// any other bare use; the shared fence applies at the alias statement.
|
|
621
|
+
if (ts.isIdentifier(stmt.moduleReference)) {
|
|
622
|
+
const refSym = L.checker.getSymbolAtLocation(stmt.moduleReference);
|
|
623
|
+
if (refSym)
|
|
624
|
+
fenceCrossBlockNsRef(L, stmt.moduleReference, refSym);
|
|
625
|
+
}
|
|
626
|
+
// Node's transform ALWAYS emits `var x = <entity>` — it never elides
|
|
627
|
+
// type-only aliases the way tsc's emit does — so an alias whose entity
|
|
628
|
+
// ROOT names an uninstantiated (type-only or ambient) namespace throws
|
|
629
|
+
// ReferenceError AT THIS STATEMENT under the oracle. Reproduce it
|
|
630
|
+
// exactly: the statement lowers to the undefRead throw and nothing
|
|
631
|
+
// after it in this module's init runs, just like Node.
|
|
632
|
+
{
|
|
633
|
+
let root = stmt.moduleReference;
|
|
634
|
+
while (ts.isQualifiedName(root))
|
|
635
|
+
root = root.left;
|
|
636
|
+
if (ts.isIdentifier(root)) {
|
|
637
|
+
let rootSym = L.checker.getSymbolAtLocation(root);
|
|
638
|
+
if (rootSym && rootSym.flags & ts.SymbolFlags.Alias) {
|
|
639
|
+
rootSym = L.checker.getAliasedSymbol(rootSym);
|
|
640
|
+
}
|
|
641
|
+
const kind = rootSym ? nsSymbolRuntimeKind(L, rootSym) : null;
|
|
642
|
+
if (kind === "typeOnly" || kind === "ambient") {
|
|
643
|
+
const loc = locOf(stmt);
|
|
644
|
+
return {
|
|
645
|
+
kind: "exprStmt",
|
|
646
|
+
expr: nsUndefRead(L, root.text, stmt, F64),
|
|
647
|
+
loc,
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const nameSym = L.checker.getSymbolAtLocation(stmt.name);
|
|
653
|
+
const aliasG = nameSym ? L.globalsBySymbol.get(nameSym) : undefined;
|
|
654
|
+
if (nameSym && aliasG) {
|
|
655
|
+
// The snapshot assignment. The target is a mutable binding with its
|
|
656
|
+
// own module global (a namespace `export var` member, a top-level
|
|
657
|
+
// var reached through an alias chain); a target whose declaration
|
|
658
|
+
// never registered storage takes the blocked-binding cascade.
|
|
659
|
+
const target = L.checker.getAliasedSymbol(nameSym);
|
|
660
|
+
const targetG = L.globalsBySymbol.get(target);
|
|
661
|
+
if (!targetG) {
|
|
662
|
+
L.rejectUnresolvedSymbol(target, stmt.name.text, stmt.name, `the alias target of '${stmt.name.text}' (no lowered storage)`);
|
|
663
|
+
}
|
|
664
|
+
const loc = locOf(stmt);
|
|
665
|
+
return {
|
|
666
|
+
kind: "assign",
|
|
667
|
+
localId: aliasG.id,
|
|
668
|
+
value: { kind: "varRef", localId: targetG.id, type: targetG.type, loc },
|
|
669
|
+
loc,
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
/** The bare-identifier fence for namespace objects used as VALUES: the
|
|
675
|
+
* namespace has no first-class runtime object here — members lower at
|
|
676
|
+
* their qualified access sites. Ambient namespaces compile to Node's
|
|
677
|
+
* ReferenceError instead when the use site's type maps (the object never
|
|
678
|
+
* exists at runtime). Returns null when `ident` is not a namespace
|
|
679
|
+
* reference at all. */
|
|
680
|
+
export function lowerNsIdentifierValue(L, ident) {
|
|
681
|
+
const sym = L.resolveValueSymbol(ident);
|
|
682
|
+
if (!sym || !(sym.flags & (ts.SymbolFlags.ValueModule | ts.SymbolFlags.NamespaceModule))) {
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
if (sym.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Function | ts.SymbolFlags.RegularEnum | ts.SymbolFlags.ConstEnum | ts.SymbolFlags.Variable)) {
|
|
686
|
+
return null; // merged with a value declaration — its own paths and fences apply
|
|
687
|
+
}
|
|
688
|
+
const ambientRoot = ambientNsRootOf(L, ident);
|
|
689
|
+
if (ambientRoot !== null) {
|
|
690
|
+
const t = ambientUndefReadType(L, ident);
|
|
691
|
+
if (t)
|
|
692
|
+
return nsUndefRead(L, ambientRoot.text, ident, t);
|
|
693
|
+
}
|
|
694
|
+
if (L.isStdlibSymbol(sym))
|
|
695
|
+
return null; // stdlib namespaces keep their chokepoints
|
|
696
|
+
// A MODULE namespace object as a first-class value (`import * as ns`
|
|
697
|
+
// passed/stored/iterated): member accesses resolve statically, but the
|
|
698
|
+
// object itself has no runtime representation — Node's frozen,
|
|
699
|
+
// alphabetically-keyed namespace object is not materialized. Named
|
|
700
|
+
// residual of the SC1013 lowering.
|
|
701
|
+
if (moduleNsSourceFileOf(L, ident) !== null) {
|
|
702
|
+
L.unsupported("SC1013", ident, `module namespace objects as first-class values (access '${ident.text}' members directly: ${ident.text}.<member>)`);
|
|
703
|
+
}
|
|
704
|
+
L.unsupported("SC1090", ident, `namespace objects as first-class values (access '${ident.text}' members directly: ${ident.text}.<member>)`);
|
|
705
|
+
}
|
|
706
|
+
//# sourceMappingURL=lower-namespaces.js.map
|