dsh-ptc-plus 0.3.2 → 0.4.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/README.en.md +63 -80
- package/README.md +63 -80
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-repair-zh.png +0 -0
- package/assets/ptc-plus-repl-workspace-en.png +0 -0
- package/assets/ptc-plus-repl-workspace-zh.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +30827 -742
- package/compiler-amaro.cjs +5 -0
- package/compiler-amaro.cjs.map +6 -0
- package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
- package/compiler-assets.json +3 -0
- package/compiler-core.cjs +556 -0
- package/compiler-core.cjs.map +6 -0
- package/compiler-platform.cjs +2 -0
- package/compiler-platform.cjs.map +6 -0
- package/compiler-typescript.cjs +388 -0
- package/compiler-typescript.cjs.map +6 -0
- package/docs/README.md +7 -0
- package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
- package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +5 -3
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
- package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
- package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
- package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
- package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +3 -1
- package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
- package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
- package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +5 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +12 -0
- package/docs/adr/0023-global-user-bindings.md +104 -0
- package/docs/adr/0024-repl-console-observation.md +33 -0
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
- package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
- package/docs/architecture.md +77 -21
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +95 -19
- package/docs/durability-design.md +28 -10
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +76 -25
- package/docs/semantic-validation.md +29 -0
- package/docs/stateful-binding-updates-proposal.md +759 -0
- package/docs/user-bindings.en.md +66 -0
- package/docs/user-bindings.md +66 -0
- package/docs/value-wire.md +3 -3
- package/docs/verification-optimization-work.md +41 -0
- package/docs/verification.md +71 -0
- package/index.js +369 -92
- package/internal/ast-traversal.js +5 -2
- package/internal/async-local-storage-intrinsics.js +38 -0
- package/internal/binding-declaration-contract.js +8 -0
- package/internal/binding-descriptors.js +1 -0
- package/internal/binding-pattern-traversal.js +25 -0
- package/internal/binding-pattern.js +26 -29
- package/internal/binding-source-analysis.js +708 -0
- package/internal/binding-update-policy.js +72 -0
- package/internal/bounded-worker-round.js +56 -0
- package/internal/callable-definition-environment.js +42 -0
- package/internal/callable-source-catalog.js +129 -0
- package/internal/callable-source-encoding.js +47 -0
- package/internal/callable-source-facts.js +459 -0
- package/internal/callable-source-format.js +2 -0
- package/internal/callable-source-lowering.js +123 -0
- package/internal/cell-analysis-contract.js +25 -0
- package/internal/cell-analysis.js +253 -290
- package/internal/cell-completion.js +15 -0
- package/internal/cell-error.js +7 -0
- package/internal/cell-parser.js +66 -0
- package/internal/cell-rewriter.js +3 -677
- package/internal/commonjs-export-evidence.js +63 -0
- package/internal/commonjs-source-evidence.js +72 -0
- package/internal/compiler-amaro.cjs +2 -0
- package/internal/compiler-compression.js +12 -0
- package/internal/compiler-data.js +58 -0
- package/internal/compiler-descriptors.js +54 -0
- package/internal/compiler-dynamic-cache.js +48 -0
- package/internal/compiler-entry.js +35 -0
- package/internal/compiler-generated-names.js +17 -0
- package/internal/compiler-intrinsics.js +50 -0
- package/internal/compiler-module-links.js +37 -0
- package/internal/compiler-operations.js +61 -0
- package/internal/compiler-platform-factory.js +84 -0
- package/internal/compiler-platform.js +136 -0
- package/internal/compiler-record-roles.js +53 -0
- package/internal/compiler-region-output.js +259 -0
- package/internal/compiler-resource-lowering.js +103 -0
- package/internal/compiler-scope-facts.js +115 -0
- package/internal/compiler-service.js +109 -0
- package/internal/compiler-source-cache.js +32 -0
- package/internal/compiler-source-pieces.js +93 -0
- package/internal/compiler-source-regions.js +132 -0
- package/internal/compiler-storage-source.js +54 -0
- package/internal/compiler-text.js +15 -0
- package/internal/compiler-typescript.cjs +2 -0
- package/internal/config-spec.js +83 -27
- package/internal/cordis-tools-owner.js +130 -21
- package/internal/diagnostic.js +27 -1
- package/internal/direct-surface-owner.js +76 -23
- package/internal/dynamic-binding-evidence.js +16 -0
- package/internal/dynamic-environment-compiler.js +779 -0
- package/internal/dynamic-environment-integration.js +91 -0
- package/internal/dynamic-environment-runtime.js +383 -0
- package/internal/dynamic-native-calls.js +153 -0
- package/internal/dynamic-native-runtime.js +262 -0
- package/internal/dynamic-scope-analysis.js +90 -0
- package/internal/edit-transport-owner.js +27 -2
- package/internal/execution-seam-compat.js +193 -0
- package/internal/failure-reporting.js +136 -28
- package/internal/host-rpc.js +66 -0
- package/internal/isolated-worker.js +555 -0
- package/internal/kernel-child.js +139 -0
- package/internal/kernel-worker.js +1179 -233
- package/internal/language-semantics.js +14 -0
- package/internal/legacy-binding-catalog.js +48 -0
- package/internal/legacy-cell-analysis.js +600 -0
- package/internal/legacy-cell-parser.js +50 -0
- package/internal/legacy-cell-rewriter.js +631 -0
- package/internal/legacy-repl-convenience.js +291 -0
- package/internal/managed-module-operations.js +170 -0
- package/internal/module-compilation.js +322 -0
- package/internal/module-runtime-platform.js +19 -0
- package/internal/module-transform-contract.js +42 -0
- package/internal/native-await-declarations.js +68 -0
- package/internal/native-root-compilation.js +54 -0
- package/internal/native-root-dynamic.js +67 -0
- package/internal/record-utils.js +24 -15
- package/internal/recovery-tips.js +27 -15
- package/internal/rejected-cell-editor.js +13 -8
- package/internal/repl-convenience.js +2 -289
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +6 -0
- package/internal/repl-scope-normalizer.js +2091 -0
- package/internal/repl-value-observer.js +117 -0
- package/internal/rpc-contract.js +52 -0
- package/internal/runtime-bridge-owner.js +172 -28
- package/internal/runtime-config.js +48 -52
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-intrinsics.js +101 -0
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +402 -87
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +123 -0
- package/internal/session-journal.js +199 -654
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +257 -95
- package/internal/session-state.js +247 -71
- package/internal/source-position-map.js +230 -71
- package/internal/stateful-module-compiler.js +154 -0
- package/internal/stateful-module-runtime.js +303 -0
- package/internal/stateful-root-compiler.js +600 -0
- package/internal/stateful-root-runtime.js +340 -0
- package/internal/typert-codec-compat.js +28 -0
- package/internal/typescript-transform.js +31 -0
- package/internal/user-binding-authoring.js +70 -0
- package/internal/user-binding-console-worker.js +244 -0
- package/internal/user-binding-console.js +148 -0
- package/internal/user-binding-draft-projection.js +335 -0
- package/internal/user-binding-model-context.js +31 -0
- package/internal/user-binding-runner.js +56 -0
- package/internal/user-bindings-owner.js +1065 -0
- package/internal/user-bindings-store.js +293 -0
- package/internal/user-bindings.js +361 -0
- package/internal/value-wire-schema.js +28 -0
- package/internal/value-wire.js +130 -91
- package/internal/worker-client.js +57 -67
- package/internal/worker-cwd-virtualization.js +80 -53
- package/internal/worker-environment.js +65 -0
- package/internal/worker-output-relay.js +29 -0
- package/internal/worker-repl-realm.js +134 -0
- package/internal/worker-shutdown.js +14 -0
- package/package.json +76 -7
- package/src/client-activity.js +95 -53
- package/src/client-authoring-view.js +580 -0
- package/src/client-binding-review.js +230 -0
- package/src/client-bindings-data.js +29 -0
- package/src/client-catalog.js +127 -0
- package/src/client-code-editor.js +84 -0
- package/src/client-console.js +112 -0
- package/src/client-copy.js +370 -0
- package/src/client-feature-gates.js +62 -0
- package/src/client-host-compat.js +150 -0
- package/src/client-indicator-view.js +267 -0
- package/src/client-repl-view.js +292 -0
- package/src/client-rpc.js +20 -0
- package/src/client-settings-view.js +221 -0
- package/src/client-styles.js +106 -0
- package/src/client-tool-view.js +108 -0
- package/src/client-transport.js +159 -0
- package/src/client-workbench.js +521 -0
- package/src/client.js +233 -615
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
/** Compile each cell into a private native frame backed by session identities. */
|
|
2
|
+
import { adaptRootModuleImports } from './managed-module-operations.js'
|
|
3
|
+
import { parse } from '@babel/parser'
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
import traverseModule from '@babel/traverse'
|
|
6
|
+
import { bindingNodes, createGeneratedNameAllocator, isWriteIdentifier } from './binding-pattern.js'
|
|
7
|
+
import { applySourceEdits, createMappedTextBuilder, identitySourceMap, mapSourceSpan } from './source-position-map.js'
|
|
8
|
+
import { CELL_PARSER_PLUGINS, RECOVERABLE_CELL_PARSE_ERRORS } from './repl-scope-normalizer.js'
|
|
9
|
+
import { adaptDynamicCell } from './dynamic-environment-integration.js'
|
|
10
|
+
import { sourceBinding, varInitializerTarget } from './dynamic-scope-analysis.js'
|
|
11
|
+
|
|
12
|
+
const traverse = traverseModule.default ?? traverseModule
|
|
13
|
+
const PARSER_OPTIONS = {
|
|
14
|
+
sourceType: 'script', allowAwaitOutsideFunction: true, allowReturnOutsideFunction: true,
|
|
15
|
+
allowImportExportEverywhere: true, allowUndeclaredExports: true, plugins: CELL_PARSER_PLUGINS,
|
|
16
|
+
}
|
|
17
|
+
const TYPE_ONLY = new Set(['TSInterfaceDeclaration', 'TSTypeAliasDeclaration', 'TSDeclareFunction'])
|
|
18
|
+
|
|
19
|
+
function span(node) {
|
|
20
|
+
return { line: node.loc.start.line, column: node.loc.start.column + 1,
|
|
21
|
+
end: { line: node.loc.end.line, column: node.loc.end.column + 1 } }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function rootDeclarations(tree) {
|
|
25
|
+
const declarations = []
|
|
26
|
+
traverse(tree, { noScope: true,
|
|
27
|
+
VariableDeclaration(path) {
|
|
28
|
+
if (path.node.declare === true) return
|
|
29
|
+
const direct = path.parent.type === 'Program' || path.parent.type === 'ExportNamedDeclaration'
|
|
30
|
+
if (direct || path.node.kind === 'var' && path.getFunctionParent() === null
|
|
31
|
+
&& path.findParent(parent => parent.isClass() || parent.isStaticBlock()) === null) declarations.push(path)
|
|
32
|
+
},
|
|
33
|
+
FunctionDeclaration(path) {
|
|
34
|
+
let owner = path.parentPath
|
|
35
|
+
while (owner.isLabeledStatement()) owner = owner.parentPath
|
|
36
|
+
if (owner.isProgram() || owner.isExportNamedDeclaration()
|
|
37
|
+
|| owner.isExportDefaultDeclaration()) declarations.push(path)
|
|
38
|
+
},
|
|
39
|
+
ClassDeclaration(path) {
|
|
40
|
+
if (path.parent.type === 'Program' || path.parent.type === 'ExportNamedDeclaration'
|
|
41
|
+
|| path.parent.type === 'ExportDefaultDeclaration') declarations.push(path)
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
return declarations
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function patternText(pattern, code, reference) {
|
|
48
|
+
const ids = new Set(bindingNodes(pattern))
|
|
49
|
+
const edits = []
|
|
50
|
+
const visit = (node, parent, grandparent) => {
|
|
51
|
+
if (node === null || typeof node !== 'object') return
|
|
52
|
+
if (ids.has(node)) {
|
|
53
|
+
const shorthand = parent?.type === 'ObjectProperty' && parent.shorthand && parent.value === node
|
|
54
|
+
|| parent?.type === 'AssignmentPattern' && grandparent?.type === 'ObjectProperty' && grandparent.shorthand
|
|
55
|
+
edits.push({ start: node.start, end: node.end,
|
|
56
|
+
text: `${shorthand ? `[${JSON.stringify(node.name)}]: ` : ''}${reference(node)}` })
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
for (const [key, value] of Object.entries(node)) {
|
|
60
|
+
if (['loc', 'start', 'end', 'extra'].includes(key)) continue
|
|
61
|
+
if (Array.isArray(value)) value.forEach(child => visit(child, node, parent))
|
|
62
|
+
else if (value?.type !== undefined) visit(value, node, parent)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
visit(pattern)
|
|
66
|
+
const selected = edits.sort((a, b) => a.start - b.start)
|
|
67
|
+
const builder = createMappedTextBuilder(code)
|
|
68
|
+
let offset = pattern.start
|
|
69
|
+
for (const edit of selected) {
|
|
70
|
+
builder.appendSource(offset, edit.start)
|
|
71
|
+
builder.appendMapped(edit.text, edit.start, edit.end)
|
|
72
|
+
offset = edit.end
|
|
73
|
+
}
|
|
74
|
+
builder.appendSource(offset, pattern.end)
|
|
75
|
+
return builder.result()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function ownsImplicitArguments(path) {
|
|
79
|
+
return path.node.name === 'arguments'
|
|
80
|
+
&& path.findParent(parent => parent.isFunction() && !parent.isArrowFunctionExpression()) !== null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function rewriteRootReferences(code, sourceMap, runtime, candidateNames, candidateReferences, originalSource) {
|
|
84
|
+
const tree = parse(code, PARSER_OPTIONS)
|
|
85
|
+
const edits = []
|
|
86
|
+
const classificationEdits = []
|
|
87
|
+
const handled = new Set()
|
|
88
|
+
const implicitDeclarations = new Map()
|
|
89
|
+
const readNames = new Set()
|
|
90
|
+
const dynamicFunctions = new WeakSet()
|
|
91
|
+
traverse(tree, { CallExpression(path) {
|
|
92
|
+
if (path.node.callee.type === 'Identifier' && path.node.callee.name === 'eval') {
|
|
93
|
+
const fn = path.getFunctionParent()
|
|
94
|
+
if (fn !== null) dynamicFunctions.add(fn.node)
|
|
95
|
+
}
|
|
96
|
+
} })
|
|
97
|
+
const sourceIdentity = createHash('sha256').update(originalSource).digest('hex')
|
|
98
|
+
traverse(tree, { Identifier(path) {
|
|
99
|
+
if (!isWriteIdentifier(path) || sourceBinding(path, path.node.name) !== undefined || ownsImplicitArguments(path)) return
|
|
100
|
+
const assignment = path.findParent(parent => parent.isAssignmentExpression() || parent.isUpdateExpression()
|
|
101
|
+
|| parent.isForOfStatement() || parent.isForInStatement())
|
|
102
|
+
const position = mapSourceSpan(span(path.node), code, originalSource, sourceMap)
|
|
103
|
+
implicitDeclarations.set(path.node.start, { name: path.node.name, kind: 'variable', writable: true,
|
|
104
|
+
target: `write:${sourceIdentity}:${position.line}:${position.column}:${path.node.name}`,
|
|
105
|
+
definitionSpan: mapSourceSpan(span(assignment.node), code, originalSource, sourceMap) })
|
|
106
|
+
} })
|
|
107
|
+
const candidateFor = path => {
|
|
108
|
+
const assignment = path.findParent(parent => parent.isAssignmentExpression()
|
|
109
|
+
&& (parent.node.left.type === 'ArrayPattern' || parent.node.left.type === 'ObjectPattern'))
|
|
110
|
+
if (assignment === null || path.node.start > assignment.node.left.end) return undefined
|
|
111
|
+
for (const [candidate, names] of candidateNames) {
|
|
112
|
+
if (!names.has(path.node.name)) continue
|
|
113
|
+
const mentions = node => {
|
|
114
|
+
if (node === null || typeof node !== 'object') return false
|
|
115
|
+
if (node.type === 'Identifier' && node.name === candidate) return true
|
|
116
|
+
return Object.values(node).some(value => Array.isArray(value)
|
|
117
|
+
? value.some(mentions) : value?.type !== undefined && mentions(value))
|
|
118
|
+
}
|
|
119
|
+
if (mentions(assignment.node.left)) return candidate
|
|
120
|
+
}
|
|
121
|
+
return undefined
|
|
122
|
+
}
|
|
123
|
+
traverse(tree, { Decorator(path) {
|
|
124
|
+
const expression = path.node.expression
|
|
125
|
+
if (expression.extra?.parenthesized) return
|
|
126
|
+
// A computed root reference has a wider grammar than a bare decorator name.
|
|
127
|
+
const boundaries = [{ start: expression.start, end: expression.start, text: '(' },
|
|
128
|
+
{ start: expression.end, end: expression.end, text: ')' }]
|
|
129
|
+
edits.push(...boundaries)
|
|
130
|
+
classificationEdits.push(...boundaries)
|
|
131
|
+
}, TSTypeAssertion(path) {
|
|
132
|
+
const edit = { start: path.node.start, end: path.node.expression.start,
|
|
133
|
+
text: ' '.repeat(path.node.expression.start - path.node.start) }
|
|
134
|
+
edits.push(edit)
|
|
135
|
+
classificationEdits.push(edit)
|
|
136
|
+
}, Identifier(path) {
|
|
137
|
+
if (handled.has(path.node.start) || sourceBinding(path, path.node.name) !== undefined || ownsImplicitArguments(path)) return
|
|
138
|
+
if (!path.isReferencedIdentifier() && !isWriteIdentifier(path)) return
|
|
139
|
+
if (path.findParent(parent => parent.isVariableDeclarator() && parent.node.id.name === runtime
|
|
140
|
+
|| parent.isCallExpression() && parent.node.callee.type === 'MemberExpression'
|
|
141
|
+
&& parent.node.callee.object.name === runtime && parent.node.callee.property.name === 'import'
|
|
142
|
+
&& path.node.start >= parent.node.arguments[1].start && path.node.end <= parent.node.arguments[1].end) !== null) return
|
|
143
|
+
const parent = path.parent
|
|
144
|
+
// The direct-eval environment adapter owns this syntax and its callable
|
|
145
|
+
// identity check. A property read here would turn it into indirect eval.
|
|
146
|
+
if (path.node.name === 'eval' && parent.type === 'CallExpression' && parent.callee === path.node) return
|
|
147
|
+
const selectedCandidate = candidateFor(path)
|
|
148
|
+
if (selectedCandidate === undefined && path.findParent(parent => parent.isWithStatement() && parent.get('body').isAncestor(path)) !== null) return
|
|
149
|
+
if (path.findParent(parent => parent.isFunction() && dynamicFunctions.has(parent.node)) !== null) return
|
|
150
|
+
if (parent.type.startsWith('TS') && parent.expression !== path.node) return
|
|
151
|
+
// A static reference to an outer identity is a reuse event candidate.
|
|
152
|
+
// `for (x of ...)` and `for (x in ...)` bind the left target without
|
|
153
|
+
// reading its previous value, and `delete x` removes the reference without
|
|
154
|
+
// reading it, so both stay out; compound assignment, updates and `typeof`
|
|
155
|
+
// do read it.
|
|
156
|
+
const write = isWriteIdentifier(path)
|
|
157
|
+
const forHeadWrite = (parent.type === 'ForOfStatement' || parent.type === 'ForInStatement') && path.key === 'left'
|
|
158
|
+
const deleted = parent.type === 'UnaryExpression' && parent.operator === 'delete'
|
|
159
|
+
if ((path.isReferencedIdentifier() && !forHeadWrite && !deleted)
|
|
160
|
+
|| write && parent.type === 'AssignmentExpression' && parent.operator !== '=') readNames.add(path.node.name)
|
|
161
|
+
const owner = selectedCandidate ?? runtime
|
|
162
|
+
const selectedReference = candidateReferences.has(owner)
|
|
163
|
+
let text = selectedReference ? `${owner}().value` : `${owner}.values[${JSON.stringify(path.node.name)}]`
|
|
164
|
+
if (!selectedReference && isWriteIdentifier(path)) {
|
|
165
|
+
text = `${owner}.reference(${JSON.stringify(path.node.name)}, ${path.isInStrictMode()}, ${JSON.stringify(implicitDeclarations.get(path.node.start).target)}).value`
|
|
166
|
+
}
|
|
167
|
+
if (parent.type === 'UnaryExpression' && (parent.operator === 'typeof' && owner === runtime
|
|
168
|
+
|| selectedReference && ['typeof', 'delete'].includes(parent.operator))) {
|
|
169
|
+
edits.push({ start: parent.start, end: parent.end,
|
|
170
|
+
text: selectedReference ? `${owner}().${parent.operator}()` : `${runtime}.typeof(${JSON.stringify(path.node.name)})` })
|
|
171
|
+
handled.add(path.node.start)
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
const callable = (parent.type === 'CallExpression' || parent.type === 'OptionalCallExpression') && parent.callee === path.node
|
|
175
|
+
|| parent.type === 'TaggedTemplateExpression' && parent.tag === path.node
|
|
176
|
+
if (callable) text = selectedReference ? `${owner}().callee` : `(0, ${text})`
|
|
177
|
+
if (parent.type === 'ObjectProperty' && parent.shorthand && parent.value === path.node) text = `[${JSON.stringify(path.node.name)}]: ${text}`
|
|
178
|
+
edits.push({ start: path.node.start, end: path.node.end, text,
|
|
179
|
+
...(callable ? { mappings: [{ generatedStart: 0, generatedEnd: text.length,
|
|
180
|
+
originalStart: path.node.start, originalEnd: path.node.start + 1 }] } : {}) })
|
|
181
|
+
if (callable) {
|
|
182
|
+
// V8 reports an indirect call at its opening argument delimiter. Keep
|
|
183
|
+
// that generated call frame anchored to the source callee; arguments
|
|
184
|
+
// retain their own independent mappings.
|
|
185
|
+
const end = parent.type === 'TaggedTemplateExpression' ? parent.quasi.start + 1
|
|
186
|
+
: parent.arguments[0]?.start ?? parent.end
|
|
187
|
+
const punctuation = code.slice(path.node.end, end)
|
|
188
|
+
edits.push({ start: path.node.end, end, text: punctuation,
|
|
189
|
+
mappings: [{ generatedStart: 0, generatedEnd: punctuation.length,
|
|
190
|
+
originalStart: path.node.start, originalEnd: path.node.start + 1 }] })
|
|
191
|
+
}
|
|
192
|
+
handled.add(path.node.start)
|
|
193
|
+
} })
|
|
194
|
+
const classificationSource = applySourceEdits(code, sourceMap, classificationEdits)
|
|
195
|
+
return { ...applySourceEdits(code, sourceMap, edits),
|
|
196
|
+
classificationCode: classificationSource.code,
|
|
197
|
+
classificationSourceMap: classificationSource.sourceMap,
|
|
198
|
+
implicitDeclarations: [...implicitDeclarations.values()],
|
|
199
|
+
readNames: [...readNames] }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function compileStatefulRoot(program, {
|
|
203
|
+
sourceMap = identitySourceMap(program.length), knownBindings = new Set(), reservedBindings = new Set(),
|
|
204
|
+
originalSource = program,
|
|
205
|
+
unavailableNames = [], languageSemantics = 'stateful-v1',
|
|
206
|
+
importBindings = new Map(), writableBindings = new Set(),
|
|
207
|
+
nativeBindings = new Set(),
|
|
208
|
+
nativeLexicalBindings = new Set(),
|
|
209
|
+
internalBindings = new Set(),
|
|
210
|
+
dynamicBindings = [],
|
|
211
|
+
privateBindings = [],
|
|
212
|
+
dynamicOrigins = [],
|
|
213
|
+
rootCandidates = new Map(),
|
|
214
|
+
establishedRoots = new Map(),
|
|
215
|
+
runtimeExpression, moduleExpression = global => `this[${JSON.stringify(global)}]`,
|
|
216
|
+
} = {}) {
|
|
217
|
+
let file = parse(program, { ...PARSER_OPTIONS, errorRecovery: true })
|
|
218
|
+
// Identifier name inference must precede root and pattern reference lowering.
|
|
219
|
+
const inferredNames = []
|
|
220
|
+
traverse(file, { noScope: true, 'AssignmentExpression|AssignmentPattern|VariableDeclarator'(path) {
|
|
221
|
+
if (path.node.type === 'AssignmentExpression' && !['=', '&&=', '||=', '??='].includes(path.node.operator)) return
|
|
222
|
+
const initializer = path.node.type === 'VariableDeclarator' ? path.node.init : path.node.right
|
|
223
|
+
if (initializer === null) return
|
|
224
|
+
let name = path.node.type === 'VariableDeclarator' ? path.node.id : path.node.left
|
|
225
|
+
let value = initializer
|
|
226
|
+
while ((name.type.startsWith('TS') || name.type === 'ParenthesizedExpression') && name.expression) name = name.expression
|
|
227
|
+
while ((value.type.startsWith('TS') || value.type === 'ParenthesizedExpression') && value.expression) value = value.expression
|
|
228
|
+
if (name.type !== 'Identifier' || !(value.type === 'ArrowFunctionExpression'
|
|
229
|
+
|| ['FunctionExpression', 'ClassExpression'].includes(value.type) && value.id === null)) return
|
|
230
|
+
inferredNames.push({ start: initializer.start, end: initializer.start, text: `({[${JSON.stringify(name.name)}]:(` },
|
|
231
|
+
{ start: initializer.end, end: initializer.end, text: `)})[${JSON.stringify(name.name)}]` })
|
|
232
|
+
} })
|
|
233
|
+
if (inferredNames.length !== 0) {
|
|
234
|
+
const named = applySourceEdits(program, sourceMap, inferredNames)
|
|
235
|
+
program = named.code
|
|
236
|
+
sourceMap = named.sourceMap
|
|
237
|
+
file = parse(program, { ...PARSER_OPTIONS, errorRecovery: true })
|
|
238
|
+
}
|
|
239
|
+
const protectedError = file.errors.find(error => error.reasonCode !== 'UnexpectedUsingDeclaration')
|
|
240
|
+
if (languageSemantics === 'protected-v1' && protectedError !== undefined) throw protectedError
|
|
241
|
+
const syntaxError = file.errors.find(error => !RECOVERABLE_CELL_PARSE_ERRORS.has(error.reasonCode))
|
|
242
|
+
if (syntaxError !== undefined) throw syntaxError
|
|
243
|
+
const root = rootDeclarations(file).filter(path => path.node.type === 'VariableDeclaration'
|
|
244
|
+
? !path.node.declarations.every(declarator => {
|
|
245
|
+
const bindings = bindingNodes(declarator.id)
|
|
246
|
+
return bindings.length > 0 && bindings.every(binding => internalBindings.has(binding.name))
|
|
247
|
+
})
|
|
248
|
+
: !internalBindings.has(path.node.id?.name))
|
|
249
|
+
const paths = new Map()
|
|
250
|
+
traverse(file, { noScope: true, enter(path) { paths.set(path.node, path) } })
|
|
251
|
+
const logicalNames = new Map(dynamicBindings.map(binding => [binding.physicalName, binding.name]))
|
|
252
|
+
const allocate = createGeneratedNameAllocator(file, [...knownBindings, ...reservedBindings, ...unavailableNames])
|
|
253
|
+
const runtime = allocate('root')
|
|
254
|
+
const rootRuntimeName = allocate('root_runtime')
|
|
255
|
+
const commitSignal = allocate('root_commit')
|
|
256
|
+
const declarations = []
|
|
257
|
+
const declarationKinds = new Map()
|
|
258
|
+
const commitTargets = new Set()
|
|
259
|
+
const candidateNames = new Map()
|
|
260
|
+
const candidateReferences = new Set()
|
|
261
|
+
const moduleLoads = []
|
|
262
|
+
const imports = new Map()
|
|
263
|
+
const rewrites = []
|
|
264
|
+
const edits = []
|
|
265
|
+
const prologue = []
|
|
266
|
+
let varReferenceMarker
|
|
267
|
+
const varReference = () => {
|
|
268
|
+
if (!varReferenceMarker) {
|
|
269
|
+
varReferenceMarker = allocate('var_reference')
|
|
270
|
+
internalBindings.add(varReferenceMarker)
|
|
271
|
+
dynamicBindings.push({ physicalName: varReferenceMarker, role: 'var-reference' })
|
|
272
|
+
prologue.push(`const ${varReferenceMarker}=void 0;`)
|
|
273
|
+
}
|
|
274
|
+
return varReferenceMarker
|
|
275
|
+
}
|
|
276
|
+
const hoisted = []
|
|
277
|
+
const readOnly = new Set()
|
|
278
|
+
const rootKinds = new Map()
|
|
279
|
+
const sourceCopies = new Map()
|
|
280
|
+
const remember = (text, mappings) => { sourceCopies.set(text, mappings); return text }
|
|
281
|
+
const mapParts = parts => {
|
|
282
|
+
let offset = 0
|
|
283
|
+
return parts.flatMap(text => {
|
|
284
|
+
const mappings = (sourceCopies.get(text) ?? []).map(mapping => ({ ...mapping,
|
|
285
|
+
generatedStart: mapping.generatedStart + offset, generatedEnd: mapping.generatedEnd + offset }))
|
|
286
|
+
offset += text.length
|
|
287
|
+
return mappings
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
const emptyCompletion = expression => `const ${allocate('declaration_completion')} = (${expression});`
|
|
291
|
+
for (const path of root) {
|
|
292
|
+
const node = path.node
|
|
293
|
+
for (const binding of node.type === 'VariableDeclaration'
|
|
294
|
+
? node.declarations.flatMap(declarator => bindingNodes(declarator.id)) : node.id === null ? [] : [node.id]) {
|
|
295
|
+
const kinds = rootKinds.get(binding.name) ?? new Set()
|
|
296
|
+
kinds.add(node.type === 'VariableDeclaration' ? node.kind : node.type)
|
|
297
|
+
rootKinds.set(binding.name, kinds)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const canInstantiate = name => !knownBindings.has(name) && !imports.has(name)
|
|
301
|
+
&& rootKinds.has(name)
|
|
302
|
+
&& [...rootKinds.get(name)].every(kind => kind === 'var' || kind === 'FunctionDeclaration')
|
|
303
|
+
const record = (name, kind, node, binding = node, suffix = '') => {
|
|
304
|
+
const target = `root:${node.start}:${name}${suffix}`
|
|
305
|
+
declarationKinds.set(target, kind === 'import' ? 'const' : kind === 'function' ? 'hoisted'
|
|
306
|
+
: kind === 'class' ? 'let' : node.kind ?? 'const')
|
|
307
|
+
declarations.push({ name, kind, writable: languageSemantics === 'stateful-v1'
|
|
308
|
+
|| kind !== 'import' && (kind !== 'variable' || node.kind === 'let' || node.kind === 'var'),
|
|
309
|
+
original: true, span: mapSourceSpan(span(binding), program, originalSource, sourceMap),
|
|
310
|
+
definitionSpan: mapSourceSpan(span(node), program, originalSource, sourceMap), commitDependency: target })
|
|
311
|
+
commitTargets.add(target)
|
|
312
|
+
return target
|
|
313
|
+
}
|
|
314
|
+
const variable = (node, declarator, supplied = undefined) => {
|
|
315
|
+
const bindings = bindingNodes(declarator.id)
|
|
316
|
+
const names = [...new Set(bindings.map(binding => binding.name))]
|
|
317
|
+
if (names.length === 0) {
|
|
318
|
+
const original = supplied === undefined ? declarator : declarator.id
|
|
319
|
+
const source = program.slice(original.start, original.end)
|
|
320
|
+
const text = `const ${source}${supplied === undefined ? '' : ` = (${supplied})`};`
|
|
321
|
+
return remember(text, [{ generatedStart: 6, generatedEnd: 6 + source.length,
|
|
322
|
+
originalStart: original.start, originalEnd: original.end }])
|
|
323
|
+
}
|
|
324
|
+
const target = record(names[0], 'variable', node, bindings[0], `:${declarator.start}`)
|
|
325
|
+
for (const binding of bindings.slice(1)) {
|
|
326
|
+
record(binding.name, 'variable', node, binding, `:${declarator.start}`)
|
|
327
|
+
declarations.at(-1).commitDependency = target
|
|
328
|
+
}
|
|
329
|
+
if (declarator.init === null && supplied === undefined) {
|
|
330
|
+
return emptyCompletion(`${runtime}.declare(${JSON.stringify(names[0])}, ${JSON.stringify(target)})`)
|
|
331
|
+
}
|
|
332
|
+
let value = supplied ?? program.slice(declarator.init.start, declarator.init.end)
|
|
333
|
+
const redirected = new Map()
|
|
334
|
+
const initializerTargets = new Map()
|
|
335
|
+
if (node.kind === 'var') for (const binding of bindings) {
|
|
336
|
+
const selected = varInitializerTarget(paths.get(binding), file.program, binding.name,
|
|
337
|
+
node => logicalNames.get(node.name) ?? node.name)
|
|
338
|
+
initializerTargets.set(binding.name, selected)
|
|
339
|
+
if (selected.catchNode) {
|
|
340
|
+
redirected.set(binding.name, selected.catchNode.param.name)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (declarator.id.type === 'Identifier') {
|
|
344
|
+
const selected = redirected.get(names[0])
|
|
345
|
+
const withDepth = initializerTargets.get(names[0])?.withNodes.length
|
|
346
|
+
const fallbackRead = selected ?? `${runtime}.values[${JSON.stringify(names[0])}]`
|
|
347
|
+
const fallbackWrite = selected === undefined
|
|
348
|
+
? `${runtime}.assign(${JSON.stringify(names[0])}, next, ${JSON.stringify(target)})` : `${selected}=next`
|
|
349
|
+
const text = withDepth
|
|
350
|
+
? emptyCompletion(`${varReference()}(${JSON.stringify(names[0])},${withDepth},{get:()=>${fallbackRead},set:next=>${fallbackWrite}}).value = (${value})`)
|
|
351
|
+
: selected === undefined
|
|
352
|
+
? emptyCompletion(`${runtime}.assign(${JSON.stringify(names[0])}, (${value}), ${JSON.stringify(target)})`)
|
|
353
|
+
: emptyCompletion(`(${selected} = (${value}), ${runtime}.declare(${JSON.stringify(names[0])}, ${JSON.stringify(target)}))`)
|
|
354
|
+
if (supplied === undefined) {
|
|
355
|
+
const original = program.slice(declarator.init.start, declarator.init.end)
|
|
356
|
+
const start = text.indexOf(withDepth ? `= (${value})` : selected === undefined ? `, (${value}),` : `= (${value}),`) + 3 + value.indexOf(original)
|
|
357
|
+
remember(text, [{ generatedStart: start, generatedEnd: start + original.length,
|
|
358
|
+
originalStart: declarator.init.start, originalEnd: declarator.init.end }])
|
|
359
|
+
}
|
|
360
|
+
return text
|
|
361
|
+
}
|
|
362
|
+
const candidate = allocate('candidate')
|
|
363
|
+
const rhs = allocate('candidate_rhs')
|
|
364
|
+
const stored = names.filter(name => !redirected.has(name))
|
|
365
|
+
candidateNames.set(candidate, new Set(stored))
|
|
366
|
+
const captured = stored.filter(name => !initializerTargets.get(name)?.withNodes.length)
|
|
367
|
+
if (captured.length > 0) dynamicBindings.push({ physicalName: candidate, names: captured,
|
|
368
|
+
kind: node.kind, role: 'candidate', property: 'values', referenceMethod: 'reference' })
|
|
369
|
+
const references = new Map()
|
|
370
|
+
const referenceDeclarations = []
|
|
371
|
+
for (const name of names) {
|
|
372
|
+
const withDepth = initializerTargets.get(name)?.withNodes.length
|
|
373
|
+
if (!withDepth) continue
|
|
374
|
+
const reference = allocate('candidate_reference')
|
|
375
|
+
const caught = redirected.get(name)
|
|
376
|
+
const pending = allocate('candidate_written')
|
|
377
|
+
const fallback = caught ?? `${candidate}.values[${JSON.stringify(name)}]`
|
|
378
|
+
const get = caught ?? `${pending}?${fallback}:${runtime}.values[${JSON.stringify(name)}]`
|
|
379
|
+
const set = caught === undefined ? `(${pending}=true,${fallback}=next)` : `${caught}=next`
|
|
380
|
+
referenceDeclarations.push(`${caught === undefined ? `let ${pending}=false;` : ''}const ${reference}=()=>${varReference()}(${JSON.stringify(name)},${withDepth},{get:()=>${get},set:next=>${set}});`)
|
|
381
|
+
internalBindings.add(pending)
|
|
382
|
+
candidateReferences.add(reference)
|
|
383
|
+
candidateNames.set(reference, new Set([name]))
|
|
384
|
+
references.set(name, `${reference}().value`)
|
|
385
|
+
dynamicBindings.push({ physicalName: reference, name, kind: 'let', role: 'candidate', reference: true, accessor: true })
|
|
386
|
+
}
|
|
387
|
+
const pattern = patternText(declarator.id, program, binding => references.get(binding.name) ?? redirected.get(binding.name)
|
|
388
|
+
?? `${candidate}.values[${JSON.stringify(binding.name)}]`)
|
|
389
|
+
const text = `{ const ${rhs} = (${value}); const ${candidate} = ${runtime}.candidate(${JSON.stringify(stored)}); ${referenceDeclarations.join('')} ${emptyCompletion(`((${pattern.text} = ${rhs}), ${candidate}.commit(${JSON.stringify(target)}))`)} }`
|
|
390
|
+
const valueStart = text.indexOf(`= (${value})`) + 3
|
|
391
|
+
const patternStart = text.indexOf(pattern.text)
|
|
392
|
+
return remember(text, [...(supplied === undefined ? [{ generatedStart: valueStart, generatedEnd: valueStart + value.length,
|
|
393
|
+
originalStart: declarator.init.start, originalEnd: declarator.init.end }] : []),
|
|
394
|
+
...pattern.mappings.map(mapping => ({ ...mapping, generatedStart: patternStart + mapping.generatedStart,
|
|
395
|
+
generatedEnd: patternStart + mapping.generatedEnd }))])
|
|
396
|
+
}
|
|
397
|
+
const addLoad = node => {
|
|
398
|
+
const global = allocate('root_module')
|
|
399
|
+
const requiredExports = node.type === 'ImportDeclaration' ? node.specifiers
|
|
400
|
+
.filter(specifier => specifier.importKind !== 'type' && specifier.type !== 'ImportNamespaceSpecifier')
|
|
401
|
+
.map(specifier => specifier.type === 'ImportDefaultSpecifier' ? 'default' : specifier.imported.name ?? specifier.imported.value) : []
|
|
402
|
+
const attributes = node.attributes?.length > 0 ? { with: Object.fromEntries(node.attributes
|
|
403
|
+
.map(attribute => [attribute.key.name ?? attribute.key.value, attribute.value.value])) } : undefined
|
|
404
|
+
moduleLoads.push({ source: node.source.value, global, position: mapSourceSpan(span(node.source), program, originalSource, sourceMap),
|
|
405
|
+
...(requiredExports.length === 0 ? {} : { requiredExports: [...new Set(requiredExports)] }),
|
|
406
|
+
...(attributes === undefined ? {} : { options: attributes }) })
|
|
407
|
+
return global
|
|
408
|
+
}
|
|
409
|
+
for (const node of file.program.body) {
|
|
410
|
+
if (node.type === 'ImportDeclaration') {
|
|
411
|
+
if (node.importKind !== 'type' && (node.specifiers.length === 0
|
|
412
|
+
|| node.specifiers.some(specifier => specifier.importKind !== 'type'))) {
|
|
413
|
+
const global = addLoad(node)
|
|
414
|
+
for (const specifier of node.specifiers.filter(specifier => specifier.importKind !== 'type')) {
|
|
415
|
+
const name = specifier.local.name
|
|
416
|
+
readOnly.add(name)
|
|
417
|
+
const imported = specifier.type === 'ImportNamespaceSpecifier' ? null
|
|
418
|
+
: specifier.type === 'ImportDefaultSpecifier' ? 'default' : specifier.imported.name ?? specifier.imported.value
|
|
419
|
+
const target = record(name, 'import', node, specifier.local)
|
|
420
|
+
imports.set(name, { namespace: global, ...(imported === null ? {} : { imported }), commitDependency: target })
|
|
421
|
+
prologue.push(emptyCompletion(`${runtime}.import(${JSON.stringify(name)}, ${moduleExpression(global)}, ${JSON.stringify(imported)}, ${JSON.stringify(target)})`))
|
|
422
|
+
}
|
|
423
|
+
rewrites.push({ kind: 'import', description: `adapted the static import of ${JSON.stringify(node.source.value)}`, source: node.source.value })
|
|
424
|
+
} else {
|
|
425
|
+
rewrites.push({ kind: 'import', description: `removed the type-only import of ${JSON.stringify(node.source.value)}`, source: node.source.value })
|
|
426
|
+
}
|
|
427
|
+
edits.push({ start: node.start, end: node.end, text: '' })
|
|
428
|
+
} else if (node.type === 'ExportAllDeclaration' || node.type === 'ExportNamedDeclaration' && node.declaration === null) {
|
|
429
|
+
if (node.source !== null && node.exportKind !== 'type') addLoad(node)
|
|
430
|
+
rewrites.push({ kind: 'export', description: node.exportKind === 'type'
|
|
431
|
+
? 'removed a type-only export declaration'
|
|
432
|
+
: node.source == null ? 'removed a local re-export declaration'
|
|
433
|
+
: `converted the re-export of ${JSON.stringify(node.source.value)} into a side-effect import` })
|
|
434
|
+
edits.push({ start: node.start, end: node.end, text: '' })
|
|
435
|
+
} else if (node.type === 'ExportNamedDeclaration') {
|
|
436
|
+
rewrites.push({ kind: 'export', description: 'stripped the export modifier from a top-level declaration' })
|
|
437
|
+
edits.push({ start: node.start, end: node.declaration.start, text: '' })
|
|
438
|
+
} else if (node.type === 'ExportDefaultDeclaration') {
|
|
439
|
+
rewrites.push({ kind: 'export', description: TYPE_ONLY.has(node.declaration.type)
|
|
440
|
+
? 'removed a type-only export declaration'
|
|
441
|
+
: 'converted the default export into a local __default binding' })
|
|
442
|
+
if (TYPE_ONLY.has(node.declaration.type)) {
|
|
443
|
+
edits.push({ start: node.start, end: node.end, text: '' })
|
|
444
|
+
} else if (!['FunctionDeclaration', 'ClassDeclaration'].includes(node.declaration.type)) {
|
|
445
|
+
const target = record('__default', 'variable', node)
|
|
446
|
+
edits.push({ start: node.start, end: node.end,
|
|
447
|
+
text: emptyCompletion(`${runtime}.assign("__default", (${program.slice(node.declaration.start, node.declaration.end)}), ${JSON.stringify(target)})`) })
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
for (const path of root) {
|
|
452
|
+
const node = path.node
|
|
453
|
+
if (node.type === 'VariableDeclaration') {
|
|
454
|
+
if (node.kind === 'const' || node.kind === 'using' || node.kind === 'await using') {
|
|
455
|
+
for (const binding of node.declarations.flatMap(declarator => bindingNodes(declarator.id))) readOnly.add(binding.name)
|
|
456
|
+
}
|
|
457
|
+
if (node.kind === 'using' || node.kind === 'await using') {
|
|
458
|
+
// Resource lifetime owns the acquired value even when the public
|
|
459
|
+
// logical binding is replaced before the scope ends.
|
|
460
|
+
const parts = node.declarations.flatMap(declarator => {
|
|
461
|
+
const resource = allocate('root_resource')
|
|
462
|
+
const value = program.slice(declarator.init.start, declarator.init.end)
|
|
463
|
+
const resourceText = `${node.kind} ${resource} = (${value});`
|
|
464
|
+
const valueStart = resourceText.indexOf(`= (${value})`) + 3
|
|
465
|
+
remember(resourceText, [{ generatedStart: valueStart, generatedEnd: valueStart + value.length,
|
|
466
|
+
originalStart: declarator.init.start, originalEnd: declarator.init.end }])
|
|
467
|
+
return [resourceText, variable(node, declarator, resource)]
|
|
468
|
+
})
|
|
469
|
+
edits.push({ start: node.start, end: node.end, text: parts.join(''), mappings: mapParts(parts) })
|
|
470
|
+
continue
|
|
471
|
+
}
|
|
472
|
+
for (const declarator of node.declarations) {
|
|
473
|
+
if (node.kind !== 'var') continue
|
|
474
|
+
for (const binding of bindingNodes(declarator.id)) {
|
|
475
|
+
if (!canInstantiate(binding.name) || hoisted.includes(binding.name)) continue
|
|
476
|
+
hoisted.push(binding.name)
|
|
477
|
+
const target = record(binding.name, 'variable', node, binding, ':hoist')
|
|
478
|
+
prologue.push(emptyCompletion(`${runtime}.declare(${JSON.stringify(binding.name)}, ${JSON.stringify(target)})`))
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if (path.parent.type === 'ForOfStatement' || path.parent.type === 'ForInStatement') {
|
|
482
|
+
if (node.declarations[0].init !== null) {
|
|
483
|
+
let owner = path.parentPath
|
|
484
|
+
while (owner.parentPath?.isLabeledStatement()) owner = owner.parentPath
|
|
485
|
+
const prefix = ['{ ', variable(node, node.declarations[0]), '\n']
|
|
486
|
+
edits.push({ start: owner.node.start, end: owner.node.start, text: prefix.join(''), mappings: mapParts(prefix) })
|
|
487
|
+
edits.push({ start: owner.node.end, end: owner.node.end, text: ' }' })
|
|
488
|
+
}
|
|
489
|
+
const temporary = allocate('iteration')
|
|
490
|
+
edits.push({ start: node.start, end: node.end, text: `const ${temporary}` })
|
|
491
|
+
const body = path.parent.body
|
|
492
|
+
const assignments = variable(node, node.declarations[0], temporary)
|
|
493
|
+
if (body.type === 'BlockStatement') edits.push({ start: body.start + 1, end: body.start + 1, text: assignments,
|
|
494
|
+
mappings: sourceCopies.get(assignments) })
|
|
495
|
+
else {
|
|
496
|
+
edits.push({ start: body.start, end: body.start, text: `{ ${assignments}`, mappings: mapParts(['{ ', assignments]) })
|
|
497
|
+
edits.push({ start: body.end, end: body.end, text: '}' })
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
const variables = node.declarations.flatMap(declarator => [variable(node, declarator), '\n'])
|
|
501
|
+
const parts = ['{ ', ...variables, ' }']
|
|
502
|
+
if (path.parent.type === 'ForStatement') {
|
|
503
|
+
let owner = path.parentPath
|
|
504
|
+
while (owner.parentPath?.isLabeledStatement()) owner = owner.parentPath
|
|
505
|
+
const prefix = ['{ ', ...parts]
|
|
506
|
+
edits.push({ start: owner.node.start, end: owner.node.start, text: prefix.join(''), mappings: mapParts(prefix) })
|
|
507
|
+
edits.push({ start: node.start, end: node.end, text: '' })
|
|
508
|
+
edits.push({ start: owner.node.end, end: owner.node.end, text: ' }' })
|
|
509
|
+
} else edits.push({ start: node.start, end: node.end, text: parts.join(''), mappings: mapParts(parts) })
|
|
510
|
+
}
|
|
511
|
+
continue
|
|
512
|
+
}
|
|
513
|
+
const name = node.id?.name ?? '__default'
|
|
514
|
+
const kind = node.type === 'FunctionDeclaration' ? 'function' : 'class'
|
|
515
|
+
const target = record(name, kind, node, node.id ?? node)
|
|
516
|
+
let expression = program.slice(node.start, node.end)
|
|
517
|
+
if (kind === 'function' && node.id !== null) {
|
|
518
|
+
expression = `({ [${JSON.stringify(name)}]: ${program.slice(node.start, node.id.start)}${program.slice(node.id.end, node.end)} })[${JSON.stringify(name)}]`
|
|
519
|
+
}
|
|
520
|
+
if (node.id === null && path.parent.type === 'ExportDefaultDeclaration') {
|
|
521
|
+
expression = `({ ["default"]: (${expression}) })["default"]`
|
|
522
|
+
}
|
|
523
|
+
const text = emptyCompletion(`${runtime}.assign(${JSON.stringify(name)}, (${expression}), ${JSON.stringify(target)})`)
|
|
524
|
+
const expressionStart = text.indexOf(`, (${expression}),`) + 3
|
|
525
|
+
const sourceStart = kind === 'function' && node.id !== null ? node.id.end : node.start
|
|
526
|
+
const copied = program.slice(sourceStart, node.end)
|
|
527
|
+
const copiedStart = expressionStart + expression.indexOf(copied)
|
|
528
|
+
remember(text, [{ generatedStart: copiedStart, generatedEnd: copiedStart + copied.length,
|
|
529
|
+
originalStart: sourceStart, originalEnd: node.end }])
|
|
530
|
+
const canHoist = kind === 'function' && canInstantiate(name)
|
|
531
|
+
if (canHoist) prologue.push(text)
|
|
532
|
+
const defaultExport = path.parent.type === 'ExportDefaultDeclaration'
|
|
533
|
+
const suffix = defaultExport
|
|
534
|
+
? emptyCompletion(`${runtime}.link("__default", ${JSON.stringify(name)}, ${JSON.stringify(record('__default', kind, path.parent))})`) : ''
|
|
535
|
+
const replacement = [canHoist ? '' : text, suffix]
|
|
536
|
+
if (path.parentPath.isLabeledStatement()) {
|
|
537
|
+
replacement.unshift('{')
|
|
538
|
+
replacement.push('}')
|
|
539
|
+
}
|
|
540
|
+
edits.push({ start: defaultExport ? path.parent.start : node.start,
|
|
541
|
+
end: defaultExport ? path.parent.end : node.end, text: replacement.join(''),
|
|
542
|
+
mappings: mapParts(replacement) })
|
|
543
|
+
}
|
|
544
|
+
const declared = new Set(declarations.map(declaration => declaration.name))
|
|
545
|
+
const rootBindings = { declared: [...declared], known: [...knownBindings], candidates: [...rootCandidates],
|
|
546
|
+
declarationKinds: [...declarationKinds],
|
|
547
|
+
declaredKinds: [...new Map(declarations.map(declaration => [declaration.name, declarationKinds.get(declaration.commitDependency)]))],
|
|
548
|
+
established: [...establishedRoots], readOnly: [...readOnly], languageSemantics,
|
|
549
|
+
legacyImports: [...importBindings], legacyLexicals: [...knownBindings].filter(name => !importBindings.has(name)),
|
|
550
|
+
legacyWritable: [...writableBindings].filter(name => !importBindings.has(name)),
|
|
551
|
+
legacyNative: [...nativeBindings], legacyNativeLexicals: [...nativeLexicalBindings] }
|
|
552
|
+
const accessRuntime = runtimeExpression === undefined ? `this[${JSON.stringify(rootRuntimeName)}]`
|
|
553
|
+
: runtimeExpression(rootBindings)
|
|
554
|
+
const prefixParts = [`;{\nconst ${runtime} = ${accessRuntime};\n`, ...prologue.flatMap(text => [text, '\n'])]
|
|
555
|
+
const directiveEnd = file.program.directives.at(-1)?.end ?? 0
|
|
556
|
+
if (directiveEnd === program.length) prefixParts.push('\n}')
|
|
557
|
+
edits.unshift({ start: directiveEnd, end: directiveEnd, text: prefixParts.join(''), mappings: mapParts(prefixParts) })
|
|
558
|
+
if (directiveEnd !== program.length) edits.push({ start: program.length, end: program.length, text: '\n}' })
|
|
559
|
+
const merged = []
|
|
560
|
+
for (const edit of edits.sort((left, right) => left.start - right.start || left.end - right.end)) {
|
|
561
|
+
const previous = merged.at(-1)
|
|
562
|
+
if (edit.start === edit.end && previous?.start === edit.start && previous.end === edit.end) {
|
|
563
|
+
previous.mappings = [...(previous.mappings ?? []), ...(edit.mappings ?? []).map(mapping => ({ ...mapping,
|
|
564
|
+
generatedStart: mapping.generatedStart + previous.text.length, generatedEnd: mapping.generatedEnd + previous.text.length }))]
|
|
565
|
+
previous.text += edit.text
|
|
566
|
+
} else merged.push({ ...edit })
|
|
567
|
+
}
|
|
568
|
+
const framed = applySourceEdits(program, sourceMap, merged)
|
|
569
|
+
let rewritten = rewriteRootReferences(framed.code, framed.sourceMap, runtime, candidateNames, candidateReferences,
|
|
570
|
+
originalSource)
|
|
571
|
+
rewritten = adaptRootModuleImports(rewritten, runtime)
|
|
572
|
+
rewritten = { ...rewritten, ...adaptDynamicCell(rewritten.code, rewritten.sourceMap, {
|
|
573
|
+
rootRuntime: runtime, bindings: dynamicBindings, privateBindings, internalBindings, parserPlugins: CELL_PARSER_PLUGINS, originalSource,
|
|
574
|
+
}) }
|
|
575
|
+
rootBindings.dynamicOrigins = [...dynamicOrigins, ...rewritten.dynamicOrigins.map(origin => origin.target)]
|
|
576
|
+
rootBindings.candidates = [...new Map([...rootCandidates, ...rewritten.implicitDeclarations.map(declaration => [declaration.target, declaration.name])])]
|
|
577
|
+
// Each record carries the reason this owner determined, so the diagnostic surface does not
|
|
578
|
+
// re-derive reserved-ness or protected policy from the request descriptors.
|
|
579
|
+
const collisions = declarations.filter(declaration => reservedBindings.has(declaration.name)
|
|
580
|
+
|| languageSemantics === 'protected-v1' && knownBindings.has(declaration.name))
|
|
581
|
+
.map(declaration => ({ name: declaration.name, kind: declaration.kind,
|
|
582
|
+
reason: reservedBindings.has(declaration.name)
|
|
583
|
+
? 'reserved-program-binding-not-shadowable' : 'protected-root-redeclaration',
|
|
584
|
+
start: { line: declaration.span.line, column: declaration.span.column }, end: declaration.span.end }))
|
|
585
|
+
// A binding is reused only when this cell's rewritten source statically
|
|
586
|
+
// references an identity that already existed and does not (re)declare that
|
|
587
|
+
// same name. The count is a source fact, not a runtime trace: an unreached or
|
|
588
|
+
// never-invoked reference still counts, and a redeclaration neither counts as
|
|
589
|
+
// a reuse here nor resets the accumulated count it carries.
|
|
590
|
+
const reusedNames = [...(rewritten.readNames ?? [])]
|
|
591
|
+
.filter(name => knownBindings.has(name) && !declared.has(name))
|
|
592
|
+
return {
|
|
593
|
+
...rewritten, languageSemantics, rootRuntimeName, rootFrameBinding: runtime, commitSignal, commitTargets, declarations, declared,
|
|
594
|
+
rootPlan: rootBindings,
|
|
595
|
+
rootBindings,
|
|
596
|
+
rootBindingFacts: true, moduleLoads, imports, importNamespaces: new Set(), collisions,
|
|
597
|
+
redeclared: declarations.filter(declaration => knownBindings.has(declaration.name)), rewrites,
|
|
598
|
+
establishedRootLexicals: new Set(), reusedNames,
|
|
599
|
+
}
|
|
600
|
+
}
|