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,2091 @@
|
|
|
1
|
+
import { commonJsCompilerImport, commonJsCompilerGlobals } from './compiler-module-links.js'
|
|
2
|
+
import { compilerStorageSource } from './compiler-storage-source.js'
|
|
3
|
+
import { compilerDescriptorSource } from './compiler-descriptors.js'
|
|
4
|
+
import { privateGeneratedRecords } from './compiler-record-roles.js'
|
|
5
|
+
import { lowerResourceOperations } from './compiler-resource-lowering.js'
|
|
6
|
+
import { callableDefinitionOwner, closeDefinitionExpression } from './callable-definition-environment.js'
|
|
7
|
+
/** Build logical binding identities from recovered syntax and Babel scope facts. */
|
|
8
|
+
import { parse } from '@babel/parser'
|
|
9
|
+
import traverseImport from '@babel/traverse'
|
|
10
|
+
import { SourceMap } from 'node:module'
|
|
11
|
+
import loadTypeScript from './compiler-typescript.cjs'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
13
|
+
import { transformSync, transformFromAstSync, types as babelTypes } from '@babel/core'
|
|
14
|
+
import decoratorTransform from '@babel/plugin-proposal-decorators'
|
|
15
|
+
import typescriptTransform from '@babel/plugin-transform-typescript'
|
|
16
|
+
import resourceTransform from '@babel/plugin-transform-explicit-resource-management'
|
|
17
|
+
import generatorImport from '@babel/generator'
|
|
18
|
+
import { injectInitialization } from '@babel/helper-create-class-features-plugin'
|
|
19
|
+
import * as legacyDecoratorHelpers from 'tslib'
|
|
20
|
+
import { bindingNodes, createGeneratedNameAllocator } from './binding-pattern.js'
|
|
21
|
+
import { applySourceEdits, identitySourceMap, mappedSourceTransform, mapSourcePosition } from './source-position-map.js'
|
|
22
|
+
import { supportsNativeUsing, transformTypeScriptSource } from './typescript-transform.js'
|
|
23
|
+
import { createDynamicScopeAnalysis, varInitializerTarget } from './dynamic-scope-analysis.js'
|
|
24
|
+
import { visitSource } from './compiler-source-regions.js'
|
|
25
|
+
import { analyzeSourceDeclarations, resolveSourceBinding, COMMONJS_PARAMETERS } from './compiler-scope-facts.js'
|
|
26
|
+
import { callableMarkerPosition } from './callable-source-facts.js'
|
|
27
|
+
import { createSourcePieces } from './compiler-source-pieces.js'
|
|
28
|
+
import { indexSourceRegions, validateRegionSource, sourceFeatures } from './compiler-region-output.js'
|
|
29
|
+
|
|
30
|
+
export const CELL_PARSER_PLUGINS = ['typescript', 'importAttributes', 'decorators', 'decoratorAutoAccessors']
|
|
31
|
+
|
|
32
|
+
/** Babel parse errors the stateful cell compiler recovers from by design. */
|
|
33
|
+
export const RECOVERABLE_CELL_PARSE_ERRORS = new Set(['VarRedeclaration', 'DeclarationMissingInitializer',
|
|
34
|
+
'DuplicateExport', 'DuplicateDefaultExport', 'UnexpectedUsingDeclaration'])
|
|
35
|
+
|
|
36
|
+
const PARSER_OPTIONS = {
|
|
37
|
+
sourceType: 'script',
|
|
38
|
+
allowAwaitOutsideFunction: true,
|
|
39
|
+
allowImportExportEverywhere: true,
|
|
40
|
+
allowReturnOutsideFunction: true,
|
|
41
|
+
allowUndeclaredExports: true,
|
|
42
|
+
plugins: CELL_PARSER_PLUGINS,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const parserOptionsForTarget = target => ({ ...PARSER_OPTIONS,
|
|
46
|
+
sourceType: target === 'module' || target === 'reflection-module' ? 'module' : target === 'commonjs' ? 'commonjs' : 'script',
|
|
47
|
+
...(target === 'commonjs' ? { allowAwaitOutsideFunction: undefined, allowReturnOutsideFunction: undefined } : {}),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const LOOP_HEADS = new Set(['ForStatement', 'ForInStatement', 'ForOfStatement'])
|
|
51
|
+
|
|
52
|
+
function isFunctionNode(node) {
|
|
53
|
+
return node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression'
|
|
54
|
+
|| node.type === 'ArrowFunctionExpression' || node.type === 'ObjectMethod'
|
|
55
|
+
|| node.type === 'ClassMethod' || node.type === 'ClassPrivateMethod'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function eachNode(root, visit) {
|
|
59
|
+
babelTypes.traverseFast(root, visit)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseRecovered(code) {
|
|
63
|
+
return parse(code, { ...PARSER_OPTIONS, errorRecovery: true }).program
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function singleStatementBodies(program) {
|
|
67
|
+
const bodies = new Map()
|
|
68
|
+
eachNode(program, (node) => {
|
|
69
|
+
if (node.type === 'IfStatement') {
|
|
70
|
+
if (node.consequent !== null) bodies.set(node.consequent, node)
|
|
71
|
+
if (node.alternate !== null) bodies.set(node.alternate, node)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
if (node.type === 'WhileStatement' || node.type === 'DoWhileStatement'
|
|
75
|
+
|| LOOP_HEADS.has(node.type) || node.type === 'WithStatement' || node.type === 'LabeledStatement') {
|
|
76
|
+
bodies.set(node.body, node)
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
return bodies
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A function declaration written as the body of an `if`, loop, `with`, or `do`
|
|
84
|
+
* statement is the Annex B single-statement form, which the language defines as
|
|
85
|
+
* if it were written in a block. Writing that block keeps the declaration's own
|
|
86
|
+
* binding: without it, the parser's scope analysis reads the declaration as a
|
|
87
|
+
* var collision with an enclosing lexical and rejects a cell the language runs.
|
|
88
|
+
* A labelled function declaration hoists into the enclosing scope instead, so it
|
|
89
|
+
* is not this form and stays as written.
|
|
90
|
+
*/
|
|
91
|
+
function annexBBlockEdits(program) {
|
|
92
|
+
const bodies = singleStatementBodies(program)
|
|
93
|
+
const edits = []
|
|
94
|
+
for (const [body, statement] of bodies) {
|
|
95
|
+
if (body.type !== 'FunctionDeclaration' || statement.type === 'LabeledStatement') continue
|
|
96
|
+
edits.push({ start: body.start, end: body.start, text: '{' })
|
|
97
|
+
edits.push({ start: body.end, end: body.end, text: '}' })
|
|
98
|
+
}
|
|
99
|
+
return edits
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const traverseScopes = traverseImport.default ?? traverseImport
|
|
103
|
+
const generate = generatorImport.default ?? generatorImport
|
|
104
|
+
|
|
105
|
+
/** Owned entries obtain compiler operations from their prepared realm. Source
|
|
106
|
+
* bindings and mutable constructor properties never select helper authority. */
|
|
107
|
+
function intrinsicBootstrap(name, target, intrinsicContext) {
|
|
108
|
+
if (intrinsicContext) {
|
|
109
|
+
intrinsicContext.bindings.add(name)
|
|
110
|
+
return `const ${name}=(${intrinsicContext.expression ?? 'void 0'});\n`
|
|
111
|
+
}
|
|
112
|
+
if (target === 'module') return `import {moduleBindingIntrinsics as ${name}} from ${JSON.stringify(new URL('./stateful-module-runtime.js', import.meta.url).href)};\n`
|
|
113
|
+
if (target === 'commonjs') return `const ${name}=${commonJsCompilerImport(new URL('./compiler-intrinsics.js', import.meta.url))}.moduleCompilerIntrinsics;\n`
|
|
114
|
+
return `const ${name}=(()=>{const native=(function(){}).constructor("return {WeakMap,WeakSet,Reflect,Promise,Array,Function,SuppressedError:typeof SuppressedError==='function'?SuppressedError:void 0}")(),object=(${compilerDescriptorSource})(({}).constructor,native.Reflect).Object;let typeError,referenceError;try{null.x}catch(error){typeError=error.constructor}try{const value=value}catch(error){referenceError=error.constructor}return {Object:object,WeakMap:native.WeakMap,WeakSet:native.WeakSet,Reflect:{apply:(function(){}).call.bind((function(){}).apply),get decorate(){return native.Reflect.decorate}},TypeError:typeError,ReferenceError:referenceError,Error:object.getPrototypeOf(typeError),Symbol:object.getOwnPropertySymbols([].constructor.prototype)[0].constructor,String:''.constructor,Number:(0).constructor,Promise:{resolve:native.Promise.resolve.bind(native.Promise),reject:native.Promise.reject.bind(native.Promise)},SuppressedError:native.SuppressedError,...(${compilerStorageSource})(object,native.WeakMap,native.WeakSet,object.getOwnPropertySymbols([].constructor.prototype)[0].constructor,native.Promise,native.Reflect,native.Array,native.Function.prototype)}})();\n`
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Resolve only compiler-recorded bootstrap declarations after the root frame
|
|
118
|
+
* has an execution owner. User declarations never select this private input. */
|
|
119
|
+
export function bindCompilerIntrinsics(result, intrinsicContext) {
|
|
120
|
+
if (intrinsicContext.bindings.size === 0) return result
|
|
121
|
+
const edits = []
|
|
122
|
+
eachNode(parseRecovered(result.code), node => {
|
|
123
|
+
if (node.type !== 'VariableDeclarator' || !intrinsicContext.bindings.has(node.id.name)) return
|
|
124
|
+
edits.push({ start: node.init.start, end: node.init.end, text: `(${intrinsicContext.expression})` })
|
|
125
|
+
})
|
|
126
|
+
return { ...result, ...applySourceEdits(result.code, result.sourceMap, edits) }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function mappedTransform(code, sourceMap, transformed) {
|
|
130
|
+
return mappedSourceTransform(code, sourceMap, transformed)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const TRANSPARENT_NAME_PARENTS = new Set(['ParenthesizedExpression', 'TSAsExpression',
|
|
134
|
+
'TSNonNullExpression', 'TSSatisfiesExpression', 'TSTypeAssertion'])
|
|
135
|
+
|
|
136
|
+
/** Native name inference sees through parentheses and type-only wrappers. */
|
|
137
|
+
function namedExpressionPath(path) {
|
|
138
|
+
while (path.parentPath && TRANSPARENT_NAME_PARENTS.has(path.parent.type)) path = path.parentPath
|
|
139
|
+
return path
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Definition regions have no callable body scope. Give each class evaluation
|
|
143
|
+
* a lexical frame before Babel assigns decorator initialization helpers. */
|
|
144
|
+
function isolateDefinitionClassEvaluations(file, allocate, intrinsics) {
|
|
145
|
+
const visited = new WeakSet()
|
|
146
|
+
// A computed field key is coerced once per class evaluation while its field value is created per
|
|
147
|
+
// instance. The key therefore travels through a parameter of the containing class's own frame,
|
|
148
|
+
// instead of shared storage that a later evaluation or a detached callable would overwrite or miss.
|
|
149
|
+
const capturedKeys = new Map()
|
|
150
|
+
file.path.traverse({ ClassExpression: { exit(path) {
|
|
151
|
+
const node = path.node
|
|
152
|
+
if (visited.has(node)) return
|
|
153
|
+
visited.add(node)
|
|
154
|
+
const captured = capturedKeys.get(node) ?? []
|
|
155
|
+
const decorated = Boolean(node.decorators?.length
|
|
156
|
+
|| node.body.body.some(member => member.decorators?.length || member.type === 'ClassAccessorProperty'))
|
|
157
|
+
if (!decorated && captured.length === 0) return
|
|
158
|
+
const expression = namedExpressionPath(path)
|
|
159
|
+
const parent = expression.parent
|
|
160
|
+
const field = ['ClassProperty', 'ClassPrivateProperty', 'ClassAccessorProperty'].includes(parent.type)
|
|
161
|
+
const propertyValue = expression.key === 'value' && (parent.type === 'ObjectProperty' || field)
|
|
162
|
+
if (node.id === null && propertyValue && parent.type === 'ObjectProperty' && parent.computed) {
|
|
163
|
+
// Coerce the original key before entering the definition frame. Its own
|
|
164
|
+
// data property carries the exact string/symbol without a shared cache.
|
|
165
|
+
const record = babelTypes.identifier(allocate('decorated_class_key_object'))
|
|
166
|
+
const key = babelTypes.identifier(allocate('decorated_class_key'))
|
|
167
|
+
const nativeObject = babelTypes.memberExpression(babelTypes.identifier(intrinsics), babelTypes.identifier('Object'))
|
|
168
|
+
const keys = method => babelTypes.memberExpression(babelTypes.callExpression(
|
|
169
|
+
babelTypes.memberExpression(nativeObject, babelTypes.identifier(method)), [record]), babelTypes.numericLiteral(0), true)
|
|
170
|
+
const declaration = babelTypes.variableDeclaration('const', [babelTypes.variableDeclarator(key,
|
|
171
|
+
babelTypes.objectExpression([babelTypes.objectProperty(babelTypes.identifier('value'),
|
|
172
|
+
babelTypes.logicalExpression('??', keys('getOwnPropertyNames'), keys('getOwnPropertySymbols')))]))])
|
|
173
|
+
const namedKey = babelTypes.memberExpression(babelTypes.cloneNode(key), babelTypes.identifier('value'))
|
|
174
|
+
const value = babelTypes.objectExpression([babelTypes.objectProperty(namedKey, expression.node, true)])
|
|
175
|
+
expression.parentPath.replaceWith(babelTypes.spreadElement(closeDefinitionExpression([declaration], value, [record, ...captured], [
|
|
176
|
+
babelTypes.objectExpression([babelTypes.objectProperty(parent.key, babelTypes.numericLiteral(0), true)]),
|
|
177
|
+
])))
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
// Definition regions have no enclosing callable scope to host the frame, so the owner gate
|
|
181
|
+
// selects them. An anonymous decorated class needs that frame for its inferred name and for a
|
|
182
|
+
// computed field value's per-evaluation key wherever the class expression appears.
|
|
183
|
+
if (!callableDefinitionOwner(path) && captured.length === 0
|
|
184
|
+
&& !(node.id === null && decorated && (!propertyValue || field))) return
|
|
185
|
+
let name
|
|
186
|
+
if (node.id === null) {
|
|
187
|
+
name = babelTypes.stringLiteral('')
|
|
188
|
+
if (expression.key === 'right' && (parent.type === 'AssignmentPattern' || parent.type === 'AssignmentExpression')
|
|
189
|
+
&& parent.left.type === 'Identifier') {
|
|
190
|
+
name = babelTypes.stringLiteral(parent.left.name)
|
|
191
|
+
} else if (expression.key === 'init' && parent.type === 'VariableDeclarator' && parent.id.type === 'Identifier') {
|
|
192
|
+
name = babelTypes.stringLiteral(parent.id.name)
|
|
193
|
+
} else if (propertyValue) {
|
|
194
|
+
if (!parent.computed) {
|
|
195
|
+
const key = parent.key.type === 'PrivateName' ? '#' + parent.key.id.name : parent.key.name ?? String(parent.key.value)
|
|
196
|
+
if (parent.type !== 'ObjectProperty' || key !== '__proto__') name = babelTypes.stringLiteral(key)
|
|
197
|
+
} else {
|
|
198
|
+
const key = babelTypes.identifier(allocate('decorated_class_key'))
|
|
199
|
+
const object = babelTypes.identifier(allocate('decorated_class_key_object'))
|
|
200
|
+
const nativeObject = babelTypes.memberExpression(babelTypes.identifier(intrinsics), babelTypes.identifier('Object'))
|
|
201
|
+
const keys = method => babelTypes.memberExpression(babelTypes.callExpression(
|
|
202
|
+
babelTypes.memberExpression(nativeObject, babelTypes.identifier(method)), [object]), babelTypes.numericLiteral(0), true)
|
|
203
|
+
const convert = babelTypes.arrowFunctionExpression([object], babelTypes.logicalExpression('??', keys('getOwnPropertyNames'), keys('getOwnPropertySymbols')))
|
|
204
|
+
parent.key = babelTypes.assignmentExpression('=', key, babelTypes.callExpression(convert,
|
|
205
|
+
[babelTypes.objectExpression([babelTypes.objectProperty(parent.key, babelTypes.numericLiteral(0), true)])]))
|
|
206
|
+
const containing = expression.parentPath.findParent(path => path.isClass()).node
|
|
207
|
+
capturedKeys.set(containing, [...(capturedKeys.get(containing) ?? []), babelTypes.cloneNode(key)])
|
|
208
|
+
name = babelTypes.cloneNode(key)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const parameter = name !== undefined && !babelTypes.isStringLiteral(name)
|
|
213
|
+
? babelTypes.identifier(allocate('decorated_class_name')) : undefined
|
|
214
|
+
const key = parameter === undefined ? name : babelTypes.memberExpression(parameter, babelTypes.identifier('value'))
|
|
215
|
+
const value = name === undefined ? node : babelTypes.memberExpression(babelTypes.objectExpression([
|
|
216
|
+
babelTypes.objectProperty(babelTypes.cloneNode(key), node, true),
|
|
217
|
+
]), babelTypes.cloneNode(key), true)
|
|
218
|
+
path.replaceWith(closeDefinitionExpression([], value,
|
|
219
|
+
[...(parameter === undefined ? [] : [parameter]), ...captured], parameter === undefined ? [] : [
|
|
220
|
+
babelTypes.objectExpression([babelTypes.objectProperty(babelTypes.identifier('value'), name)]),
|
|
221
|
+
]))
|
|
222
|
+
} } })
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Give potentially colliding public methods distinct physical keys. Babel then
|
|
226
|
+
* decorates every original descriptor, and the first class decorator publishes
|
|
227
|
+
* their final descriptors in source order before user class decorators run. */
|
|
228
|
+
function computedDecoratorMembers(helper, allocate, intrinsics, lazy = false) {
|
|
229
|
+
const access = name => babelTypes.memberExpression(lazy
|
|
230
|
+
? babelTypes.callExpression(babelTypes.identifier(helper), []) : babelTypes.identifier(helper), babelTypes.identifier(name))
|
|
231
|
+
const wrap = expression => {
|
|
232
|
+
if (expression.type !== 'MemberExpression') return babelTypes.callExpression(access('wrap'), [expression])
|
|
233
|
+
const receiver = babelTypes.identifier(allocate('computed_decorator_receiver'))
|
|
234
|
+
const key = babelTypes.identifier(allocate('computed_decorator_key'))
|
|
235
|
+
return babelTypes.callExpression(babelTypes.arrowFunctionExpression([receiver, key],
|
|
236
|
+
babelTypes.callExpression(access('wrap'), [babelTypes.memberExpression(receiver, key, true), receiver])),
|
|
237
|
+
[expression.object, expression.computed ? expression.property : babelTypes.stringLiteral(expression.property.name)])
|
|
238
|
+
}
|
|
239
|
+
return () => ({ pre(file) {
|
|
240
|
+
isolateDefinitionClassEvaluations(file, allocate, intrinsics)
|
|
241
|
+
file.path.traverse({ ObjectProperty(path) {
|
|
242
|
+
const {node}=path
|
|
243
|
+
let value=node.value
|
|
244
|
+
while((value.type.startsWith('TS')||value.type==='ParenthesizedExpression')&&value.expression) value=value.expression
|
|
245
|
+
if(node.computed||value.type!=='ClassExpression'||value.id!==null
|
|
246
|
+
||!value.decorators?.length&&!value.body.body.some(member=>member.decorators?.length)) return
|
|
247
|
+
const name=node.key.name??String(node.key.value)
|
|
248
|
+
if(name==='__proto__') return
|
|
249
|
+
node.key=babelTypes.stringLiteral(name)
|
|
250
|
+
node.computed=true
|
|
251
|
+
} })
|
|
252
|
+
file.scope.crawl()
|
|
253
|
+
}, visitor: { Class(path) {
|
|
254
|
+
const members = path.node.body.body.filter(member => member.type === 'ClassMethod' && member.kind !== 'constructor')
|
|
255
|
+
if (members.length < 2 || !members.some(member => member.computed)
|
|
256
|
+
|| !members.some(member => member.decorators?.length)) return
|
|
257
|
+
for (const member of members) {
|
|
258
|
+
const key = member.computed ? member.key : babelTypes.stringLiteral(member.key.name ?? String(member.key.value))
|
|
259
|
+
member.key = babelTypes.callExpression(access('key'), [key,
|
|
260
|
+
babelTypes.stringLiteral(member.kind), babelTypes.booleanLiteral(member.static)])
|
|
261
|
+
member.computed = true
|
|
262
|
+
member.decorators = [...(member.decorators ?? []).map(decorator => babelTypes.decorator(wrap(decorator.expression))),
|
|
263
|
+
babelTypes.decorator(access('prepare'))]
|
|
264
|
+
}
|
|
265
|
+
path.node.decorators = [...path.node.decorators ?? [], babelTypes.decorator(access('publish'))]
|
|
266
|
+
} } })
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function computedDecoratorBootstrap(helper, intrinsics, storage) {
|
|
270
|
+
return `${storage ? `var ${storage};function ${helper}(){return ${storage}??=` : `const ${helper}=`}(()=>{const O=${intrinsics}.Object,records=${intrinsics}.weakMapStore();const display=name=>typeof name==="symbol"?(${intrinsics}.symbolDescription(name)===void 0?"":"["+${intrinsics}.symbolDescription(name)+"]"):name;return {
|
|
271
|
+
key(value,kind,isStatic){const object={[value]:0},name=O.getOwnPropertyNames(object)[0]??O.getOwnPropertySymbols(object)[0];if(isStatic&&name==="prototype")throw new ${intrinsics}.TypeError("Classes may not have a static property named prototype");const symbol=${intrinsics}.Symbol();records.set(symbol,{name,kind});return symbol},
|
|
272
|
+
prepare(value,context){const record=records.get(context.name);O.defineProperty(value,"name",{value:(record.kind==="get"?"get ":record.kind==="set"?"set ":"")+display(record.name),configurable:true})},
|
|
273
|
+
wrap(decorator,receiver){return function(value,context){const name=records.get(context.name).name;context.name=name;context.access={has:object=>name in object,...(context.kind==="setter"?{}:{get:object=>object[name]}),...(context.kind==="method"||context.kind==="getter"?{}:{set:(object,value)=>{object[name]=value}})};return ${intrinsics}.Reflect.apply(decorator,receiver,[value,context])}},
|
|
274
|
+
publish(C){for(const target of ${intrinsics}.array([C,C.prototype]))for(const symbol of ${intrinsics}.array(O.getOwnPropertySymbols(target))){const record=records.get(symbol);if(!record)continue;const descriptor=O.getOwnPropertyDescriptor(target,symbol);if(record.kind==="get"||record.kind==="set"){const previous=O.getOwnPropertyDescriptor(target,record.name);const other=record.kind==="get"?"set":"get";descriptor[other]=previous?.[other]}O.defineProperty(target,record.name,descriptor);delete target[symbol];records.delete(symbol)}return C}
|
|
275
|
+
}})();${storage ? '}' : ''}\n`
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Lowerers supply helper and source provenance; this pass captures generated
|
|
279
|
+
* operations. Source nodes retain their locations and never enter this visitor's
|
|
280
|
+
* adaptation, even inside generated calls. Both lowerers consume one contract. */
|
|
281
|
+
function generatedHelperOperations(intrinsics, intrinsicNames, { arrays = true, records } = {}) {
|
|
282
|
+
const operation = name => babelTypes.memberExpression(babelTypes.identifier(intrinsics), babelTypes.identifier(name))
|
|
283
|
+
const functionOperations = { call: 'functionCall', apply: 'functionApply', bind: 'functionBind' }
|
|
284
|
+
const invocationTarget = node => babelTypes.isMemberExpression(node) && !node.computed && !node.loc
|
|
285
|
+
&& Object.hasOwn(functionOperations, node.property.name) ? operation(functionOperations[node.property.name]) : node
|
|
286
|
+
return {
|
|
287
|
+
ObjectExpression: { exit(path) {
|
|
288
|
+
if (!records?.has(path.node)) return
|
|
289
|
+
path.replaceWith(babelTypes.callExpression(operation('record'), [path.node]))
|
|
290
|
+
path.skip()
|
|
291
|
+
} },
|
|
292
|
+
ArrayExpression: { exit(path) {
|
|
293
|
+
if (path.node.loc || !arrays) return
|
|
294
|
+
path.replaceWith(babelTypes.callExpression(operation('array'), [path.node]))
|
|
295
|
+
path.skip()
|
|
296
|
+
} },
|
|
297
|
+
CallExpression: { exit(path) {
|
|
298
|
+
const callee = path.node.callee
|
|
299
|
+
if (path.node.loc || !babelTypes.isMemberExpression(callee) || callee.computed) return
|
|
300
|
+
const kind = callee.property.name
|
|
301
|
+
if (Object.hasOwn(functionOperations, kind)) {
|
|
302
|
+
const [receiver, ...args] = kind === 'bind' ? [callee.object, ...path.node.arguments] : path.node.arguments
|
|
303
|
+
const target = kind === 'bind' ? operation('functionBind') : invocationTarget(callee.object)
|
|
304
|
+
const argumentList = kind === 'apply' ? args[0] : babelTypes.arrayExpression(args)
|
|
305
|
+
path.replaceWith(babelTypes.callExpression(babelTypes.memberExpression(operation('Reflect'),
|
|
306
|
+
babelTypes.identifier('apply')), [target, invocationTarget(receiver), argumentList]))
|
|
307
|
+
} else return
|
|
308
|
+
path.skip()
|
|
309
|
+
} },
|
|
310
|
+
Identifier(path) {
|
|
311
|
+
if (path.node.loc || !intrinsicNames.has(path.node.name) || !path.isReferencedIdentifier()) return
|
|
312
|
+
path.replaceWith(operation(path.node.name))
|
|
313
|
+
path.skip()
|
|
314
|
+
},
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** The maintained helper accepts a scalar-or-list member decorator slot.
|
|
319
|
+
* Normalize that union at emission: source values are elements, and only
|
|
320
|
+
* generated arrays (including memoized ones) carry the private list role. */
|
|
321
|
+
function normalizeGeneratedDecoratorLists(file) {
|
|
322
|
+
const helper = file.declarations.applyDecs2311
|
|
323
|
+
if (helper === undefined) return
|
|
324
|
+
const isList = (path, seen = new Set()) => {
|
|
325
|
+
if (!path.node || path.node.loc || seen.has(path.node)) return false
|
|
326
|
+
if (path.isArrayExpression()) return true
|
|
327
|
+
if (!path.isIdentifier()) return false
|
|
328
|
+
seen.add(path.node)
|
|
329
|
+
const binding = path.scope.getBinding(path.node.name)
|
|
330
|
+
if (binding === undefined) return false
|
|
331
|
+
const values = []
|
|
332
|
+
if (binding.path.isVariableDeclarator() && binding.path.node.init) values.push(binding.path.get('init'))
|
|
333
|
+
for (const write of binding.constantViolations) if (write.isAssignmentExpression()) values.push(write.get('right'))
|
|
334
|
+
return values.length > 0 && values.every(value => isList(value, seen))
|
|
335
|
+
}
|
|
336
|
+
file.path.traverse({ CallExpression(path) {
|
|
337
|
+
if (path.node.loc || !babelTypes.isIdentifier(path.node.callee, helper)) return
|
|
338
|
+
// applyDecs2311: [class, class decorators, member descriptions, ...].
|
|
339
|
+
for (const info of path.get('arguments')[2].get('elements')) {
|
|
340
|
+
const list = info.get('elements')[0]
|
|
341
|
+
if (!isList(list)) list.replaceWith(babelTypes.arrayExpression([list.node]))
|
|
342
|
+
}
|
|
343
|
+
} })
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** Babel owns decorator application and initialization. Only compiler-created
|
|
347
|
+
* identifiers are redirected to captured intrinsics; user expressions retain
|
|
348
|
+
* their own lexical resolution. Native fields/private methods need no lowering
|
|
349
|
+
* on the supported Node runtime. */
|
|
350
|
+
export function lowerStatefulDecorators(result, target, intrinsicContext) {
|
|
351
|
+
if (result.sourceRegions?.decorated === false && !result.deferredHelpers?.size) return result
|
|
352
|
+
if (result.deferredHelpers?.size) {
|
|
353
|
+
const edits = []
|
|
354
|
+
eachNode(parseRecovered(result.code), node => {
|
|
355
|
+
if (node.type !== 'VariableDeclaration' || node.declarations.length !== 1) return
|
|
356
|
+
const text = result.deferredHelpers.get(node.declarations[0].id.name)
|
|
357
|
+
if (text) edits.push({ start: node.start, end: node.end, text })
|
|
358
|
+
})
|
|
359
|
+
result = { ...result, ...applySourceEdits(result.code, result.sourceMap, edits) }
|
|
360
|
+
}
|
|
361
|
+
const tree = parse(result.code, { ...parserOptionsForTarget(target), errorRecovery: true })
|
|
362
|
+
let decorated = false
|
|
363
|
+
eachNode(tree, node => { if (node.decorators?.length || node.type === 'ClassAccessorProperty') decorated = true })
|
|
364
|
+
if (!decorated) return result
|
|
365
|
+
const allocate = createGeneratedNameAllocator(tree)
|
|
366
|
+
const intrinsics = allocate('decorator_intrinsics')
|
|
367
|
+
const computedMembers = allocate('computed_members')
|
|
368
|
+
const computedStorage = target === 'module' ? allocate('computed_records') : undefined
|
|
369
|
+
const internalBindings = new Set(result.internalBindings)
|
|
370
|
+
internalBindings.add(intrinsics)
|
|
371
|
+
const intrinsicNames = new Set(['Object', 'Reflect', 'TypeError', 'ReferenceError', 'Error', 'Symbol', 'String', 'Number'])
|
|
372
|
+
const frameDeclaration = statement => statement.type === 'VariableDeclaration'
|
|
373
|
+
&& statement.declarations.some(binding => binding.id.name === result.rootFrameBinding)
|
|
374
|
+
const cellFrame = program => program.body.find(statement => statement.type === 'BlockStatement'
|
|
375
|
+
&& statement.body.some(frameDeclaration))
|
|
376
|
+
// The feature scan owns this tree through lowering. Transfer it to Babel;
|
|
377
|
+
// recovered syntax retains Babel's original parsing and diagnostic path.
|
|
378
|
+
const transform = tree.errors.length === 0
|
|
379
|
+
? options => transformFromAstSync(tree, result.code, { ...options, cloneInputAst: false })
|
|
380
|
+
: options => transformSync(result.code, options)
|
|
381
|
+
const transformed = transform({
|
|
382
|
+
filename: 'ptc-cell.ts', babelrc: false, configFile: false, browserslistConfigFile: false, sourceMaps: true,
|
|
383
|
+
parserOpts: parserOptionsForTarget(target),
|
|
384
|
+
plugins: [computedDecoratorMembers(computedMembers, allocate, intrinsics, target === 'module'),
|
|
385
|
+
[typescriptTransform, { allowDeclareFields: true, onlyRemoveTypeImports: true }],
|
|
386
|
+
[decoratorTransform, { version: '2023-11' }],
|
|
387
|
+
() => ({ post(file) {
|
|
388
|
+
normalizeGeneratedDecoratorLists(file)
|
|
389
|
+
file.path.traverse(generatedHelperOperations(intrinsics, intrinsicNames, {
|
|
390
|
+
arrays: !!intrinsicContext || target === 'module' || target === 'commonjs',
|
|
391
|
+
records: privateGeneratedRecords(file),
|
|
392
|
+
}))
|
|
393
|
+
for (const binding of Object.values(file.scope.bindings)) {
|
|
394
|
+
if (!binding.identifier.loc) internalBindings.add(binding.identifier.name)
|
|
395
|
+
}
|
|
396
|
+
if (result.rootFrameBinding !== undefined) {
|
|
397
|
+
const frame = cellFrame(file.ast.program)
|
|
398
|
+
const helpers = file.ast.program.body.filter(statement => statement !== frame && statement.type !== 'EmptyStatement')
|
|
399
|
+
file.ast.program.body = file.ast.program.body.filter(statement => statement === frame || statement.type === 'EmptyStatement')
|
|
400
|
+
frame.body.splice(frame.body.findIndex(frameDeclaration) + 1, 0, ...helpers.map(statement => {
|
|
401
|
+
if (!babelTypes.isFunctionDeclaration(statement)) return statement
|
|
402
|
+
// Native REPL block functions produce a value and may escape via
|
|
403
|
+
// Annex B. Helpers initialize before any user code in this frame.
|
|
404
|
+
const value = babelTypes.toExpression(statement)
|
|
405
|
+
return babelTypes.variableDeclaration('const', [babelTypes.variableDeclarator(babelTypes.cloneNode(value.id), value)])
|
|
406
|
+
}))
|
|
407
|
+
}
|
|
408
|
+
} })],
|
|
409
|
+
})
|
|
410
|
+
const applied = mappedTransform(result.code, result.sourceMap, transformed)
|
|
411
|
+
const parsed = parse(applied.code, parserOptionsForTarget(target))
|
|
412
|
+
const offset = result.rootFrameBinding === undefined ? parsed.program.directives.at(-1)?.end ?? 0
|
|
413
|
+
: cellFrame(parsed.program).body.find(frameDeclaration).end
|
|
414
|
+
const hasComputedMembers = transformed.code.includes(computedMembers)
|
|
415
|
+
if (hasComputedMembers) internalBindings.add(computedMembers)
|
|
416
|
+
if (hasComputedMembers && computedStorage) internalBindings.add(computedStorage)
|
|
417
|
+
const output = applySourceEdits(applied.code, applied.sourceMap, [
|
|
418
|
+
{ start: offset, end: offset, text: '\n' + intrinsicBootstrap(intrinsics, target, intrinsicContext)
|
|
419
|
+
+ (hasComputedMembers ? computedDecoratorBootstrap(computedMembers, intrinsics, computedStorage) : '') },
|
|
420
|
+
])
|
|
421
|
+
return { ...result, ...output, internalBindings,
|
|
422
|
+
...(result.sourceRegions === undefined ? {} : { sourceRegions: indexSourceRegions(output.code, parserOptionsForTarget(target)) }) }
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/** Erase type-only declarations and lower runtime TypeScript values before
|
|
426
|
+
* logical ownership and module export/link plans consume the source. */
|
|
427
|
+
export function normalizeTypeScriptValues(code, sourceMap, target) {
|
|
428
|
+
const tree=parse(code,{...parserOptionsForTarget(target),errorRecovery:true})
|
|
429
|
+
let parameterProperties = false
|
|
430
|
+
let tokens
|
|
431
|
+
const declarationTokens = (node, owner = node) => {
|
|
432
|
+
tokens ??= new Map()
|
|
433
|
+
let result = tokens.get(node)
|
|
434
|
+
if (result === undefined) {
|
|
435
|
+
result = parse(code.slice(owner.start, owner.end), { ...parserOptionsForTarget(target), errorRecovery: true, tokens: true }).tokens
|
|
436
|
+
.map(token => ({ ...token, start: token.start + owner.start, end: token.end + owner.start }))
|
|
437
|
+
tokens.set(node, result)
|
|
438
|
+
}
|
|
439
|
+
return result
|
|
440
|
+
}
|
|
441
|
+
const edits=[]
|
|
442
|
+
visitSource(tree,{
|
|
443
|
+
TSParameterProperty() { parameterProperties = true },
|
|
444
|
+
Decorator(path) {
|
|
445
|
+
const expression=path.node.expression
|
|
446
|
+
if(expression.extra?.parenthesized) return
|
|
447
|
+
edits.push({start:expression.start,end:expression.start,text:'('},
|
|
448
|
+
{start:expression.end,end:expression.end,text:')'})
|
|
449
|
+
},
|
|
450
|
+
'ExportNamedDeclaration|ExportDefaultDeclaration|ExportAllDeclaration'(path) {
|
|
451
|
+
if(path.node.exportKind==='type') {
|
|
452
|
+
edits.push({start:path.node.start,end:path.node.end,text:''})
|
|
453
|
+
path.skip()
|
|
454
|
+
return
|
|
455
|
+
}
|
|
456
|
+
const declaration=path.node.declaration
|
|
457
|
+
if(declaration?.type!=='ClassDeclaration'||declaration.declare===true
|
|
458
|
+
||declaration.decorators?.[0]?.start!==path.node.start) return
|
|
459
|
+
const afterDecorators=declaration.decorators.at(-1).end
|
|
460
|
+
const modifiers=declarationTokens(path.node).filter(token=>token.start>=afterDecorators&&token.end<=declaration.body.start
|
|
461
|
+
&&(token.type.label==='export'||token.type.label==='default'))
|
|
462
|
+
let prefix=''
|
|
463
|
+
const mappings=[]
|
|
464
|
+
for(const token of modifiers) {
|
|
465
|
+
const text=code.slice(token.start,token.end)
|
|
466
|
+
mappings.push({generatedStart:prefix.length,generatedEnd:prefix.length+text.length,
|
|
467
|
+
originalStart:token.start,originalEnd:token.end})
|
|
468
|
+
prefix+=text+' '
|
|
469
|
+
edits.push({start:token.start,end:token.end,text:''})
|
|
470
|
+
}
|
|
471
|
+
edits.push({start:path.node.start,end:path.node.start,text:prefix,mappings})
|
|
472
|
+
},
|
|
473
|
+
'TSTypeAliasDeclaration|TSInterfaceDeclaration|TSDeclareFunction'(path) {
|
|
474
|
+
const owner=path.parentPath.isExportNamedDeclaration()||path.parentPath.isExportDefaultDeclaration()?path.parent:path.node
|
|
475
|
+
edits.push({start:owner.start,end:owner.end,text:''})
|
|
476
|
+
path.skip()
|
|
477
|
+
},
|
|
478
|
+
'TSDeclareMethod|ClassProperty|ClassPrivateProperty|ClassAccessorProperty'(path) {
|
|
479
|
+
if(path.node.type!=='TSDeclareMethod'&&path.node.abstract!==true&&path.node.declare!==true) return
|
|
480
|
+
edits.push({start:path.node.start,end:path.node.end,text:''})
|
|
481
|
+
path.skip()
|
|
482
|
+
},
|
|
483
|
+
ImportDeclaration(path) {
|
|
484
|
+
if(path.node.importKind==='type') {
|
|
485
|
+
edits.push({start:path.node.start,end:path.node.end,text:''})
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
const specifiers=path.node.specifiers.filter(specifier=>specifier.type==='ImportSpecifier')
|
|
489
|
+
if(!specifiers.some(specifier=>specifier.importKind==='type')) return
|
|
490
|
+
const kept=specifiers.filter(specifier=>specifier.importKind!=='type')
|
|
491
|
+
if(kept.length===0&&specifiers.length===path.node.specifiers.length) {
|
|
492
|
+
edits.push({start:path.node.start,end:path.node.end,text:''})
|
|
493
|
+
return
|
|
494
|
+
}
|
|
495
|
+
edits.push({start:specifiers[0].start,end:specifiers.at(-1).end,
|
|
496
|
+
text:kept.map(specifier=>code.slice(specifier.start,specifier.end)).join(',')})
|
|
497
|
+
},
|
|
498
|
+
'VariableDeclaration|ClassDeclaration'(path) {
|
|
499
|
+
if(path.node.type==='ClassDeclaration'&&path.node.abstract===true&&path.node.declare!==true) {
|
|
500
|
+
const start=path.node.decorators?.at(-1)?.end??path.node.start
|
|
501
|
+
const owner=path.parentPath.isExportDefaultDeclaration()?path.parent:path.node
|
|
502
|
+
const modifier=declarationTokens(path.node,owner).find(token=>token.start>=start&&token.end<=path.node.body.start
|
|
503
|
+
&&token.type.label==='name'&&token.value==='abstract')
|
|
504
|
+
edits.push({start:modifier.start,end:modifier.end,text:''})
|
|
505
|
+
}
|
|
506
|
+
if(path.node.declare!==true) return
|
|
507
|
+
const owner=path.parentPath.isExportNamedDeclaration()||path.parentPath.isExportDefaultDeclaration()?path.parent:path.node
|
|
508
|
+
edits.push({start:owner.start,end:owner.end,text:''})
|
|
509
|
+
path.skip()
|
|
510
|
+
},
|
|
511
|
+
'TSEnumDeclaration|TSModuleDeclaration'(path) {
|
|
512
|
+
const owner=path.parentPath.isExportNamedDeclaration()?path.parent:path.node
|
|
513
|
+
let source=code.slice(path.node.start,path.node.end)
|
|
514
|
+
const enumEdits=[]
|
|
515
|
+
babelTypes.traverseFast(path.node,node=>{
|
|
516
|
+
if(node.type!=='TSEnumDeclaration'||node.const!==true) return
|
|
517
|
+
const modifier=declarationTokens(node).find(token=>token.type.label==='const')
|
|
518
|
+
enumEdits.push({start:modifier.start-path.node.start,end:modifier.end-path.node.start,
|
|
519
|
+
text:' '.repeat(modifier.end-modifier.start)})
|
|
520
|
+
})
|
|
521
|
+
if(enumEdits.length>0) source=applySourceEdits(source,identitySourceMap(source.length),enumEdits).code
|
|
522
|
+
const transformed=transformTypeScriptSource(source,{module:target!=='commonjs',sourceMap:true,
|
|
523
|
+
transform:{tsEnumIsMutable:true,noEmptyExport:true}})
|
|
524
|
+
const nativeMap=new SourceMap(JSON.parse(transformed.map))
|
|
525
|
+
const starts=[0]
|
|
526
|
+
for(let index=0;index<source.length;index++) if(source[index]==='\n') starts.push(index+1)
|
|
527
|
+
const mappings=[]
|
|
528
|
+
let line=1,column=1
|
|
529
|
+
for(let index=0;index<transformed.code.length;index++) {
|
|
530
|
+
const origin=nativeMap.findOrigin(line,column)
|
|
531
|
+
const start=Math.min(source.length,Math.max(0,(starts[origin.lineNumber-1]??0)+(origin.columnNumber??1)-1))
|
|
532
|
+
mappings.push({generatedStart:index,generatedEnd:index+1,originalStart:path.node.start+start,
|
|
533
|
+
originalEnd:Math.min(owner.end,path.node.start+start+1)})
|
|
534
|
+
if(transformed.code[index]==='\n'){line++;column=1}else column++
|
|
535
|
+
}
|
|
536
|
+
const exported=owner!==path.node&&transformed.code.trim()!==''?`\nexport {${path.node.id.name}};`:''
|
|
537
|
+
edits.push({start:owner.start,end:owner.end,text:transformed.code+exported,mappings})
|
|
538
|
+
path.skip()
|
|
539
|
+
},
|
|
540
|
+
})
|
|
541
|
+
const result = applySourceEdits(code,sourceMap,edits)
|
|
542
|
+
return parameterProperties ? lowerParameterProperties(result) : result
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/** Shared language lowering without logical binding or dynamic-environment rewriting. */
|
|
546
|
+
export function lowerNativeLanguageSource(code, { sourceType = 'script', nativeUsing } = {}) {
|
|
547
|
+
// Module syntax is a parsing context here, not a module bootstrap dependency.
|
|
548
|
+
const target = sourceType === 'module' ? 'reflection-module' : undefined
|
|
549
|
+
const options = parserOptionsForTarget(target)
|
|
550
|
+
const typed = normalizeTypeScriptValues(code, identitySourceMap(code.length), target)
|
|
551
|
+
const parameters = lowerLegacyParameterDecorators(typed, target)
|
|
552
|
+
const tree = parse(parameters.code, { ...options, errorRecovery: true })
|
|
553
|
+
const result = tree.errors.length > 0 ? normalizeStatefulScopes(code, undefined, { target, nativeUsing })
|
|
554
|
+
: lowerStatefulResources(lowerStatefulDecorators(parameters, target), { target, nativeUsing })
|
|
555
|
+
try {
|
|
556
|
+
parse(result.code, { ...options, plugins: [], errorRecovery: true })
|
|
557
|
+
return result.code
|
|
558
|
+
} catch {
|
|
559
|
+
return transformSync(result.code, { filename: 'ptc-source.ts', babelrc: false, configFile: false, browserslistConfigFile: false,
|
|
560
|
+
parserOpts: { ...options, errorRecovery: true },
|
|
561
|
+
plugins: [[typescriptTransform, { allowDeclareFields: true, onlyRemoveTypeImports: true }]],
|
|
562
|
+
}).code
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/** Parameter properties must become ordinary parameters and writes before scope
|
|
567
|
+
* ownership is assigned. Babel owns insertion after every reachable super(). */
|
|
568
|
+
function lowerParameterProperties(result) {
|
|
569
|
+
let needed = false
|
|
570
|
+
eachNode(parseRecovered(result.code), node => { if (node.type === 'TSParameterProperty') needed = true })
|
|
571
|
+
if (!needed) return result
|
|
572
|
+
const parameterProperties = new Map()
|
|
573
|
+
const facts = analyzeLogicalScopes(result.code, { parameterProperties, mutablePaths: true })
|
|
574
|
+
for (const [node, name] of facts.names) node.name = name
|
|
575
|
+
for (const [node, path] of facts.paths) {
|
|
576
|
+
if (node.type !== 'ClassMethod' || node.kind !== 'constructor') continue
|
|
577
|
+
const properties = parameterProperties.get(node) ?? []
|
|
578
|
+
if (!properties.length) continue
|
|
579
|
+
if (path.parent.body.findLast(member => member.type === 'ClassMethod' && member.kind === 'constructor') !== node) continue
|
|
580
|
+
const names = properties.map(property => bindingNodes(property.parameter)[0].name)
|
|
581
|
+
const skeleton = `class C{constructor(${names.map(name => `public ${name}:unknown`).join(',')}){}}`
|
|
582
|
+
const lowered = transformTypeScriptSource(skeleton, { transform: { noEmptyExport: true } }).code
|
|
583
|
+
const members = parse(lowered, { ...PARSER_OPTIONS, errorRecovery: true }).program.body[0].body.body
|
|
584
|
+
const assignments = members.find(member => member.type === 'ClassMethod').body.body
|
|
585
|
+
path.parent.body.unshift(...members.filter(member => member.type === 'ClassProperty'))
|
|
586
|
+
injectInitialization(path.parentPath.parentPath, path, assignments)
|
|
587
|
+
}
|
|
588
|
+
return mappedTransform(result.code, result.sourceMap, generate(facts.tree,
|
|
589
|
+
{ sourceMaps: true, sourceFileName: 'ptc-cell.ts' }, result.code))
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/** Parameter decorators select TypeScript's legacy decorator convention. The
|
|
593
|
+
* maintained compiler lowers their class/parameter phases before logical scopes;
|
|
594
|
+
* tslib helpers are private compiler bindings with captured language intrinsics. */
|
|
595
|
+
function lowerLegacyParameterDecorators(result, target, intrinsicContext) {
|
|
596
|
+
let needed = false
|
|
597
|
+
eachNode(parseRecovered(result.code), node => {
|
|
598
|
+
if (isFunctionNode(node) && node.params.some(parameter => parameter.decorators?.length)) needed = true
|
|
599
|
+
})
|
|
600
|
+
if (!needed) return result
|
|
601
|
+
const ts = loadTypeScript()
|
|
602
|
+
const expressionIntrinsics = createGeneratedNameAllocator(parseRecovered(result.code))('legacy_intrinsics')
|
|
603
|
+
const expressionBindings = new Set()
|
|
604
|
+
const internalPrivateNames = new Set()
|
|
605
|
+
const expressionClasses = new Map()
|
|
606
|
+
while (true) {
|
|
607
|
+
const facts = analyzeLogicalScopes(result.code, { mutablePaths: true })
|
|
608
|
+
const expressionTree = facts.tree
|
|
609
|
+
const names = createGeneratedNameAllocator(expressionTree, expressionClasses.keys())
|
|
610
|
+
let selected = [...facts.paths.values()].filter(path => path.isClassExpression()
|
|
611
|
+
&& path.node.body.body.some(member => member.params?.some(parameter => parameter.decorators?.length)))
|
|
612
|
+
.sort((left, right) => left.node.end - left.node.start - (right.node.end - right.node.start))[0]
|
|
613
|
+
if (!selected) break
|
|
614
|
+
// A computed field initializer runs after its key was evaluated. Its key
|
|
615
|
+
// cache belongs to this class definition, including repeated loop entries.
|
|
616
|
+
while (['ClassProperty', 'ClassAccessorProperty'].includes(selected.parent.type)
|
|
617
|
+
&& selected.parent.computed && !['StringLiteral', 'NumericLiteral'].includes(selected.parent.key.type)) {
|
|
618
|
+
const owner = selected.findParent(path => path.isClass())
|
|
619
|
+
if (expressionClasses.has(facts.names.get(owner.node.id))) break
|
|
620
|
+
selected = owner
|
|
621
|
+
}
|
|
622
|
+
const node = selected.node
|
|
623
|
+
const declarationId = selected.isClassDeclaration() ? node.id : undefined
|
|
624
|
+
const name = names('legacy_class_expression')
|
|
625
|
+
const parent = selected.parent
|
|
626
|
+
let inferredName = ''
|
|
627
|
+
let inferredKey
|
|
628
|
+
if ((parent.type === 'VariableDeclarator' && parent.init === node && parent.id.type === 'Identifier')
|
|
629
|
+
|| (parent.type === 'AssignmentPattern' || parent.type === 'AssignmentExpression') && parent.right === node && parent.left.type === 'Identifier') {
|
|
630
|
+
const identifier = parent.id ?? parent.left
|
|
631
|
+
inferredName = facts.names.get(identifier) ?? identifier.name
|
|
632
|
+
} else if (['ObjectProperty', 'ClassProperty', 'ClassAccessorProperty'].includes(parent.type) && parent.value === node
|
|
633
|
+
&& (!parent.computed || ['StringLiteral', 'NumericLiteral'].includes(parent.key.type))) {
|
|
634
|
+
inferredName = parent.key.name ?? String(parent.key.value)
|
|
635
|
+
if (parent.type === 'ObjectProperty' && !parent.computed && inferredName === '__proto__') inferredName = ''
|
|
636
|
+
} else if (['ObjectProperty', 'ClassProperty', 'ClassAccessorProperty'].includes(parent.type)
|
|
637
|
+
&& parent.value === node && parent.computed) {
|
|
638
|
+
inferredKey = names('legacy_class_key')
|
|
639
|
+
expressionBindings.add(inferredKey)
|
|
640
|
+
const object = names('legacy_key_object')
|
|
641
|
+
expressionBindings.add(object)
|
|
642
|
+
selected.scope.push({ id: babelTypes.identifier(inferredKey), kind: 'let', unique: true })
|
|
643
|
+
const coercion = parse(`(${inferredKey}=((${object})=>${expressionIntrinsics}.Object.getOwnPropertyNames(${object})[0]??${expressionIntrinsics}.Object.getOwnPropertySymbols(${object})[0])({[0]:0}))`, PARSER_OPTIONS).program.body[0].expression
|
|
644
|
+
coercion.right.arguments[0].properties[0].key = parent.key
|
|
645
|
+
parent.key = coercion
|
|
646
|
+
if (parent.type !== 'ObjectProperty') {
|
|
647
|
+
const owner = selected.findParent(path => path.isClass()).node
|
|
648
|
+
const ownerName = facts.names.get(owner.id)
|
|
649
|
+
const metadata = expressionClasses.get(ownerName)
|
|
650
|
+
if (metadata.suspending) {
|
|
651
|
+
const storage = names('legacy_field_key')
|
|
652
|
+
const field = babelTypes.classPrivateProperty(babelTypes.privateName(babelTypes.identifier(storage)),
|
|
653
|
+
babelTypes.identifier(inferredKey))
|
|
654
|
+
field.static = true
|
|
655
|
+
owner.body.body.unshift(field)
|
|
656
|
+
internalPrivateNames.add(storage)
|
|
657
|
+
metadata.retainSelf = metadata.originalName === undefined
|
|
658
|
+
inferredKey = `${metadata.originalName ?? ownerName}.#${storage}`
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
} else if (parent.type === 'ExportDefaultDeclaration') inferredName = 'default'
|
|
662
|
+
expressionClasses.set(name, { originalName: facts.names.get(node.id), inferredName, inferredKey })
|
|
663
|
+
node.type = 'ClassDeclaration'
|
|
664
|
+
node.id = babelTypes.identifier(name)
|
|
665
|
+
const invocation = babelTypes.callExpression(babelTypes.arrowFunctionExpression([], babelTypes.blockStatement([
|
|
666
|
+
node, babelTypes.returnStatement(babelTypes.identifier(name)),
|
|
667
|
+
])), [])
|
|
668
|
+
if (declarationId) {
|
|
669
|
+
const publicName = babelTypes.cloneNode(declarationId)
|
|
670
|
+
facts.names.set(publicName, facts.names.get(declarationId))
|
|
671
|
+
selected.replaceWith(babelTypes.variableDeclaration('let', [babelTypes.variableDeclarator(publicName, invocation)]))
|
|
672
|
+
selected = selected.get('declarations.0.init')
|
|
673
|
+
} else selected.replaceWith(invocation)
|
|
674
|
+
const wrapper = selected.get('callee')
|
|
675
|
+
const decorators = []
|
|
676
|
+
// Legacy decorators evaluate in the outer lexical environment. Include
|
|
677
|
+
// their expressions when classifying suspension, then restore their phase.
|
|
678
|
+
wrapper.get('body').traverse(traverseScopes.visitors.environmentVisitor({ Class(path) {
|
|
679
|
+
for (const owner of [path.node, ...path.node.body.body.flatMap(member => [member, ...member.params ?? []])]) {
|
|
680
|
+
if (!owner.decorators?.length) continue
|
|
681
|
+
const values = owner.decorators.map(decorator => {
|
|
682
|
+
const expression = babelTypes.cloneNode(decorator.expression)
|
|
683
|
+
const parsed = babelTypes.file(babelTypes.program([babelTypes.expressionStatement(expression)]))
|
|
684
|
+
traverseScopes(parsed, { noScope: true, Identifier(path) {
|
|
685
|
+
// The TypeScript parameter-decorator parser treats these tokens as
|
|
686
|
+
// identifiers; native lexical conversion requires their real nodes.
|
|
687
|
+
if (!path.isReferencedIdentifier() || !['this', 'super'].includes(path.node.name)) return
|
|
688
|
+
const replacement = path.node.name === 'this' ? babelTypes.thisExpression() : babelTypes.super()
|
|
689
|
+
replacement.loc = path.node.loc
|
|
690
|
+
path.replaceWith(replacement)
|
|
691
|
+
} })
|
|
692
|
+
return { decorator, statement: parsed.program.body[0] }
|
|
693
|
+
})
|
|
694
|
+
decorators.push({ owner, values })
|
|
695
|
+
owner.decorators = []
|
|
696
|
+
}
|
|
697
|
+
} }))
|
|
698
|
+
const probes = new Set(decorators.flatMap(({ values }) => values.map(({ statement }) => statement)))
|
|
699
|
+
wrapper.node.body.body.unshift(...probes)
|
|
700
|
+
let awaited = false, yielded = false
|
|
701
|
+
wrapper.get('body').traverse(traverseScopes.visitors.environmentVisitor({
|
|
702
|
+
ArrowFunctionExpression(path) { path.skip() },
|
|
703
|
+
AwaitExpression() { awaited = true },
|
|
704
|
+
YieldExpression() { yielded = true },
|
|
705
|
+
}))
|
|
706
|
+
if (yielded) {
|
|
707
|
+
wrapper.node.type = 'FunctionExpression'
|
|
708
|
+
wrapper.node.generator = true
|
|
709
|
+
wrapper.node.async = awaited
|
|
710
|
+
} else if (awaited) wrapper.node.async = true
|
|
711
|
+
expressionClasses.get(name).suspending = awaited || yielded
|
|
712
|
+
wrapper.node.body.body = wrapper.node.body.body.filter(statement => !probes.has(statement))
|
|
713
|
+
for (const { owner, values } of decorators) {
|
|
714
|
+
owner.decorators = values.map(({ decorator, statement }) => {
|
|
715
|
+
decorator.expression = statement.expression
|
|
716
|
+
return decorator
|
|
717
|
+
})
|
|
718
|
+
}
|
|
719
|
+
eachNode(expressionTree, current => {
|
|
720
|
+
if (current.type !== 'VariableDeclarator' || current.id.type !== 'Identifier' || current.id.loc) return
|
|
721
|
+
expressionBindings.add(current.id.name)
|
|
722
|
+
})
|
|
723
|
+
for (const [identifier, originalName] of facts.names) identifier.name = originalName
|
|
724
|
+
result = mappedTransform(result.code, result.sourceMap, generate(expressionTree,
|
|
725
|
+
{ sourceMaps: true, sourceFileName: 'ptc-cell.ts' }, result.code))
|
|
726
|
+
}
|
|
727
|
+
const tree = parse(result.code, { ...PARSER_OPTIONS, errorRecovery: true })
|
|
728
|
+
const allocate = createGeneratedNameAllocator(tree)
|
|
729
|
+
const marker = allocate('legacy_module_marker')
|
|
730
|
+
const preserved = new Map()
|
|
731
|
+
const masks = []
|
|
732
|
+
const preserve = (node, accessor = false) => {
|
|
733
|
+
const name = allocate('standard_decorator')
|
|
734
|
+
preserved.set(name, { text: result.code.slice(node.start, node.end), accessor })
|
|
735
|
+
masks.push({ start: node.start, end: node.end, text: accessor ? `${name}(){}` : `/*${name}*/` })
|
|
736
|
+
}
|
|
737
|
+
traverseScopes(tree, { noScope: true, Class(path) {
|
|
738
|
+
if (path.node.body.body.some(member => member.params?.some(parameter => parameter.decorators?.length))) return
|
|
739
|
+
for (const decorator of path.node.decorators ?? []) preserve(decorator)
|
|
740
|
+
for (const member of path.node.body.body) {
|
|
741
|
+
if (member.type === 'ClassAccessorProperty') { preserve(member, true); continue }
|
|
742
|
+
for (const decorator of member.decorators ?? []) preserve(decorator)
|
|
743
|
+
}
|
|
744
|
+
} })
|
|
745
|
+
const masked = applySourceEdits(result.code, result.sourceMap, masks)
|
|
746
|
+
const transformed = ts.transpileModule(masked.code + `\nexport const ${marker}=0;`, {
|
|
747
|
+
fileName: 'ptc-cell.ts', compilerOptions: { target: ts.ScriptTarget.ESNext,
|
|
748
|
+
module: ts.ModuleKind.ESNext, experimentalDecorators: true, importHelpers: true,
|
|
749
|
+
sourceMap: true, verbatimModuleSyntax: true },
|
|
750
|
+
})
|
|
751
|
+
let applied = mappedTransform(masked.code, masked.sourceMap,
|
|
752
|
+
{ code: transformed.outputText, map: transformed.sourceMapText })
|
|
753
|
+
const lowered = parse(applied.code, { ...PARSER_OPTIONS, errorRecovery: true })
|
|
754
|
+
const emittedMap = new SourceMap(JSON.parse(transformed.sourceMapText))
|
|
755
|
+
const intrinsics = expressionIntrinsics
|
|
756
|
+
const internalBindings = new Set([intrinsics, ...expressionClasses.keys(), ...expressionBindings])
|
|
757
|
+
const deferredHelpers = new Map([[intrinsics, intrinsicBootstrap(intrinsics, target, intrinsicContext)]])
|
|
758
|
+
const edits = []
|
|
759
|
+
const restore = (start, end, text) => edits.push({ start, end, text, mappings: [{
|
|
760
|
+
generatedStart: 0, generatedEnd: text.length, originalStart: start, originalEnd: end,
|
|
761
|
+
}] })
|
|
762
|
+
for (const comment of lowered.comments) {
|
|
763
|
+
const kept = preserved.get(comment.value)
|
|
764
|
+
if (kept && !kept.accessor) restore(comment.start, comment.end, kept.text)
|
|
765
|
+
}
|
|
766
|
+
eachNode(lowered, node => {
|
|
767
|
+
if (node.type !== 'ClassMethod') return
|
|
768
|
+
const kept = preserved.get(node.key.name)
|
|
769
|
+
if (kept?.accessor) restore(node.start, node.end, kept.text)
|
|
770
|
+
})
|
|
771
|
+
for (const node of lowered.program.body) {
|
|
772
|
+
if (node.type === 'ExportNamedDeclaration' && node.declaration?.declarations?.[0]?.id.name === marker) {
|
|
773
|
+
edits.push({ start: node.start, end: node.end, text: '' })
|
|
774
|
+
}
|
|
775
|
+
if (node.type !== 'ImportDeclaration' || node.source.value !== 'tslib'
|
|
776
|
+
|| emittedMap.findEntry(node.loc.start.line - 1, node.loc.start.column).originalLine !== undefined
|
|
777
|
+
|| !node.specifiers.every(specifier => specifier.imported?.name in legacyDecoratorHelpers)) continue
|
|
778
|
+
const helpers = node.specifiers.map(specifier => {
|
|
779
|
+
const name = specifier.local.name
|
|
780
|
+
internalBindings.add(name)
|
|
781
|
+
const helper = parse(`(${legacyDecoratorHelpers[specifier.imported.name].toString()})`, PARSER_OPTIONS)
|
|
782
|
+
traverseScopes(helper, { Identifier(path) {
|
|
783
|
+
if (!['Object', 'Reflect'].includes(path.node.name) || !path.isReferencedIdentifier()) return
|
|
784
|
+
path.replaceWith(babelTypes.memberExpression(babelTypes.identifier(intrinsics), babelTypes.identifier(path.node.name)))
|
|
785
|
+
path.skip()
|
|
786
|
+
} })
|
|
787
|
+
const declaration = helper.program.body[0].expression
|
|
788
|
+
if (target === 'module') {
|
|
789
|
+
declaration.type = 'FunctionDeclaration'
|
|
790
|
+
declaration.id = babelTypes.identifier(name)
|
|
791
|
+
deferredHelpers.set(name, generate(declaration).code)
|
|
792
|
+
} else deferredHelpers.set(name, `const ${name}=${generate(declaration).code};`)
|
|
793
|
+
return `const ${name}=void 0;`
|
|
794
|
+
}).join('\n')
|
|
795
|
+
edits.push({ start: node.start, end: node.end, text: `const ${intrinsics}=void 0;\n` + helpers })
|
|
796
|
+
}
|
|
797
|
+
applied = applySourceEdits(applied.code, applied.sourceMap, edits)
|
|
798
|
+
if (expressionClasses.size) {
|
|
799
|
+
const expressionEdits = []
|
|
800
|
+
eachNode(parseRecovered(applied.code), node => {
|
|
801
|
+
if (node.type !== 'ClassExpression' && node.type !== 'ClassDeclaration') return
|
|
802
|
+
const metadata = expressionClasses.get(node.id?.name)
|
|
803
|
+
if (!metadata) return
|
|
804
|
+
const inferred = metadata.originalName === undefined
|
|
805
|
+
const object = allocate('legacy_class_value')
|
|
806
|
+
internalBindings.add(object)
|
|
807
|
+
const keyPrefix = metadata.inferredKey
|
|
808
|
+
? `((${object})=>${object}[${intrinsics}.Object.getOwnPropertyNames(${object})[0]??${intrinsics}.Object.getOwnPropertySymbols(${object})[0]])({[${metadata.inferredKey}]:`
|
|
809
|
+
: `({[${JSON.stringify(metadata.inferredName)}]:`
|
|
810
|
+
const prefix = (node.type === 'ClassDeclaration' ? `let ${node.id.name}=` : '') + (inferred ? keyPrefix : '(')
|
|
811
|
+
const suffix = (inferred ? metadata.inferredKey ? '})' : `})[${JSON.stringify(metadata.inferredName)}]` : ')')
|
|
812
|
+
+ (node.type === 'ClassDeclaration' ? ';' : '')
|
|
813
|
+
expressionEdits.push({ start: node.start, end: node.start, text: prefix },
|
|
814
|
+
{ start: node.id.start, end: node.id.end, text: metadata.originalName ?? (metadata.retainSelf ? node.id.name : '') },
|
|
815
|
+
{ start: node.end, end: node.end, text: suffix })
|
|
816
|
+
if (metadata.retainSelf) {
|
|
817
|
+
const displayName = metadata.inferredKey
|
|
818
|
+
? `({[${metadata.inferredKey}]:class{}})[${metadata.inferredKey}].name`
|
|
819
|
+
: JSON.stringify(metadata.inferredName)
|
|
820
|
+
expressionEdits.push({ start: node.body.start + 1, end: node.body.start + 1,
|
|
821
|
+
text: `static{if(${intrinsics}.Object.getOwnPropertyDescriptor(this,"name")?.value===${JSON.stringify(node.id.name)})${intrinsics}.Object.defineProperty(this,"name",{value:${displayName}})}` })
|
|
822
|
+
}
|
|
823
|
+
})
|
|
824
|
+
applied = applySourceEdits(applied.code, applied.sourceMap, expressionEdits)
|
|
825
|
+
}
|
|
826
|
+
// Suspension scaffolds are never executed. Restore their statements to the
|
|
827
|
+
// original expression so await/yield retain their native continuation points.
|
|
828
|
+
while ([...expressionClasses.values()].some(metadata => metadata.suspending)) {
|
|
829
|
+
const tree = parse(applied.code, { ...PARSER_OPTIONS, errorRecovery: true, allowSuperOutsideMethod: true })
|
|
830
|
+
let selected
|
|
831
|
+
traverseScopes(tree, { noScope: true, CallExpression: { exit(path) {
|
|
832
|
+
const body = path.node.callee.body
|
|
833
|
+
if (selected || !body || body.type !== 'BlockStatement') return
|
|
834
|
+
const returned = body.body.at(-1)
|
|
835
|
+
if (returned?.type === 'ReturnStatement' && expressionClasses.get(returned.argument?.name)?.suspending) selected = path
|
|
836
|
+
} } })
|
|
837
|
+
if (!selected) break
|
|
838
|
+
const body = selected.node.callee.body.body
|
|
839
|
+
const metadata = expressionClasses.get(body.at(-1).argument.name)
|
|
840
|
+
const declarations = []
|
|
841
|
+
const statements = body.map(statement => {
|
|
842
|
+
if (statement.type === 'VariableDeclaration') return { ...statement, kind: 'var' }
|
|
843
|
+
if (statement.type === 'ReturnStatement') return babelTypes.expressionStatement(statement.argument)
|
|
844
|
+
return statement
|
|
845
|
+
})
|
|
846
|
+
const expression = babelTypes.toSequenceExpression(statements, { push({ id }) { declarations.push(id) } })
|
|
847
|
+
const owner = selected.findParent(path => path.isBlockStatement() || path.isStaticBlock() || path.isProgram())
|
|
848
|
+
for (const identifier of declarations) internalBindings.add(identifier.name)
|
|
849
|
+
owner.node.body.unshift(babelTypes.variableDeclaration('let', declarations.map(id => babelTypes.variableDeclarator(id))))
|
|
850
|
+
selected.replaceWith(expression)
|
|
851
|
+
metadata.suspending = false
|
|
852
|
+
applied = mappedTransform(applied.code, applied.sourceMap, generate(tree,
|
|
853
|
+
{ sourceMaps: true, sourceFileName: 'ptc-cell.ts' }, applied.code))
|
|
854
|
+
}
|
|
855
|
+
return { ...applied, internalBindings, deferredHelpers, internalPrivateNames }
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Recover syntax first, then give Babel unique declaration occurrences to build
|
|
860
|
+
* scope facts without imposing JavaScript's repeated-declaration early errors.
|
|
861
|
+
* Logical names are grouped only after Babel has assigned their actual owners.
|
|
862
|
+
*/
|
|
863
|
+
export function analyzeLogicalScopes(code, { target, parameterProperties, compact = false, compactNames = false, mutablePaths = false, onPhase } = {}) {
|
|
864
|
+
const tree = parse(code, { ...parserOptionsForTarget(target), errorRecovery: true, attachComment: !compact })
|
|
865
|
+
onPhase?.('scope-parsed')
|
|
866
|
+
// Read-only normalization owns exact UTF-16 ranges and its input map. The
|
|
867
|
+
// duplicated line/column objects are needed only by AST-generating clients.
|
|
868
|
+
if (compact) babelTypes.traverseFast(tree, node => { node.loc = undefined })
|
|
869
|
+
onPhase?.('scope-locations')
|
|
870
|
+
if (parameterProperties) eachNode(tree, node => {
|
|
871
|
+
if (node.type !== 'ClassMethod') return
|
|
872
|
+
parameterProperties.set(node, node.params.filter(parameter => parameter.type === 'TSParameterProperty'))
|
|
873
|
+
node.params = node.params.map(parameter => {
|
|
874
|
+
if (parameter.type !== 'TSParameterProperty') return parameter
|
|
875
|
+
parameter.parameter.decorators = parameter.decorators
|
|
876
|
+
return parameter.parameter
|
|
877
|
+
})
|
|
878
|
+
})
|
|
879
|
+
const invalidDelete=tree.errors.find(error=>error.reasonCode==='DeletePrivateField'||error.reasonCode==='StrictDelete')
|
|
880
|
+
if(invalidDelete) throw invalidDelete
|
|
881
|
+
const allocate = createGeneratedNameAllocator(tree, [], { compact: compactNames })
|
|
882
|
+
const occurrences = new Map()
|
|
883
|
+
const declarations = new Map()
|
|
884
|
+
const names = new Map()
|
|
885
|
+
const parameterExpressions = new Set()
|
|
886
|
+
const register = (node, declaration, role, declarator) => {
|
|
887
|
+
for (const binding of bindingNodes(node)) {
|
|
888
|
+
const occurrence = { node: binding, name: binding.name, declaration, declarator, role }
|
|
889
|
+
occurrences.set(binding, occurrence)
|
|
890
|
+
const list = declarations.get(declaration) ?? []
|
|
891
|
+
list.push(occurrence)
|
|
892
|
+
declarations.set(declaration, list)
|
|
893
|
+
names.set(binding, binding.name)
|
|
894
|
+
binding.name = allocate('scope_occurrence')
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
visitSource(tree, {
|
|
898
|
+
TSType(path) { path.skip() },
|
|
899
|
+
VariableDeclaration(path) {
|
|
900
|
+
for (const declarator of path.node.declarations) register(declarator.id, path.node, 'variable', declarator)
|
|
901
|
+
},
|
|
902
|
+
Function(path) {
|
|
903
|
+
const node = path.node
|
|
904
|
+
for (const parameter of node.params) eachNode(parameter, child => {
|
|
905
|
+
if (child.type === 'AssignmentPattern' || child.type === 'ObjectProperty' && child.computed) parameterExpressions.add(node)
|
|
906
|
+
})
|
|
907
|
+
if (node.id) register(node.id, node, node.type === 'FunctionDeclaration' ? 'function' : 'self')
|
|
908
|
+
for (const parameter of node.params) register(parameter.type === 'TSParameterProperty' ? parameter.parameter : parameter, node, 'parameter', parameter)
|
|
909
|
+
},
|
|
910
|
+
Class(path) {
|
|
911
|
+
if (path.node.id) register(path.node.id, path.node, path.isClassDeclaration() ? 'class' : 'self')
|
|
912
|
+
},
|
|
913
|
+
CatchClause(path) {
|
|
914
|
+
if (path.node.param) register(path.node.param, path.node, 'catch', path.node.param)
|
|
915
|
+
},
|
|
916
|
+
ImportDeclaration(path) {
|
|
917
|
+
for (const specifier of path.node.specifiers) register(specifier.local, path.node, 'import')
|
|
918
|
+
},
|
|
919
|
+
})
|
|
920
|
+
const scopes = new Map()
|
|
921
|
+
const functionSelfScopes = new Map()
|
|
922
|
+
const functionBodyScopes = new Map()
|
|
923
|
+
const { paths, owners } = analyzeSourceDeclarations(tree, declarations)
|
|
924
|
+
onPhase?.('scope-owners')
|
|
925
|
+
visitSource(tree, {
|
|
926
|
+
Identifier(path) {
|
|
927
|
+
const occurrence = occurrences.get(path.node)
|
|
928
|
+
if (occurrence === undefined) return
|
|
929
|
+
let scope = owners.get(path.node)
|
|
930
|
+
// Babel groups a named function's immutable self with its parameters and
|
|
931
|
+
// body declarations. The native self environment encloses both, so its
|
|
932
|
+
// identity must not participate in updates to a shadowing local binding.
|
|
933
|
+
if (occurrence.role === 'self' && occurrence.declaration.type === 'FunctionExpression') {
|
|
934
|
+
const self = { block: scope.block, parent: scope.parent }
|
|
935
|
+
functionSelfScopes.set(scope, self)
|
|
936
|
+
scope = self
|
|
937
|
+
}
|
|
938
|
+
const groups = scopes.get(scope) ?? new Map()
|
|
939
|
+
scopes.set(scope, groups)
|
|
940
|
+
const group = groups.get(occurrence.name) ?? { name: occurrence.name, scope, occurrences: [],
|
|
941
|
+
nativeInput: target === 'commonjs' && scope.block.type === 'Program' && (COMMONJS_PARAMETERS.includes(occurrence.name)
|
|
942
|
+
|| occurrence.name === 'arguments' && occurrence.declaration.kind === 'var') }
|
|
943
|
+
groups.set(occurrence.name, group)
|
|
944
|
+
group.occurrences.push(occurrence)
|
|
945
|
+
occurrence.group = group
|
|
946
|
+
occurrence.scope = scope
|
|
947
|
+
},
|
|
948
|
+
})
|
|
949
|
+
for (const [scope, groups] of scopes) {
|
|
950
|
+
if (!parameterExpressions.has(scope.block)) continue
|
|
951
|
+
for (const group of [...groups.values()]) {
|
|
952
|
+
const parameters = group.occurrences.filter(item => item.role === 'parameter')
|
|
953
|
+
const body = group.occurrences.filter(item => item.role !== 'parameter')
|
|
954
|
+
// A valid var/function redeclaration after parameter expressions creates
|
|
955
|
+
// a body binding. Conflicting lexical declarations retain the selected
|
|
956
|
+
// stateful parameter-update policy instead.
|
|
957
|
+
if (!parameters.length || !body.length
|
|
958
|
+
|| body.some(item => item.role !== 'function' && item.declaration.kind !== 'var')) continue
|
|
959
|
+
let bodyScope = functionBodyScopes.get(scope)
|
|
960
|
+
if (!bodyScope) {
|
|
961
|
+
bodyScope = { block: scope.block.body, parent: scope }
|
|
962
|
+
functionBodyScopes.set(scope, bodyScope)
|
|
963
|
+
scopes.set(bodyScope, new Map())
|
|
964
|
+
}
|
|
965
|
+
const local = { name: group.name, scope: bodyScope, occurrences: body, parameterSource: group }
|
|
966
|
+
scopes.get(bodyScope).set(group.name, local)
|
|
967
|
+
group.occurrences = parameters
|
|
968
|
+
for (const occurrence of body) {
|
|
969
|
+
occurrence.group = local
|
|
970
|
+
occurrence.scope = bodyScope
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
let emissionPaths = paths
|
|
975
|
+
if (mutablePaths) {
|
|
976
|
+
emissionPaths = new Map()
|
|
977
|
+
traverseScopes(tree, { enter(path) { emissionPaths.set(path.node, path) } })
|
|
978
|
+
}
|
|
979
|
+
return { tree, allocate, occurrences, declarations, names, scopes, functionSelfScopes, functionBodyScopes, paths: emissionPaths }
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function annexBPromotions(facts, target) {
|
|
983
|
+
if (target === 'module') return []
|
|
984
|
+
const promotions = []
|
|
985
|
+
for (const groups of facts.scopes.values()) for (const group of groups.values()) {
|
|
986
|
+
if (facts.functionBodyScopes.get(group.scope.parent) === group.scope) continue
|
|
987
|
+
if (!['BlockStatement', 'SwitchStatement'].includes(group.scope.block.type)
|
|
988
|
+
|| !group.occurrences.every(item => item.role === 'function'
|
|
989
|
+
&& !item.declaration.async && !item.declaration.generator)) continue
|
|
990
|
+
if (facts.paths.get(group.occurrences[0].declaration).isInStrictMode()) continue
|
|
991
|
+
const owner = group.scope.getFunctionParent() ?? group.scope.getProgramParent()
|
|
992
|
+
let blocked = false
|
|
993
|
+
for (let scope = group.scope.parent; scope; scope = scope.parent) {
|
|
994
|
+
const outer = facts.scopes.get(scope)?.get(group.name)
|
|
995
|
+
const simpleCatch = outer?.occurrences.every(item => item.role === 'catch' && item.declaration.param.type === 'Identifier')
|
|
996
|
+
if (outer && !simpleCatch && (scope !== owner || outer.occurrences.some(item => item.role === 'parameter'
|
|
997
|
+
|| item.role === 'import' || item.role === 'class'
|
|
998
|
+
|| item.role === 'variable' && item.declaration.kind !== 'var'))) { blocked = true; break }
|
|
999
|
+
if (scope === owner) break
|
|
1000
|
+
}
|
|
1001
|
+
if (!blocked) promotions.push({ group, owner, outer: facts.scopes.get(owner)?.get(group.name) })
|
|
1002
|
+
}
|
|
1003
|
+
return promotions
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function prepareAnnexBBlocks(code, sourceMap, target, onPhase) {
|
|
1007
|
+
const program = parseRecovered(code)
|
|
1008
|
+
onPhase?.('annex-parsed')
|
|
1009
|
+
let promote = false
|
|
1010
|
+
if (target !== 'module') visitSource(program, { FunctionDeclaration(path) {
|
|
1011
|
+
if (path.node.async || path.node.generator || path.isInStrictMode() || path.parentPath.isProgram()
|
|
1012
|
+
|| path.parentPath.isBlockStatement() && path.parentPath.parentPath.isFunction()) return
|
|
1013
|
+
promote = true
|
|
1014
|
+
path.stop()
|
|
1015
|
+
} })
|
|
1016
|
+
return { ...applySourceEdits(code, sourceMap, annexBBlockEdits(program)),
|
|
1017
|
+
promote }
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
function introduceAnnexBVars(code, sourceMap, target, onPhase) {
|
|
1021
|
+
const blocks = prepareAnnexBBlocks(code, sourceMap, target, onPhase)
|
|
1022
|
+
onPhase?.('annex-blocks')
|
|
1023
|
+
if (!blocks.promote) return blocks
|
|
1024
|
+
code = blocks.code
|
|
1025
|
+
sourceMap = blocks.sourceMap
|
|
1026
|
+
const facts = analyzeLogicalScopes(code, { target, compact: true, onPhase })
|
|
1027
|
+
const missing = new Map()
|
|
1028
|
+
for (const promotion of annexBPromotions(facts, target)) {
|
|
1029
|
+
if (promotion.outer) continue
|
|
1030
|
+
const names = missing.get(promotion.owner) ?? new Map()
|
|
1031
|
+
names.set(promotion.group.name, promotion.group.occurrences[0].node)
|
|
1032
|
+
missing.set(promotion.owner, names)
|
|
1033
|
+
}
|
|
1034
|
+
const edits = []
|
|
1035
|
+
for (const [owner, names] of missing) {
|
|
1036
|
+
const node = owner.block.type === 'Program' ? owner.block : owner.block.body
|
|
1037
|
+
const start = node.directives?.at(-1)?.end ?? (node.type === 'Program' ? 0 : node.start + 1)
|
|
1038
|
+
const text = '\n' + [...names.keys()].map(name => `var ${name};`).join('') + '\n'
|
|
1039
|
+
edits.push({ start, end: start, text, mappings: [...names].map(([name, binding]) => ({
|
|
1040
|
+
generatedStart: text.indexOf(`var ${name}`) + 4,
|
|
1041
|
+
generatedEnd: text.indexOf(`var ${name}`) + 4 + name.length,
|
|
1042
|
+
originalStart: binding.start, originalEnd: binding.end,
|
|
1043
|
+
})) })
|
|
1044
|
+
}
|
|
1045
|
+
return applySourceEdits(code, sourceMap, edits)
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Later phases parse and execute the emitted text again, and an expression
|
|
1050
|
+
* rewrite can turn the reference that starts an expression statement into a
|
|
1051
|
+
* parenthesized token. Automatic semicolon insertion would then join it to the
|
|
1052
|
+
* previous statement, so make that boundary explicit before any rewrite runs.
|
|
1053
|
+
*
|
|
1054
|
+
* A later rewrite can emit such a leading token for an expression statement's
|
|
1055
|
+
* callable reference, a declaration protected module/CommonJS mode emits as
|
|
1056
|
+
* `(pattern = (init));` (including one wrapped in an export), an exported
|
|
1057
|
+
* declaration or default value, and a block-level function declaration the
|
|
1058
|
+
* scope planner republishes as a candidate assignment. Every such boundary is
|
|
1059
|
+
* made explicit before any rewrite runs. A statement the module phases delete
|
|
1060
|
+
* as a whole cannot own the separator, because that deletion removes a trailing
|
|
1061
|
+
* semicolon with it, so the separator is attached to the last statement that
|
|
1062
|
+
* survives.
|
|
1063
|
+
*
|
|
1064
|
+
* The pass is semantics-neutral and idempotent. It changes the compiled form
|
|
1065
|
+
* of a stateful-v1/protected-v1 cell, so a journal recorded with an earlier
|
|
1066
|
+
* build may no longer reproduce its recorded completion; that cell is then
|
|
1067
|
+
* unreconstructable and recovery contracts the frontier as designed.
|
|
1068
|
+
*/
|
|
1069
|
+
export function preserveStatementBoundaries(code, sourceMap = identitySourceMap(code.length), parserOptions = {}) {
|
|
1070
|
+
let tree
|
|
1071
|
+
try {
|
|
1072
|
+
tree = parse(code, { ...PARSER_OPTIONS, ...parserOptions, errorRecovery: true })
|
|
1073
|
+
} catch {
|
|
1074
|
+
// Invalid source keeps its existing diagnostic owner; this pass only
|
|
1075
|
+
// protects boundaries the parser already proved.
|
|
1076
|
+
return { code, sourceMap }
|
|
1077
|
+
}
|
|
1078
|
+
// A parse error outside the recoverable set owns its diagnostic: inserting
|
|
1079
|
+
// anything would change the program that diagnostic describes.
|
|
1080
|
+
if (tree.errors.some(error => !RECOVERABLE_CELL_PARSE_ERRORS.has(error.reasonCode))) return { code, sourceMap }
|
|
1081
|
+
const edits = []
|
|
1082
|
+
// A later module phase deletes these statements as a whole, a trailing
|
|
1083
|
+
// semicolon included, and a reparsed `;` placed directly before the next
|
|
1084
|
+
// statement is itself absorbed as that deleted statement's terminator. The
|
|
1085
|
+
// separator therefore belongs to the last statement that survives.
|
|
1086
|
+
const removedAsWhole = node => node.type === 'ImportDeclaration'
|
|
1087
|
+
|| node.type === 'ExportAllDeclaration'
|
|
1088
|
+
|| node.type === 'ExportNamedDeclaration' && node.declaration === null
|
|
1089
|
+
|| node.type === 'ExportDefaultDeclaration'
|
|
1090
|
+
// Every statement form a later rewrite can emit with a `(` first token.
|
|
1091
|
+
// A declaration-less export or an import is deleted rather than re-emitted,
|
|
1092
|
+
// so only a surviving or re-emitted statement needs the guard.
|
|
1093
|
+
const parenthesized = (node, blockLevel) => node.type === 'ExpressionStatement'
|
|
1094
|
+
|| node.type === 'VariableDeclaration'
|
|
1095
|
+
|| node.type === 'ExportNamedDeclaration' && node.declaration !== null
|
|
1096
|
+
|| node.type === 'ExportDefaultDeclaration'
|
|
1097
|
+
|| blockLevel && node.type === 'FunctionDeclaration'
|
|
1098
|
+
const list = (statements, directives, blockLevel) => {
|
|
1099
|
+
const entries = [...directives ?? [], ...statements]
|
|
1100
|
+
let survivor
|
|
1101
|
+
let guardedAt
|
|
1102
|
+
for (const next of entries) {
|
|
1103
|
+
if (parenthesized(next, blockLevel) && survivor !== undefined && survivor.end !== guardedAt
|
|
1104
|
+
&& code[survivor.end - 1] !== ';' && code[survivor.end] !== ';') {
|
|
1105
|
+
edits.push({ start: survivor.end, end: survivor.end, text: ';' })
|
|
1106
|
+
guardedAt = survivor.end
|
|
1107
|
+
}
|
|
1108
|
+
if (!removedAsWhole(next)) survivor = next
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
const walk = node => {
|
|
1112
|
+
if (node.type === 'Program') list(node.body, node.directives, false)
|
|
1113
|
+
else if (node.type === 'BlockStatement' || node.type === 'StaticBlock') list(node.body, node.directives, true)
|
|
1114
|
+
else if (node.type === 'SwitchCase') list(node.consequent, undefined, true)
|
|
1115
|
+
for (const key of babelTypes.VISITOR_KEYS[node.type] ?? []) {
|
|
1116
|
+
const child = node[key]
|
|
1117
|
+
if (Array.isArray(child)) { for (const value of child) if (value?.type !== undefined) walk(value) }
|
|
1118
|
+
else if (child?.type !== undefined) walk(child)
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
walk(tree.program)
|
|
1122
|
+
return edits.length === 0 ? { code, sourceMap } : applySourceEdits(code, sourceMap, edits)
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* stateful-v1 local activations use stable cells. A declaration evaluates into
|
|
1127
|
+
* candidate cells and links them to the committed identity only on success.
|
|
1128
|
+
* Escaped candidate closures consequently retain failed candidates, while a
|
|
1129
|
+
* successful candidate follows later writes to the original identity.
|
|
1130
|
+
* The root compiler owns Program bindings and calls this pass after lowering
|
|
1131
|
+
* those declarations. protected-v1 keeps native local binding policy.
|
|
1132
|
+
*/
|
|
1133
|
+
export function normalizeStatefulScopes(code, sourceMap = identitySourceMap(code.length), options = {}) {
|
|
1134
|
+
const { outputPlan, ...metadata } = normalizeStatefulScopeInput(code, sourceMap, options)
|
|
1135
|
+
let result = metadata
|
|
1136
|
+
if (outputPlan) {
|
|
1137
|
+
// Release declaration ASTs and scope ancestry before materializing the
|
|
1138
|
+
// expanded output. This phase retains only numeric pieces and source facts.
|
|
1139
|
+
const { pieces, root, code, sourceMap, regionFacts } = outputPlan
|
|
1140
|
+
options.onPhase?.('mapping')
|
|
1141
|
+
const output = pieces.emit(root)
|
|
1142
|
+
result = { ...metadata, ...applySourceEdits(code, sourceMap,
|
|
1143
|
+
[{ start: 0, end: code.length, ...output }]),
|
|
1144
|
+
sourceRegions: { ...regionFacts, regions: output.regions } }
|
|
1145
|
+
}
|
|
1146
|
+
options.onPhase?.('validation')
|
|
1147
|
+
if (options.target === 'module' || options.target === 'commonjs') validateRegionSource(result, parserOptionsForTarget(options.target))
|
|
1148
|
+
result = options.deferDecorators ? result : lowerStatefulDecorators(result, options.target, options.intrinsicContext)
|
|
1149
|
+
return options.deferResources ? result : lowerStatefulResources(result, options)
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/** Resource lifetime is lowered only after declaration owners have consumed
|
|
1153
|
+
* the original lexical kind. Babel places lifetime boundaries; the shared
|
|
1154
|
+
* resource emitter keeps source operations in their owning activation. */
|
|
1155
|
+
export function lowerStatefulResources(result, { target, nativeUsing = supportsNativeUsing(), intrinsicContext } = {}) {
|
|
1156
|
+
if (nativeUsing || result.sourceRegions?.resources === false || !result.code.includes('using')) return result
|
|
1157
|
+
const parserOpts = parserOptionsForTarget(target)
|
|
1158
|
+
const tree = parse(result.code, parserOpts)
|
|
1159
|
+
let resources = false
|
|
1160
|
+
eachNode(tree, node => {
|
|
1161
|
+
if (node.type === 'VariableDeclaration' && (node.kind === 'using' || node.kind === 'await using')) resources = true
|
|
1162
|
+
})
|
|
1163
|
+
if (!resources) return result
|
|
1164
|
+
const allocate = createGeneratedNameAllocator(tree)
|
|
1165
|
+
const intrinsics = allocate('resource_intrinsics')
|
|
1166
|
+
const internalBindings = new Set(result.internalBindings)
|
|
1167
|
+
internalBindings.add(intrinsics)
|
|
1168
|
+
const intrinsicNames = new Set(['Object', 'TypeError', 'Error', 'Symbol', 'Promise', 'SuppressedError'])
|
|
1169
|
+
const ownsFrame = node => {
|
|
1170
|
+
let found = false
|
|
1171
|
+
eachNode(node, child => {
|
|
1172
|
+
if (child.type === 'VariableDeclarator' && child.id.name === result.rootFrameBinding) found = true
|
|
1173
|
+
})
|
|
1174
|
+
return found
|
|
1175
|
+
}
|
|
1176
|
+
const transformed = transformFromAstSync(tree, result.code, {
|
|
1177
|
+
filename: 'ptc-resource.js', babelrc: false, configFile: false, browserslistConfigFile: false, sourceMaps: true, parserOpts,
|
|
1178
|
+
cloneInputAst: false,
|
|
1179
|
+
plugins: [() => ({ pre(file) {
|
|
1180
|
+
// Keep the head's native lexical environment while expressing async
|
|
1181
|
+
// disposal as a body lifetime supported by the maintained lowerer.
|
|
1182
|
+
file.path.traverse({ ForOfStatement(path) {
|
|
1183
|
+
const { left } = path.node
|
|
1184
|
+
if (!babelTypes.isVariableDeclaration(left, { kind: 'await using' })) return
|
|
1185
|
+
const resource = babelTypes.identifier(allocate('iteration_resource'))
|
|
1186
|
+
const acquired = babelTypes.cloneNode(left.declarations[0].id)
|
|
1187
|
+
left.kind = 'const'
|
|
1188
|
+
path.node.body = babelTypes.blockStatement([
|
|
1189
|
+
babelTypes.variableDeclaration('await using', [babelTypes.variableDeclarator(resource, acquired)]),
|
|
1190
|
+
path.node.body,
|
|
1191
|
+
])
|
|
1192
|
+
} })
|
|
1193
|
+
if (file.ast.program.sourceType !== 'module' && file.ast.program.body.some(node =>
|
|
1194
|
+
node.type === 'VariableDeclaration' && (node.kind === 'using' || node.kind === 'await using'))) {
|
|
1195
|
+
file.ast.program.body = [babelTypes.blockStatement(file.ast.program.body)]
|
|
1196
|
+
}
|
|
1197
|
+
} }), resourceTransform, () => ({ post(file) {
|
|
1198
|
+
lowerResourceOperations(file, intrinsics, allocate)
|
|
1199
|
+
file.path.traverse(generatedHelperOperations(intrinsics, intrinsicNames))
|
|
1200
|
+
for (const binding of Object.values(file.scope.bindings)) {
|
|
1201
|
+
if (!binding.identifier.loc) internalBindings.add(binding.identifier.name)
|
|
1202
|
+
}
|
|
1203
|
+
if (result.rootFrameBinding !== undefined) {
|
|
1204
|
+
const frame = file.ast.program.body.find(ownsFrame)
|
|
1205
|
+
const helpers = file.ast.program.body.filter(statement => statement !== frame && statement.type !== 'EmptyStatement')
|
|
1206
|
+
file.ast.program.body = [babelTypes.blockStatement([...helpers.map(statement => {
|
|
1207
|
+
if (!babelTypes.isFunctionDeclaration(statement)) return statement
|
|
1208
|
+
const value = babelTypes.toExpression(statement)
|
|
1209
|
+
return babelTypes.variableDeclaration('const', [babelTypes.variableDeclarator(babelTypes.cloneNode(value.id), value)])
|
|
1210
|
+
}), frame])]
|
|
1211
|
+
}
|
|
1212
|
+
} })],
|
|
1213
|
+
})
|
|
1214
|
+
const applied = mappedSourceTransform(result.code, result.sourceMap, transformed)
|
|
1215
|
+
const parsed = parse(applied.code, parserOpts)
|
|
1216
|
+
const offset = result.rootFrameBinding === undefined ? parsed.program.directives.at(-1)?.end ?? 0
|
|
1217
|
+
: parsed.program.body[0].start + 1
|
|
1218
|
+
const output = applySourceEdits(applied.code, applied.sourceMap, [
|
|
1219
|
+
{ start: offset, end: offset, text: '\n' + intrinsicBootstrap(intrinsics, target, intrinsicContext) },
|
|
1220
|
+
])
|
|
1221
|
+
return { ...result, ...output, internalBindings,
|
|
1222
|
+
...(result.sourceRegions === undefined ? {} : { sourceRegions: indexSourceRegions(output.code, parserOpts) }) }
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
export function hasModuleResources(code) {
|
|
1226
|
+
return code.includes('using') && parse(code, parserOptionsForTarget('module')).program.body.some(node =>
|
|
1227
|
+
node.type === 'VariableDeclaration' && (node.kind === 'using' || node.kind === 'await using'))
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
function normalizeStatefulScopeInput(code, sourceMap, { mode = 'stateful-v1', target, moduleImport, intrinsicContext, onPhase,
|
|
1231
|
+
nativeJavaScript = false, nativeUsing = supportsNativeUsing() } = {}) {
|
|
1232
|
+
if (mode !== 'stateful-v1' && mode !== 'protected-v1') throw new TypeError('ptc-plus: unsupported scope semantics')
|
|
1233
|
+
onPhase?.('typescript')
|
|
1234
|
+
const typescript = nativeJavaScript ? { code, sourceMap }
|
|
1235
|
+
: lowerLegacyParameterDecorators(normalizeTypeScriptValues(code,sourceMap,target),target,intrinsicContext)
|
|
1236
|
+
const separated = preserveStatementBoundaries(typescript.code, typescript.sourceMap, parserOptionsForTarget(target))
|
|
1237
|
+
code=separated.code
|
|
1238
|
+
sourceMap=separated.sourceMap
|
|
1239
|
+
const finish = result => {
|
|
1240
|
+
result = { ...result, deferredHelpers: typescript.deferredHelpers,
|
|
1241
|
+
internalBindings: new Set([...typescript.internalBindings ?? [], ...result.internalBindings ?? []]) }
|
|
1242
|
+
return result
|
|
1243
|
+
}
|
|
1244
|
+
// Acquisition bindings own lifetime independently of exported identities.
|
|
1245
|
+
// Shared module cells retain lexical policy across native disposal, fallback
|
|
1246
|
+
// try regions and cyclic linking, without exporting a native using binding.
|
|
1247
|
+
const protectedModule = mode === 'protected-v1' && target === 'module' && hasModuleResources(code)
|
|
1248
|
+
const protectedCommonJs = mode === 'protected-v1' && target === 'commonjs'
|
|
1249
|
+
if (mode === 'protected-v1' && !protectedModule && !protectedCommonJs) return finish({ code, sourceMap })
|
|
1250
|
+
if (protectedCommonJs) {
|
|
1251
|
+
const parserOptions = parserOptionsForTarget(target)
|
|
1252
|
+
const tree = parse(code, parserOptions)
|
|
1253
|
+
let compilerBinding = false
|
|
1254
|
+
// A declaration-free name cannot shadow bootstrap. Prove that absence
|
|
1255
|
+
// from binding positions before allocating logical scope ancestry. If any
|
|
1256
|
+
// declaration might bind it, the complete scope planner decides ownership.
|
|
1257
|
+
eachNode(tree, node => {
|
|
1258
|
+
const patterns = node.type === 'VariableDeclarator' ? [node.id]
|
|
1259
|
+
: babelTypes.isFunction(node) ? [node.id, ...node.params]
|
|
1260
|
+
: babelTypes.isClass(node) ? [node.id]
|
|
1261
|
+
: node.type === 'CatchClause' ? [node.param] : []
|
|
1262
|
+
for (const pattern of patterns) {
|
|
1263
|
+
if (pattern && bindingNodes(pattern).some(binding => commonJsCompilerGlobals.has(binding.name))) compilerBinding = true
|
|
1264
|
+
}
|
|
1265
|
+
})
|
|
1266
|
+
if (!compilerBinding) return finish({ code, sourceMap, sourceRegions: indexSourceRegions(code, parserOptions, tree) })
|
|
1267
|
+
}
|
|
1268
|
+
onPhase?.('annex')
|
|
1269
|
+
const annex = introduceAnnexBVars(code, sourceMap, target, onPhase)
|
|
1270
|
+
code = annex.code
|
|
1271
|
+
sourceMap = annex.sourceMap
|
|
1272
|
+
onPhase?.('facts')
|
|
1273
|
+
const facts = analyzeLogicalScopes(code, { target, compact: true, compactNames: target === 'module' || target === 'commonjs', onPhase })
|
|
1274
|
+
onPhase?.('planning')
|
|
1275
|
+
const { tree, allocate, occurrences, scopes, paths } = facts
|
|
1276
|
+
const { decorated, resources, lexicalDynamic } = sourceFeatures(tree)
|
|
1277
|
+
const dynamicScopes = createDynamicScopeAnalysis(tree)
|
|
1278
|
+
const guardedBodies = singleStatementBodies(tree.program)
|
|
1279
|
+
const factory = allocate('local_cell')
|
|
1280
|
+
const intrinsics = allocate('local_intrinsics')
|
|
1281
|
+
const decoratorAdapter = allocate('member_decorator')
|
|
1282
|
+
const privateNames = allocate('private_names')
|
|
1283
|
+
const promotedFunctions = new Map(annexBPromotions(facts, target).flatMap(({ group, outer }) =>
|
|
1284
|
+
group.occurrences.map(item => [item.declaration, outer])))
|
|
1285
|
+
const protectedGroups = new Set()
|
|
1286
|
+
if (protectedCommonJs) {
|
|
1287
|
+
const root = [...scopes.values()].flatMap(scope => [...scope.values()])
|
|
1288
|
+
.filter(group => group.scope.block.type === 'Program')
|
|
1289
|
+
for (const group of root) if (commonJsCompilerGlobals.has(group.name)) protectedGroups.add(group)
|
|
1290
|
+
// A native declaration pattern owns all of its names together. Preserve
|
|
1291
|
+
// that ownership when only one name conflicts with bootstrap inputs.
|
|
1292
|
+
for (const group of protectedGroups) {
|
|
1293
|
+
const declarations = new Set(group.occurrences.map(item => item.declaration))
|
|
1294
|
+
for (const peer of root) if (peer.occurrences.some(item => declarations.has(item.declaration))) protectedGroups.add(peer)
|
|
1295
|
+
}
|
|
1296
|
+
if (protectedGroups.size === 0) return finish({ code, sourceMap,
|
|
1297
|
+
sourceRegions: indexSourceRegions(code, parserOptionsForTarget(target)) })
|
|
1298
|
+
}
|
|
1299
|
+
const groups = [...scopes.values()].flatMap(scope => [...scope.values()])
|
|
1300
|
+
.filter(group => (target === 'module' || target === 'commonjs' || group.scope.block.type !== 'Program')
|
|
1301
|
+
&& !group.occurrences.some(item => item.role === 'self')
|
|
1302
|
+
&& !(group.scope.block.type === 'Program' && typescript.internalBindings?.has(group.name))
|
|
1303
|
+
&& (!protectedCommonJs || protectedGroups.has(group))
|
|
1304
|
+
&& (!protectedModule || group.scope.block.type === 'Program' && group.occurrences.every(item =>
|
|
1305
|
+
item.role === 'class' || item.role === 'variable' && item.declaration.kind !== 'var')))
|
|
1306
|
+
const active = new Set(groups)
|
|
1307
|
+
const introductions = new Map()
|
|
1308
|
+
const declarationGroups = new Map()
|
|
1309
|
+
const parameterNames = new Map()
|
|
1310
|
+
const dynamicBindings = []
|
|
1311
|
+
const compilerLocalBindings = new Set()
|
|
1312
|
+
const rootPublications = new Map()
|
|
1313
|
+
const referenceCells = new Set()
|
|
1314
|
+
let varReferenceMarker
|
|
1315
|
+
const varReference = () => {
|
|
1316
|
+
if (!varReferenceMarker) {
|
|
1317
|
+
varReferenceMarker = allocate('var_reference')
|
|
1318
|
+
compilerLocalBindings.add(varReferenceMarker)
|
|
1319
|
+
dynamicBindings.push({ physicalName: varReferenceMarker, role: 'var-reference' })
|
|
1320
|
+
}
|
|
1321
|
+
return varReferenceMarker
|
|
1322
|
+
}
|
|
1323
|
+
const moduleExports = new Map()
|
|
1324
|
+
const importNames = new Map()
|
|
1325
|
+
const defaultExports = new Map()
|
|
1326
|
+
const labels = new Map()
|
|
1327
|
+
const jumps = new Map()
|
|
1328
|
+
const replacedConstructors = new Set()
|
|
1329
|
+
const privateClasses = new Map()
|
|
1330
|
+
const privateDeclarations = new Map()
|
|
1331
|
+
const privateMembers = new Map()
|
|
1332
|
+
const privateAccesses = new Map()
|
|
1333
|
+
const privateBrands = new Map()
|
|
1334
|
+
const decoratorContexts = new Map()
|
|
1335
|
+
visitSource(tree, {
|
|
1336
|
+
LabeledStatement(path) { labels.set(path.node, allocate('local_label')) },
|
|
1337
|
+
'BreakStatement|ContinueStatement'(path) {
|
|
1338
|
+
if (path.node.label === null) return
|
|
1339
|
+
const owner = path.findParent(parent => parent.isLabeledStatement() && parent.node.label.name === path.node.label.name)
|
|
1340
|
+
if (owner) jumps.set(path.node, owner.node)
|
|
1341
|
+
},
|
|
1342
|
+
ClassBody(path) {
|
|
1343
|
+
const implementations = path.node.body.filter(member => member.type === 'ClassMethod' && member.kind === 'constructor' && member.body)
|
|
1344
|
+
for (const member of implementations.slice(0,-1)) replacedConstructors.add(member)
|
|
1345
|
+
const publicMethods = new Map()
|
|
1346
|
+
for (const member of path.node.body) {
|
|
1347
|
+
if (member.type !== 'ClassMethod' || member.kind === 'constructor'
|
|
1348
|
+
|| member.computed && !['StringLiteral', 'NumericLiteral'].includes(member.key.type)) continue
|
|
1349
|
+
const name = member.key.name ?? String(member.key.value)
|
|
1350
|
+
const key = `${member.static}:${name}`
|
|
1351
|
+
const members = publicMethods.get(key) ?? []
|
|
1352
|
+
members.push(member)
|
|
1353
|
+
publicMethods.set(key, members)
|
|
1354
|
+
}
|
|
1355
|
+
for (const members of publicMethods.values()) {
|
|
1356
|
+
if (members.length < 2 || !members.some(member => member.decorators?.length)) continue
|
|
1357
|
+
let selected = new Map()
|
|
1358
|
+
for (const member of members) {
|
|
1359
|
+
const accessor = member.kind === 'get' || member.kind === 'set'
|
|
1360
|
+
if (!accessor || ![...selected.keys()].every(kind => kind === 'get' || kind === 'set')) selected = new Map()
|
|
1361
|
+
selected.set(accessor ? member.kind : 'value', member)
|
|
1362
|
+
}
|
|
1363
|
+
const kept = new Set(selected.values())
|
|
1364
|
+
for (const member of members) {
|
|
1365
|
+
if (kept.has(member)) continue
|
|
1366
|
+
const physical = allocate('decorated_effect')
|
|
1367
|
+
privateMembers.set(member, { physical, public: true })
|
|
1368
|
+
for (const decorator of member.decorators ?? []) decoratorContexts.set(decorator, {
|
|
1369
|
+
name: member.key.name ?? String(member.key.value), private: false,
|
|
1370
|
+
})
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
const grouped = new Map()
|
|
1374
|
+
for (const member of path.node.body) {
|
|
1375
|
+
if (member.key?.type !== 'PrivateName') continue
|
|
1376
|
+
const name = member.key.id.name
|
|
1377
|
+
const group = grouped.get(name) ?? { name, members: [], targets: new Map() }
|
|
1378
|
+
grouped.set(name,group)
|
|
1379
|
+
group.members.push(member)
|
|
1380
|
+
const owner = group.targets.get(member.static) ?? { static: member.static, members: [] }
|
|
1381
|
+
group.targets.set(member.static,owner)
|
|
1382
|
+
owner.members.push(member)
|
|
1383
|
+
}
|
|
1384
|
+
const groups = [...grouped.values()].filter(group => group.members.length > 1
|
|
1385
|
+
&& !(group.members.length === 2 && group.targets.size === 1
|
|
1386
|
+
&& new Set(group.members.map(member=>member.kind)).has('get')
|
|
1387
|
+
&& new Set(group.members.map(member=>member.kind)).has('set')))
|
|
1388
|
+
privateDeclarations.set(path.parent,new Set(grouped.keys()))
|
|
1389
|
+
if (groups.length === 0) return
|
|
1390
|
+
const classNode = path.parent
|
|
1391
|
+
const plan = { node: classNode, groups: new Map() }
|
|
1392
|
+
privateClasses.set(classNode,plan)
|
|
1393
|
+
for (const group of groups) {
|
|
1394
|
+
plan.groups.set(group.name,group)
|
|
1395
|
+
for (const owner of group.targets.values()) {
|
|
1396
|
+
owner.physical = group.targets.size===1?group.name:allocate('private_member')
|
|
1397
|
+
let selected = new Map()
|
|
1398
|
+
for (const member of owner.members) {
|
|
1399
|
+
const accessor = member.kind === 'get' || member.kind === 'set'
|
|
1400
|
+
if (!accessor || ![...selected.keys()].every(kind=>kind==='get'||kind==='set')) selected = new Map()
|
|
1401
|
+
selected.set(accessor ? member.kind : 'value',member)
|
|
1402
|
+
}
|
|
1403
|
+
owner.selected = new Set(selected.values())
|
|
1404
|
+
if(owner.physical!==group.name&&[...owner.selected].some(member=>member.type==='ClassPrivateMethod'&&member.kind==='method'&&!member.decorators?.length)) {
|
|
1405
|
+
owner.nameReady=true
|
|
1406
|
+
}
|
|
1407
|
+
owner.last = owner.members.at(-1).start
|
|
1408
|
+
for (const member of owner.members) {
|
|
1409
|
+
const physical = owner.selected.has(member) ? owner.physical
|
|
1410
|
+
: member.type === 'ClassPrivateProperty' || member.type === 'ClassAccessorProperty' || member.decorators?.length
|
|
1411
|
+
? allocate('private_effect') : undefined
|
|
1412
|
+
privateMembers.set(member, { physical })
|
|
1413
|
+
if (physical !== group.name) for (const decorator of member.decorators ?? []) {
|
|
1414
|
+
decoratorContexts.set(decorator, { name: '#' + group.name, private: true })
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
},
|
|
1420
|
+
})
|
|
1421
|
+
visitSource(tree,{
|
|
1422
|
+
'MemberExpression|OptionalMemberExpression'(path) {
|
|
1423
|
+
if(path.node.property.type!=='PrivateName') return
|
|
1424
|
+
const owner=path.findParent(parent=>parent.isClass()&&privateDeclarations.get(parent.node)?.has(path.node.property.id.name))
|
|
1425
|
+
const group=owner&&privateClasses.get(owner.node)?.groups.get(path.node.property.id.name)
|
|
1426
|
+
if(group) privateAccesses.set(path.node,group)
|
|
1427
|
+
},
|
|
1428
|
+
BinaryExpression(path) {
|
|
1429
|
+
if(path.node.operator!=='in'||path.node.left.type!=='PrivateName') return
|
|
1430
|
+
const owner=path.findParent(parent=>parent.isClass()&&privateDeclarations.get(parent.node)?.has(path.node.left.id.name))
|
|
1431
|
+
const group=owner&&privateClasses.get(owner.node)?.groups.get(path.node.left.id.name)
|
|
1432
|
+
if(group) privateBrands.set(path.node,group)
|
|
1433
|
+
},
|
|
1434
|
+
})
|
|
1435
|
+
for (const group of groups) {
|
|
1436
|
+
group.cell = allocate('local_binding')
|
|
1437
|
+
group.occurrences.sort((a, b) => a.node.start - b.node.start)
|
|
1438
|
+
const parameters = group.occurrences.filter(item => item.role === 'parameter' || item.role === 'catch')
|
|
1439
|
+
for (const item of parameters) parameterNames.set(item.node, allocate('local_parameter'))
|
|
1440
|
+
group.parameter = parameters.at(-1)
|
|
1441
|
+
const kind = group.parameter || group.nativeInput ? 'param' : group.occurrences[0].declaration.kind
|
|
1442
|
+
?? (group.occurrences[0].role === 'function' ? 'hoisted' : 'let')
|
|
1443
|
+
group.readOnly = mode === 'protected-v1' && ['const', 'using', 'await using'].includes(kind)
|
|
1444
|
+
for (const parameter of parameters) {
|
|
1445
|
+
const physicalName = parameterNames.get(parameter.node)
|
|
1446
|
+
if (typescript.internalBindings?.has(group.name)) compilerLocalBindings.add(physicalName)
|
|
1447
|
+
else dynamicBindings.push({ physicalName, name: group.name, kind: 'param', role: 'parameter',
|
|
1448
|
+
scopeStart: group.scope.block.start, scopeEnd: group.scope.block.end })
|
|
1449
|
+
}
|
|
1450
|
+
for (const item of group.occurrences.filter(item => item.role === 'import')) {
|
|
1451
|
+
const physicalName = allocate('local_import')
|
|
1452
|
+
importNames.set(item.node, physicalName)
|
|
1453
|
+
compilerLocalBindings.add(physicalName)
|
|
1454
|
+
}
|
|
1455
|
+
group.hoistedDeclaration = group.occurrences.filter(item => item.role === 'function').at(-1)?.declaration
|
|
1456
|
+
group.hoisted = group.hoistedDeclaration !== undefined && group.occurrences.every(item =>
|
|
1457
|
+
item.role === 'function' || item.role === 'parameter' || item.declaration.kind === 'var')
|
|
1458
|
+
if (target === 'module' && group.scope.block.type === 'Program') {
|
|
1459
|
+
group.publicName = allocate('module_binding')
|
|
1460
|
+
group.accessor = group.cell
|
|
1461
|
+
group.storage = allocate('module_record')
|
|
1462
|
+
group.cell = `${group.accessor}()`
|
|
1463
|
+
if (group.hoisted) group.publicName = group.name
|
|
1464
|
+
group.nativeLexical = !group.hoisted && !group.occurrences.some(item => item.role === 'import' || item.declaration.kind === 'var')
|
|
1465
|
+
compilerLocalBindings.add(group.storage)
|
|
1466
|
+
}
|
|
1467
|
+
if (group.publicName && !group.hoisted) compilerLocalBindings.add(group.publicName)
|
|
1468
|
+
const physicalName = group.accessor ?? group.cell
|
|
1469
|
+
if (typescript.internalBindings?.has(group.name)) compilerLocalBindings.add(physicalName)
|
|
1470
|
+
else dynamicBindings.push({ physicalName, name: group.name, kind, property: 'v',
|
|
1471
|
+
...(group.accessor ? { accessor: true } : {}),
|
|
1472
|
+
role: 'binding', scopeStart: group.scope.block.start, scopeEnd: group.scope.block.end })
|
|
1473
|
+
const owner = group.scope.block
|
|
1474
|
+
const list = introductions.get(owner) ?? []
|
|
1475
|
+
list.push(group)
|
|
1476
|
+
introductions.set(owner, list)
|
|
1477
|
+
for (const item of group.occurrences) {
|
|
1478
|
+
const list = declarationGroups.get(item.declaration) ?? new Set()
|
|
1479
|
+
list.add(group)
|
|
1480
|
+
declarationGroups.set(item.declaration, list)
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
if (target === 'module') {
|
|
1484
|
+
for (const statement of tree.program.body) {
|
|
1485
|
+
if (statement.type === 'ExportDefaultDeclaration') {
|
|
1486
|
+
const declaration = statement.declaration
|
|
1487
|
+
if (declaration.type === 'FunctionDeclaration' && declaration.id === null) {
|
|
1488
|
+
defaultExports.set(statement, { native: true })
|
|
1489
|
+
continue
|
|
1490
|
+
}
|
|
1491
|
+
const originalGroup = declaration.id ? occurrences.get(declaration.id)?.group : undefined
|
|
1492
|
+
const group = active.has(originalGroup) ? originalGroup : undefined
|
|
1493
|
+
if (protectedModule && declaration.type === 'FunctionDeclaration') {
|
|
1494
|
+
defaultExports.set(statement, { native: true })
|
|
1495
|
+
continue
|
|
1496
|
+
}
|
|
1497
|
+
const exported = group ?? { publicName: allocate('module_default') }
|
|
1498
|
+
defaultExports.set(statement, { group, exported })
|
|
1499
|
+
moduleExports.set('default', exported)
|
|
1500
|
+
continue
|
|
1501
|
+
}
|
|
1502
|
+
if (statement.type !== 'ExportNamedDeclaration' || statement.source) continue
|
|
1503
|
+
if (statement.declaration) {
|
|
1504
|
+
for (const occurrence of facts.declarations.get(statement.declaration) ?? []) {
|
|
1505
|
+
moduleExports.set(occurrence.name, active.has(occurrence.group) ? occurrence.group : { publicName: occurrence.name })
|
|
1506
|
+
}
|
|
1507
|
+
} else {
|
|
1508
|
+
for (const specifier of statement.specifiers) {
|
|
1509
|
+
const group = scopes.get(paths.scopeFor(statement))?.get(specifier.local.name)
|
|
1510
|
+
if (group) moduleExports.set(specifier.exported.name ?? specifier.exported.value,
|
|
1511
|
+
active.has(group) ? group : { publicName: specifier.local.name })
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
if (groups.length === 0 && defaultExports.size === 0 && labels.size === 0 && replacedConstructors.size === 0
|
|
1517
|
+
&& privateClasses.size === 0 && privateMembers.size === 0) return finish({ code, sourceMap })
|
|
1518
|
+
const resolve = (path, name) => resolveSourceBinding(facts, path, name)
|
|
1519
|
+
const references = new Map()
|
|
1520
|
+
const initializerTargets = new Map()
|
|
1521
|
+
for (const occurrence of occurrences.values()) {
|
|
1522
|
+
if (occurrence.declaration.kind !== 'var') continue
|
|
1523
|
+
const target = varInitializerTarget(paths.get(occurrence.node), occurrence.scope.block,
|
|
1524
|
+
occurrence.name, node => facts.names.get(node))
|
|
1525
|
+
if (target.catchNode || target.withNodes.length) {
|
|
1526
|
+
initializerTargets.set(occurrence.node, { withDepth: target.withNodes.length,
|
|
1527
|
+
catchGroup: target.catchNode && occurrences.get(target.catchNode.param).group })
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
const deferredDynamicReferences = new Set()
|
|
1531
|
+
const deleteOperands = new Map()
|
|
1532
|
+
visitSource(tree, {
|
|
1533
|
+
UnaryExpression(path) {
|
|
1534
|
+
if (path.node.operator !== 'delete') return
|
|
1535
|
+
let operand = path.node.argument
|
|
1536
|
+
while ((operand.type.startsWith('TS') || operand.type === 'ParenthesizedExpression') && operand.expression) operand = operand.expression
|
|
1537
|
+
if (operand.type === 'Identifier') deleteOperands.set(path.node, operand)
|
|
1538
|
+
},
|
|
1539
|
+
Identifier(path) {
|
|
1540
|
+
if (occurrences.has(path.node)) return
|
|
1541
|
+
if (!path.isReferencedIdentifier() && !path.isBindingIdentifier()) return
|
|
1542
|
+
const sourcePath = paths.get(path.node)
|
|
1543
|
+
const group = resolve(sourcePath, path.node.name)
|
|
1544
|
+
if (active.has(group)) {
|
|
1545
|
+
references.set(path.node, group)
|
|
1546
|
+
const withOwner = sourcePath.findParent(parent => parent.isWithStatement()
|
|
1547
|
+
&& path.node.start >= parent.node.body.start
|
|
1548
|
+
&& !(group.scope.block.start >= parent.node.body.start && group.scope.block.end <= parent.node.body.end))
|
|
1549
|
+
if (withOwner || dynamicScopes.mayEvalShadow(sourcePath, group.scope.block)) {
|
|
1550
|
+
deferredDynamicReferences.add(path.node)
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
},
|
|
1554
|
+
})
|
|
1555
|
+
onPhase?.('references')
|
|
1556
|
+
const pieces = createSourcePieces(code)
|
|
1557
|
+
const { text: part, join, original, mapped } = pieces
|
|
1558
|
+
const empty = () => part('')
|
|
1559
|
+
const regionContext = node => {
|
|
1560
|
+
const path = paths.get(node)
|
|
1561
|
+
const owner = isFunctionNode(node) ? path : path.findParent(parent => parent.isFunction())
|
|
1562
|
+
const privateNames = new Set()
|
|
1563
|
+
const regionLabels = []
|
|
1564
|
+
for (let current = path; current; current = current.parentPath) {
|
|
1565
|
+
if (current.isClass()) {
|
|
1566
|
+
for (const name of privateDeclarations.get(current.node) ?? []) privateNames.add(name)
|
|
1567
|
+
for (const group of privateClasses.get(current.node)?.groups.values() ?? []) {
|
|
1568
|
+
for (const target of group.targets.values()) privateNames.add(target.physical)
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
if (current.isLabeledStatement()) regionLabels.push(labels.get(current.node))
|
|
1572
|
+
}
|
|
1573
|
+
return { strict: path.isInStrictMode() || owner?.node.body.directives?.some(item => item.value.value === 'use strict'),
|
|
1574
|
+
async: owner?.node.async ?? target === 'module', generator: owner?.node.generator ?? false,
|
|
1575
|
+
labels: regionLabels, privateNames: [...privateNames], top: node.type === 'Program' }
|
|
1576
|
+
}
|
|
1577
|
+
const statementPieces = (values, node) => {
|
|
1578
|
+
if (values.length === 0) return empty()
|
|
1579
|
+
const chunks = []
|
|
1580
|
+
let pending = [], size = 0
|
|
1581
|
+
const flush = () => {
|
|
1582
|
+
const piece = join(pending)
|
|
1583
|
+
chunks.push(pieces.region(piece, { kind: 'statements', ...regionContext(node) }))
|
|
1584
|
+
pending = []; size = 0
|
|
1585
|
+
}
|
|
1586
|
+
for (const value of values) {
|
|
1587
|
+
pending.push(value)
|
|
1588
|
+
size += pieces.length(value)
|
|
1589
|
+
if (size >= 16384) flush()
|
|
1590
|
+
}
|
|
1591
|
+
if (pending.length) flush()
|
|
1592
|
+
return join(chunks)
|
|
1593
|
+
}
|
|
1594
|
+
const cellFor = (group, candidates) => candidates.get(group) ?? group.cell
|
|
1595
|
+
// A lazy cell may call its accessor. Keep that complete reference grouped
|
|
1596
|
+
// when source uses it as a constructor or the base of a constructor member.
|
|
1597
|
+
const read = (group, candidates) => {
|
|
1598
|
+
if (!active.has(group)) return `(${group.name})`
|
|
1599
|
+
const cell = cellFor(group, candidates)
|
|
1600
|
+
return referenceCells.has(cell) ? `(${cell}().value)` : `(${cell}.v)`
|
|
1601
|
+
}
|
|
1602
|
+
const parameterReference = (node, group) => {
|
|
1603
|
+
const owner = group.scope.block
|
|
1604
|
+
if (!isFunctionNode(owner) || node.start >= owner.body.start) return undefined
|
|
1605
|
+
const parameters = group.occurrences.filter(item=>item.role==='parameter').map(item=>parameterNames.get(item.node)).reverse()
|
|
1606
|
+
if(parameters.length===0) return undefined
|
|
1607
|
+
if(parameters.length===1) return parameters[0]
|
|
1608
|
+
const fallback=parameters.at(-1)
|
|
1609
|
+
const read=parameters.slice(0,-1).map(name=>`try{return ${name}}catch{}`).join('')+`return ${fallback}`
|
|
1610
|
+
const write=parameters.slice(0,-1).map(name=>`try{${name};${name}=value;return}catch{}`).join('')+`${fallback}=value`
|
|
1611
|
+
return `({get v(){${read}},set v(value){${write}}}).v`
|
|
1612
|
+
}
|
|
1613
|
+
const initialization = (group, candidates) => {
|
|
1614
|
+
if (group.publicName) {
|
|
1615
|
+
const imported = group.occurrences.filter(item => item.role === 'import').at(-1)
|
|
1616
|
+
const initial = group.hoisted ? group.publicName : 'void 0'
|
|
1617
|
+
let getter = imported ? `()=>${importNames.get(imported.node)}` : 'void 0'
|
|
1618
|
+
if (moduleImport && imported) {
|
|
1619
|
+
const specifier = imported.declaration.specifiers.find(specifier => specifier.local === imported.node)
|
|
1620
|
+
const name = specifier.type === 'ImportNamespaceSpecifier' ? null : specifier.type === 'ImportDefaultSpecifier' ? 'default'
|
|
1621
|
+
: specifier.imported.name ?? specifier.imported.value
|
|
1622
|
+
const attributes = Object.fromEntries((imported.declaration.attributes ?? [])
|
|
1623
|
+
.map(attribute => [attribute.key.name ?? attribute.key.value, attribute.value.value]))
|
|
1624
|
+
getter = `()=>${moduleImport}(${JSON.stringify(imported.declaration.source.value)},${JSON.stringify(name)},${getter},${JSON.stringify(attributes)})`
|
|
1625
|
+
}
|
|
1626
|
+
// Hoisted accessors expose the cell before evaluation. Creating a record
|
|
1627
|
+
// neither reads an import nor initializes a lexical source declaration.
|
|
1628
|
+
return part(`${group.hoisted || group.nativeLexical ? '' : `var ${group.publicName};`}var ${group.storage};function ${group.accessor}(){if(${group.storage})return ${group.storage};return ${group.storage}=${factory}(${JSON.stringify(group.name)},${initial},${!group.nativeLexical},${getter},void 0,${group.nativeLexical},${group.readOnly})}${group.cell};`)
|
|
1629
|
+
}
|
|
1630
|
+
if (group.parameter || group.nativeInput) {
|
|
1631
|
+
const bridge = group.nativeInput ? allocate('native_parameter') : undefined
|
|
1632
|
+
if (bridge) compilerLocalBindings.add(bridge)
|
|
1633
|
+
const name = group.parameter && parameterNames.get(group.parameter.node)
|
|
1634
|
+
const reference = bridge ? `${bridge}.get,${bridge}.set` : `()=>${name},v=>${name}=v`
|
|
1635
|
+
return join([...(bridge ? [part(`const ${bridge}={get:()=>${group.name},set:next=>${group.name}=next};`)] : []),
|
|
1636
|
+
part(`let ${group.cell}=${factory}(${JSON.stringify(group.name)},void 0,false,${reference});`),
|
|
1637
|
+
...(group.hoisted ? [part(`${group.cell}.v=(`),
|
|
1638
|
+
emit(group.hoistedDeclaration, candidates, new Set([group.hoistedDeclaration])), part(');')] : [])])
|
|
1639
|
+
}
|
|
1640
|
+
if (group.hoisted) {
|
|
1641
|
+
const declaration = group.hoistedDeclaration
|
|
1642
|
+
return join([part(`let ${group.cell}=${factory}(${JSON.stringify(group.name)},(`),
|
|
1643
|
+
emit(declaration, candidates, new Set([declaration])), part('),true);')])
|
|
1644
|
+
}
|
|
1645
|
+
if (group.parameterSource) return part(`let ${group.cell}=${factory}(${JSON.stringify(group.name)},${read(group.parameterSource, candidates)},true);`)
|
|
1646
|
+
const hoistedVar = group.occurrences.some(item => item.declaration.kind === 'var')
|
|
1647
|
+
const owner = group.scope.block
|
|
1648
|
+
const initial = hoistedVar && group.name === 'arguments' && isFunctionNode(owner)
|
|
1649
|
+
&& owner.type !== 'ArrowFunctionExpression' ? allocate('parameter_initial') : undefined
|
|
1650
|
+
if (initial !== undefined) {
|
|
1651
|
+
// The initial value belongs to the parameter environment, before the body
|
|
1652
|
+
// cell exists. Dynamic adaptation must not resolve it as a body reference.
|
|
1653
|
+
compilerLocalBindings.add(initial)
|
|
1654
|
+
dynamicBindings.push({ physicalName: initial, name: group.name, role: 'parameter-initializer' })
|
|
1655
|
+
}
|
|
1656
|
+
return part(`${initial === undefined ? '' : `const ${initial}=arguments;`}let ${group.cell}=${factory}(${JSON.stringify(group.name)},${initial ?? 'void 0'},${hoistedVar},void 0,void 0,${mode === 'protected-v1' && !hoistedVar},${group.readOnly});`)
|
|
1657
|
+
}
|
|
1658
|
+
const initializations = (node, candidates) => statementPieces((introductions.get(node) ?? []).map(group => initialization(group, candidates)), node)
|
|
1659
|
+
const emitPattern = (node, candidates) => {
|
|
1660
|
+
const result=emit(node,candidates)
|
|
1661
|
+
if(node.type==='Identifier'||!node.typeAnnotation) return result
|
|
1662
|
+
return pieces.slice(result, 0, pieces.length(result) - (node.end - node.typeAnnotation.start))
|
|
1663
|
+
}
|
|
1664
|
+
const emitInitializer = (node, candidates, name) => {
|
|
1665
|
+
let expression = node
|
|
1666
|
+
while ((expression.type.startsWith('TS') || expression.type === 'ParenthesizedExpression') && expression.expression) expression = expression.expression
|
|
1667
|
+
const anonymous = expression.type === 'ArrowFunctionExpression'
|
|
1668
|
+
|| ['FunctionExpression', 'ClassExpression', 'ClassDeclaration', 'FunctionDeclaration'].includes(expression.type) && expression.id === null
|
|
1669
|
+
return anonymous && name !== undefined
|
|
1670
|
+
? join([part(`({[${JSON.stringify(name)}]:(`), emit(node, candidates), part(`)})[${JSON.stringify(name)}]`)])
|
|
1671
|
+
: emit(node, candidates)
|
|
1672
|
+
}
|
|
1673
|
+
const declarator = (node, candidates, expression = false, iterationValue) => {
|
|
1674
|
+
const involved = [...new Set(bindingNodes(node.id).map(binding => occurrences.get(binding)?.group).filter(group => active.has(group)))]
|
|
1675
|
+
if (involved.length === 0) return expression ? emit(node, candidates)
|
|
1676
|
+
: join([part(paths.parentFor(node).kind + ' '), emit(node, candidates), part(';')])
|
|
1677
|
+
const lexical = involved.filter(group => group.nativeLexical && group.occurrences[0].node.start >= node.start
|
|
1678
|
+
&& group.occurrences[0].node.end <= node.end)
|
|
1679
|
+
if (node.init === null && iterationValue === undefined) return part(lexical.map(group => `let ${group.publicName};`).join('')
|
|
1680
|
+
+ involved.map(group => `${group.cell}.ensure()`).join(',') + (expression ? '' : ';'))
|
|
1681
|
+
const initializer = () => iterationValue ?? emitInitializer(node.init, candidates,
|
|
1682
|
+
node.id.type === 'Identifier' ? occurrences.get(node.id).name : undefined)
|
|
1683
|
+
if (mode === 'protected-v1') {
|
|
1684
|
+
// Native lexical patterns initialize each binding as its element is
|
|
1685
|
+
// reached. The readonly write path is distinct from initialization;
|
|
1686
|
+
// callbacks during later defaults can already observe earlier elements.
|
|
1687
|
+
const initializing = new Map(candidates)
|
|
1688
|
+
const declarations = []
|
|
1689
|
+
for (const binding of bindingNodes(node.id)) {
|
|
1690
|
+
const group = occurrences.get(binding)?.group
|
|
1691
|
+
if (!active.has(group)) continue
|
|
1692
|
+
const target = initializerTargets.get(binding)
|
|
1693
|
+
if (target?.withDepth) {
|
|
1694
|
+
const name = allocate('local_var_reference')
|
|
1695
|
+
const fallback = read(target.catchGroup ?? group, candidates)
|
|
1696
|
+
declarations.push(`${name}=()=>${varReference()}(${JSON.stringify(group.name)},${target.withDepth},{get:()=>${fallback},set:value=>${fallback}=value})`)
|
|
1697
|
+
compilerLocalBindings.add(name)
|
|
1698
|
+
referenceCells.add(name)
|
|
1699
|
+
initializing.set(target.catchGroup ?? group, name)
|
|
1700
|
+
} else if (!target?.catchGroup) initializing.set(group,
|
|
1701
|
+
`({get v(){return ${group.cell}.v},set v(value){${group.cell}.initialize(value)}})`)
|
|
1702
|
+
}
|
|
1703
|
+
const assignment = join([part('('), emitPattern(node.id, initializing), part('=('), initializer(), part('))')])
|
|
1704
|
+
return expression ? join([part(`${declarations.length ? declarations.join(',') + ',' : ''}${allocate('local_initialization')}=`), assignment])
|
|
1705
|
+
: join([part(declarations.length ? `{const ${declarations.join(',')};` : ''), assignment,
|
|
1706
|
+
part(declarations.length ? ';}' : ';')])
|
|
1707
|
+
}
|
|
1708
|
+
const targets = new Map(bindingNodes(node.id).map(binding =>
|
|
1709
|
+
[occurrences.get(binding)?.group, initializerTargets.get(binding)]))
|
|
1710
|
+
const stored = involved.filter(group => !targets.get(group)?.catchGroup)
|
|
1711
|
+
const candidateMap = new Map(candidates)
|
|
1712
|
+
const baseCandidates = new Map()
|
|
1713
|
+
const names = []
|
|
1714
|
+
for (const group of stored) {
|
|
1715
|
+
const name = allocate('local_candidate')
|
|
1716
|
+
if (typescript.internalBindings?.has(group.name) || targets.get(group)?.withDepth) compilerLocalBindings.add(name)
|
|
1717
|
+
else dynamicBindings.push({ physicalName: name, name: group.name, kind: 'let', property: 'v',
|
|
1718
|
+
role: 'candidate', linkPhysicalName: group.accessor ?? group.cell,
|
|
1719
|
+
...(group.accessor ? { linkAccessor: true } : {}), scopeStart: node.start, scopeEnd: node.end,
|
|
1720
|
+
...(node.init === null ? {} : { initializerStart: node.init.start, initializerEnd: node.init.end }),
|
|
1721
|
+
patternStart: node.id.start, patternEnd: node.id.end })
|
|
1722
|
+
candidateMap.set(group, name)
|
|
1723
|
+
baseCandidates.set(group, name)
|
|
1724
|
+
names.push(`${name}=${factory}(${JSON.stringify(group.name)},void 0,false,()=>${group.cell}.v)`)
|
|
1725
|
+
}
|
|
1726
|
+
for (const group of involved) {
|
|
1727
|
+
const target = targets.get(group)
|
|
1728
|
+
if (!target?.withDepth) continue
|
|
1729
|
+
const name = allocate('local_var_reference')
|
|
1730
|
+
const fallback = target.catchGroup ? read(target.catchGroup, candidates) : `${baseCandidates.get(group)}.v`
|
|
1731
|
+
names.push(`${name}=()=>${varReference()}(${JSON.stringify(group.name)},${target.withDepth},{get:()=>${fallback},set:value=>${fallback}=value})`)
|
|
1732
|
+
referenceCells.add(name)
|
|
1733
|
+
candidateMap.set(group, name)
|
|
1734
|
+
if (target.catchGroup) candidateMap.set(target.catchGroup, name)
|
|
1735
|
+
dynamicBindings.push({ physicalName: name, name: group.name, kind: 'let', role: 'candidate', reference: true, accessor: true,
|
|
1736
|
+
linkPhysicalName: group.accessor ?? group.cell, scopeStart: node.start, scopeEnd: node.end })
|
|
1737
|
+
}
|
|
1738
|
+
const candidateNames = stored.map(group => baseCandidates.get(group))
|
|
1739
|
+
const body = join([
|
|
1740
|
+
part(names.length ? `let ${names.join(',')};` : ''),
|
|
1741
|
+
part('('), emitPattern(node.id, candidateMap), part('=('),
|
|
1742
|
+
initializer(), part('));'),
|
|
1743
|
+
part(lexical.map(group => `let ${group.publicName}=${candidateMap.get(group)}.v;`).join('')),
|
|
1744
|
+
part(stored.map((group, index) => `${candidateNames[index]}.commit(${group.cell});`).join('')),
|
|
1745
|
+
])
|
|
1746
|
+
if (expression) {
|
|
1747
|
+
return join([part(`${names.length ? names.join(',') + ',' : ''}${allocate('local_initialization')}=(`),
|
|
1748
|
+
part('('), emitPattern(node.id, candidateMap), part('=('),
|
|
1749
|
+
initializer(), part('))'),
|
|
1750
|
+
part(stored.map((group, index) => `,${candidateNames[index]}.commit(${group.cell})`).join('')), part(')')])
|
|
1751
|
+
}
|
|
1752
|
+
return lexical.length ? body : join([part('{'), body, part('}')])
|
|
1753
|
+
}
|
|
1754
|
+
const privateBridge = group => {
|
|
1755
|
+
const targets = [...group.targets.values()].sort((a,b)=>b.last-a.last)
|
|
1756
|
+
const branches = targets.map(owner => {
|
|
1757
|
+
const names = target === 'module' ? `(${privateNames}??=${intrinsics}.weakSetStore())` : privateNames
|
|
1758
|
+
const name = owner.nameReady ? `if(!${names}.has(receiver.#${owner.physical})){${intrinsics}.Object.defineProperty(receiver.#${owner.physical},"name",{value:${JSON.stringify('#'+group.name)},configurable:true});${privateNames}.add(receiver.#${owner.physical});}` : ''
|
|
1759
|
+
const setter = [...owner.selected].some(member => member.type === 'ClassPrivateMethod' && member.kind === 'method')
|
|
1760
|
+
? `throw new ${intrinsics}.TypeError("Private method is not writable")` : `receiver.#${owner.physical}=value`
|
|
1761
|
+
return `if(#${owner.physical} in receiver){if(test)return true;${name}return {get v(){return receiver.#${owner.physical}},set v(value){${setter}},get call(){const fn=receiver.#${owner.physical};return fn==null?fn:(...args)=>${intrinsics}.Reflect.apply(fn,receiver,args)}}}`
|
|
1762
|
+
}).join('')
|
|
1763
|
+
// Private names themselves carry the class lexical environment. Keeping the
|
|
1764
|
+
// bridge there avoids manufacturing a class self-name and preserves native
|
|
1765
|
+
// inferred names even in delayed computed field initializers.
|
|
1766
|
+
return `((receiver,optional=false,test=false)=>{if(optional&&receiver==null)return void 0;${branches}if(test)return false;throw new ${intrinsics}.TypeError(${JSON.stringify(`Cannot access private member #${group.name} on this receiver`)})})`
|
|
1767
|
+
}
|
|
1768
|
+
const emit = (node, candidates = new Map(), suppress = new Set(), loopLabels = '') => {
|
|
1769
|
+
if (babelTypes.isTSType(node)) return original(node.start, node.end)
|
|
1770
|
+
if (node.type === 'Decorator') {
|
|
1771
|
+
const context = decoratorContexts.get(node)
|
|
1772
|
+
if (!context) return join([part('@('), emit(node.expression, candidates), part(')')])
|
|
1773
|
+
const tail = `,${JSON.stringify(context.name)},${context.private})`
|
|
1774
|
+
const expression = node.expression
|
|
1775
|
+
if (expression.type === 'MemberExpression') {
|
|
1776
|
+
const receiver = allocate('decorator_receiver')
|
|
1777
|
+
const key = allocate('decorator_key')
|
|
1778
|
+
return join([part(`@(((${receiver},${key})=>${decoratorAdapter}(${receiver}[${key}],${receiver}${tail})(`),
|
|
1779
|
+
emit(expression.object, candidates), part(','),
|
|
1780
|
+
expression.computed ? emit(expression.property, candidates) : part(JSON.stringify(expression.property.name)), part('))')])
|
|
1781
|
+
}
|
|
1782
|
+
return join([part(`@(${decoratorAdapter}(`), emit(expression, candidates), part(',void 0' + tail + ')')])
|
|
1783
|
+
}
|
|
1784
|
+
const member = paths.parentFor(node)
|
|
1785
|
+
if (privateMembers.get(member)?.public && member.key === node) {
|
|
1786
|
+
return mapped(`#${privateMembers.get(member).physical}`, node)
|
|
1787
|
+
}
|
|
1788
|
+
if(privateMembers.has(node)) {
|
|
1789
|
+
const plan=privateMembers.get(node)
|
|
1790
|
+
if(plan.physical===undefined) return part('')
|
|
1791
|
+
}
|
|
1792
|
+
if (replacedConstructors.has(node)) return part('')
|
|
1793
|
+
if (node.type === 'LabeledStatement') {
|
|
1794
|
+
let loop = node
|
|
1795
|
+
let prefix = ''
|
|
1796
|
+
while (loop.type === 'LabeledStatement') {
|
|
1797
|
+
prefix += `${labels.get(loop)}:`
|
|
1798
|
+
loop = loop.body
|
|
1799
|
+
}
|
|
1800
|
+
if ((loop.type === 'ForOfStatement' || loop.type === 'ForInStatement')
|
|
1801
|
+
&& (introductions.has(loop) || declarationGroups.has(loop.left) && loop.left.declarations[0].init)) {
|
|
1802
|
+
return emit(loop, candidates, suppress, prefix)
|
|
1803
|
+
}
|
|
1804
|
+
return join([part(`${labels.get(node)}:`),emit(node.body,candidates)])
|
|
1805
|
+
}
|
|
1806
|
+
if (jumps.has(node)) return mapped(`${node.type === 'BreakStatement' ? 'break' : 'continue'} ${labels.get(jumps.get(node))};`,node)
|
|
1807
|
+
if(node.type==='PrivateName' && privateMembers.has(member)) {
|
|
1808
|
+
return mapped(`#${privateMembers.get(member).physical}`,node)
|
|
1809
|
+
}
|
|
1810
|
+
if(privateBrands.has(node)) {
|
|
1811
|
+
const group=privateBrands.get(node)
|
|
1812
|
+
return join([part(`${privateBridge(group)}((`),emit(node.right,candidates),part('),false,true)')])
|
|
1813
|
+
}
|
|
1814
|
+
if((node.type==='CallExpression'||node.type==='OptionalCallExpression')&&privateAccesses.has(node.callee)) {
|
|
1815
|
+
const member=node.callee
|
|
1816
|
+
const group=privateAccesses.get(member)
|
|
1817
|
+
return join([part(`${privateBridge(group)}((`),emit(member.object,candidates),
|
|
1818
|
+
part(`)${member.optional?',true':''})${member.optional?'?.':'.'}call${node.optional?'?.':''}(`),
|
|
1819
|
+
...node.arguments.flatMap((argument,index)=>[...(index?[part(',')]:[]),emit(argument,candidates)]),part(')')])
|
|
1820
|
+
}
|
|
1821
|
+
if(node.type==='TaggedTemplateExpression'&&privateAccesses.has(node.tag)) {
|
|
1822
|
+
const group=privateAccesses.get(node.tag)
|
|
1823
|
+
return join([part(`${privateBridge(group)}((`),emit(node.tag.object,candidates),part(')).call'),emit(node.quasi,candidates)])
|
|
1824
|
+
}
|
|
1825
|
+
if(privateAccesses.has(node)) {
|
|
1826
|
+
const group=privateAccesses.get(node)
|
|
1827
|
+
return join([part(`(${privateBridge(group)}((`),emit(node.object,candidates),part(`)${node.optional?',true':''}))${node.optional?'?.':'.'}v`)])
|
|
1828
|
+
}
|
|
1829
|
+
if (deleteOperands.has(node)) {
|
|
1830
|
+
const operand = deleteOperands.get(node)
|
|
1831
|
+
if (paths.get(node).isInStrictMode()) {
|
|
1832
|
+
const position = mapSourcePosition({ line: 1, column: node.start + 1 }, code, code, identitySourceMap(code.length))
|
|
1833
|
+
throw Object.assign(new SyntaxError('Delete of an unqualified identifier in strict mode.'),
|
|
1834
|
+
{ loc: { line: position.line, column: position.column - 1 } })
|
|
1835
|
+
}
|
|
1836
|
+
// Deleting a declarative reference never reads its value, even in TDZ.
|
|
1837
|
+
// With-object and eval-created references keep their dynamic owner.
|
|
1838
|
+
const group = references.get(operand)
|
|
1839
|
+
const candidate = candidates.get(group)
|
|
1840
|
+
if (referenceCells.has(candidate) && !dynamicScopes.mayEvalShadow(paths.get(operand), group.scope.block)) {
|
|
1841
|
+
return mapped(`${candidate}().delete()`, node)
|
|
1842
|
+
}
|
|
1843
|
+
if (references.has(operand) && !deferredDynamicReferences.has(operand)) return mapped('false', node)
|
|
1844
|
+
return join([mapped('delete (', node), emit(operand, candidates), part(')')])
|
|
1845
|
+
}
|
|
1846
|
+
if (node.type === 'Identifier') {
|
|
1847
|
+
const referenced = references.get(node)
|
|
1848
|
+
const candidate = candidates.get(referenced)
|
|
1849
|
+
if (deferredDynamicReferences.has(node) && (!referenceCells.has(candidate)
|
|
1850
|
+
|| dynamicScopes.mayEvalShadow(paths.get(node), referenced.scope.block))) return original(node.start, node.end)
|
|
1851
|
+
if (node.name === 'eval' && member.type === 'CallExpression' && member.callee === node) return original(node.start, node.end)
|
|
1852
|
+
const occurrence = occurrences.get(node)
|
|
1853
|
+
if (active.has(occurrence?.group) && initializerTargets.has(node)) {
|
|
1854
|
+
return mapped(read(initializerTargets.get(node).catchGroup ?? occurrence.group, candidates), node)
|
|
1855
|
+
}
|
|
1856
|
+
if (importNames.has(node)) return mapped(importNames.get(node), node)
|
|
1857
|
+
if (parameterNames.has(node)) return mapped(parameterNames.get(node), node)
|
|
1858
|
+
const group = references.get(node) ?? (active.has(occurrence?.group) ? occurrence.group : undefined)
|
|
1859
|
+
if (group !== undefined && !(occurrence && ['function', 'class'].includes(occurrence.role))) {
|
|
1860
|
+
let text = parameterReference(node, group) ?? read(group, candidates)
|
|
1861
|
+
const path = paths.get(node)
|
|
1862
|
+
if ((path.parent.type === 'CallExpression' || path.parent.type === 'OptionalCallExpression') && path.key === 'callee'
|
|
1863
|
+
|| path.parent.type === 'TaggedTemplateExpression' && path.key === 'tag') {
|
|
1864
|
+
text = referenceCells.has(candidate) ? `${candidate}().callee` : `(0,${text})`
|
|
1865
|
+
}
|
|
1866
|
+
return mapped(text, node)
|
|
1867
|
+
}
|
|
1868
|
+
return original(node.start, node.end)
|
|
1869
|
+
}
|
|
1870
|
+
if (target === 'module' && node.type === 'ImportDeclaration') {
|
|
1871
|
+
const edits = node.specifiers.filter(specifier => importNames.has(specifier.local)).map(specifier => ({ start: specifier.local.start, end: specifier.local.end,
|
|
1872
|
+
text: `${specifier.type === 'ImportSpecifier' && specifier.imported.start === specifier.local.start
|
|
1873
|
+
? `${code.slice(specifier.imported.start, specifier.imported.end)} as ` : ''}${importNames.get(specifier.local)}` }))
|
|
1874
|
+
let cursor = node.start
|
|
1875
|
+
const parts = []
|
|
1876
|
+
for (const edit of edits) { parts.push(original(cursor, edit.start), part(edit.text)); cursor = edit.end }
|
|
1877
|
+
parts.push(original(cursor, node.end))
|
|
1878
|
+
return join(parts)
|
|
1879
|
+
}
|
|
1880
|
+
if (target === 'module' && node.type === 'ExportNamedDeclaration' && !node.source) {
|
|
1881
|
+
return node.declaration ? emit(node.declaration, candidates) : part('')
|
|
1882
|
+
}
|
|
1883
|
+
if (target === 'module' && node.type === 'ExportDefaultDeclaration') {
|
|
1884
|
+
const { group, exported, native } = defaultExports.get(node)
|
|
1885
|
+
if (native) return join([original(node.start, node.declaration.start), emit(node.declaration, candidates)])
|
|
1886
|
+
return group ? emit(node.declaration,candidates)
|
|
1887
|
+
: join([part(`let ${exported.publicName}=(`),emitInitializer(node.declaration,candidates,'default'),part(');')])
|
|
1888
|
+
}
|
|
1889
|
+
if (target === 'module' && (node.type === 'ExportAllDeclaration'
|
|
1890
|
+
|| node.type === 'ExportNamedDeclaration' && node.source)) return original(node.start,node.end)
|
|
1891
|
+
if (node.type === 'ObjectProperty' && node.shorthand) {
|
|
1892
|
+
return join([part(`[${JSON.stringify(node.key.name)}]: `), emit(node.value, candidates, suppress)])
|
|
1893
|
+
}
|
|
1894
|
+
if (node.type === 'AssignmentPattern') {
|
|
1895
|
+
return join([emit(node.left, candidates), part('=('), emitInitializer(node.right, candidates,
|
|
1896
|
+
occurrences.get(node.left)?.name ?? node.left.name), part(')')])
|
|
1897
|
+
}
|
|
1898
|
+
if (node.type === 'AssignmentExpression' && ['=', '&&=', '||=', '??='].includes(node.operator)) {
|
|
1899
|
+
let target = node.left
|
|
1900
|
+
while ((target.type.startsWith('TS') || target.type === 'ParenthesizedExpression') && target.expression) target = target.expression
|
|
1901
|
+
if (target.type === 'Identifier') return join([emit(node.left, candidates), part(`${node.operator}(`),
|
|
1902
|
+
emitInitializer(node.right, candidates, target.name), part(')')])
|
|
1903
|
+
}
|
|
1904
|
+
if (LOOP_HEADS.has(node.type)) {
|
|
1905
|
+
const head = node.type === 'ForStatement' ? node.init : node.left
|
|
1906
|
+
if (head?.type === 'VariableDeclaration' && declarationGroups.has(head)) {
|
|
1907
|
+
const headGroups = introductions.get(node) ?? []
|
|
1908
|
+
if (node.type === 'ForStatement') {
|
|
1909
|
+
const heads = headGroups.map(group => `${group.cell}=${factory}(${JSON.stringify(group.name)})`)
|
|
1910
|
+
const firstIteration=headGroups.length?allocate('local_first_iteration'):undefined
|
|
1911
|
+
const inputs = head.declarations.map(entry => {
|
|
1912
|
+
if (entry.init !== null) return declarator(entry, candidates, true)
|
|
1913
|
+
const ensures = bindingNodes(entry.id).map(binding => occurrences.get(binding).group)
|
|
1914
|
+
.map(group => `${group.cell}.ensure()`).join(',')
|
|
1915
|
+
return part(`${allocate('local_initialization')}=(${ensures})`)
|
|
1916
|
+
})
|
|
1917
|
+
return join([part(`for(let ${heads.length ? `${heads.join(',')},` : ''}`),
|
|
1918
|
+
...inputs.flatMap((input, index) => index ? [part(','), input] : [input]),
|
|
1919
|
+
part(firstIteration?`,${firstIteration}=true;${firstIteration}&&(${firstIteration}=false,${headGroups.map(group=>`${group.cell}=${group.cell}.copy()`).join(',')}),`:';'),
|
|
1920
|
+
...(node.test ? [emit(node.test, candidates)] : firstIteration?[part('true')]:[]), part(';'),
|
|
1921
|
+
part(headGroups.map(group => `${group.cell}=${group.cell}.copy()`).join(',')),
|
|
1922
|
+
...(node.update ? [part(headGroups.length ? ',' : ''), emit(node.update, candidates)] : []),
|
|
1923
|
+
part(')'), emit(node.body, candidates)])
|
|
1924
|
+
}
|
|
1925
|
+
const iteration = allocate('local_iteration')
|
|
1926
|
+
const keyword = head.kind === 'using' || head.kind === 'await using' ? head.kind : 'let'
|
|
1927
|
+
const sourceInitialization = node.type === 'ForInStatement' && head.declarations[0].init !== null
|
|
1928
|
+
? declarator(head.declarations[0], candidates) : undefined
|
|
1929
|
+
const sourceCandidates = new Map(candidates)
|
|
1930
|
+
const headCells = headGroups.map(group => {
|
|
1931
|
+
const physicalName = allocate('iteration_head')
|
|
1932
|
+
sourceCandidates.set(group, physicalName)
|
|
1933
|
+
dynamicBindings.push({ physicalName, name: group.name, kind: 'let', property: 'v',
|
|
1934
|
+
role: 'binding', scopeStart: node.start, scopeEnd: node.right.end })
|
|
1935
|
+
return `let ${physicalName}=${factory}(${JSON.stringify(group.name)},void 0,false,void 0,void 0,true);`
|
|
1936
|
+
}).join('')
|
|
1937
|
+
// The source sees a separate uninitialized head environment. Escaped
|
|
1938
|
+
// source closures retain it; each iteration creates its own live cells.
|
|
1939
|
+
const wrapped = headCells || sourceInitialization
|
|
1940
|
+
return join([part(wrapped ? `{${headCells}` : ''), ...(sourceInitialization ? [sourceInitialization] : []),
|
|
1941
|
+
part(`${loopLabels}for${node.await ? ' await' : ''}(${keyword} ${iteration} ${node.type === 'ForOfStatement' ? 'of' : 'in'} (`),
|
|
1942
|
+
emit(node.right, sourceCandidates), part(')){'), initializations(node, candidates),
|
|
1943
|
+
declarator(head.declarations[0], candidates, false, part(iteration)), emit(node.body, candidates),
|
|
1944
|
+
part(wrapped ? '}}' : '}')])
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
if (node.type === 'VariableDeclaration' && declarationGroups.has(node) && !suppress.has(node)) {
|
|
1948
|
+
if (node.kind === 'using' || node.kind === 'await using') {
|
|
1949
|
+
return join(node.declarations.flatMap(entry => {
|
|
1950
|
+
const resource = allocate('local_resource')
|
|
1951
|
+
const group = occurrences.get(entry.id)?.group
|
|
1952
|
+
if (group?.nativeLexical && group.occurrences[0].node === entry.id) {
|
|
1953
|
+
return [part(`${node.kind} ${resource}=(`), emitInitializer(entry.init, candidates, occurrences.get(entry.id).name),
|
|
1954
|
+
part(`);let ${group.publicName}=${resource};${group.cell}.initialize(${resource});`)]
|
|
1955
|
+
}
|
|
1956
|
+
return mode === 'protected-v1'
|
|
1957
|
+
? [part(`${node.kind} ${resource}=(`), emitInitializer(entry.init, candidates, occurrences.get(entry.id).name),
|
|
1958
|
+
part(`);${group.cell}.initialize(${resource});`)]
|
|
1959
|
+
: [part(`${node.kind} ${resource}=(`), emitInitializer(entry.init, candidates, occurrences.get(entry.id).name), part(');('),
|
|
1960
|
+
emitPattern(entry.id, candidates), part(`=${resource});`)]
|
|
1961
|
+
}))
|
|
1962
|
+
}
|
|
1963
|
+
const operations = join(node.declarations.map(entry => declarator(entry, candidates)))
|
|
1964
|
+
return guardedBodies.has(node)
|
|
1965
|
+
? join([part('{'), operations, part('}')]) : operations
|
|
1966
|
+
}
|
|
1967
|
+
if ((node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration')
|
|
1968
|
+
&& node.id && declarationGroups.has(node) && !suppress.has(node)) {
|
|
1969
|
+
const group = occurrences.get(node.id).group
|
|
1970
|
+
if (active.has(group)) {
|
|
1971
|
+
if (group.hoisted) {
|
|
1972
|
+
if (group.accessor) return node === group.hoistedDeclaration
|
|
1973
|
+
? emit(node, candidates, new Set([node])) : part(';')
|
|
1974
|
+
const promoted = promotedFunctions.get(node)
|
|
1975
|
+
if (!promoted) return part(';')
|
|
1976
|
+
if (active.has(promoted)) return mapped(`${read(promoted, candidates)}=${read(group, candidates)};`, node)
|
|
1977
|
+
// Annex B publishes to the proven var owner, outside any with or
|
|
1978
|
+
// catch environment surrounding the block declaration's execution.
|
|
1979
|
+
let publication = rootPublications.get(promoted)
|
|
1980
|
+
if (!publication) {
|
|
1981
|
+
publication = allocate('annex_publication')
|
|
1982
|
+
rootPublications.set(promoted, publication)
|
|
1983
|
+
compilerLocalBindings.add(publication)
|
|
1984
|
+
}
|
|
1985
|
+
return mapped(`${publication}(${read(group, candidates)});`, node)
|
|
1986
|
+
}
|
|
1987
|
+
if (group.nativeLexical && node === group.occurrences[0].declaration) {
|
|
1988
|
+
return join([part(`let ${group.publicName}=(`), emit(node, candidates, new Set([node])),
|
|
1989
|
+
part(`);${group.cell}.initialize(${group.publicName});`)])
|
|
1990
|
+
}
|
|
1991
|
+
return join([part(mode === 'protected-v1' ? `${group.cell}.initialize(` : `${group.cell}.v=(`),
|
|
1992
|
+
emit(node, candidates, new Set([node])), part(');')])
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
const discriminant = node.type === 'SwitchStatement' && introductions.has(node)
|
|
1996
|
+
? allocate('switch_discriminant') : undefined
|
|
1997
|
+
if (discriminant) {
|
|
1998
|
+
compilerLocalBindings.add(discriminant)
|
|
1999
|
+
dynamicBindings.push({ physicalName: discriminant, role: 'source-initializer' })
|
|
2000
|
+
}
|
|
2001
|
+
const children = []
|
|
2002
|
+
for (const key of babelTypes.VISITOR_KEYS[node.type] ?? []) {
|
|
2003
|
+
const value = node[key]
|
|
2004
|
+
for (const child of Array.isArray(value) ? value : [value]) {
|
|
2005
|
+
if (child && typeof child === 'object' && typeof child.type === 'string'
|
|
2006
|
+
&& Number.isInteger(child.start) && child.start >= node.start && child.end <= node.end) children.push(child)
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
children.sort((a, b) => a.start - b.start || b.end - a.end)
|
|
2010
|
+
let cursor = node.start
|
|
2011
|
+
const parts = []
|
|
2012
|
+
const statementList = node.type === 'BlockStatement' || node.type === 'Program' ? new Set(node.body) : undefined
|
|
2013
|
+
const statements = []
|
|
2014
|
+
for (const child of children) {
|
|
2015
|
+
if (child.start < cursor) continue
|
|
2016
|
+
const privateKey = privateMembers.get(node)?.public && node.computed && child === node.key
|
|
2017
|
+
const gap = original(cursor, privateKey ? code.lastIndexOf('[', child.start) : child.start)
|
|
2018
|
+
const value = discriminant && child === node.discriminant ? mapped(discriminant, child) : emit(child, candidates, suppress)
|
|
2019
|
+
if (statementList?.has(child)) {
|
|
2020
|
+
if (statements.length === 0) parts.push(gap)
|
|
2021
|
+
else statements.push(gap)
|
|
2022
|
+
statements.push(value)
|
|
2023
|
+
} else parts.push(gap, value)
|
|
2024
|
+
cursor = privateKey ? code.indexOf(']', child.end) + 1 : child.end
|
|
2025
|
+
}
|
|
2026
|
+
if (statements.length) parts.push(statementPieces(statements, node))
|
|
2027
|
+
parts.push(original(cursor, node.end))
|
|
2028
|
+
let result = join(parts)
|
|
2029
|
+
const parent = paths.parentFor(node)
|
|
2030
|
+
const owner = introductions.has(node) ? node
|
|
2031
|
+
: node.type === 'BlockStatement' && (isFunctionNode(parent) || parent.type === 'CatchClause')
|
|
2032
|
+
&& introductions.has(parent) ? parent : undefined
|
|
2033
|
+
if (owner && (node.type === 'BlockStatement' || node.type === 'StaticBlock')) {
|
|
2034
|
+
const prefix = join([...(owner === node && isFunctionNode(parent) ? [initializations(parent, candidates)] : []),
|
|
2035
|
+
initializations(owner, candidates)])
|
|
2036
|
+
const offset = node.directives?.length ? node.directives.at(-1).end-node.start
|
|
2037
|
+
: node.type === 'StaticBlock' ? code.indexOf('{', node.start) - node.start + 1 : 1
|
|
2038
|
+
// The opening token precedes every copied child, so inserting here cannot
|
|
2039
|
+
// split a transformed identifier or declaration.
|
|
2040
|
+
result = join([pieces.slice(result, 0, offset), prefix, pieces.slice(result, offset)])
|
|
2041
|
+
}
|
|
2042
|
+
if (discriminant) {
|
|
2043
|
+
// The physical case cells must also be outside the discriminant's
|
|
2044
|
+
// environment, including direct eval and closures that escape it.
|
|
2045
|
+
result = join([part(`{const ${discriminant}=(`), emit(node.discriminant, candidates),
|
|
2046
|
+
part(');{'), initializations(node, candidates), result, part('}}')])
|
|
2047
|
+
}
|
|
2048
|
+
if (node.type === 'ArrowFunctionExpression' && node.body.type !== 'BlockStatement' && introductions.has(node)) {
|
|
2049
|
+
const body = emit(node.body, candidates)
|
|
2050
|
+
const arrowEnd = callableMarkerPosition(node, code, tree.comments) + 2
|
|
2051
|
+
const head = []
|
|
2052
|
+
let headCursor = node.start
|
|
2053
|
+
for (const parameter of node.params) {
|
|
2054
|
+
head.push(original(headCursor, parameter.start), emit(parameter, candidates))
|
|
2055
|
+
headCursor = parameter.end
|
|
2056
|
+
}
|
|
2057
|
+
head.push(original(headCursor, arrowEnd))
|
|
2058
|
+
result = join([...head, part('{'), initializations(node, candidates), part('return ('), body, part(');}')])
|
|
2059
|
+
}
|
|
2060
|
+
return node.type === 'BlockStatement' && pieces.length(result) > 16384
|
|
2061
|
+
? pieces.region(result, { kind: 'block', ...regionContext(node) }) : result
|
|
2062
|
+
}
|
|
2063
|
+
onPhase?.('emit')
|
|
2064
|
+
const transformed = emit(tree.program)
|
|
2065
|
+
onPhase?.('emitted')
|
|
2066
|
+
const adapter = decoratorContexts.size === 0 ? '' : `${target === 'module' ? '' : `const ${decoratorAdapter}=`}function ${decoratorAdapter}(decorator,receiver,name,isPrivate){return function(value,context){context.name=name;context.private=isPrivate;if(typeof value==="function")${intrinsics}.Object.defineProperty(value,"name",{value:(context.kind==="getter"?"get ":context.kind==="setter"?"set ":"")+name,configurable:true});if(!isPrivate){context.access={has:object=>name in object,...(context.kind==="setter"?{}:{get:object=>object[name]}),...(context.kind==="method"||context.kind==="getter"?{}:{set:(object,value)=>{object[name]=value}})}}return ${intrinsics}.Reflect.apply(decorator,receiver,[value,context])}}${target === 'module' ? '' : ';'}\n`
|
|
2067
|
+
const needsPrivateNames = [...privateClasses.values()].some(plan => [...plan.groups.values()]
|
|
2068
|
+
.some(group => [...group.targets.values()].some(owner => owner.nameReady)))
|
|
2069
|
+
const helper = intrinsicBootstrap(intrinsics, target, intrinsicContext) + adapter
|
|
2070
|
+
+ (needsPrivateNames ? target === 'module' ? `var ${privateNames};\n` : `const ${privateNames}=${intrinsics}.weakSetStore();\n` : '')
|
|
2071
|
+
+ (varReferenceMarker ? `const ${varReferenceMarker}=void 0;\n` : '')
|
|
2072
|
+
+ (target === 'module' ? '' : `const ${factory}=`)
|
|
2073
|
+
+ `function ${factory}(name,value,ready=false,get,set,lexical=false,readOnly=false){let target;return {get v(){if(target)return target.v;if(get)return get();if(!ready)throw new ${intrinsics}.ReferenceError("Cannot access '"+name+"' before initialization");return value},set v(next){if(lexical&&!ready)throw new ${intrinsics}.ReferenceError("Cannot access '"+name+"' before initialization");if(readOnly)throw new ${intrinsics}.TypeError("Assignment to constant variable.");this.initialize(next)},initialize(next){if(target){target.v=next;return}if(set){set(next);return}value=next;ready=true;get=void 0},ensure(){if(!ready&&!get)this.initialize(void 0)},commit(next){if(!ready)return;next.initialize(this.v);target=next},copy(){return ${factory}(name,this.v,true)}}}${target === 'module' ? '' : ';'}\n`
|
|
2074
|
+
const exports = moduleExports.size === 0 ? '' : `\nexport {${[...moduleExports].map(([name,group])=>`${group.accessor??group.publicName} as ${JSON.stringify(name)}`).join(',')}};`
|
|
2075
|
+
const directiveEnd=tree.program.directives.at(-1)?.end??0
|
|
2076
|
+
const body = pieces.slice(transformed, directiveEnd)
|
|
2077
|
+
const publications = [...rootPublications].map(([group, name]) => mapped(
|
|
2078
|
+
`function ${name}(value){${group.name}=value;}\n`, group.occurrences[0].node))
|
|
2079
|
+
const result = join([original(0,directiveEnd),part('\n'+helper), ...publications,
|
|
2080
|
+
initializations(tree.program,new Map()), body, part(exports)])
|
|
2081
|
+
const privateBindings = [...privateDeclarations].map(([node, declared]) => ({ scopeStart: node.start, scopeEnd: node.end,
|
|
2082
|
+
hiddenNames: [...declared].filter(name => typescript.internalPrivateNames?.has(name)),
|
|
2083
|
+
names: [...privateClasses.get(node)?.groups ?? []].filter(([name]) => !typescript.internalPrivateNames?.has(name)).map(([name, group]) => ({ name, bridgeExpression: privateBridge(group),
|
|
2084
|
+
targets: [...group.targets.values()].map(owner => ({ physicalName: owner.physical, static: owner.static, sourceStart: owner.last })) })) }))
|
|
2085
|
+
return finish({ outputPlan: { pieces, root: result, code, sourceMap,
|
|
2086
|
+
regionFacts: { allocate, decorated, resources, lexicalDynamic } },
|
|
2087
|
+
dynamicBindings, privateBindings,
|
|
2088
|
+
...(protectedModule ? { moduleLexicalExports: new Set([...moduleExports].filter(([, group]) => active.has(group)).map(([name]) => name)) } : {}),
|
|
2089
|
+
internalBindings: new Set([factory, intrinsics, ...compilerLocalBindings, ...(decoratorContexts.size ? [decoratorAdapter] : []),
|
|
2090
|
+
...(needsPrivateNames ? [privateNames] : [])]) })
|
|
2091
|
+
}
|