@geastack/compiler 1.0.15 → 1.0.17
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/dist/conversion/nodes.d.ts +31 -0
- package/dist/conversion/nodes.js +46 -2
- package/dist/conversion/record-view.d.ts +24 -0
- package/dist/conversion/record-view.js +54 -0
- package/dist/ir/certify.js +23 -1
- package/dist/ir/integers.js +24 -2
- package/dist/ir/lower-operands.d.ts +10 -1
- package/dist/ir/lower-operands.js +58 -2
- package/dist/ir/lower.js +12 -3
- package/dist/ir/reflection-demand.js +5 -0
- package/dist/ir/transfer.js +17 -8
- package/dist/plugins/apple/host.d.ts +2 -0
- package/dist/plugins/apple/host.js +13 -6
- package/dist/plugins/gea/host.d.ts +2 -0
- package/dist/plugins/gea/host.js +11 -4
- package/dist/plugins/host-package.d.ts +37 -0
- package/dist/plugins/host-package.js +28 -0
- package/dist/plugins/load.js +15 -0
- package/dist/plugins/webgl/host.d.ts +9 -3
- package/dist/plugins/webgl/host.js +15 -6
- package/dist/plugins/webgl/plugin.js +4 -1
- package/dist/representation/embedded-carriers.d.ts +22 -0
- package/dist/representation/embedded-carriers.js +105 -0
- package/dist/semantics/frontend.js +4 -1
- package/dist/semantics/model/operands.d.ts +17 -0
- package/dist/semantics/model/operations.d.ts +28 -1
- package/dist/semantics/normalize/flow/callable-reach.js +1 -3
- package/dist/semantics/normalize/global-host-mutations.js +2 -2
- package/dist/semantics/normalize/producers/allocations.js +2 -0
- package/dist/semantics/normalize/producers/bindings.js +5 -1
- package/dist/semantics/normalize/producers/boundary.d.ts +1 -0
- package/dist/semantics/normalize/producers/boundary.js +6 -2
- package/dist/semantics/normalize/producers/class-lifecycle.js +3 -0
- package/dist/semantics/normalize/producers/control.js +2 -2
- package/dist/semantics/normalize/producers/erasure.d.ts +7 -0
- package/dist/semantics/normalize/producers/erasure.js +19 -0
- package/dist/semantics/normalize/producers/exact-arms.d.ts +12 -0
- package/dist/semantics/normalize/producers/exact-arms.js +12 -0
- package/dist/semantics/normalize/producers/invocations.js +33 -1
- package/dist/semantics/normalize/reachability.d.ts +20 -5
- package/dist/semantics/normalize/reachability.js +119 -14
- package/dist/semantics/normalize/specialization.js +1 -4
- package/dist/targets/cpp/class-layout.d.ts +40 -0
- package/dist/targets/cpp/class-layout.js +112 -0
- package/dist/targets/cpp/class-properties/emit-class-properties.js +4 -4
- package/dist/targets/cpp/conversions.js +63 -1
- package/dist/targets/cpp/emit-callable.js +7 -6
- package/dist/targets/cpp/emit-context.d.ts +38 -1
- package/dist/targets/cpp/emit-context.js +28 -2
- package/dist/targets/cpp/emit-narrowing.d.ts +1 -1
- package/dist/targets/cpp/emit-narrowing.js +54 -1
- package/dist/targets/cpp/emit-properties.js +13 -3
- package/dist/targets/cpp/emit-record-view.js +31 -0
- package/dist/targets/cpp/emit.d.ts +3 -3
- package/dist/targets/cpp/emit.js +11 -5
- package/dist/targets/cpp/prototype/emit-prototype-regexp.d.ts +1 -1
- package/dist/targets/cpp/prototype/emit-prototype-regexp.js +58 -2
- package/dist/targets/cpp/translation-unit.js +32 -16
- package/package.json +5 -5
- package/src/targets/cpp/runtime/gea_dynamic_proxy.h +38 -0
- package/src/targets/cpp/runtime/gea_runtime.h +247 -45
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a built-in host adapter loads the package that states its tables.
|
|
3
|
+
*
|
|
4
|
+
* Each adapter reads its host's own statement -- gea's `host-shims` and
|
|
5
|
+
* `cpp-ir`, Apple's SDK fixture and plugin, the ANGLE plugin -- by package
|
|
6
|
+
* name, which resolves from THIS compiler's `node_modules`. That copy and the
|
|
7
|
+
* checkout a build names with `--plugin <path>` are two spellings of one
|
|
8
|
+
* package, and they drift: the compiler's is an `npm pack` snapshot, the path
|
|
9
|
+
* the build passes is the sibling checkout. A table added to the checkout then
|
|
10
|
+
* never reached a compile that named the checkout explicitly, and the failure
|
|
11
|
+
* surfaced far downstream with nothing saying why (an ambient-type
|
|
12
|
+
* realization stated by the checkout fell through to another host's by-name
|
|
13
|
+
* protocols; an absent global stated by the checkout stayed live and emitted
|
|
14
|
+
* lib.dom's whole `Window`).
|
|
15
|
+
*
|
|
16
|
+
* The stated path is the authority. When `plugins/load.ts` answers a legacy
|
|
17
|
+
* package entry with its built-in adapter, the adapter ADOPTS that file:
|
|
18
|
+
* every later load resolves from the file's own package first -- a package
|
|
19
|
+
* self-reference through its `exports`, or its own dependencies -- and falls
|
|
20
|
+
* back to the compiler's copy only for a specifier the stated package cannot
|
|
21
|
+
* see at all (Apple's SDK is a peer the plugin package does not carry).
|
|
22
|
+
* `MODULE_NOT_FOUND` is the only failure the fallback absorbs: a package that
|
|
23
|
+
* resolves and then fails to load is a defect wherever it lives, and the
|
|
24
|
+
* adapters' own two-failures rule (`gea/host.ts`) still tells it apart.
|
|
25
|
+
*
|
|
26
|
+
* Adoption must precede every read. An adapter table computed at module
|
|
27
|
+
* initialization would freeze the compiler's copy before the CLI ever saw
|
|
28
|
+
* `--plugin`, so the adapters read their tables lazily and `adopt` resets
|
|
29
|
+
* whatever they cached.
|
|
30
|
+
*/
|
|
31
|
+
export interface HostPackageLoader {
|
|
32
|
+
readonly load: (specifier: string) => unknown;
|
|
33
|
+
readonly resolve: (specifier: string) => string;
|
|
34
|
+
/** Resolve from this file's package first; the caller resets its own caches. */
|
|
35
|
+
readonly adopt: (file: string) => void;
|
|
36
|
+
}
|
|
37
|
+
export declare const createHostPackageLoader: (ownUrl: string) => HostPackageLoader;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
const absent = (error) => error.code === 'MODULE_NOT_FOUND';
|
|
3
|
+
export const createHostPackageLoader = (ownUrl) => {
|
|
4
|
+
const own = createRequire(ownUrl);
|
|
5
|
+
let adopted = null;
|
|
6
|
+
const attempt = (run) => {
|
|
7
|
+
const bases = adopted === null ? [own] : [adopted, own];
|
|
8
|
+
let missing = null;
|
|
9
|
+
for (const base of bases) {
|
|
10
|
+
try {
|
|
11
|
+
return run(base);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (!absent(error))
|
|
15
|
+
throw error;
|
|
16
|
+
missing = error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
throw missing;
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
load: (specifier) => attempt((base) => base(specifier)),
|
|
23
|
+
resolve: (specifier) => attempt((base) => base.resolve(specifier)),
|
|
24
|
+
adopt: (file) => {
|
|
25
|
+
adopted = createRequire(file);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
};
|
package/dist/plugins/load.js
CHANGED
|
@@ -4,6 +4,12 @@ import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
5
|
import { installedPlugins } from './installed.js';
|
|
6
6
|
import { noPluginCapabilities } from './model.js';
|
|
7
|
+
import { adoptApplePackage } from './apple/host.js';
|
|
8
|
+
import { applePlugin } from './apple/plugin.js';
|
|
9
|
+
import { adoptGeaPackage } from './gea/host.js';
|
|
10
|
+
import { geaPlugin } from './gea/plugin.js';
|
|
11
|
+
import { adoptWebglPackage } from './webgl/host.js';
|
|
12
|
+
import { webglPlugin } from './webgl/plugin.js';
|
|
7
13
|
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
8
14
|
const messageOf = (error) => (error instanceof Error ? error.message : String(error));
|
|
9
15
|
const resolvePlugin = (specifier) => {
|
|
@@ -172,6 +178,15 @@ export const loadCliPlugins = async (specifiers) => {
|
|
|
172
178
|
const adapter = typeof plugin['configure'] === 'function' ? await legacyAdapter(file, name, exported) : undefined;
|
|
173
179
|
if (!adapter)
|
|
174
180
|
throw new Error(`plugin "${name}" is incompatible: expected instantiate(options); legacy hooks are not supported`);
|
|
181
|
+
// The adapter's tables come from the package's own statement, and the
|
|
182
|
+
// statement must be the package this path names -- not the copy the
|
|
183
|
+
// compiler's own `node_modules` happens to hold (`host-package.ts`).
|
|
184
|
+
if (adapter === webglPlugin)
|
|
185
|
+
adoptWebglPackage(file);
|
|
186
|
+
else if (adapter === geaPlugin)
|
|
187
|
+
adoptGeaPackage(file);
|
|
188
|
+
else if (adapter === applePlugin)
|
|
189
|
+
adoptApplePackage(file);
|
|
175
190
|
process.stderr.write(`compile: --plugin ${specifier}: using built-in "${adapter.name}" adapter for the legacy package\n`);
|
|
176
191
|
loadedPaths.add(file);
|
|
177
192
|
continue;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { AmbientTypeRealization } from '../../semantics/ambient-type-realization-transform.js';
|
|
2
2
|
import type { PluginSourceFile } from '../model.js';
|
|
3
|
+
/** The package entry the CLI loaded is the one this host's tables come from -- see `plugins/host-package.ts`. */
|
|
4
|
+
export declare const adoptWebglPackage: (file: string) => void;
|
|
3
5
|
/**
|
|
4
6
|
* The ambient free functions this host defines, to the C++ each is.
|
|
5
7
|
*
|
|
@@ -54,10 +56,14 @@ export declare const webglSourceTransform: (input: PluginSourceFile) => string |
|
|
|
54
56
|
* for programs that use it to detect WebGL 2. The WebGL 1 constructor is
|
|
55
57
|
* absent: this host only creates NativeWebGL2RenderingContext. In three's
|
|
56
58
|
* WebGL 1 rejection guard, absence correctly skips the rejected path.
|
|
57
|
-
* - `
|
|
58
|
-
*
|
|
59
|
+
* - `postMessage`, `importScripts`: worker globals, and whether this target
|
|
60
|
+
* has workers is not a fact about its GL context. `self` IS stated -- by the
|
|
61
|
+
* package, in its own `absentGlobals` -- because the package ships the
|
|
62
|
+
* `self`-less animation driver three's renderer is aliased to
|
|
63
|
+
* (`nativeWebGLAnimation.ts`): the one fact the package does know about
|
|
64
|
+
* `self` is that three's only use of it has already been answered natively.
|
|
59
65
|
*/
|
|
60
|
-
export declare const webglAbsentGlobals: ReadonlySet<string>;
|
|
66
|
+
export declare const webglAbsentGlobals: () => ReadonlySet<string>;
|
|
61
67
|
/**
|
|
62
68
|
* The ambient WebGL context names three's own source (and `@types/three`'s
|
|
63
69
|
* mirrored declarations) states, to the concrete class
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const
|
|
1
|
+
import { createHostPackageLoader } from '../host-package.js';
|
|
2
|
+
const packageLoader = createHostPackageLoader(import.meta.url);
|
|
3
3
|
let loaded;
|
|
4
4
|
/**
|
|
5
5
|
* The package's own statement, loaded once.
|
|
@@ -18,7 +18,7 @@ const shims = () => {
|
|
|
18
18
|
const specifier = requested ?? '@geastack/native-webgl-angle/geatsc-plugin';
|
|
19
19
|
let module_;
|
|
20
20
|
try {
|
|
21
|
-
module_ =
|
|
21
|
+
module_ = packageLoader.load(specifier);
|
|
22
22
|
}
|
|
23
23
|
catch (error) {
|
|
24
24
|
const absent = error.code === 'MODULE_NOT_FOUND';
|
|
@@ -39,6 +39,11 @@ const shims = () => {
|
|
|
39
39
|
loaded = configured;
|
|
40
40
|
return configured;
|
|
41
41
|
};
|
|
42
|
+
/** The package entry the CLI loaded is the one this host's tables come from -- see `plugins/host-package.ts`. */
|
|
43
|
+
export const adoptWebglPackage = (file) => {
|
|
44
|
+
packageLoader.adopt(file);
|
|
45
|
+
loaded = undefined;
|
|
46
|
+
};
|
|
42
47
|
/**
|
|
43
48
|
* The ambient free functions this host defines, to the C++ each is.
|
|
44
49
|
*
|
|
@@ -108,10 +113,14 @@ export const webglSourceTransform = (input) => shims()?.transformSource?.(input)
|
|
|
108
113
|
* for programs that use it to detect WebGL 2. The WebGL 1 constructor is
|
|
109
114
|
* absent: this host only creates NativeWebGL2RenderingContext. In three's
|
|
110
115
|
* WebGL 1 rejection guard, absence correctly skips the rejected path.
|
|
111
|
-
* - `
|
|
112
|
-
*
|
|
116
|
+
* - `postMessage`, `importScripts`: worker globals, and whether this target
|
|
117
|
+
* has workers is not a fact about its GL context. `self` IS stated -- by the
|
|
118
|
+
* package, in its own `absentGlobals` -- because the package ships the
|
|
119
|
+
* `self`-less animation driver three's renderer is aliased to
|
|
120
|
+
* (`nativeWebGLAnimation.ts`): the one fact the package does know about
|
|
121
|
+
* `self` is that three's only use of it has already been answered natively.
|
|
113
122
|
*/
|
|
114
|
-
export const webglAbsentGlobals = new Set([
|
|
123
|
+
export const webglAbsentGlobals = () => new Set([
|
|
115
124
|
...(shims()?.absentGlobals ?? []),
|
|
116
125
|
'WebGLRenderingContext',
|
|
117
126
|
'ImageData',
|
|
@@ -65,7 +65,10 @@ export const webglPlugin = {
|
|
|
65
65
|
hostNamespaceRootTypes: new Map(),
|
|
66
66
|
hostSingletons: new Set(),
|
|
67
67
|
hostSingletonsByDeclaration: new Map(),
|
|
68
|
-
|
|
68
|
+
// Read at instantiate, never at import: the package's statement is the
|
|
69
|
+
// one `plugins/load.ts` adopted from `--plugin`, and adoption happens
|
|
70
|
+
// after this module is loaded.
|
|
71
|
+
absentGlobals: webglAbsentGlobals(),
|
|
69
72
|
hostPreambles: webglHostPreambles(),
|
|
70
73
|
nativeBases: new Map(),
|
|
71
74
|
// Declarations arrive per spelling through `hostPreambles`, not per
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Representation } from './model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Every carrier a representation embeds, visited once each, depth first.
|
|
4
|
+
*
|
|
5
|
+
* A carrier is not only its own kind: a `record` renders as a struct whose
|
|
6
|
+
* every field is a carrier of its own, a `tagged-union` as an alternative of
|
|
7
|
+
* each arm's, a callable as a signature spelling each parameter's. What the
|
|
8
|
+
* emitted C++ names is the whole tree, so a fact that must hold of every
|
|
9
|
+
* carrier the program spells -- that a `native-handle` names a protocol the
|
|
10
|
+
* host registered -- has to be asked of the whole tree, not of its root. A
|
|
11
|
+
* census that stopped at the root let lib.dom's `Window` reach clang as a
|
|
12
|
+
* struct whose `Navigator`/`History`/`ScreenOrientation` fields named tag
|
|
13
|
+
* types no host had declared: a certified program that did not compile.
|
|
14
|
+
*
|
|
15
|
+
* The walk mirrors what the backend renders (`targets/cpp/records.ts`'s
|
|
16
|
+
* `visitRepresentation`): `native-record-ref` names a struct defined
|
|
17
|
+
* elsewhere and carries no fields, `class-ref` names a class whose layout
|
|
18
|
+
* is censused with the class, and a callable contributes its ABI carriers.
|
|
19
|
+
* A representation tree is finite -- recursion is broken by
|
|
20
|
+
* `native-record-ref` -- but each node is visited once regardless.
|
|
21
|
+
*/
|
|
22
|
+
export declare const forEachEmbeddedRepresentation: (root: Representation, visit: (carrier: Representation) => void) => void;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every carrier a representation embeds, visited once each, depth first.
|
|
3
|
+
*
|
|
4
|
+
* A carrier is not only its own kind: a `record` renders as a struct whose
|
|
5
|
+
* every field is a carrier of its own, a `tagged-union` as an alternative of
|
|
6
|
+
* each arm's, a callable as a signature spelling each parameter's. What the
|
|
7
|
+
* emitted C++ names is the whole tree, so a fact that must hold of every
|
|
8
|
+
* carrier the program spells -- that a `native-handle` names a protocol the
|
|
9
|
+
* host registered -- has to be asked of the whole tree, not of its root. A
|
|
10
|
+
* census that stopped at the root let lib.dom's `Window` reach clang as a
|
|
11
|
+
* struct whose `Navigator`/`History`/`ScreenOrientation` fields named tag
|
|
12
|
+
* types no host had declared: a certified program that did not compile.
|
|
13
|
+
*
|
|
14
|
+
* The walk mirrors what the backend renders (`targets/cpp/records.ts`'s
|
|
15
|
+
* `visitRepresentation`): `native-record-ref` names a struct defined
|
|
16
|
+
* elsewhere and carries no fields, `class-ref` names a class whose layout
|
|
17
|
+
* is censused with the class, and a callable contributes its ABI carriers.
|
|
18
|
+
* A representation tree is finite -- recursion is broken by
|
|
19
|
+
* `native-record-ref` -- but each node is visited once regardless.
|
|
20
|
+
*/
|
|
21
|
+
export const forEachEmbeddedRepresentation = (root, visit) => {
|
|
22
|
+
const seen = new Set([root]);
|
|
23
|
+
const abi = (value) => {
|
|
24
|
+
if (value === null)
|
|
25
|
+
return;
|
|
26
|
+
for (const parameter of value.parameters)
|
|
27
|
+
walk(parameter.value);
|
|
28
|
+
walk(value.result);
|
|
29
|
+
if (value.receiver !== null)
|
|
30
|
+
walk(value.receiver);
|
|
31
|
+
};
|
|
32
|
+
const walk = (value) => {
|
|
33
|
+
if (seen.has(value))
|
|
34
|
+
return;
|
|
35
|
+
seen.add(value);
|
|
36
|
+
visit(value);
|
|
37
|
+
children(value);
|
|
38
|
+
};
|
|
39
|
+
const children = (value) => {
|
|
40
|
+
switch (value.kind) {
|
|
41
|
+
case 'record':
|
|
42
|
+
for (const field of value.fields)
|
|
43
|
+
walk(field.value);
|
|
44
|
+
for (const accessor of value.accessors)
|
|
45
|
+
walk(accessor.value);
|
|
46
|
+
return;
|
|
47
|
+
case 'record-with-index':
|
|
48
|
+
for (const field of value.fields)
|
|
49
|
+
walk(field.value);
|
|
50
|
+
for (const index of value.indexes)
|
|
51
|
+
walk(index.value);
|
|
52
|
+
return;
|
|
53
|
+
case 'proxy-object':
|
|
54
|
+
walk(value.target);
|
|
55
|
+
walk(value.handler);
|
|
56
|
+
return;
|
|
57
|
+
case 'borrowed-ref':
|
|
58
|
+
walk(value.referent);
|
|
59
|
+
return;
|
|
60
|
+
case 'array-object':
|
|
61
|
+
case 'dense-buffer':
|
|
62
|
+
case 'native-sequence':
|
|
63
|
+
walk(value.element);
|
|
64
|
+
return;
|
|
65
|
+
case 'iterator':
|
|
66
|
+
walk(value.element);
|
|
67
|
+
walk(value.resume);
|
|
68
|
+
walk(value.completion);
|
|
69
|
+
return;
|
|
70
|
+
case 'promise':
|
|
71
|
+
walk(value.value);
|
|
72
|
+
return;
|
|
73
|
+
case 'keyed-collection':
|
|
74
|
+
walk(value.key);
|
|
75
|
+
if (value.value !== null)
|
|
76
|
+
walk(value.value);
|
|
77
|
+
return;
|
|
78
|
+
case 'dictionary':
|
|
79
|
+
walk(value.value);
|
|
80
|
+
return;
|
|
81
|
+
case 'optional':
|
|
82
|
+
walk(value.payload);
|
|
83
|
+
return;
|
|
84
|
+
case 'tagged-union':
|
|
85
|
+
for (const arm of value.arms)
|
|
86
|
+
walk(arm.value);
|
|
87
|
+
return;
|
|
88
|
+
case 'function':
|
|
89
|
+
case 'function-family':
|
|
90
|
+
case 'constructor-family':
|
|
91
|
+
case 'constructor-value-dispatch':
|
|
92
|
+
case 'function-value-family':
|
|
93
|
+
case 'function-value-dispatch':
|
|
94
|
+
abi(value.abi);
|
|
95
|
+
return;
|
|
96
|
+
case 'function-and-constructor':
|
|
97
|
+
abi(value.call);
|
|
98
|
+
abi(value.construct);
|
|
99
|
+
return;
|
|
100
|
+
default:
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
children(root);
|
|
105
|
+
};
|
|
@@ -44,7 +44,7 @@ import { censusRefusalCounts } from './normalize/census-refusal.js';
|
|
|
44
44
|
import { createCellPolicyRegistry, defaultCellEvidencePolicies, buildCellFactsTable, publishCellFacts } from './normalize/cells/index.js';
|
|
45
45
|
import { settleBindingCensus } from './normalize/binding-fixpoint.js';
|
|
46
46
|
import { withJsDocTypeNames } from './normalize/jsdoc-type-names.js';
|
|
47
|
-
import { censusReachability, moduleEvaluationOrder, nodeIsReachable } from './normalize/reachability.js';
|
|
47
|
+
import { censusReachability, fileEvaluates, moduleEvaluationOrder, nodeIsReachable } from './normalize/reachability.js';
|
|
48
48
|
import { createProgram, defaultCompilerOptions } from './program.js';
|
|
49
49
|
import { createFrontendTiming } from './frontend-timing.js';
|
|
50
50
|
import { classHeritageOf } from './class-heritage.js';
|
|
@@ -1105,7 +1105,10 @@ export const runFrontend = (input) => {
|
|
|
1105
1105
|
// reachability walk pruned whole, or one whose top level censused nothing,
|
|
1106
1106
|
// has no region -- and a name in this list that no region answers would be
|
|
1107
1107
|
// a call to a function nothing defines.
|
|
1108
|
+
// `fileEvaluates` is the reached-statement half of that: a file kept only
|
|
1109
|
+
// for a layout-only class has a region and, by design, no body.
|
|
1108
1110
|
moduleOrder: moduleEvaluationOrder({ checker: compiled.checker, files: compiled.sourceFiles, entries: compiled.entryFiles })
|
|
1111
|
+
.filter((file) => fileEvaluates(reachable, file))
|
|
1109
1112
|
.map((file) => regionId(identities.nodeIdOf(file), 'module-body'))
|
|
1110
1113
|
.filter((region) => normalized.graph.regions.has(region)),
|
|
1111
1114
|
sourceFileNames: identities.sourceFileNames,
|
|
@@ -98,6 +98,23 @@ export interface SemanticOperand {
|
|
|
98
98
|
* (`spread-arguments.ts`). Absent means 0, the whole source.
|
|
99
99
|
*/
|
|
100
100
|
readonly from?: number;
|
|
101
|
+
/**
|
|
102
|
+
* The program's own type assertion (`x as T`, `<T>x`) wraps the expression
|
|
103
|
+
* this operand cites. The assertion is erased -- it evaluates nothing and
|
|
104
|
+
* changes no value, which is why the operand cites the wrapped expression's
|
|
105
|
+
* result and carries its type -- but it is also the author STATING which
|
|
106
|
+
* arm of a union the value holds here, the same promise `@gea-exact-arms`
|
|
107
|
+
* makes for a whole body (`ConversionRoleTarget.owner`'s `exact-arm`). A
|
|
108
|
+
* lowering that must put the value into a slot that is exactly one arm of
|
|
109
|
+
* its union, and that the census has no sound per-arm answer for, may take
|
|
110
|
+
* the stated arm -- checked at runtime, a `TypeError` when the assertion
|
|
111
|
+
* was false. hono's `Context.executionCtx` returns `this.#executionCtx as
|
|
112
|
+
* ExecutionContext` off a `FetchEventLike | ExecutionContext` field: the
|
|
113
|
+
* class arm has no view as the interface, so without the assertion the
|
|
114
|
+
* store is refused, and with it the store is the projection the author
|
|
115
|
+
* wrote. An assertion to `any`/`unknown` states no arm and is not marked.
|
|
116
|
+
*/
|
|
117
|
+
readonly asserted?: true;
|
|
101
118
|
}
|
|
102
119
|
/** One result an operation publishes, keyed by the role it fills. */
|
|
103
120
|
export interface SemanticResult {
|
|
@@ -17,7 +17,17 @@ import type { HostMethodBinding } from '../host-methods.js';
|
|
|
17
17
|
export interface ConversionRoleTarget {
|
|
18
18
|
readonly role: string;
|
|
19
19
|
readonly ordinal: number;
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* `exact-arm` is the caller's half of `AllocationOperation.exactArms`: for
|
|
22
|
+
* an argument into an `@gea-exact-arms` implementation, `type` is the
|
|
23
|
+
* parameter type of the overload the checker RESOLVED, not the
|
|
24
|
+
* implementation's union slot the `parameter-slot` role names. The lowering
|
|
25
|
+
* enters that arm of the union and no other, so the arm the body projects
|
|
26
|
+
* is the arm the checker chose -- without it a `() => 2` under `Typed |
|
|
27
|
+
* Generic` widens into whichever arm can adapt it first, and the body's
|
|
28
|
+
* projection then throws on a call the checker accepted.
|
|
29
|
+
*/
|
|
30
|
+
readonly owner: 'parameter-slot' | 'array-element' | 'declared-field' | 'exact-arm';
|
|
21
31
|
readonly type: StructuralTypeId;
|
|
22
32
|
}
|
|
23
33
|
/**
|
|
@@ -311,6 +321,23 @@ export interface AllocationOperation extends SemanticOperationBase {
|
|
|
311
321
|
* the same condition as `functionSource`.
|
|
312
322
|
*/
|
|
313
323
|
readonly generatorFunction?: boolean;
|
|
324
|
+
/**
|
|
325
|
+
* Whether the declaration carries `@gea-exact-arms`: inside this body, a
|
|
326
|
+
* tagged-union value entering a slot that is EXACTLY one of its arms
|
|
327
|
+
* projects that arm and throws a `TypeError` on any other, instead of the
|
|
328
|
+
* per-arm dispatch that converts every arm into the target.
|
|
329
|
+
*
|
|
330
|
+
* A fact of the declaration because the checker cannot state it. TS
|
|
331
|
+
* function assignability makes any two callable arms convertible in at
|
|
332
|
+
* least one direction, so `fn as RequestListener` over `EventHandler |
|
|
333
|
+
* RequestListener` always installs an adapter for the catch-all arm -- and
|
|
334
|
+
* that adapter publishes the typed arm's parameters to a dynamic listener,
|
|
335
|
+
* which is precisely what an overload implementation keeping typed and
|
|
336
|
+
* generic listeners in separate storage never does. Only the author knows
|
|
337
|
+
* the branch guarding the cast makes the other arms unreachable, so the
|
|
338
|
+
* author states it, per body, and pays with a runtime check.
|
|
339
|
+
*/
|
|
340
|
+
readonly exactArms?: boolean;
|
|
314
341
|
/**
|
|
315
342
|
* Whether the declaration performs ECMA-262 10.2.5 `MakeConstructor` and so
|
|
316
343
|
* owns a `prototype` object -- true for an ordinary `function` declaration
|
|
@@ -5146,9 +5146,7 @@ const closedMemberCallableUses = (checker, flow, member, countedCalls, givenRece
|
|
|
5146
5146
|
const origins = key === null ? null : allocationOriginsOf(receiver);
|
|
5147
5147
|
traceClosedCallee(call, 'invocationTargetsOf/origins', `key=${key ?? 'null'} origins=${origins === null ? 'null' : origins.classes.size}`);
|
|
5148
5148
|
if (origins !== null) {
|
|
5149
|
-
const privateDeclarations = ts.isPropertyAccessExpression(callee) && ts.isPrivateIdentifier(callee.name)
|
|
5150
|
-
? privateMemberDeclarationsOf(callee.name)
|
|
5151
|
-
: null;
|
|
5149
|
+
const privateDeclarations = ts.isPropertyAccessExpression(callee) && ts.isPrivateIdentifier(callee.name) ? privateMemberDeclarationsOf(callee.name) : null;
|
|
5152
5150
|
const selected = new Set();
|
|
5153
5151
|
for (const owner of origins.classes) {
|
|
5154
5152
|
const slotDeclarations = privateDeclarations ??
|
|
@@ -4416,7 +4416,7 @@ computedKeysOf = () => null, trustSeed = initialCensusSeed) => {
|
|
|
4416
4416
|
// value.
|
|
4417
4417
|
if ((ts.isImportSpecifier(parent) || ts.isExportSpecifier(parent)) && parent.propertyName === node)
|
|
4418
4418
|
return true;
|
|
4419
|
-
return ((
|
|
4419
|
+
return ((ts.isVariableDeclaration(parent) ||
|
|
4420
4420
|
ts.isImportSpecifier(parent) ||
|
|
4421
4421
|
ts.isExportSpecifier(parent) ||
|
|
4422
4422
|
ts.isImportClause(parent) ||
|
|
@@ -4441,7 +4441,7 @@ computedKeysOf = () => null, trustSeed = initialCensusSeed) => {
|
|
|
4441
4441
|
ts.isPropertyAssignment(parent) ||
|
|
4442
4442
|
ts.isShorthandPropertyAssignment(parent) ||
|
|
4443
4443
|
ts.isTypeParameterDeclaration(parent)) &&
|
|
4444
|
-
parent.name === node)
|
|
4444
|
+
parent.name === node);
|
|
4445
4445
|
};
|
|
4446
4446
|
let globalEscapeAnswer = null;
|
|
4447
4447
|
const globalObjectEscapesIntoData = () => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { operationId, semanticResultId } from '../../../identity/ids.js';
|
|
3
3
|
import { bindingKindOf } from './binding-kind.js';
|
|
4
|
+
import { declaresExactArms } from './exact-arms.js';
|
|
4
5
|
import { blocked, mintOperationId, mintResult, operand } from './mint.js';
|
|
5
6
|
import { citeExpressionResult } from './references.js';
|
|
6
7
|
import { sourceForValue, valueEdgesInto, hasNativeIterationCursor, staticPropertyKeyTextOf, isRuntimeSymbolMember, staticSpreadMembersOf, staticFunctionNameOf, staticClassNameOf, staticClassLengthOf, expectedParameterCountOf, ownPrototypePropertyOf } from './shared.js';
|
|
@@ -797,6 +798,7 @@ export const createAllocationProducer = (context) => ({
|
|
|
797
798
|
functionName: staticFunctionNameOf(node),
|
|
798
799
|
functionLength: expectedParameterCountOf(node),
|
|
799
800
|
generatorFunction: 'asteriskToken' in node && node.asteriskToken !== undefined,
|
|
801
|
+
...(declaresExactArms(node) ? { exactArms: true } : {}),
|
|
800
802
|
...(ownPrototypePropertyOf(node) === null ? {} : { ownPrototypeProperty: ownPrototypePropertyOf(node) === true })
|
|
801
803
|
}
|
|
802
804
|
: {}),
|
|
@@ -6,6 +6,7 @@ import { normalCompletion, pureEffects } from '../../model/operands.js';
|
|
|
6
6
|
import { blocked, mintOperationId, mintResult, operand } from './mint.js';
|
|
7
7
|
import { bindingKindOf, bindingKindOfElement } from './binding-kind.js';
|
|
8
8
|
import { boundElementType, citeBoundElementValue } from './destructuring.js';
|
|
9
|
+
import { assertsType } from './erasure.js';
|
|
9
10
|
import { citeExpressionResult } from './references.js';
|
|
10
11
|
import { resultEdge } from './shared.js';
|
|
11
12
|
/**
|
|
@@ -643,7 +644,10 @@ const contributeVariableDeclaration = (candidate, node, context) => {
|
|
|
643
644
|
const cited = citeExpressionResult(initializer, context);
|
|
644
645
|
if (cited.kind === 'unmodelled')
|
|
645
646
|
return { kind: 'blocked', blocker: blocked(candidate.id, 'binding', cited.reason, null) };
|
|
646
|
-
operands.push(
|
|
647
|
+
operands.push({
|
|
648
|
+
...operand('initializer', 0, cited.source, context.types.typeAt(initializer)),
|
|
649
|
+
...(assertsType(initializer, context.checker) ? { asserted: true } : {})
|
|
650
|
+
});
|
|
647
651
|
}
|
|
648
652
|
else if (loopValueResult) {
|
|
649
653
|
operands.push(operand('initializer', 0, { kind: 'result', result: loopValueResult }, context.types.typeAt(node)));
|
|
@@ -21,6 +21,7 @@ import type { ProducerContext } from '../producer-context.js';
|
|
|
21
21
|
export declare const resolveExpressionOperand: (context: ProducerContext, node: ts.Node) => {
|
|
22
22
|
readonly source: OperandSource;
|
|
23
23
|
readonly type: StructuralTypeId;
|
|
24
|
+
readonly asserted?: true;
|
|
24
25
|
} | null;
|
|
25
26
|
/**
|
|
26
27
|
* Catch clauses: the one candidate family `'boundary'` receives from the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { normalCompletion, pureEffects } from '../../model/operands.js';
|
|
3
|
-
import { unwrapErased } from './erasure.js';
|
|
3
|
+
import { assertsType, unwrapErased } from './erasure.js';
|
|
4
4
|
import { blocked, mintOperationId, mintResult, operand } from './mint.js';
|
|
5
5
|
import { citeExpressionResult } from './references.js';
|
|
6
6
|
import { valueEdgesInto } from './shared.js';
|
|
@@ -25,7 +25,11 @@ export const resolveExpressionOperand = (context, node) => {
|
|
|
25
25
|
const cited = citeExpressionResult(node, context);
|
|
26
26
|
if (cited.kind === 'unmodelled')
|
|
27
27
|
return null;
|
|
28
|
-
return {
|
|
28
|
+
return {
|
|
29
|
+
source: cited.source,
|
|
30
|
+
type: context.types.typeAt(unwrapErased(node)),
|
|
31
|
+
...(assertsType(node, context.checker) ? { asserted: true } : {})
|
|
32
|
+
};
|
|
29
33
|
};
|
|
30
34
|
/**
|
|
31
35
|
* Catch clauses: the one candidate family `'boundary'` receives from the
|
|
@@ -4,6 +4,7 @@ import { normalCompletion, pureEffects, throwingCompletion } from '../../model/o
|
|
|
4
4
|
import { isParameterProperty } from '../census.js';
|
|
5
5
|
import { parameterBindingResultOf } from './bindings.js';
|
|
6
6
|
import { parameterSlotTypeOf } from '../parameter-slot.js';
|
|
7
|
+
import { declaresExactArms } from './exact-arms.js';
|
|
7
8
|
import { mintOperationId, mintResult, operand } from './mint.js';
|
|
8
9
|
import { resultOf } from '../../model/operands.js';
|
|
9
10
|
import { asBlocked, resultEdge, uniqueSymbolKeyTextOf, staticFunctionNameOf, staticClassNameOf, staticClassLengthOf, expectedParameterCountOf, ownPrototypePropertyOf } from './shared.js';
|
|
@@ -209,6 +210,7 @@ export const createClassLifecycleProducer = (context) => {
|
|
|
209
210
|
? {
|
|
210
211
|
functionSource: callable.getText(),
|
|
211
212
|
generatorFunction: 'asteriskToken' in callable && callable.asteriskToken !== undefined,
|
|
213
|
+
...(declaresExactArms(callable) ? { exactArms: true } : {}),
|
|
212
214
|
...(ownPrototypePropertyOf(callable) === null ? {} : { ownPrototypeProperty: ownPrototypePropertyOf(callable) === true }),
|
|
213
215
|
...(isNamedCallableMember(callable)
|
|
214
216
|
? { functionName: staticFunctionNameOf(callable), functionLength: expectedParameterCountOf(callable) }
|
|
@@ -301,6 +303,7 @@ export const createClassLifecycleProducer = (context) => {
|
|
|
301
303
|
classConstructorBodyOf: context.identities.declarationIdOf(node),
|
|
302
304
|
functionSource: written.getText(),
|
|
303
305
|
generatorFunction: 'asteriskToken' in written && written.asteriskToken !== undefined,
|
|
306
|
+
...(declaresExactArms(written) ? { exactArms: true } : {}),
|
|
304
307
|
...(ownPrototypePropertyOf(written) === null ? {} : { ownPrototypeProperty: ownPrototypePropertyOf(written) === true }),
|
|
305
308
|
caller: candidate.caller,
|
|
306
309
|
operands: [],
|
|
@@ -436,7 +436,7 @@ const contributeReturn = (context, candidate, node) => {
|
|
|
436
436
|
const resolved = resolveExpressionOperand(context, node.expression);
|
|
437
437
|
if (!resolved)
|
|
438
438
|
return blockedContribution(candidate, 'no normalized operation identifies the returned value');
|
|
439
|
-
valueOperand = operand('value', 0, resolved.source, resolved.type);
|
|
439
|
+
valueOperand = { ...operand('value', 0, resolved.source, resolved.type), ...(resolved.asserted ? { asserted: true } : {}) };
|
|
440
440
|
}
|
|
441
441
|
else {
|
|
442
442
|
const undefinedType = context.table.intern({ kind: 'primitive', primitive: 'undefined' });
|
|
@@ -487,7 +487,7 @@ const contributeImplicitReturn = (context, candidate, node) => {
|
|
|
487
487
|
id,
|
|
488
488
|
form: 'return',
|
|
489
489
|
caller: candidate.caller,
|
|
490
|
-
operands: [operand('value', 0, resolved.source, resolved.type)],
|
|
490
|
+
operands: [{ ...operand('value', 0, resolved.source, resolved.type), ...(resolved.asserted ? { asserted: true } : {}) }],
|
|
491
491
|
results: [mintResult(id, 'completion', resolved.type)],
|
|
492
492
|
completion: { canThrow: false, canReturn: true, canBreak: false, canContinue: false, canSuspend: false },
|
|
493
493
|
effects: pureEffects,
|
|
@@ -18,6 +18,13 @@ import ts from 'typescript';
|
|
|
18
18
|
export declare const unwrapErased: (node: ts.Node) => ts.Node;
|
|
19
19
|
/** The same rule where the caller already knows it holds an expression. */
|
|
20
20
|
export declare const unwrapErasedExpression: (expression: ts.Expression) => ts.Expression;
|
|
21
|
+
/**
|
|
22
|
+
* Whether one of the erased wrappers around `expression` is a type assertion
|
|
23
|
+
* that names a type -- `SemanticOperand.asserted`. `as any`/`as unknown`
|
|
24
|
+
* assert nothing about which arm a value holds, so they do not count; `!` and
|
|
25
|
+
* `satisfies` are not assertions of a type at all.
|
|
26
|
+
*/
|
|
27
|
+
export declare const assertsType: (expression: ts.Expression, checker: ts.TypeChecker) => boolean;
|
|
21
28
|
/**
|
|
22
29
|
* The node whose PARENT decides this expression's syntactic position.
|
|
23
30
|
*
|
|
@@ -31,6 +31,25 @@ export const unwrapErased = (node) => {
|
|
|
31
31
|
};
|
|
32
32
|
/** The same rule where the caller already knows it holds an expression. */
|
|
33
33
|
export const unwrapErasedExpression = (expression) => unwrapErased(expression);
|
|
34
|
+
/**
|
|
35
|
+
* Whether one of the erased wrappers around `expression` is a type assertion
|
|
36
|
+
* that names a type -- `SemanticOperand.asserted`. `as any`/`as unknown`
|
|
37
|
+
* assert nothing about which arm a value holds, so they do not count; `!` and
|
|
38
|
+
* `satisfies` are not assertions of a type at all.
|
|
39
|
+
*/
|
|
40
|
+
export const assertsType = (expression, checker) => {
|
|
41
|
+
let current = expression;
|
|
42
|
+
for (;;) {
|
|
43
|
+
if (ts.isAsExpression(current) || ts.isTypeAssertionExpression(current)) {
|
|
44
|
+
if ((checker.getTypeFromTypeNode(current.type).flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) === 0)
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
else if (!ts.isParenthesizedExpression(current) && !ts.isNonNullExpression(current) && !ts.isSatisfiesExpression(current)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
current = current.expression;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
34
53
|
/**
|
|
35
54
|
* The node whose PARENT decides this expression's syntactic position.
|
|
36
55
|
*
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a callable declaration states `@gea-exact-arms` -- see
|
|
4
|
+
* `AllocationOperation.exactArms` for what the tag promises and why only the
|
|
5
|
+
* author can promise it.
|
|
6
|
+
*
|
|
7
|
+
* Read off the declaration that owns the body, which for an overloaded method
|
|
8
|
+
* is the implementation signature: the overloads are types, and the tag
|
|
9
|
+
* changes how the one body lowers, so the one body's own JSDoc is where it
|
|
10
|
+
* belongs.
|
|
11
|
+
*/
|
|
12
|
+
export declare const declaresExactArms: (node: ts.Node) => boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a callable declaration states `@gea-exact-arms` -- see
|
|
4
|
+
* `AllocationOperation.exactArms` for what the tag promises and why only the
|
|
5
|
+
* author can promise it.
|
|
6
|
+
*
|
|
7
|
+
* Read off the declaration that owns the body, which for an overloaded method
|
|
8
|
+
* is the implementation signature: the overloads are types, and the tag
|
|
9
|
+
* changes how the one body lowers, so the one body's own JSDoc is where it
|
|
10
|
+
* belongs.
|
|
11
|
+
*/
|
|
12
|
+
export const declaresExactArms = (node) => ts.getJSDocTags(node).some((tag) => tag.tagName.text === 'gea-exact-arms');
|