@intentius/tsad-reference 1.4.0
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/CAVEATS.md +95 -0
- package/README.md +42 -0
- package/dist/adapter.d.ts +5 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +50 -0
- package/dist/adapter.js.map +1 -0
- package/dist/fnbody.d.ts +11 -0
- package/dist/fnbody.d.ts.map +1 -0
- package/dist/fnbody.js +68 -0
- package/dist/fnbody.js.map +1 -0
- package/dist/fold.d.ts +86 -0
- package/dist/fold.d.ts.map +1 -0
- package/dist/fold.js +558 -0
- package/dist/fold.js.map +1 -0
- package/dist/foldable-helpers.d.ts +23 -0
- package/dist/foldable-helpers.d.ts.map +1 -0
- package/dist/foldable-helpers.js +25 -0
- package/dist/foldable-helpers.js.map +1 -0
- package/dist/host.d.ts +45 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +5 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +20 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +74 -0
- package/dist/module.js.map +1 -0
- package/dist/project.d.ts +22 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +481 -0
- package/dist/project.js.map +1 -0
- package/dist/revive.d.ts +16 -0
- package/dist/revive.d.ts.map +1 -0
- package/dist/revive.js +109 -0
- package/dist/revive.js.map +1 -0
- package/dist/subset.d.ts +42 -0
- package/dist/subset.d.ts.map +1 -0
- package/dist/subset.js +214 -0
- package/dist/subset.js.map +1 -0
- package/package.json +37 -0
- package/src/adapter.ts +48 -0
- package/src/fnbody.ts +61 -0
- package/src/fold.ts +581 -0
- package/src/foldable-helpers.ts +38 -0
- package/src/host.ts +38 -0
- package/src/index.ts +8 -0
- package/src/module.ts +52 -0
- package/src/no-own-execution.test.ts +86 -0
- package/src/prebuild.test.ts +68 -0
- package/src/project.ts +495 -0
- package/src/revive.ts +120 -0
- package/src/subset.ts +224 -0
package/CAVEATS.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# What the reference implementation does not do, and what it found
|
|
2
|
+
|
|
3
|
+
This package is written from `spec/` and nothing else (#50). Where the
|
|
4
|
+
specification is silent, ambiguous, or asks for something the package cannot
|
|
5
|
+
supply, the gap is recorded here rather than papered over in the code.
|
|
6
|
+
|
|
7
|
+
## Two identity predicates
|
|
8
|
+
|
|
9
|
+
Resolved in the specification by **F-Identity** (J3), which names the entity
|
|
10
|
+
test and the reference test, says the entity test is the normative one, and
|
|
11
|
+
says F-Import over-approximates on purpose. This package follows it: the
|
|
12
|
+
reference test for import and re-export captures, the entity test for call
|
|
13
|
+
leaks. Kept here as a pointer, because the code comments cite F-Identity and
|
|
14
|
+
a reader of an older revision will not find it (#59).
|
|
15
|
+
|
|
16
|
+
## Revival needs a host, and one envelope has no fate here
|
|
17
|
+
|
|
18
|
+
**F-Val-Fate** is implemented (#61): a declarator's value is revived through
|
|
19
|
+
the folding file's own imports, so a `{__resource}` becomes a real instance of
|
|
20
|
+
the class the host supplies, `{__intrinsic}` and `{__helper}` are invoked, and
|
|
21
|
+
`{__symbol}` resolves as a dotted chain. `{__attrRef}` passes through, and is
|
|
22
|
+
rejected inside a host call's arguments per **F-Val-Position**.
|
|
23
|
+
|
|
24
|
+
Two limits remain.
|
|
25
|
+
|
|
26
|
+
`{__compositeStep}` has no fate here. Its revival is "resolve the composite
|
|
27
|
+
(J2 F-Call), then read `.step` off the real result", and this package has no
|
|
28
|
+
composite factory form, so revival rejects rather than guessing.
|
|
29
|
+
|
|
30
|
+
Revival can only construct what a host supplies. With `EMPTY_HOST` an envelope
|
|
31
|
+
has no class to become and revival rejects, which is correct rather than
|
|
32
|
+
silent: a build that folds a resource and cannot revive it has not folded the
|
|
33
|
+
file. Conformance fixtures name the host they need.
|
|
34
|
+
|
|
35
|
+
## The composite factory form, measured
|
|
36
|
+
|
|
37
|
+
The corpus cross-check (#25) puts a number on the gap above: 272 of chant's
|
|
38
|
+
409 corpus files reach a composite factory call, directly or through a file
|
|
39
|
+
that does, and none of them can be compared until this package has the form.
|
|
40
|
+
The count is in `packages/conformance/corpus-report.md`.
|
|
41
|
+
|
|
42
|
+
## Capture reaches inside an entity
|
|
43
|
+
|
|
44
|
+
**F-Capture** is decided over the produced namespace, and the walk that decides
|
|
45
|
+
it descends through an entity's own data properties, enumerable or not, and
|
|
46
|
+
through a `WeakRef` to what it holds. chant's entity classes keep their props
|
|
47
|
+
on a non-enumerable property and an attribute reference keeps its entity
|
|
48
|
+
behind a `WeakRef`, so a walk over `Object.values` records no capture for the
|
|
49
|
+
two ordinary cases: a shared object passed to a constructor, and an output
|
|
50
|
+
built from another file's attribute. Neither was visible until the corpus ran
|
|
51
|
+
against a real host (#25), because with `EMPTY_HOST` an entity never becomes
|
|
52
|
+
an instance. Accessors are skipped, not invoked; one of chant's throws when
|
|
53
|
+
read too early.
|
|
54
|
+
|
|
55
|
+
## Same-file constructions are pre-built
|
|
56
|
+
|
|
57
|
+
**F-Prebuild** (#68) is implemented: every top-level `const n = new T(…)` is
|
|
58
|
+
constructed once, in source order, before any declarator is evaluated, and a
|
|
59
|
+
reference to it reads that instance. This package originally rejected such a
|
|
60
|
+
reference, which was the reading F-Eval-Ident step 1 licensed before the rule
|
|
61
|
+
existed; the corpus found twelve chant examples that fold the shape.
|
|
62
|
+
|
|
63
|
+
## Capture at the import, and over the namespace
|
|
64
|
+
|
|
65
|
+
**F-Import** records a capture at the import for any imported value with
|
|
66
|
+
identity, by F-Identity's reference test; **F-Capture** is decided over the
|
|
67
|
+
produced namespace as well. This package did only the second until the
|
|
68
|
+
corpus at `chant-v0.71.0` showed three files that read a primitive out of an
|
|
69
|
+
imported object, held no object in their namespace, and were tainted by
|
|
70
|
+
chant as F-Import's text says; it now does both (#96). A project-local
|
|
71
|
+
function is excluded at the import, since it is a callable rather than a
|
|
72
|
+
value and F-CallLeak decides its edge at the call.
|
|
73
|
+
|
|
74
|
+
## No rules, and no provenance
|
|
75
|
+
|
|
76
|
+
`rules.md` (spec `1.4`) specifies the contract a semantic rule runs under. This package runs none: it has no rule hook, and no value provenance, so a finding it produced could name no source line. Both wait on the harness half (#101).
|
|
77
|
+
|
|
78
|
+
## No filesystem, no module resolution algorithm
|
|
79
|
+
|
|
80
|
+
`foldProject` takes a map of path to source. Specifier resolution joins the
|
|
81
|
+
specifier to the importer's directory, normalises `..` segments, and tries
|
|
82
|
+
the three obvious candidates, `g`, `g.ts`, `g/index.ts`, against that map's
|
|
83
|
+
keys. An earlier version left `../` specifiers unjoined, which dropped an
|
|
84
|
+
import edge and a forward taint with it; the corpus found it (#96). The
|
|
85
|
+
specification does not define a resolution algorithm, and does not need to:
|
|
86
|
+
J3's `→` is "`f` imports `g`, `g ∈ F`" for whatever resolution the host uses.
|
|
87
|
+
A conformance fixture therefore never depends on a resolution subtlety.
|
|
88
|
+
|
|
89
|
+
## No isolation mode
|
|
90
|
+
|
|
91
|
+
`ι` is always `open`. **F-IsolatedRefusal** distinguishes a mode in which
|
|
92
|
+
resolving a binding would import or invoke project code, and this package
|
|
93
|
+
never imports or invokes anything: every call it admits is J1's project-local
|
|
94
|
+
call, which F-IsolatedRefusal explicitly does not restrict. The mode is
|
|
95
|
+
therefore unobservable here, not unimplemented.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @intentius/tsad-reference
|
|
2
|
+
|
|
3
|
+
A reference implementation of the specification in `spec/`, written from the
|
|
4
|
+
specification text and nothing else (#50).
|
|
5
|
+
|
|
6
|
+
## What it implements, and what it does not
|
|
7
|
+
|
|
8
|
+
Implemented: shape classification, expression evaluation (J1), the per-file
|
|
9
|
+
verdict and the identity-taint fixpoint (J2, J3, since #60), revival through
|
|
10
|
+
a host's real constructors (F-Val-Fate, since #64), and the host interface's
|
|
11
|
+
registry, helper allowlist, owned-specifier prefixes and values. It declares
|
|
12
|
+
the specification version it implements (`specVersion` on its adapter), and
|
|
13
|
+
the suite holds that to `spec/VERSION`.
|
|
14
|
+
|
|
15
|
+
Not implemented, with the reason for each: `CAVEATS.md`. The composite factory
|
|
16
|
+
form and isolation mode are the two that cost fixtures.
|
|
17
|
+
|
|
18
|
+
## As a package
|
|
19
|
+
|
|
20
|
+
`@intentius/tsad-reference` depends on `@intentius/tsad-conformance` for the
|
|
21
|
+
adapter and host types only. Its major and minor are the specification version
|
|
22
|
+
it implements; the patch is its own.
|
|
23
|
+
|
|
24
|
+
## Provenance
|
|
25
|
+
|
|
26
|
+
From #19 until #50 these files were a *port* of chant's, kept current by a
|
|
27
|
+
sync script and a list of recorded cuts. That guaranteed agreement and made
|
|
28
|
+
agreement uninformative: the cross-check was comparing the same code with
|
|
29
|
+
itself. #50 replaced the ported evaluation layer with one written from the
|
|
30
|
+
specification text.
|
|
31
|
+
|
|
32
|
+
**One honest limit on how independent that is.** The author of the rewrite had
|
|
33
|
+
previously read chant's implementation closely, while writing the
|
|
34
|
+
specification from it. The rewrite was done from `grammar.md` and
|
|
35
|
+
`judgments.md` without consulting the source, but it is not the test a reader
|
|
36
|
+
who had never seen chant would constitute. What it does establish is that the
|
|
37
|
+
specification text is complete enough to implement from, and it found one
|
|
38
|
+
place where it was not: `spec/grammar.md`'s definition of an *unclaimed*
|
|
39
|
+
callee, which four normative sentences used and none defined (#51).
|
|
40
|
+
|
|
41
|
+
The cut list and the sync script are in git history at the commit that removed
|
|
42
|
+
them, as the record of what the host interface was derived from.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ConformanceAdapter } from "@intentius/tsad-conformance";
|
|
2
|
+
export declare const referenceAdapter: ConformanceAdapter;
|
|
3
|
+
/** The same implementation judged in the data-host profile: no runtime, and S-ExportDefault admitted. */
|
|
4
|
+
export declare const referenceDataHostAdapter: ConformanceAdapter;
|
|
5
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAkC,MAAM,6BAA6B,CAAC;AA6CtG,eAAO,MAAM,gBAAgB,EAAE,kBAAuC,CAAC;AACvE,yGAAyG;AACzG,eAAO,MAAM,wBAAwB,EAAE,kBAA4C,CAAC"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { EMPTY_HOST } from "./host.js";
|
|
2
|
+
import { shapeOfExport, foldExport } from "./module.js";
|
|
3
|
+
import { foldProject } from "./project.js";
|
|
4
|
+
/** A named conformance host, in this implementation's own terms. */
|
|
5
|
+
function hostOf(h, profile) {
|
|
6
|
+
if (!h)
|
|
7
|
+
return { ...EMPTY_HOST, profile };
|
|
8
|
+
return {
|
|
9
|
+
profile,
|
|
10
|
+
intrinsics: h.intrinsics.map((i) => ({ name: i.name, isTag: i.isTag, foldsAsCall: i.foldsAsCall, foldsEagerly: i.foldsEagerly, outputKey: i.outputKey })),
|
|
11
|
+
helpers: h.helpers,
|
|
12
|
+
ownedSpecifierPrefixes: h.ownedSpecifierPrefixes,
|
|
13
|
+
values: h.values,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** The reference reports spec rule identifiers directly: it is written from the spec. One adapter per profile (F-Profile). */
|
|
17
|
+
function adapterFor(profile) {
|
|
18
|
+
return {
|
|
19
|
+
name: profile === "full" ? "reference" : `reference/${profile}`,
|
|
20
|
+
// Bumped by hand when the rule set this package implements moves; the
|
|
21
|
+
// conformance suite fails when it and spec/VERSION disagree (#18).
|
|
22
|
+
specVersion: "1.4",
|
|
23
|
+
shape(source, exportName) {
|
|
24
|
+
const v = shapeOfExport(source, exportName, { ...EMPTY_HOST, profile });
|
|
25
|
+
if (v === "no-such-export")
|
|
26
|
+
return { accepted: false, line: 1, column: 1, message: `no export named ${exportName}` };
|
|
27
|
+
if (!v)
|
|
28
|
+
return { accepted: true };
|
|
29
|
+
const { line, character } = v.node.getSourceFile().getLineAndCharacterOfPosition(v.node.getStart());
|
|
30
|
+
return { accepted: false, rule: v.rule, line: line + 1, column: character + 1, message: v.message };
|
|
31
|
+
},
|
|
32
|
+
foldExport(source, exportName) { return foldExport(source, exportName, { ...EMPTY_HOST, profile }); },
|
|
33
|
+
foldProject(files, host) {
|
|
34
|
+
const r = foldProject(files, hostOf(host, profile));
|
|
35
|
+
const out = { verdicts: {}, tentative: {}, taintedBy: {} };
|
|
36
|
+
for (const [path, v] of r.verdicts) {
|
|
37
|
+
out.verdicts[path] = v.kind === "fold" ? { kind: "fold", exports: Object.fromEntries(v.exports) } : { kind: "run", rule: v.rule, reason: v.reason };
|
|
38
|
+
}
|
|
39
|
+
for (const [path, v] of r.tentative)
|
|
40
|
+
out.tentative[path] = v.kind;
|
|
41
|
+
for (const [path, from] of r.taintSource)
|
|
42
|
+
out.taintedBy[path] = from;
|
|
43
|
+
return out;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export const referenceAdapter = adapterFor("full");
|
|
48
|
+
/** The same implementation judged in the data-host profile: no runtime, and S-ExportDefault admitted. */
|
|
49
|
+
export const referenceDataHostAdapter = adapterFor("data-host");
|
|
50
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/fnbody.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S-FnBody, grammar.md §3: the statement sub-grammar a project-local function
|
|
3
|
+
* must satisfy before J1's F-Eval-CallLocal will evaluate it.
|
|
4
|
+
*/
|
|
5
|
+
import * as ts from "typescript";
|
|
6
|
+
export type FnDecl = ts.FunctionDeclaration | ts.ArrowFunction | ts.FunctionExpression;
|
|
7
|
+
/** A binding element the sub-grammar admits: `{ a }` or `{ a: b }`, nothing else. */
|
|
8
|
+
export declare function plainBindingKey(el: ts.BindingElement): string | undefined;
|
|
9
|
+
/** A reason the function is outside S-FnBody, or undefined when it is admissible. */
|
|
10
|
+
export declare function findFnBodyViolation(fn: FnDecl): string | undefined;
|
|
11
|
+
//# sourceMappingURL=fnbody.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fnbody.d.ts","sourceRoot":"","sources":["../src/fnbody.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,MAAM,MAAM,MAAM,GAAG,EAAE,CAAC,mBAAmB,GAAG,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,CAAC;AAEvF,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,cAAc,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA4ClE"}
|
package/dist/fnbody.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S-FnBody, grammar.md §3: the statement sub-grammar a project-local function
|
|
3
|
+
* must satisfy before J1's F-Eval-CallLocal will evaluate it.
|
|
4
|
+
*/
|
|
5
|
+
import * as ts from "typescript";
|
|
6
|
+
/** A binding element the sub-grammar admits: `{ a }` or `{ a: b }`, nothing else. */
|
|
7
|
+
export function plainBindingKey(el) {
|
|
8
|
+
if (el.dotDotDotToken || el.initializer || !ts.isIdentifier(el.name))
|
|
9
|
+
return undefined;
|
|
10
|
+
const key = el.propertyName ?? el.name;
|
|
11
|
+
return ts.isIdentifier(key) || ts.isStringLiteral(key) || ts.isNumericLiteral(key) ? key.text : undefined;
|
|
12
|
+
}
|
|
13
|
+
/** A reason the function is outside S-FnBody, or undefined when it is admissible. */
|
|
14
|
+
export function findFnBodyViolation(fn) {
|
|
15
|
+
if (fn.asteriskToken)
|
|
16
|
+
return "a generator function is not foldable";
|
|
17
|
+
if (fn.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword))
|
|
18
|
+
return "an async function is not foldable";
|
|
19
|
+
if (!fn.body)
|
|
20
|
+
return "a function without a body is not foldable";
|
|
21
|
+
for (const p of fn.parameters) {
|
|
22
|
+
if (p.dotDotDotToken)
|
|
23
|
+
return "a rest parameter is not foldable";
|
|
24
|
+
if (ts.isIdentifier(p.name))
|
|
25
|
+
continue;
|
|
26
|
+
if (ts.isObjectBindingPattern(p.name)) {
|
|
27
|
+
for (const el of p.name.elements) {
|
|
28
|
+
if (plainBindingKey(el) === undefined)
|
|
29
|
+
return "a destructured parameter with a rest, default, or nested element is not foldable";
|
|
30
|
+
}
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
return "an array-destructured parameter is not foldable";
|
|
34
|
+
}
|
|
35
|
+
if (!ts.isBlock(fn.body))
|
|
36
|
+
return undefined; // a concise expression body
|
|
37
|
+
const statements = fn.body.statements;
|
|
38
|
+
for (let i = 0; i < statements.length; i += 1) {
|
|
39
|
+
const st = statements[i];
|
|
40
|
+
const last = i === statements.length - 1;
|
|
41
|
+
if (ts.isReturnStatement(st)) {
|
|
42
|
+
if (!last)
|
|
43
|
+
return "an early `return` is not foldable";
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (!ts.isVariableStatement(st)) {
|
|
47
|
+
return `\`${ts.SyntaxKind[st.kind]}\` in a function body is not foldable, only \`const\` declarations and a final \`return\` are`;
|
|
48
|
+
}
|
|
49
|
+
if ((st.declarationList.flags & ts.NodeFlags.Const) === 0)
|
|
50
|
+
return "`let`/`var` in a function body is not foldable";
|
|
51
|
+
for (const d of st.declarationList.declarations) {
|
|
52
|
+
if (!d.initializer)
|
|
53
|
+
return "an uninitialized `const` in a function body is not foldable";
|
|
54
|
+
if (ts.isIdentifier(d.name))
|
|
55
|
+
continue;
|
|
56
|
+
if (ts.isObjectBindingPattern(d.name)) {
|
|
57
|
+
for (const el of d.name.elements) {
|
|
58
|
+
if (plainBindingKey(el) === undefined)
|
|
59
|
+
return "a destructured `const` with a rest, default, or nested element is not foldable";
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
return "an array-destructured `const` in a function body is not foldable";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=fnbody.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fnbody.js","sourceRoot":"","sources":["../src/fnbody.ts"],"names":[],"mappings":"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"}
|
package/dist/fold.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expression evaluation: judgments.md J1, one branch per F-Eval-* rule.
|
|
3
|
+
*
|
|
4
|
+
* Written from the specification text, not derived from any implementation
|
|
5
|
+
* (#50). Executes nothing of the source under fold. Its only executions are
|
|
6
|
+
* F-Eval-CallEager and F-Eval-CallMethod on a real receiver, both of which
|
|
7
|
+
* run code the file imported rather than code it wrote.
|
|
8
|
+
*/
|
|
9
|
+
import * as ts from "typescript";
|
|
10
|
+
import { type IntrinsicDef } from "./host.js";
|
|
11
|
+
import { type FnDecl } from "./fnbody.js";
|
|
12
|
+
/** A located rejection, per R9.3: the node and the rule, wording unconstrained. */
|
|
13
|
+
export declare class FoldRejection extends Error {
|
|
14
|
+
readonly rule: string;
|
|
15
|
+
readonly line: number;
|
|
16
|
+
readonly column: number;
|
|
17
|
+
constructor(rule: string, line: number, column: number, message: string);
|
|
18
|
+
}
|
|
19
|
+
/** Γ = (consts, externals, depth), per R6.6. */
|
|
20
|
+
export interface Scope {
|
|
21
|
+
readonly consts: Map<string, ts.Expression>;
|
|
22
|
+
readonly externals: ReadonlyMap<string, unknown>;
|
|
23
|
+
readonly depth: number;
|
|
24
|
+
/**
|
|
25
|
+
* F-Capture's sink for the file being folded, when there is one. A call that
|
|
26
|
+
* leaks the defining module's identity (F-CallLeak) records that module here,
|
|
27
|
+
* which is the same edge an import capture records.
|
|
28
|
+
*/
|
|
29
|
+
readonly captures?: Set<string>;
|
|
30
|
+
}
|
|
31
|
+
/** H = (ρ, helpers). The helper allowlist is consulted through foldable-helpers. */
|
|
32
|
+
export interface EvalHost {
|
|
33
|
+
readonly intrinsics: readonly IntrinsicDef[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* F-Val-Callable's marker. J1 may *call* one; it is never a value. Only the
|
|
37
|
+
* module layer populates `externals` with these, so nothing produces one in
|
|
38
|
+
* this implementation yet (#21, #22).
|
|
39
|
+
*/
|
|
40
|
+
export declare class FoldableFunction {
|
|
41
|
+
readonly name: string;
|
|
42
|
+
readonly fn: FnDecl;
|
|
43
|
+
/** The defining module's path, for the re-anchored reason of F-Eval-CallLocal step 7. */
|
|
44
|
+
readonly file: string;
|
|
45
|
+
/** The DEFINING module's scope: a body folds there, not in the caller's (R6.6). */
|
|
46
|
+
readonly consts: Map<string, ts.Expression>;
|
|
47
|
+
readonly externals: ReadonlyMap<string, unknown>;
|
|
48
|
+
/** Set when a call returned a live object the body produced (F-CallLeak, J3). */
|
|
49
|
+
leakedIdentity: boolean;
|
|
50
|
+
constructor(name: string, fn: FnDecl,
|
|
51
|
+
/** The defining module's path, for the re-anchored reason of F-Eval-CallLocal step 7. */
|
|
52
|
+
file: string,
|
|
53
|
+
/** The DEFINING module's scope: a body folds there, not in the caller's (R6.6). */
|
|
54
|
+
consts: Map<string, ts.Expression>, externals: ReadonlyMap<string, unknown>);
|
|
55
|
+
}
|
|
56
|
+
export declare const isFoldableFunction: (v: unknown) => v is FoldableFunction;
|
|
57
|
+
/**
|
|
58
|
+
* F-Val-Live: carries a live object when it, or anything reachable through
|
|
59
|
+
* plain objects and arrays, has a prototype other than the plain ones, or is
|
|
60
|
+
* a function. This is F-Identity's entity test, the normative one, and the
|
|
61
|
+
* test F-CallLeak uses. F-Import uses the broader reference test instead, on
|
|
62
|
+
* purpose; F-Identity in J3 says why.
|
|
63
|
+
*/
|
|
64
|
+
export declare function carriesLiveObject(v: unknown, seen?: Set<unknown>): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* The non-recursive half of F-Val-Live: this value *is* a live object, rather
|
|
67
|
+
* than a plain structure that may hold one. Revival passes these through
|
|
68
|
+
* unchanged (L6.1); rebuilding one would destroy the identity J3 preserves.
|
|
69
|
+
*/
|
|
70
|
+
export declare function isLiveObject(v: unknown): boolean;
|
|
71
|
+
export declare function isEnvelope(v: unknown): boolean;
|
|
72
|
+
/** The depth bound of F-Eval-CallLocal step 2, stated per F-Depth. */
|
|
73
|
+
export declare const MAX_CALL_DEPTH = 32;
|
|
74
|
+
/** Γ, H ⊢ e ⇓ v. */
|
|
75
|
+
export declare function foldExpr(node: ts.Expression, scope: Scope, host: EvalHost): unknown;
|
|
76
|
+
/** F-Bind: a top-level `const` with an identifier name and an initializer. */
|
|
77
|
+
export declare function collectConsts(sf: ts.SourceFile): Map<string, ts.Expression>;
|
|
78
|
+
/**
|
|
79
|
+
* S-LocalFunction, F-Bind: every top-level `function` declaration, exported or
|
|
80
|
+
* not, and every top-level `const` bound to an arrow or function expression,
|
|
81
|
+
* as `FoldableFunction` markers for the file's own scope. A call reaches
|
|
82
|
+
* F-Eval-CallLocal; a use as a value is F-Eval-Ident step 3's rejection. The
|
|
83
|
+
* body is judged by S-FnBody at the call, never here (spec 1.2, #95).
|
|
84
|
+
*/
|
|
85
|
+
export declare function collectLocalFunctions(sf: ts.SourceFile, file: string, consts: Map<string, ts.Expression>, externals: ReadonlyMap<string, unknown>): FoldableFunction[];
|
|
86
|
+
//# sourceMappingURL=fold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../src/fold.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAwC,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAEhF,mFAAmF;AACnF,qBAAa,aAAc,SAAQ,KAAK;IAEpC,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAFd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM;CAKlB;AAED,gDAAgD;AAChD,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACjC;AACD,oFAAoF;AACpF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,UAAU,EAAE,SAAS,YAAY,EAAE,CAAC;CAC9C;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAIzB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM;IACnB,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;IATlD,iFAAiF;IACjF,cAAc,UAAS;gBAEZ,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM;IACnB,yFAAyF;IAChF,IAAI,EAAE,MAAM;IACrB,mFAAmF;IAC1E,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,EAClC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;CAEnD;AACD,eAAO,MAAM,kBAAkB,GAAI,GAAG,OAAO,KAAG,CAAC,IAAI,gBAAiD,CAAC;AASvG;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,eAAqB,GAAG,OAAO,CAMhF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAKhD;AAID,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAG9C;AAqGD,sEAAsE;AACtE,eAAO,MAAM,cAAc,KAAK,CAAC;AAqEjC,oBAAoB;AACpB,wBAAgB,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CA6PnF;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,CAY3E;AAKD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,EAAE,CAAC,UAAU,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,EAClC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GACtC,gBAAgB,EAAE,CAepB"}
|