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
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { registerHooks } from 'node:module'
|
|
1
|
+
import { create as createDomain } from 'node:domain'
|
|
2
|
+
import { createRequire, registerHooks } from 'node:module'
|
|
3
|
+
import { managedModuleImport, managedRequire, readModuleImport, statefulModuleLink } from './stateful-module-runtime.js'
|
|
4
|
+
import { staticModuleLinkReference } from './compiler-module-links.js'
|
|
5
|
+
import { compileStatefulModule, createUserModuleCompilationHooks } from './stateful-module-compiler.js'
|
|
6
|
+
import { USER_BINDING_TRANSFORM, LEGACY_USER_BINDING_TRANSFORM } from './module-transform-contract.js'
|
|
3
7
|
import { isAbsolute, resolve } from 'node:path'
|
|
4
8
|
import repl from 'node:repl'
|
|
5
9
|
import { PassThrough } from 'node:stream'
|
|
@@ -7,10 +11,90 @@ import { pathToFileURL } from 'node:url'
|
|
|
7
11
|
import { formatWithOptions } from 'node:util'
|
|
8
12
|
import { MessageChannel, parentPort, workerData } from 'node:worker_threads'
|
|
9
13
|
import { synchronizeBuiltinEsmExports } from './builtin-esm-sync.js'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
14
|
+
import { createPrivateAsyncLocalStorage } from './async-local-storage-intrinsics.js'
|
|
15
|
+
import { createExceptionOriginScope, errorDetails, messageOf, programBindingError } from './failure-reporting.js'
|
|
16
|
+
import { DURABLE_IMPORTS, FORBIDDEN_IMPORTS } from './module-policy.js'
|
|
12
17
|
import { decodeValue, encodeValue } from './value-wire.js'
|
|
18
|
+
import { LEGACY_USER_BINDINGS_REUSE_POLICY, LIVE_USER_BINDINGS_SHADOW_POLICY, normalizeUserBindingNames } from './session-journal-schema.js'
|
|
13
19
|
import { installWorkerCwdVirtualization } from './worker-cwd-virtualization.js'
|
|
20
|
+
import { createReplValueObserver, supportsAwaitLexicals, previewBindingValue } from './repl-value-observer.js'
|
|
21
|
+
import { createStatefulRootRuntime } from './stateful-root-runtime.js'
|
|
22
|
+
import { createNativeRootDynamic } from './native-root-dynamic.js'
|
|
23
|
+
import { moduleRuntimeIntrinsics } from './compiler-intrinsics.js'
|
|
24
|
+
import { compilerDescriptors } from './compiler-descriptors.js'
|
|
25
|
+
import { createCellCompletionObserver } from './cell-completion.js'
|
|
26
|
+
import { WORKER_SHUTDOWN_ACKNOWLEDGEMENT, WORKER_SHUTDOWN_REQUEST } from './worker-shutdown.js'
|
|
27
|
+
import { WORKER_REPL_OPTIONS, createWorkerControlPromise, createWorkerReplErrorHandler,
|
|
28
|
+
createWorkerUncaughtExceptionHandler,
|
|
29
|
+
disableWorkerReplDomain,
|
|
30
|
+
captureWorkerReplGlobals, protectWorkerReplAsyncContext, restoreWorkerReplGlobals, runInWorkerReplRealm,
|
|
31
|
+
workerReplContext } from './worker-repl-realm.js'
|
|
32
|
+
import { installProgramAmbientResolver } from './dynamic-environment-runtime.js'
|
|
33
|
+
|
|
34
|
+
const { Object, Map, Set, Proxy, Error,
|
|
35
|
+
mapGet, mapSet, mapHas, mapDelete, mapClear, mapSize, mapForEach,
|
|
36
|
+
setHas, setAdd, setDelete, setClear, setForEach,
|
|
37
|
+
everyArray, someArray, appendArray, copyArray, join, Reflect: privateReflect, promiseResolve, promiseReject } = moduleRuntimeIntrinsics
|
|
38
|
+
const PublicPromise = globalThis.Promise
|
|
39
|
+
const jsonStringify = globalThis.JSON.stringify
|
|
40
|
+
const bufferByteLength = globalThis.Buffer.byteLength
|
|
41
|
+
const String = globalThis.String
|
|
42
|
+
const process = globalThis.process
|
|
43
|
+
const ControlPromise = createWorkerControlPromise()
|
|
44
|
+
const hasProperty = privateReflect.has
|
|
45
|
+
|
|
46
|
+
function mapArray(values, visit) {
|
|
47
|
+
const result = []
|
|
48
|
+
for (let index = 0; index < values.length; index++) appendArray(result, visit(values[index], index))
|
|
49
|
+
return result
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function filterArray(values, select) {
|
|
53
|
+
const result = []
|
|
54
|
+
for (let index = 0; index < values.length; index++) {
|
|
55
|
+
if (select(values[index], index)) appendArray(result, values[index])
|
|
56
|
+
}
|
|
57
|
+
return result
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function mapFromArray(values, visit) {
|
|
61
|
+
const result = new Map()
|
|
62
|
+
for (let index = 0; index < values.length; index++) {
|
|
63
|
+
const entry = visit(values[index], index)
|
|
64
|
+
mapSet(result, entry[0], entry[1])
|
|
65
|
+
}
|
|
66
|
+
return result
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function setFromArray(values) {
|
|
70
|
+
const result = new Set()
|
|
71
|
+
for (let index = 0; index < values.length; index++) setAdd(result, values[index])
|
|
72
|
+
return result
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function mapKeysArray(value) {
|
|
76
|
+
const result = []
|
|
77
|
+
mapForEach(value, (_item, key) => appendArray(result, key))
|
|
78
|
+
return result
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function mapEntriesArray(value) {
|
|
82
|
+
const result = []
|
|
83
|
+
mapForEach(value, (item, key) => appendArray(result, [key, item]))
|
|
84
|
+
return result
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function mapValuesArray(value) {
|
|
88
|
+
const result = []
|
|
89
|
+
mapForEach(value, item => appendArray(result, item))
|
|
90
|
+
return result
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function setValuesArray(value) {
|
|
94
|
+
const result = []
|
|
95
|
+
setForEach(value, item => appendArray(result, item))
|
|
96
|
+
return result
|
|
97
|
+
}
|
|
14
98
|
|
|
15
99
|
if (parentPort === null) throw new Error('ptc-plus kernel worker started without a parent port')
|
|
16
100
|
const { port1, port2: channel } = new MessageChannel()
|
|
@@ -18,35 +102,102 @@ const { port1, port2: channel } = new MessageChannel()
|
|
|
18
102
|
const input = new PassThrough()
|
|
19
103
|
const output = new PassThrough()
|
|
20
104
|
output.resume()
|
|
105
|
+
const evaluationScope = createPrivateAsyncLocalStorage()
|
|
21
106
|
const sessionCwd = typeof workerData?.cwd === 'string' ? workerData.cwd : undefined
|
|
22
107
|
if (sessionCwd !== undefined && !isAbsolute(sessionCwd)) {
|
|
23
|
-
throw new Error(`ptc-plus session cwd must be absolute, got ${
|
|
108
|
+
throw new Error(`ptc-plus session cwd must be absolute, got ${jsonStringify(sessionCwd)}`)
|
|
24
109
|
}
|
|
110
|
+
const workerGlobalBaseline = captureWorkerReplGlobals()
|
|
25
111
|
const server = repl.start({
|
|
26
112
|
input,
|
|
27
113
|
output,
|
|
28
114
|
terminal: false,
|
|
29
115
|
prompt: '',
|
|
30
|
-
|
|
116
|
+
...WORKER_REPL_OPTIONS,
|
|
117
|
+
handleError: createWorkerReplErrorHandler(evaluationScope, (finish, error) => finish(true, error)),
|
|
31
118
|
ignoreUndefined: true,
|
|
32
119
|
})
|
|
33
|
-
|
|
120
|
+
protectWorkerReplAsyncContext(server)
|
|
121
|
+
const originalRequire = server.context.require
|
|
122
|
+
// domain.bind exposes its owner on a bound evaluator in older Node REPLs.
|
|
123
|
+
// Newer REPLs honor an explicitly entered domain. Capture before REPL error
|
|
124
|
+
// formatting can evaluate user-defined stack/name getters.
|
|
125
|
+
const errorDomain = disableWorkerReplDomain(server.eval.domain ?? createDomain())
|
|
126
|
+
errorDomain.removeAllListeners('error')
|
|
127
|
+
errorDomain.on('error', error => evaluationScope.getStore()?.(true, error))
|
|
128
|
+
const handleUncaughtException = createWorkerUncaughtExceptionHandler(evaluationScope, process)
|
|
129
|
+
process.on('uncaughtException', handleUncaughtException)
|
|
130
|
+
const context = workerReplContext(server)
|
|
131
|
+
restoreWorkerReplGlobals(workerGlobalBaseline, context)
|
|
132
|
+
const workerBaselineGlobals = setFromArray(mapKeysArray(workerGlobalBaseline))
|
|
133
|
+
const contextGlobal = runInWorkerReplRealm('globalThis')
|
|
134
|
+
const completionObserver = createCellCompletionObserver(runInWorkerReplRealm('Function'))
|
|
135
|
+
let replModuleRealm
|
|
136
|
+
const statefulRoots = createStatefulRootRuntime({
|
|
137
|
+
refresh: name => refreshLegacyPublication(name),
|
|
138
|
+
importModule: (source, options) => managedModuleImport(replParent, source, options, replModuleRealm),
|
|
139
|
+
errors: runInWorkerReplRealm('({ ReferenceError, TypeError })'),
|
|
140
|
+
dynamicIntrinsics: runInWorkerReplRealm('({intrinsicEval:eval,realmFunction:Function,globalObject:globalThis,errors:{ReferenceError,TypeError,SyntaxError},reflect:Reflect,object:Object})'),
|
|
141
|
+
readAmbient: name => runInWorkerReplRealm(name),
|
|
142
|
+
typeofAmbient: name => runInWorkerReplRealm(`typeof ${name}`),
|
|
143
|
+
canWriteAmbient: name => writableRootProperty(name),
|
|
144
|
+
writeAmbient(name, value, strict) {
|
|
145
|
+
const argument = name === '__ptc_value' ? '__ptc_other_value' : '__ptc_value'
|
|
146
|
+
runInWorkerReplRealm(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`)(value)
|
|
147
|
+
},
|
|
148
|
+
deleteAmbient: name => runInWorkerReplRealm(`delete ${name}`),
|
|
149
|
+
hasAmbient: name => setHas(installedGlobals, name)
|
|
150
|
+
? mapGet(installedGlobalOriginals, name).descriptor !== undefined : hasProperty(context, name),
|
|
151
|
+
// Request namespaces stay on globalThis; source declarations own separate
|
|
152
|
+
// logical storage. Provider overlays retain their existing write-through.
|
|
153
|
+
hasOverlay: name => setHas(installedGlobals, name) && mapGet(dynamicNamespaces, name)?.shadowable !== true,
|
|
154
|
+
publish(name) {
|
|
155
|
+
// Logical lexical identities do not redefine the global object's properties.
|
|
156
|
+
if (mapHas(userBindingSources, name)) recordUserBindingAssignment(name)
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
const nativeRootDynamic = createNativeRootDynamic({
|
|
160
|
+
logicalReference: (name, writable) => statefulRoots.legacyReference(name, writable),
|
|
161
|
+
intrinsics: runInWorkerReplRealm('({intrinsicEval:eval,realmFunction:Function,globalObject:globalThis,errors:{ReferenceError,TypeError,SyntaxError},reflect:Reflect,object:Object})'),
|
|
162
|
+
importModule: (source, options) => managedModuleImport(replParent, source, options, replModuleRealm),
|
|
163
|
+
read: name => runInWorkerReplRealm(name),
|
|
164
|
+
typeOf: name => runInWorkerReplRealm(`typeof ${name}`),
|
|
165
|
+
write(name, value, strict) {
|
|
166
|
+
const argument = name === '__ptc_value' ? '__ptc_other_value' : '__ptc_value'
|
|
167
|
+
runInWorkerReplRealm(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`)(value)
|
|
168
|
+
},
|
|
169
|
+
remove: name => runInWorkerReplRealm(`delete ${name}`),
|
|
170
|
+
})
|
|
171
|
+
let valueObserver
|
|
34
172
|
const REPL_IMPORT_CANARY = 'data:text/javascript,export default 1'
|
|
35
173
|
let replParent
|
|
36
174
|
const sessionReplParent = sessionCwd === undefined ? undefined : pathToFileURL(resolve(sessionCwd, 'repl')).href
|
|
37
175
|
const staticAdapterParents = new Set()
|
|
176
|
+
const userBindingModuleParents = new Map()
|
|
177
|
+
const userModuleCompilation = createUserModuleCompilationHooks({
|
|
178
|
+
transformForParent: parent => (parent === replParent || parent === sessionReplParent) && activeExecution !== undefined
|
|
179
|
+
? activeExecution.moduleTransform : undefined,
|
|
180
|
+
})
|
|
38
181
|
registerHooks({
|
|
39
182
|
resolve(specifier, context, nextResolve) {
|
|
40
183
|
if (specifier === REPL_IMPORT_CANARY && replParent === undefined) replParent = context.parentURL
|
|
41
|
-
return
|
|
42
|
-
|
|
43
|
-
|
|
184
|
+
return userModuleCompilation.resolve(specifier, context, (source, resolvedContext) => {
|
|
185
|
+
const userBindingParent = mapGet(userBindingModuleParents, resolvedContext.parentURL)
|
|
186
|
+
return nextResolve(source, userBindingParent !== undefined
|
|
187
|
+
? { ...resolvedContext, parentURL: userBindingParent }
|
|
188
|
+
: resolvedContext.parentURL === replParent || setHas(staticAdapterParents, resolvedContext.parentURL)
|
|
189
|
+
? { ...resolvedContext, parentURL: sessionReplParent ?? replParent }
|
|
190
|
+
: resolvedContext)
|
|
191
|
+
})
|
|
44
192
|
},
|
|
193
|
+
load: userModuleCompilation.load,
|
|
45
194
|
})
|
|
46
|
-
const logScope =
|
|
195
|
+
const logScope = createPrivateAsyncLocalStorage()
|
|
196
|
+
const userBindingActivationScope = createPrivateAsyncLocalStorage()
|
|
47
197
|
const pending = new Map()
|
|
48
198
|
const installedGlobals = new Set()
|
|
49
|
-
const
|
|
199
|
+
const installedGlobalOriginals = new Map()
|
|
200
|
+
const PROCESS_CONTROLS = setFromArray(['exit', 'abort', 'kill', 'chdir'])
|
|
50
201
|
const CELL_FRAME_SUFFIX = '\n;'
|
|
51
202
|
let filenameSequence = 0
|
|
52
203
|
let activeFilename = 'ptc-plus-repl'
|
|
@@ -55,14 +206,34 @@ const CONFORMANCE_CELL = `"use strict";
|
|
|
55
206
|
if (this !== globalThis) throw new Error('invalid REPL global receiver semantics')
|
|
56
207
|
const __ptc_canary = await Promise.resolve(1)
|
|
57
208
|
if (__ptc_canary !== 1) throw new Error('invalid REPL await semantics')
|
|
58
|
-
const __ptc_import_canary = await import(${
|
|
209
|
+
const __ptc_import_canary = await import(${jsonStringify(REPL_IMPORT_CANARY)})
|
|
59
210
|
if (__ptc_import_canary.default !== 1) throw new Error('invalid REPL import semantics')
|
|
60
211
|
}`
|
|
61
212
|
let activeRun
|
|
62
213
|
let activeExecution
|
|
63
|
-
let
|
|
214
|
+
let suppressReplCwdObservation = false
|
|
64
215
|
let nextCallId = 0
|
|
65
216
|
let nextStaticAdapterId = 0
|
|
217
|
+
let nextUserBindingModuleId = 0
|
|
218
|
+
const userBindingEntries = new Map()
|
|
219
|
+
const userBindingNames = new Map()
|
|
220
|
+
const userBindingSources = new Map()
|
|
221
|
+
const dynamicNamespaces = new Map()
|
|
222
|
+
const originalDynamicNamespaceGlobals = new Map()
|
|
223
|
+
const dynamicNamespaceViews = new Map()
|
|
224
|
+
const dynamicNamespaceGlobalGetters = new Map()
|
|
225
|
+
let retainedLegacyUserBindingRuntime = false
|
|
226
|
+
|
|
227
|
+
installProgramAmbientResolver(name => {
|
|
228
|
+
if (!mapHas(dynamicNamespaces, name) && !mapHas(originalDynamicNamespaceGlobals, name)
|
|
229
|
+
&& !mapHas(dynamicNamespaceViews, name)) return undefined
|
|
230
|
+
return {
|
|
231
|
+
get: () => dynamicNamespaceView(name),
|
|
232
|
+
set() { throw new TypeError(`${name} cannot be overwritten because reserved program bindings are not shadowable`) },
|
|
233
|
+
typeof: () => 'object',
|
|
234
|
+
delete: () => false,
|
|
235
|
+
}
|
|
236
|
+
})
|
|
66
237
|
|
|
67
238
|
class StaticImportFailure {
|
|
68
239
|
constructor(error, position) {
|
|
@@ -76,6 +247,10 @@ class CellReturn extends Error {
|
|
|
76
247
|
super('cell returned')
|
|
77
248
|
this.value = value
|
|
78
249
|
}
|
|
250
|
+
|
|
251
|
+
static complete() {
|
|
252
|
+
evaluationScope.getStore().completed = true
|
|
253
|
+
}
|
|
79
254
|
}
|
|
80
255
|
function appendLog(...values) {
|
|
81
256
|
const current = logScope.getStore()
|
|
@@ -85,14 +260,14 @@ function appendLog(...values) {
|
|
|
85
260
|
|
|
86
261
|
function appendText(current, text) {
|
|
87
262
|
if (current.open !== true || current.outputLimited) return
|
|
88
|
-
const bytes =
|
|
263
|
+
const bytes = bufferByteLength(jsonStringify(text), 'utf8') + (current.logs.length === 0 ? 0 : 1)
|
|
89
264
|
if (current.logBytes + bytes > current.maxOutputBytes) {
|
|
90
265
|
current.outputLimited = true
|
|
91
266
|
channel.postMessage({ type: 'output-limit', id: current.id, logs: current.logs })
|
|
92
267
|
return
|
|
93
268
|
}
|
|
94
269
|
current.logBytes += bytes
|
|
95
|
-
current.logs
|
|
270
|
+
appendArray(current.logs, text)
|
|
96
271
|
}
|
|
97
272
|
|
|
98
273
|
const consoleView = Object.freeze({
|
|
@@ -108,7 +283,8 @@ Object.defineProperty(context, 'console', { configurable: true, value: consoleVi
|
|
|
108
283
|
function captureWrite(chunk, ...rest) {
|
|
109
284
|
const current = logScope.getStore()
|
|
110
285
|
if (current?.open === true) appendText(current, typeof chunk === 'string' ? chunk : String(chunk))
|
|
111
|
-
const callback =
|
|
286
|
+
const callback = typeof rest[0] === 'function' ? rest[0]
|
|
287
|
+
: typeof rest[1] === 'function' ? rest[1] : undefined
|
|
112
288
|
if (callback !== undefined) queueMicrotask(() => callback(null))
|
|
113
289
|
return true
|
|
114
290
|
}
|
|
@@ -117,10 +293,7 @@ process.stderr.write = captureWrite
|
|
|
117
293
|
|
|
118
294
|
function markVolatile(reason) {
|
|
119
295
|
const current = activeExecution
|
|
120
|
-
if (current === undefined)
|
|
121
|
-
pendingVolatileReason ??= reason
|
|
122
|
-
return
|
|
123
|
-
}
|
|
296
|
+
if (current === undefined) return
|
|
124
297
|
if (current.durability === 'volatile') return
|
|
125
298
|
current.durability = 'volatile'
|
|
126
299
|
current.volatileReason ??= reason
|
|
@@ -134,10 +307,10 @@ function completionDurability(execution) {
|
|
|
134
307
|
}
|
|
135
308
|
}
|
|
136
309
|
|
|
137
|
-
const originalRequire = context.require
|
|
138
|
-
|
|
139
310
|
function guardProcessControls() {
|
|
140
|
-
|
|
311
|
+
const properties = setValuesArray(PROCESS_CONTROLS)
|
|
312
|
+
for (let index = 0; index < properties.length; index += 1) {
|
|
313
|
+
const property = properties[index]
|
|
141
314
|
const descriptor = Object.getOwnPropertyDescriptor(process, property)
|
|
142
315
|
Object.defineProperty(process, property, {
|
|
143
316
|
configurable: false,
|
|
@@ -151,243 +324,1002 @@ function guardProcessControls() {
|
|
|
151
324
|
}
|
|
152
325
|
|
|
153
326
|
guardProcessControls()
|
|
154
|
-
installWorkerCwdVirtualization(sessionCwd, originalRequire
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
)
|
|
160
|
-
Object.defineProperty(context, 'require', {
|
|
161
|
-
configurable: true,
|
|
162
|
-
value(specifier) {
|
|
163
|
-
if (FORBIDDEN_IMPORTS.has(specifier)) throw new Error(`module ${specifier} is forbidden because it exposes kernel control`)
|
|
164
|
-
if (!DURABLE_IMPORTS.has(specifier)) markVolatile(`require(${JSON.stringify(specifier)})`)
|
|
165
|
-
return originalRequire(specifier)
|
|
166
|
-
},
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
for (const [name, value] of Object.entries(originalGlobals)) {
|
|
170
|
-
Object.defineProperty(context, name, {
|
|
171
|
-
configurable: true,
|
|
172
|
-
get() {
|
|
173
|
-
markVolatile(`ambient ${name}`)
|
|
174
|
-
return value
|
|
175
|
-
},
|
|
176
|
-
set(next) {
|
|
177
|
-
markVolatile(`ambient ${name}`)
|
|
178
|
-
Object.defineProperty(context, name, { configurable: true, writable: true, value: next })
|
|
179
|
-
},
|
|
180
|
-
})
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const capturedOutput = Object.freeze({ write: captureWrite })
|
|
184
|
-
const processView = new Proxy(process, {
|
|
185
|
-
get(target, property) {
|
|
186
|
-
if (property === 'stdout' || property === 'stderr') return capturedOutput
|
|
187
|
-
if (property === 'cwd') {
|
|
188
|
-
if (sessionCwd !== undefined) return () => sessionCwd
|
|
189
|
-
markVolatile('process.cwd')
|
|
190
|
-
return target.cwd.bind(target)
|
|
191
|
-
}
|
|
192
|
-
if (PROCESS_CONTROLS.has(property)) return Reflect.get(target, property, target)
|
|
193
|
-
markVolatile(`process.${String(property)}`)
|
|
194
|
-
const value = Reflect.get(target, property, target)
|
|
195
|
-
return typeof value === 'function' ? value.bind(target) : value
|
|
196
|
-
},
|
|
197
|
-
set(target, property, value) {
|
|
198
|
-
if (property === 'stdout' || property === 'stderr') return false
|
|
199
|
-
markVolatile(`process.${String(property)}`)
|
|
200
|
-
return Reflect.set(target, property, value, target)
|
|
201
|
-
},
|
|
202
|
-
ownKeys(target) {
|
|
203
|
-
markVolatile('process reflection')
|
|
204
|
-
return Reflect.ownKeys(target)
|
|
205
|
-
},
|
|
327
|
+
installWorkerCwdVirtualization(sessionCwd, originalRequire, reason => {
|
|
328
|
+
if (suppressReplCwdObservation) {
|
|
329
|
+
suppressReplCwdObservation = false
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
markVolatile(reason)
|
|
206
333
|
})
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
},
|
|
334
|
+
synchronizeBuiltinEsmExports()
|
|
335
|
+
const providedRequire = sessionReplParent === undefined ? originalRequire : createRequire(sessionReplParent)
|
|
336
|
+
function selectProvidedRequire(args) {
|
|
337
|
+
const specifier = args[0]
|
|
338
|
+
if (setHas(FORBIDDEN_IMPORTS, specifier)) throw new Error(`module ${specifier} is forbidden because it exposes kernel control`)
|
|
339
|
+
if (!setHas(DURABLE_IMPORTS, specifier)) markVolatile(`require(${jsonStringify(specifier)})`)
|
|
340
|
+
return managedRequire(activeExecution?.languageSemantics === 'legacy-v1' ? replParent : sessionReplParent ?? replParent,
|
|
341
|
+
activeExecution?.languageSemantics === 'legacy-v1' ? originalRequire : providedRequire)
|
|
216
342
|
}
|
|
217
|
-
|
|
218
|
-
Object.defineProperty(context, 'Math', {
|
|
343
|
+
Object.defineProperty(context, 'require', {
|
|
219
344
|
configurable: true,
|
|
220
|
-
value:
|
|
345
|
+
value: new Proxy(providedRequire, {
|
|
346
|
+
apply: (_, receiver, args) => privateReflect.apply(selectProvidedRequire(args), receiver, args),
|
|
347
|
+
construct: (_, args, newTarget) => privateReflect.construct(selectProvidedRequire(args), args, newTarget),
|
|
348
|
+
}),
|
|
221
349
|
})
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return new Promise((resolve, reject) => {
|
|
225
|
-
// REPLServer routes runtime throws to its domain instead of the eval
|
|
226
|
-
// callback. Cells are serialized, so temporarily replacing that one error
|
|
227
|
-
// handler gives both syntax and runtime failures one settlement path.
|
|
228
|
-
const domain = server._domain
|
|
229
|
-
const prior = domain.listeners('error')
|
|
230
|
-
domain.removeAllListeners('error')
|
|
350
|
+
function evaluate(program, completionSignal, asyncCompletion = false) {
|
|
351
|
+
return new ControlPromise((resolve, reject) => {
|
|
231
352
|
let settled = false
|
|
232
|
-
const finish = (
|
|
353
|
+
const finish = (failed, value) => {
|
|
233
354
|
if (settled) return
|
|
234
355
|
settled = true
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (error instanceof CellReturn) {
|
|
238
|
-
Promise.resolve(error.value).then(
|
|
356
|
+
if (failed && value instanceof CellReturn) {
|
|
357
|
+
completionObserver.settle(value.value,
|
|
239
358
|
value => resolve({ hasValue: true, value }),
|
|
240
359
|
reject,
|
|
241
360
|
)
|
|
242
|
-
} else if (
|
|
361
|
+
} else if (failed) reject(value)
|
|
243
362
|
else {
|
|
244
|
-
|
|
363
|
+
completionObserver.settle(value,
|
|
245
364
|
value => resolve({ hasValue: value !== undefined, value }),
|
|
246
365
|
reject,
|
|
247
366
|
)
|
|
248
367
|
}
|
|
249
368
|
}
|
|
250
|
-
const onError = error => finish(error)
|
|
251
|
-
domain.on('error', onError)
|
|
252
369
|
activeFilename = `ptc-plus-repl-${++filenameSequence}`
|
|
253
|
-
|
|
370
|
+
suppressReplCwdObservation = true
|
|
371
|
+
evaluationScope.run(finish, () => errorDomain.run(() => {
|
|
372
|
+
// Older REPL callbacks conflate null/undefined await rejection with a
|
|
373
|
+
// successful empty result. A reached EOF distinguishes those outcomes.
|
|
374
|
+
// A block-scoped declaration has empty completion and preserves the
|
|
375
|
+
// preceding non-await expression value without adding a session binding.
|
|
376
|
+
// Keep the final semicolon so REPL cannot guess a block is an object literal.
|
|
377
|
+
const suffix = completionSignal === undefined || asyncCompletion ? CELL_FRAME_SUFFIX
|
|
378
|
+
: `${CELL_FRAME_SUFFIX}{ let completed = this[${jsonStringify(completionSignal)}].complete(); }${CELL_FRAME_SUFFIX}`
|
|
379
|
+
server.eval(program + suffix, context, activeFilename, (error, value) => {
|
|
380
|
+
if (asyncCompletion && (error === null || error === undefined)) {
|
|
381
|
+
completionObserver.observe(value, value => finish(false, value), error => finish(true, error))
|
|
382
|
+
return
|
|
383
|
+
}
|
|
384
|
+
const failed = error !== null && error !== undefined || completionSignal !== undefined && finish.completed !== true
|
|
385
|
+
finish(failed, failed ? error : value)
|
|
386
|
+
})
|
|
387
|
+
}))
|
|
388
|
+
suppressReplCwdObservation = false
|
|
254
389
|
})
|
|
255
390
|
}
|
|
256
391
|
|
|
257
|
-
function
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
392
|
+
async function verifyEvaluation() {
|
|
393
|
+
await evaluate(CONFORMANCE_CELL)
|
|
394
|
+
const marker = new Error('PTC Plus REPL settlement probe')
|
|
395
|
+
Object.defineProperty(marker, 'stack', { get() { throw new Error('REPL must not format an error before settlement') } })
|
|
396
|
+
context.__ptc_settlement_probe__ = marker
|
|
397
|
+
context.__ptc_completion_probe__ = CellReturn
|
|
398
|
+
try {
|
|
399
|
+
const values = [marker, null, undefined, false, 0]
|
|
400
|
+
for (let valueIndex = 0; valueIndex < values.length; valueIndex += 1) {
|
|
401
|
+
const value = values[valueIndex]
|
|
402
|
+
context.__ptc_settlement_probe__ = value
|
|
403
|
+
const sources = ['throw __ptc_settlement_probe__', 'await Promise.reject(__ptc_settlement_probe__)']
|
|
404
|
+
for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
|
|
405
|
+
const source = sources[sourceIndex]
|
|
406
|
+
let caught = false
|
|
407
|
+
try { await evaluate(source, '__ptc_completion_probe__') } catch (error) {
|
|
408
|
+
if (error !== value) throw new Error('REPL did not preserve the original thrown value')
|
|
409
|
+
caught = true
|
|
410
|
+
}
|
|
411
|
+
if (!caught) throw new Error('REPL did not reject a failed evaluation')
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
let syntaxRejected = false
|
|
415
|
+
try { await evaluate('const =') } catch (error) { syntaxRejected = error?.name === 'SyntaxError' }
|
|
416
|
+
if (!syntaxRejected) throw new Error('REPL did not preserve syntax failure')
|
|
417
|
+
context.__ptc_settlement_probe__ = new CellReturn(promiseResolve(marker))
|
|
418
|
+
const returned = await evaluate('throw __ptc_settlement_probe__')
|
|
419
|
+
if (!returned.hasValue || returned.value !== marker) throw new Error('REPL did not preserve cell return')
|
|
420
|
+
await evaluate(CONFORMANCE_CELL)
|
|
421
|
+
await evaluate('{ const value = await Promise.resolve(42); if (value !== 42) throw new Error("invalid block await") }', '__ptc_completion_probe__')
|
|
422
|
+
const expression = await evaluate('42', '__ptc_completion_probe__')
|
|
423
|
+
if (expression.value !== 42) throw new Error('REPL did not preserve expression completion')
|
|
424
|
+
} finally {
|
|
425
|
+
delete context.__ptc_settlement_probe__
|
|
426
|
+
delete context.__ptc_completion_probe__
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function staticImportClause(options) {
|
|
431
|
+
if (options === undefined) return { attributes: undefined, clause: '' }
|
|
432
|
+
const option = Object.entries(options)[0]
|
|
433
|
+
const keyword = option[0]
|
|
434
|
+
const attributes = option[1]
|
|
435
|
+
const entries = mapArray(Object.entries(attributes), entry => `${jsonStringify(entry[0])}: ${jsonStringify(entry[1])}`)
|
|
436
|
+
return { attributes, clause: ` ${keyword} { ${join(entries, ', ')} }` }
|
|
263
437
|
}
|
|
264
438
|
|
|
265
439
|
function staticAdapterSource(load) {
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
if (load.global === undefined) return `import ${source}${
|
|
269
|
-
const requirements = load.requiredExports
|
|
270
|
-
const imported = name === 'default' ? 'default' :
|
|
440
|
+
const { attributes, clause } = staticImportClause(load.options)
|
|
441
|
+
const source = jsonStringify(statefulModuleLink(load.source, staticModuleLinkReference(load.source, attributes)))
|
|
442
|
+
if (load.global === undefined) return `import ${source}${clause};`
|
|
443
|
+
const requirements = load.requiredExports === undefined ? [] : mapArray(load.requiredExports, (name, index) => {
|
|
444
|
+
const imported = name === 'default' ? 'default' : jsonStringify(name)
|
|
271
445
|
return `${imported} as __required_${index}__`
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
]),
|
|
278
|
-
'export { namespace };',
|
|
279
|
-
].join('\n')
|
|
446
|
+
})
|
|
447
|
+
const lines = [`import * as namespace from ${source}${clause};`]
|
|
448
|
+
if (requirements.length > 0) appendArray(lines, `export { ${join(requirements, ', ')} } from ${source}${clause};`)
|
|
449
|
+
appendArray(lines, 'export { namespace };')
|
|
450
|
+
return join(lines, '\n')
|
|
280
451
|
}
|
|
281
452
|
|
|
282
453
|
async function loadStaticModule(load) {
|
|
454
|
+
if (load.operation === 'native-dynamic') return { namespace: nativeRootDynamic.environment(load.callableSources,
|
|
455
|
+
load.awaitRoot === true, filterArray(load.logicalRoots, name => statefulRoots.has(name)
|
|
456
|
+
&& rootBindingStorage(name, moduleRuntimeIntrinsics.includes(load.nativeLexicals, name)) !== 'lexical'), load.writableRoots) }
|
|
457
|
+
if (load.operation === 'import') {
|
|
458
|
+
return { namespace: (source, options) => managedModuleImport(replParent, source, options, replModuleRealm) }
|
|
459
|
+
}
|
|
283
460
|
const adapter = `data:text/javascript,${encodeURIComponent(staticAdapterSource(load))}#${++nextStaticAdapterId}`
|
|
284
|
-
staticAdapterParents
|
|
461
|
+
setAdd(staticAdapterParents, adapter)
|
|
462
|
+
userModuleCompilation.mark(adapter, { compiled: true,
|
|
463
|
+
transform: activeExecution?.moduleTransform })
|
|
285
464
|
try {
|
|
286
|
-
const completion = await evaluate(`import(${
|
|
287
|
-
return
|
|
465
|
+
const completion = await evaluate(`import(${jsonStringify(adapter)})`)
|
|
466
|
+
if (load.global === undefined) return { namespace: undefined }
|
|
467
|
+
const namespace = readModuleImport(adapter, load.source, null, () => completion.value.namespace, load.options?.with)
|
|
468
|
+
return { namespace }
|
|
288
469
|
} finally {
|
|
289
|
-
staticAdapterParents
|
|
470
|
+
setDelete(staticAdapterParents, adapter)
|
|
290
471
|
}
|
|
291
472
|
}
|
|
292
473
|
|
|
293
|
-
function
|
|
294
|
-
|
|
474
|
+
function hasExecutionLease(runId) {
|
|
475
|
+
return runId !== undefined && activeRun === runId && logScope.getStore()?.id === runId
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// BoundError is the error class installed by this cell's wrapper, not a name
|
|
479
|
+
// looked up later: the cell can replace or delete the context binding while a
|
|
480
|
+
// call is pending, and the call must keep its submitted error identity.
|
|
481
|
+
function callHost(runId, global, member, args, BoundError) {
|
|
482
|
+
if (!hasExecutionLease(runId)) {
|
|
483
|
+
return promiseReject(programBindingError('lease', 'PTC execution lease expired'))
|
|
484
|
+
}
|
|
485
|
+
const activation = userBindingActivationScope.getStore()
|
|
486
|
+
if (activation !== undefined) {
|
|
487
|
+
activation.called = true
|
|
488
|
+
if (activation.failed) {
|
|
489
|
+
markVolatile(`failed user binding ${jsonStringify(activation.id)} issued a host call`)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
295
492
|
const id = ++nextCallId
|
|
493
|
+
const valueLimits = activeExecution.valueLimits
|
|
296
494
|
let settle
|
|
297
|
-
const result = new
|
|
298
|
-
|
|
299
|
-
|
|
495
|
+
const result = new PublicPromise((resolve, reject) => { settle = { resolve, reject } })
|
|
496
|
+
const settled = new ControlPromise(resolve => {
|
|
497
|
+
moduleRuntimeIntrinsics.Reflect.apply(moduleRuntimeIntrinsics.promiseThen, result, [resolve, resolve])
|
|
498
|
+
})
|
|
499
|
+
mapSet(pending, id, { ...settle, runId, valueLimits, settled, member, BoundError })
|
|
300
500
|
try {
|
|
301
501
|
channel.postMessage({
|
|
302
502
|
type: 'call', runId, id, global, member,
|
|
303
|
-
args: encodeValue(args,
|
|
503
|
+
args: encodeValue(args, valueLimits),
|
|
304
504
|
})
|
|
305
505
|
} catch (error) {
|
|
306
|
-
pending
|
|
506
|
+
mapDelete(pending, id)
|
|
307
507
|
settle.reject(error)
|
|
308
508
|
}
|
|
309
509
|
return result
|
|
310
510
|
}
|
|
311
511
|
|
|
512
|
+
function dynamicNamespace(name) {
|
|
513
|
+
const assertLease = () => {
|
|
514
|
+
if (!hasExecutionLease(logScope.getStore()?.id)) {
|
|
515
|
+
throw programBindingError('lease', 'PTC execution lease expired')
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return new Proxy(Object.create(null), {
|
|
519
|
+
get(_target, property) {
|
|
520
|
+
if (typeof property !== 'string') return undefined
|
|
521
|
+
assertLease()
|
|
522
|
+
const descriptor = mapGet(dynamicNamespaces, name)
|
|
523
|
+
if (descriptor === undefined || !setHas(descriptor.members, property)) return undefined
|
|
524
|
+
return (...args) => {
|
|
525
|
+
if (!hasExecutionLease(logScope.getStore()?.id)) {
|
|
526
|
+
return promiseReject(programBindingError('lease', 'PTC execution lease expired'))
|
|
527
|
+
}
|
|
528
|
+
const current = mapGet(dynamicNamespaces, name)
|
|
529
|
+
if (current === undefined || !setHas(current.members, property)) {
|
|
530
|
+
return promiseReject(programBindingError('capability', `unknown binding ${name}.${property}`))
|
|
531
|
+
}
|
|
532
|
+
return callHost(
|
|
533
|
+
logScope.getStore()?.id,
|
|
534
|
+
name,
|
|
535
|
+
property,
|
|
536
|
+
args.length === 0 && setHas(current.emptyObjectMembers, property) ? {} : args[0],
|
|
537
|
+
current.BoundError,
|
|
538
|
+
)
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
has(_target, property) {
|
|
542
|
+
assertLease()
|
|
543
|
+
const descriptor = mapGet(dynamicNamespaces, name)
|
|
544
|
+
return typeof property === 'string' && descriptor !== undefined && setHas(descriptor.members, property)
|
|
545
|
+
},
|
|
546
|
+
ownKeys() {
|
|
547
|
+
assertLease()
|
|
548
|
+
const descriptor = mapGet(dynamicNamespaces, name)
|
|
549
|
+
return descriptor === undefined ? [] : setValuesArray(descriptor.members)
|
|
550
|
+
},
|
|
551
|
+
getOwnPropertyDescriptor(_target, property) {
|
|
552
|
+
assertLease()
|
|
553
|
+
const descriptor = mapGet(dynamicNamespaces, name)
|
|
554
|
+
return typeof property === 'string' && descriptor !== undefined && setHas(descriptor.members, property)
|
|
555
|
+
? compilerDescriptors.descriptor({ configurable: true, enumerable: true })
|
|
556
|
+
: undefined
|
|
557
|
+
},
|
|
558
|
+
set() { return false },
|
|
559
|
+
})
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function dynamicNamespaceView(name) {
|
|
563
|
+
let view = mapGet(dynamicNamespaceViews, name)
|
|
564
|
+
if (view === undefined) {
|
|
565
|
+
view = dynamicNamespace(name)
|
|
566
|
+
mapSet(dynamicNamespaceViews, name, view)
|
|
567
|
+
}
|
|
568
|
+
return view
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function namespaceGlobalGetter(name) {
|
|
572
|
+
let getter = mapGet(dynamicNamespaceGlobalGetters, name)
|
|
573
|
+
if (getter === undefined) {
|
|
574
|
+
getter = () => mapGet(dynamicNamespaces, name)?.cellView ?? dynamicNamespaceView(name)
|
|
575
|
+
mapSet(dynamicNamespaceGlobalGetters, name, getter)
|
|
576
|
+
}
|
|
577
|
+
return getter
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function installNamespaceGlobal(name) {
|
|
581
|
+
Object.defineProperty(globalThis, name, {
|
|
582
|
+
configurable: true,
|
|
583
|
+
get: namespaceGlobalGetter(name),
|
|
584
|
+
})
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function installDynamicNamespaceGlobals() {
|
|
588
|
+
const pendingGlobals = []
|
|
589
|
+
const names = mapKeysArray(dynamicNamespaces)
|
|
590
|
+
for (let index = 0; index < names.length; index += 1) {
|
|
591
|
+
const name = names[index]
|
|
592
|
+
if (mapHas(originalDynamicNamespaceGlobals, name)) {
|
|
593
|
+
installNamespaceGlobal(name)
|
|
594
|
+
continue
|
|
595
|
+
}
|
|
596
|
+
const original = setHas(installedGlobals, name)
|
|
597
|
+
? mapGet(installedGlobalOriginals, name)
|
|
598
|
+
: capturedGlobalDescriptor(name)
|
|
599
|
+
if (setHas(workerBaselineGlobals, name)) {
|
|
600
|
+
throw new Error(
|
|
601
|
+
`program namespace ${jsonStringify(name)} cannot be bridged into Global User Binding modules because the worker global already exists`,
|
|
602
|
+
)
|
|
603
|
+
}
|
|
604
|
+
appendArray(pendingGlobals, [name, original.descriptor])
|
|
605
|
+
}
|
|
606
|
+
for (let index = 0; index < pendingGlobals.length; index += 1) {
|
|
607
|
+
const entry = pendingGlobals[index]
|
|
608
|
+
const name = entry[0]
|
|
609
|
+
const descriptor = entry[1]
|
|
610
|
+
mapSet(originalDynamicNamespaceGlobals, name, descriptor)
|
|
611
|
+
if (!setHas(installedGlobals, name)) installNamespaceGlobal(name)
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function restoreDynamicNamespaceGlobals() {
|
|
616
|
+
const names = mapKeysArray(originalDynamicNamespaceGlobals)
|
|
617
|
+
for (let index = 0; index < names.length; index += 1) {
|
|
618
|
+
const name = names[index]
|
|
619
|
+
setHas(installedGlobals, name) ? installNamespaceGlobal(name) : privateReflect.deleteProperty(globalThis, name)
|
|
620
|
+
}
|
|
621
|
+
mapClear(originalDynamicNamespaceGlobals)
|
|
622
|
+
}
|
|
623
|
+
|
|
312
624
|
function installBindings(message) {
|
|
313
|
-
|
|
314
|
-
|
|
625
|
+
const installedNames = setValuesArray(installedGlobals)
|
|
626
|
+
for (let index = 0; index < installedNames.length; index += 1) {
|
|
627
|
+
const name = installedNames[index]
|
|
628
|
+
const original = mapGet(installedGlobalOriginals, name)
|
|
629
|
+
const attachedEntry = original.userGlobalEntryId === undefined
|
|
630
|
+
? undefined : mapGet(userBindingEntries, original.userGlobalEntryId)
|
|
631
|
+
const attachment = attachedEntry === undefined ? undefined : mapGet(attachedEntry.descriptors, name)
|
|
632
|
+
if (mapHas(originalDynamicNamespaceGlobals, name)) {
|
|
633
|
+
installNamespaceGlobal(name)
|
|
634
|
+
} else if (original.userGlobalEntryId !== undefined
|
|
635
|
+
&& (mapGet(userBindingNames, name) !== original.userGlobalEntryId
|
|
636
|
+
|| attachment !== original.attachment)) {
|
|
637
|
+
delete context[name]
|
|
638
|
+
} else if (original.descriptor === undefined) delete context[name]
|
|
639
|
+
else Object.defineProperty(context, name, original.descriptor)
|
|
640
|
+
}
|
|
641
|
+
setClear(installedGlobals)
|
|
642
|
+
mapClear(installedGlobalOriginals)
|
|
643
|
+
mapClear(dynamicNamespaces)
|
|
644
|
+
let namespaceError
|
|
315
645
|
|
|
316
|
-
for (
|
|
646
|
+
for (let namespaceIndex = 0; namespaceIndex < message.namespaces.length; namespaceIndex += 1) {
|
|
647
|
+
const namespace = message.namespaces[namespaceIndex]
|
|
648
|
+
const descriptor = namespace.errorClass
|
|
649
|
+
// The wrapper owns the constructor, so a pending call keeps its submitted
|
|
650
|
+
// error identity even after the cell shadows the context binding.
|
|
651
|
+
const BoundError = descriptor === undefined ? undefined : class extends Error {
|
|
652
|
+
constructor(member, detail, cause) {
|
|
653
|
+
super(detail)
|
|
654
|
+
this.name = descriptor.name
|
|
655
|
+
Object.defineProperty(this, descriptor.memberNameProperty, { enumerable: true, value: member })
|
|
656
|
+
if (cause !== undefined) Object.defineProperty(this, 'ptcCause', { value: cause })
|
|
657
|
+
}
|
|
658
|
+
}
|
|
317
659
|
const view = Object.create(null)
|
|
318
|
-
const emptyObjectMembers =
|
|
319
|
-
|
|
660
|
+
const emptyObjectMembers = setFromArray(namespace.emptyObjectMembers ?? [])
|
|
661
|
+
mapSet(dynamicNamespaces, namespace.global, {
|
|
662
|
+
members: setFromArray(namespace.members),
|
|
663
|
+
shadowable: namespace.shadowable === true,
|
|
664
|
+
emptyObjectMembers,
|
|
665
|
+
BoundError,
|
|
666
|
+
cellView: view,
|
|
667
|
+
})
|
|
668
|
+
for (let memberIndex = 0; memberIndex < namespace.members.length; memberIndex += 1) {
|
|
669
|
+
const member = namespace.members[memberIndex]
|
|
320
670
|
Object.defineProperty(view, member, {
|
|
321
671
|
enumerable: true,
|
|
322
672
|
value: (...args) => callHost(
|
|
323
673
|
message.id,
|
|
324
674
|
namespace.global,
|
|
325
675
|
member,
|
|
326
|
-
args.length === 0 && emptyObjectMembers
|
|
327
|
-
|
|
676
|
+
args.length === 0 && setHas(emptyObjectMembers, member) ? {} : args[0],
|
|
677
|
+
BoundError,
|
|
328
678
|
),
|
|
329
679
|
})
|
|
330
680
|
}
|
|
331
681
|
Object.freeze(view)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
682
|
+
const original = capturedGlobalDescriptor(namespace.global)
|
|
683
|
+
mapSet(installedGlobalOriginals, namespace.global, original)
|
|
684
|
+
if (setHas(workerBaselineGlobals, namespace.global)) {
|
|
685
|
+
namespaceError ??= new Error(
|
|
686
|
+
`program namespace ${jsonStringify(namespace.global)} cannot be bridged into the worker realm because the global already exists`,
|
|
687
|
+
)
|
|
688
|
+
} else {
|
|
689
|
+
installNamespaceGlobal(namespace.global)
|
|
690
|
+
setAdd(installedGlobals, namespace.global)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (descriptor !== undefined) {
|
|
694
|
+
mapSet(installedGlobalOriginals, descriptor.name, capturedGlobalDescriptor(descriptor.name))
|
|
695
|
+
Object.defineProperty(context, descriptor.name, { configurable: true, value: BoundError })
|
|
696
|
+
setAdd(installedGlobals, descriptor.name)
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return namespaceError
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function capturedGlobalDescriptor(name) {
|
|
703
|
+
const descriptor = mapHas(originalDynamicNamespaceGlobals, name)
|
|
704
|
+
? mapGet(originalDynamicNamespaceGlobals, name)
|
|
705
|
+
: Object.getOwnPropertyDescriptor(context, name)
|
|
706
|
+
const userGlobalEntryId = mapGet(userBindingNames, name)
|
|
707
|
+
const entry = mapGet(userBindingEntries, userGlobalEntryId)
|
|
708
|
+
const installed = entry === undefined ? undefined : mapGet(entry.descriptors, name)
|
|
709
|
+
return {
|
|
710
|
+
descriptor,
|
|
711
|
+
...(userGlobalEntryId !== undefined && descriptorsEqual(descriptor, installed)
|
|
712
|
+
? { userGlobalEntryId, attachment: installed }
|
|
713
|
+
: {}),
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function underlyingUserBindingDescriptor(name) {
|
|
718
|
+
return setHas(installedGlobals, name)
|
|
719
|
+
? mapGet(installedGlobalOriginals, name).descriptor
|
|
720
|
+
: Object.getOwnPropertyDescriptor(context, name)
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function recordUserBindingAssignment(name, legacy = false) {
|
|
724
|
+
if (!legacy || activeExecution?.userBindingsShadowPolicy === LIVE_USER_BINDINGS_SHADOW_POLICY) {
|
|
725
|
+
if (!setHas(installedGlobals, name)) {
|
|
726
|
+
mapDelete(userBindingNames, name)
|
|
727
|
+
mapSet(userBindingSources, name, { state: 'local' })
|
|
728
|
+
}
|
|
729
|
+
} else {
|
|
730
|
+
// Historical setters record invocation before defining the property. A
|
|
731
|
+
// restored accessor does not erase that operation, even for the same value.
|
|
732
|
+
if (activeExecution !== undefined) setAdd(activeExecution.legacyAssignedUserBindingNames, name)
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// A temporary private getter distinguishes native lexical storage without
|
|
737
|
+
// invoking provider/user getters or comparing arbitrary values. A failed read
|
|
738
|
+
// proves no initialized value; it must never be promoted from the static catalog.
|
|
739
|
+
function rootBindingStorage(name, nativeLexical = false) {
|
|
740
|
+
const original = Object.getOwnPropertyDescriptor(context, name)
|
|
741
|
+
if (original?.configurable === false) {
|
|
742
|
+
if (!nativeLexical && !Object.hasOwn(original, 'value')) return 'unknown'
|
|
743
|
+
try {
|
|
744
|
+
// Both an own data property and an initialized lexical can be read
|
|
745
|
+
// without executing an accessor. Either proves an available local name.
|
|
746
|
+
runInWorkerReplRealm(name, { displayErrors: false })
|
|
747
|
+
return nativeLexical ? 'lexical' : 'local'
|
|
748
|
+
} catch {
|
|
749
|
+
return 'unknown'
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
let propertyRead = false
|
|
753
|
+
try {
|
|
754
|
+
Object.defineProperty(context, name, { configurable: true, get() { propertyRead = true } })
|
|
755
|
+
runInWorkerReplRealm(name, { displayErrors: false })
|
|
756
|
+
return propertyRead ? 'property' : 'lexical'
|
|
757
|
+
} catch {
|
|
758
|
+
return 'unknown'
|
|
759
|
+
} finally {
|
|
760
|
+
if (original === undefined) delete context[name]
|
|
761
|
+
else Object.defineProperty(context, name, original)
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function writableRootProperty(name) {
|
|
766
|
+
const descriptor = Object.getOwnPropertyDescriptor(context, name)
|
|
767
|
+
return descriptor !== undefined && (Object.hasOwn(descriptor, 'value')
|
|
768
|
+
? descriptor.writable === true : typeof descriptor.set === 'function')
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function userBindingRootStorage(name) {
|
|
772
|
+
if (statefulRoots.has(name)) return 'local'
|
|
773
|
+
const namespaces = activeExecution.importBindingNamespaces
|
|
774
|
+
const namespace = namespaces === undefined ? undefined : mapGet(namespaces, name)
|
|
775
|
+
if (namespace === undefined) return rootBindingStorage(name)
|
|
776
|
+
// Compiler-validated imports use native namespace slots, not public alias
|
|
777
|
+
// properties. Probe only the slot; reading an export could invoke user code.
|
|
778
|
+
return rootBindingStorage(namespace) === 'lexical' ? 'lexical' : 'unknown'
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function reconcileUserBindingNames() {
|
|
782
|
+
const names = mapKeysArray(userBindingSources)
|
|
783
|
+
for (let index = 0; index < names.length; index += 1) {
|
|
784
|
+
const name = names[index]
|
|
785
|
+
const storage = userBindingRootStorage(name)
|
|
786
|
+
const descriptor = underlyingUserBindingDescriptor(name)
|
|
787
|
+
const id = mapGet(userBindingNames, name)
|
|
788
|
+
const entry = mapGet(userBindingEntries, id)
|
|
789
|
+
const installed = entry === undefined ? undefined : mapGet(entry.descriptors, name)
|
|
790
|
+
const state = storage === 'lexical' || storage === 'local' ? 'local'
|
|
791
|
+
: storage === 'unknown' ? 'unknown'
|
|
792
|
+
: descriptor === undefined ? 'absent'
|
|
793
|
+
: installed !== undefined && descriptorsEqual(descriptor, installed) ? 'provider' : 'local'
|
|
794
|
+
mapSet(userBindingSources, name, { state, ...(state === 'provider' ? { entryId: id } : {}) })
|
|
795
|
+
if (state !== 'provider') mapDelete(userBindingNames, name)
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
function removePerNameUserBindingEntry(id) {
|
|
800
|
+
const entry = mapGet(userBindingEntries, id)
|
|
801
|
+
for (let index = 0; index < entry.names.length; index += 1) {
|
|
802
|
+
const name = entry.names[index]
|
|
803
|
+
if (mapGet(userBindingNames, name) !== id) continue
|
|
804
|
+
const installed = mapGet(entry.descriptors, name)
|
|
805
|
+
mapDelete(userBindingNames, name)
|
|
806
|
+
if (setHas(installedGlobals, name)) {
|
|
807
|
+
const original = mapGet(installedGlobalOriginals, name)
|
|
808
|
+
if (original.attachment === installed) original.descriptor = undefined
|
|
809
|
+
} else if (descriptorsEqual(Object.getOwnPropertyDescriptor(context, name), installed)) delete context[name]
|
|
810
|
+
// Lifecycle removal permits reattachment; explicit deletion retains absent.
|
|
811
|
+
mapDelete(userBindingSources, name)
|
|
812
|
+
}
|
|
813
|
+
mapDelete(userBindingEntries, id)
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
async function activatePerNameUserBindings(snapshot, shadowedNames, cwd, reusePolicy, initialFailures = []) {
|
|
817
|
+
// Import preceding legacy attachments into the new source owner once.
|
|
818
|
+
const existingNames = mapEntriesArray(userBindingNames)
|
|
819
|
+
for (let index = 0; index < existingNames.length; index += 1) {
|
|
820
|
+
const name = existingNames[index][0]
|
|
821
|
+
const entryId = existingNames[index][1]
|
|
822
|
+
if (mapHas(userBindingSources, name)) continue
|
|
823
|
+
mapSet(userBindingSources, name, { state: 'provider', entryId })
|
|
824
|
+
// The legacy catalog includes actual setter writes as well as descriptor
|
|
825
|
+
// changes. Do not reattach a restored local accessor by descriptor equality.
|
|
826
|
+
if (setHas(shadowedNames, name) && !setHas(installedGlobals, name)) mapDelete(userBindingNames, name)
|
|
827
|
+
}
|
|
828
|
+
reconcileUserBindingNames()
|
|
829
|
+
const blockedIds = setFromArray(mapArray(initialFailures, failure => failure.id))
|
|
830
|
+
const desired = mapFromArray(filterArray(snapshot?.entries ?? [], entry => !setHas(blockedIds, entry.id)), entry => [entry.id, entry])
|
|
831
|
+
try {
|
|
832
|
+
if (mapSize(desired) > 0 || retainedLegacyUserBindingRuntime) installDynamicNamespaceGlobals()
|
|
833
|
+
} catch (error) {
|
|
834
|
+
const ids = mapKeysArray(userBindingEntries)
|
|
835
|
+
for (let index = 0; index < ids.length; index += 1) removePerNameUserBindingEntry(ids[index])
|
|
836
|
+
const failures = copyArray(initialFailures)
|
|
837
|
+
const desiredFailures = mapArray(mapKeysArray(desired), id => ({ id, error: messageOf(error) }))
|
|
838
|
+
for (let index = 0; index < desiredFailures.length; index += 1) appendArray(failures, desiredFailures[index])
|
|
839
|
+
return { activated: [], failures, error }
|
|
840
|
+
}
|
|
841
|
+
const currentEntries = mapEntriesArray(userBindingEntries)
|
|
842
|
+
for (let index = 0; index < currentEntries.length; index += 1) {
|
|
843
|
+
const id = currentEntries[index][0]
|
|
844
|
+
const current = currentEntries[index][1]
|
|
845
|
+
const next = mapGet(desired, id)
|
|
846
|
+
if (next === undefined || current.transform !== snapshot.transform || !(reusePolicy === LEGACY_USER_BINDINGS_REUSE_POLICY
|
|
847
|
+
? next.fingerprint === current.entry.fingerprint : userBindingImplementationMatches(next, current.entry))) {
|
|
848
|
+
removePerNameUserBindingEntry(id)
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const activated = []
|
|
852
|
+
const failures = copyArray(initialFailures)
|
|
853
|
+
const desiredEntries = mapValuesArray(desired)
|
|
854
|
+
for (let entryIndex = 0; entryIndex < desiredEntries.length; entryIndex += 1) {
|
|
855
|
+
const entry = desiredEntries[entryIndex]
|
|
856
|
+
const current = mapGet(userBindingEntries, entry.id)
|
|
857
|
+
if (current !== undefined) {
|
|
858
|
+
current.entry = entry
|
|
859
|
+
appendArray(activated, entry.id)
|
|
860
|
+
continue
|
|
861
|
+
}
|
|
862
|
+
const names = entry.scope === 'namespace' ? [entry.name] : entry.symbols
|
|
863
|
+
const previousSources = mapFromArray(names, name => [name, mapGet(userBindingSources, name)])
|
|
864
|
+
const installedNames = []
|
|
865
|
+
let evaluated
|
|
866
|
+
const activation = { id: entry.id, called: false, failed: false }
|
|
867
|
+
try {
|
|
868
|
+
if (entry.durability === 'volatile') markVolatile(`user binding ${jsonStringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
|
|
869
|
+
evaluated = await userBindingActivationScope.run(activation, () => evaluateUserBinding(entry, cwd, snapshot.transform))
|
|
870
|
+
const namespace = evaluated.namespace
|
|
871
|
+
for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
|
|
872
|
+
const symbol = entry.symbols[symbolIndex]
|
|
873
|
+
if (!Object.hasOwn(namespace, symbol)) throw new Error(`named export ${jsonStringify(symbol)} is unavailable after evaluation`)
|
|
874
|
+
}
|
|
875
|
+
const view = Object.create(null)
|
|
876
|
+
for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
|
|
877
|
+
const symbol = entry.symbols[symbolIndex]
|
|
878
|
+
Object.defineProperty(view, symbol, { enumerable: true, get: () => namespace[symbol] })
|
|
879
|
+
}
|
|
880
|
+
Object.freeze(view)
|
|
881
|
+
const descriptors = new Map()
|
|
882
|
+
for (let nameIndex = 0; nameIndex < names.length; nameIndex += 1) {
|
|
883
|
+
const name = names[nameIndex]
|
|
884
|
+
const storage = userBindingRootStorage(name)
|
|
885
|
+
const prior = mapGet(userBindingSources, name)
|
|
886
|
+
if (storage === 'lexical' || storage === 'unknown') {
|
|
887
|
+
mapSet(userBindingSources, name, { state: storage === 'lexical' ? 'local' : 'unknown' })
|
|
888
|
+
} else if (prior === undefined && setHas(shadowedNames, name)) {
|
|
889
|
+
const descriptor = underlyingUserBindingDescriptor(name)
|
|
890
|
+
mapSet(userBindingSources, name, { state: descriptor === undefined ? 'unknown' : 'local' })
|
|
343
891
|
}
|
|
892
|
+
const source = mapGet(userBindingSources, name)
|
|
893
|
+
if (source !== undefined && source.state !== 'provider') continue
|
|
894
|
+
const descriptor = Object.getOwnPropertyDescriptor(context, name)
|
|
895
|
+
Object.defineProperty(context, name, {
|
|
896
|
+
configurable: true,
|
|
897
|
+
enumerable: true,
|
|
898
|
+
get: () => entry.scope === 'namespace' ? view : namespace[name],
|
|
899
|
+
set(value) {
|
|
900
|
+
const receiver = this === contextGlobal ? context : this
|
|
901
|
+
Object.defineProperty(receiver, name, { configurable: true, enumerable: true, writable: true, value })
|
|
902
|
+
if (receiver === context) recordUserBindingAssignment(name)
|
|
903
|
+
},
|
|
904
|
+
})
|
|
905
|
+
appendArray(installedNames, { name, descriptor })
|
|
906
|
+
mapSet(descriptors, name, Object.getOwnPropertyDescriptor(context, name))
|
|
907
|
+
mapSet(userBindingNames, name, entry.id)
|
|
908
|
+
mapSet(userBindingSources, name, { state: 'provider', entryId: entry.id })
|
|
344
909
|
}
|
|
345
|
-
|
|
346
|
-
|
|
910
|
+
mapSet(userBindingEntries, entry.id, { entry, transform: snapshot.transform, moduleUrl: evaluated.moduleUrl, names, descriptors })
|
|
911
|
+
if (snapshot.transform !== USER_BINDING_TRANSFORM) retainedLegacyUserBindingRuntime = true
|
|
912
|
+
appendArray(activated, entry.id)
|
|
913
|
+
} catch (error) {
|
|
914
|
+
activation.failed = true
|
|
915
|
+
if (activation.called) markVolatile(`failed user binding ${jsonStringify(entry.id)} issued a host call`)
|
|
916
|
+
for (let index = installedNames.length - 1; index >= 0; index--) {
|
|
917
|
+
const installed = installedNames[index]
|
|
918
|
+
mapDelete(userBindingNames, installed.name)
|
|
919
|
+
if (installed.descriptor === undefined) delete context[installed.name]
|
|
920
|
+
else Object.defineProperty(context, installed.name, installed.descriptor)
|
|
921
|
+
}
|
|
922
|
+
const previousEntries = mapEntriesArray(previousSources)
|
|
923
|
+
for (let previousIndex = 0; previousIndex < previousEntries.length; previousIndex += 1) {
|
|
924
|
+
const name = previousEntries[previousIndex][0]
|
|
925
|
+
const prior = previousEntries[previousIndex][1]
|
|
926
|
+
if (prior === undefined) mapDelete(userBindingSources, name)
|
|
927
|
+
else mapSet(userBindingSources, name, prior)
|
|
928
|
+
}
|
|
929
|
+
if (evaluated !== undefined) mapDelete(userBindingModuleParents, evaluated.moduleUrl)
|
|
930
|
+
appendArray(failures, { id: entry.id, error: messageOf(error) })
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (mapSize(userBindingEntries) === 0 && !retainedLegacyUserBindingRuntime) restoreDynamicNamespaceGlobals()
|
|
934
|
+
return { activated, failures }
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
function removeUserBindingEntry(id, shadowedNames) {
|
|
938
|
+
const previous = mapGet(userBindingEntries, id)
|
|
939
|
+
if (previous === undefined) return
|
|
940
|
+
for (let index = 0; index < previous.names.length; index += 1) {
|
|
941
|
+
const name = previous.names[index]
|
|
942
|
+
if (mapGet(userBindingNames, name) !== id) continue
|
|
943
|
+
mapDelete(userBindingNames, name)
|
|
944
|
+
if (!setHas(shadowedNames, name)) delete context[name]
|
|
945
|
+
}
|
|
946
|
+
mapDelete(userBindingEntries, id)
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
function descriptorsEqual(left, right) {
|
|
950
|
+
if (left === undefined || right === undefined) return left === right
|
|
951
|
+
return left.configurable === right.configurable
|
|
952
|
+
&& left.enumerable === right.enumerable
|
|
953
|
+
&& left.get === right.get
|
|
954
|
+
&& left.set === right.set
|
|
955
|
+
&& left.value === right.value
|
|
956
|
+
&& left.writable === right.writable
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
function reconcileUserBindingShadows(shadowedNames) {
|
|
960
|
+
const entries = mapEntriesArray(userBindingEntries)
|
|
961
|
+
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
|
|
962
|
+
const id = entries[entryIndex][0]
|
|
963
|
+
const entry = entries[entryIndex][1]
|
|
964
|
+
for (let nameIndex = 0; nameIndex < entry.names.length; nameIndex += 1) {
|
|
965
|
+
const name = entry.names[nameIndex]
|
|
966
|
+
if (mapGet(userBindingNames, name) !== id) continue
|
|
967
|
+
const installed = mapGet(entry.descriptors, name)
|
|
968
|
+
const current = Object.getOwnPropertyDescriptor(context, name)
|
|
969
|
+
if (!descriptorsEqual(current, installed)) setAdd(shadowedNames, name)
|
|
347
970
|
}
|
|
348
971
|
}
|
|
349
972
|
}
|
|
350
973
|
|
|
974
|
+
async function evaluateUserBinding(entry, cwd, transform) {
|
|
975
|
+
if (typeof cwd !== 'string' || !isAbsolute(cwd)) {
|
|
976
|
+
throw new Error('user binding activation requires an absolute storage directory')
|
|
977
|
+
}
|
|
978
|
+
const prepared = compileStatefulModule(entry.source, {
|
|
979
|
+
transform,
|
|
980
|
+
programBindings: transform === USER_BINDING_TRANSFORM,
|
|
981
|
+
})
|
|
982
|
+
const url = `data:text/javascript,${encodeURIComponent(prepared.code)}#ptc-plus-${entry.fingerprint}-${++nextUserBindingModuleId}`
|
|
983
|
+
mapSet(userBindingModuleParents, url, pathToFileURL(resolve(cwd, 'bindings.json')).href)
|
|
984
|
+
userModuleCompilation.mark(url, { transform, compiled: true, moduleInterface: prepared.moduleInterface, sourceRegions: prepared.sourceRegions })
|
|
985
|
+
try {
|
|
986
|
+
return { namespace: transform === LEGACY_USER_BINDING_TRANSFORM ? await import(url)
|
|
987
|
+
: await managedModuleImport(url, url), moduleUrl: url }
|
|
988
|
+
} catch (error) {
|
|
989
|
+
mapDelete(userBindingModuleParents, url)
|
|
990
|
+
throw error
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function userBindingImplementationMatches(left, right) {
|
|
995
|
+
return left.source === right.source
|
|
996
|
+
&& left.scope === right.scope
|
|
997
|
+
&& (left.scope !== 'namespace' || left.name === right.name)
|
|
998
|
+
&& left.symbols.length === right.symbols.length
|
|
999
|
+
&& everyArray(left.symbols, (symbol, index) => symbol === right.symbols[index])
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, initialFailures = []) {
|
|
1003
|
+
reconcileUserBindingShadows(shadowedNames)
|
|
1004
|
+
const blockedIds = setFromArray(mapArray(initialFailures, failure => failure.id))
|
|
1005
|
+
const desired = mapFromArray(filterArray(snapshot?.entries ?? [], (entry) => {
|
|
1006
|
+
const names = entry.scope === 'namespace' ? [entry.name] : entry.symbols
|
|
1007
|
+
return !setHas(blockedIds, entry.id) && !someArray(names, name => setHas(shadowedNames, name))
|
|
1008
|
+
}), entry => [entry.id, entry])
|
|
1009
|
+
try {
|
|
1010
|
+
if (mapSize(desired) > 0 || retainedLegacyUserBindingRuntime) installDynamicNamespaceGlobals()
|
|
1011
|
+
} catch (error) {
|
|
1012
|
+
const ids = mapKeysArray(userBindingEntries)
|
|
1013
|
+
for (let index = 0; index < ids.length; index += 1) removeUserBindingEntry(ids[index], shadowedNames)
|
|
1014
|
+
const failures = copyArray(initialFailures)
|
|
1015
|
+
const desiredFailures = mapArray(mapKeysArray(desired), id => ({ id, error: messageOf(error) }))
|
|
1016
|
+
for (let index = 0; index < desiredFailures.length; index += 1) appendArray(failures, desiredFailures[index])
|
|
1017
|
+
return { activated: [], failures, error }
|
|
1018
|
+
}
|
|
1019
|
+
const existingIds = mapKeysArray(userBindingEntries)
|
|
1020
|
+
for (let index = 0; index < existingIds.length; index += 1) {
|
|
1021
|
+
const id = existingIds[index]
|
|
1022
|
+
const current = mapGet(userBindingEntries, id)
|
|
1023
|
+
const next = mapGet(desired, id)
|
|
1024
|
+
// Historical cells retain fingerprint-based resets, including presentation edits.
|
|
1025
|
+
const reusable = next !== undefined && current.transform === snapshot.transform && (reusePolicy === LEGACY_USER_BINDINGS_REUSE_POLICY
|
|
1026
|
+
? next.fingerprint === current.entry.fingerprint
|
|
1027
|
+
: userBindingImplementationMatches(next, current.entry))
|
|
1028
|
+
if (!reusable) {
|
|
1029
|
+
removeUserBindingEntry(id, shadowedNames)
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
const activated = []
|
|
1033
|
+
const failures = copyArray(initialFailures)
|
|
1034
|
+
const desiredEntries = mapValuesArray(desired)
|
|
1035
|
+
for (let entryIndex = 0; entryIndex < desiredEntries.length; entryIndex += 1) {
|
|
1036
|
+
const entry = desiredEntries[entryIndex]
|
|
1037
|
+
const current = mapGet(userBindingEntries, entry.id)
|
|
1038
|
+
if (current !== undefined) {
|
|
1039
|
+
current.entry = entry
|
|
1040
|
+
appendArray(activated, entry.id)
|
|
1041
|
+
continue
|
|
1042
|
+
}
|
|
1043
|
+
let evaluated
|
|
1044
|
+
const installedNames = []
|
|
1045
|
+
const activation = { id: entry.id, called: false, failed: false }
|
|
1046
|
+
try {
|
|
1047
|
+
if (entry.durability === 'volatile') {
|
|
1048
|
+
markVolatile(`user binding ${jsonStringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
|
|
1049
|
+
}
|
|
1050
|
+
evaluated = await userBindingActivationScope.run(
|
|
1051
|
+
activation,
|
|
1052
|
+
() => evaluateUserBinding(entry, cwd, snapshot.transform),
|
|
1053
|
+
)
|
|
1054
|
+
const namespace = evaluated.namespace
|
|
1055
|
+
for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
|
|
1056
|
+
const symbol = entry.symbols[symbolIndex]
|
|
1057
|
+
if (!Object.hasOwn(namespace, symbol)) {
|
|
1058
|
+
throw new Error(`named export ${jsonStringify(symbol)} is unavailable after evaluation`)
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
const names = []
|
|
1062
|
+
if (entry.scope === 'namespace') {
|
|
1063
|
+
const view = Object.create(null)
|
|
1064
|
+
for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
|
|
1065
|
+
const symbol = entry.symbols[symbolIndex]
|
|
1066
|
+
Object.defineProperty(view, symbol, {
|
|
1067
|
+
enumerable: true,
|
|
1068
|
+
get: () => namespace[symbol],
|
|
1069
|
+
})
|
|
1070
|
+
}
|
|
1071
|
+
Object.freeze(view)
|
|
1072
|
+
const descriptor = Object.getOwnPropertyDescriptor(context, entry.name)
|
|
1073
|
+
Object.defineProperty(context, entry.name, {
|
|
1074
|
+
configurable: true,
|
|
1075
|
+
enumerable: true,
|
|
1076
|
+
get: () => view,
|
|
1077
|
+
set(value) {
|
|
1078
|
+
recordUserBindingAssignment(entry.name, true)
|
|
1079
|
+
Object.defineProperty(context, entry.name, {
|
|
1080
|
+
configurable: true,
|
|
1081
|
+
enumerable: true,
|
|
1082
|
+
writable: true,
|
|
1083
|
+
value,
|
|
1084
|
+
})
|
|
1085
|
+
},
|
|
1086
|
+
})
|
|
1087
|
+
appendArray(installedNames, {
|
|
1088
|
+
name: entry.name,
|
|
1089
|
+
descriptor,
|
|
1090
|
+
})
|
|
1091
|
+
mapSet(userBindingNames, entry.name, entry.id)
|
|
1092
|
+
appendArray(names, entry.name)
|
|
1093
|
+
} else {
|
|
1094
|
+
for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
|
|
1095
|
+
const symbol = entry.symbols[symbolIndex]
|
|
1096
|
+
const descriptor = Object.getOwnPropertyDescriptor(context, symbol)
|
|
1097
|
+
Object.defineProperty(context, symbol, {
|
|
1098
|
+
configurable: true,
|
|
1099
|
+
enumerable: true,
|
|
1100
|
+
get: () => namespace[symbol],
|
|
1101
|
+
set(value) {
|
|
1102
|
+
recordUserBindingAssignment(symbol, true)
|
|
1103
|
+
Object.defineProperty(context, symbol, {
|
|
1104
|
+
configurable: true,
|
|
1105
|
+
enumerable: true,
|
|
1106
|
+
writable: true,
|
|
1107
|
+
value,
|
|
1108
|
+
})
|
|
1109
|
+
},
|
|
1110
|
+
})
|
|
1111
|
+
appendArray(installedNames, { name: symbol, descriptor })
|
|
1112
|
+
mapSet(userBindingNames, symbol, entry.id)
|
|
1113
|
+
appendArray(names, symbol)
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
mapSet(userBindingEntries, entry.id, {
|
|
1117
|
+
entry,
|
|
1118
|
+
transform: snapshot.transform,
|
|
1119
|
+
moduleUrl: evaluated.moduleUrl,
|
|
1120
|
+
names,
|
|
1121
|
+
descriptors: mapFromArray(names, name => [name, Object.getOwnPropertyDescriptor(context, name)]),
|
|
1122
|
+
})
|
|
1123
|
+
if (snapshot.transform !== USER_BINDING_TRANSFORM) retainedLegacyUserBindingRuntime = true
|
|
1124
|
+
appendArray(activated, entry.id)
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
activation.failed = true
|
|
1127
|
+
if (activation.called) {
|
|
1128
|
+
markVolatile(`failed user binding ${jsonStringify(entry.id)} issued a host call`)
|
|
1129
|
+
}
|
|
1130
|
+
for (let index = installedNames.length - 1; index >= 0; index--) {
|
|
1131
|
+
const installed = installedNames[index]
|
|
1132
|
+
mapDelete(userBindingNames, installed.name)
|
|
1133
|
+
if (installed.descriptor === undefined) delete context[installed.name]
|
|
1134
|
+
else Object.defineProperty(context, installed.name, installed.descriptor)
|
|
1135
|
+
}
|
|
1136
|
+
if (evaluated !== undefined) mapDelete(userBindingModuleParents, evaluated.moduleUrl)
|
|
1137
|
+
removeUserBindingEntry(entry.id, shadowedNames)
|
|
1138
|
+
appendArray(failures, {
|
|
1139
|
+
id: entry.id,
|
|
1140
|
+
error: messageOf(error),
|
|
1141
|
+
})
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
if (mapSize(userBindingEntries) === 0 && !retainedLegacyUserBindingRuntime) restoreDynamicNamespaceGlobals()
|
|
1145
|
+
return { activated, failures }
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
async function closeExecution(execution) {
|
|
1149
|
+
activeRun = undefined
|
|
1150
|
+
execution.open = false
|
|
1151
|
+
const settling = []
|
|
1152
|
+
mapForEach(pending, call => {
|
|
1153
|
+
if (call.runId === execution.id) appendArray(settling, call.settled)
|
|
1154
|
+
})
|
|
1155
|
+
for (let index = 0; index < settling.length; index++) await settling[index]
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
function failureOutcome(error, phase, program) {
|
|
1159
|
+
if (phase === 'encode') return { invalidOutput: messageOf(error) }
|
|
1160
|
+
const failure = error instanceof StaticImportFailure ? error.error : error
|
|
1161
|
+
// The V8 frame is reported in the executed text; validate it there so an
|
|
1162
|
+
// out-of-range frame is an explicit absence instead of a mapped position.
|
|
1163
|
+
const detail = errorDetails(failure, activeFilename, program)
|
|
1164
|
+
// A compiler-created call error has only an adapter stack. Its source fact
|
|
1165
|
+
// owns the operation; ordinary exceptions retain their native stack position.
|
|
1166
|
+
const position = error instanceof StaticImportFailure ? error.position
|
|
1167
|
+
: activeExecution.exceptionOrigins.sourceFailure(failure) ? undefined : detail.position
|
|
1168
|
+
return {
|
|
1169
|
+
error: detail.message,
|
|
1170
|
+
errorName: detail.name,
|
|
1171
|
+
...(detail.toolName === undefined ? {} : { toolName: detail.toolName }),
|
|
1172
|
+
...(error instanceof StaticImportFailure ? { moduleLoadFailed: true } : {}),
|
|
1173
|
+
...(position === undefined ? {} : { position }),
|
|
1174
|
+
...(position === undefined ? { exceptionOrigins: activeExecution.exceptionOrigins.origins(failure) } : {}),
|
|
1175
|
+
...(detail.cause === undefined ? {} : { cause: detail.cause }),
|
|
1176
|
+
...(detail.failureOrigin === undefined ? {} : { failureOrigin: detail.failureOrigin }),
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function observeBindings(names) {
|
|
1181
|
+
const observation = valueObserver.observe(names)
|
|
1182
|
+
return { ...observation, entries: mapArray(observation.entries, entry => {
|
|
1183
|
+
const local = statefulRoots.localValue(entry.name)
|
|
1184
|
+
return local === undefined ? entry : { name: entry.name, ...previewBindingValue(local.value) }
|
|
1185
|
+
}) }
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function refreshLegacyPublication(name) {
|
|
1189
|
+
const publications = activeExecution?.nativePublications
|
|
1190
|
+
const publication = publications === undefined ? undefined : mapGet(publications, name)
|
|
1191
|
+
// A completed native declaration is visible to saved logical closures in
|
|
1192
|
+
// the same cell. A failed or pending initializer retains the prior source.
|
|
1193
|
+
if (publication === undefined) return
|
|
1194
|
+
const storage = rootBindingStorage(publication.import?.namespace ?? name, publication.nativeLexical)
|
|
1195
|
+
if (storage === 'lexical' || publication.import === undefined
|
|
1196
|
+
&& (storage === 'local' || storage === 'property' && Object.hasOwn(context, name))) {
|
|
1197
|
+
mapDelete(publications, name)
|
|
1198
|
+
statefulRoots.publishLegacy(name, publication.writable, publication.import, storage !== 'lexical')
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
function sendCompletion(message, execution, userBindings, committedRedeclarations, outcome) {
|
|
1203
|
+
const publicationNames = execution.nativePublications === undefined
|
|
1204
|
+
? [] : mapKeysArray(execution.nativePublications)
|
|
1205
|
+
for (let index = 0; index < publicationNames.length; index += 1) refreshLegacyPublication(publicationNames[index])
|
|
1206
|
+
const shadowedNames = execution.legacyAssignedUserBindingNames
|
|
1207
|
+
const perName = message.userBindingsShadowPolicy === LIVE_USER_BINDINGS_SHADOW_POLICY
|
|
1208
|
+
if (perName) reconcileUserBindingNames()
|
|
1209
|
+
else reconcileUserBindingShadows(shadowedNames)
|
|
1210
|
+
const names = message.observeNames ?? []
|
|
1211
|
+
channel.postMessage({
|
|
1212
|
+
type: 'done',
|
|
1213
|
+
id: message.id,
|
|
1214
|
+
logs: execution.logs,
|
|
1215
|
+
...completionDurability(execution),
|
|
1216
|
+
committedRedeclarations: setValuesArray(committedRedeclarations),
|
|
1217
|
+
...(message.rootBindings === undefined ? {} : { rootBindingFacts: statefulRoots.facts() }),
|
|
1218
|
+
...(perName ? { userBindingNames: normalizeUserBindingNames(mapArray(mapEntriesArray(userBindingSources), entry => ({
|
|
1219
|
+
name: entry[0], state: entry[1].state, ...(entry[1].state === 'provider' ? { entryId: entry[1].entryId } : {}),
|
|
1220
|
+
}))) } : {}),
|
|
1221
|
+
...(message.userBindings === undefined ? {} : {
|
|
1222
|
+
activatedUserBindings: userBindings.activated,
|
|
1223
|
+
userBindingFailures: userBindings.failures,
|
|
1224
|
+
...(perName ? {} : { shadowedUserBindings: setValuesArray(shadowedNames) }),
|
|
1225
|
+
}),
|
|
1226
|
+
...outcome,
|
|
1227
|
+
observing: names.length > 0,
|
|
1228
|
+
})
|
|
1229
|
+
if (outcome.error === undefined) valueObserver.record(message.program)
|
|
1230
|
+
if (names.length > 0) {
|
|
1231
|
+
channel.postMessage({ type: 'observation', id: message.id, observation: observeBindings(names) })
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
351
1235
|
async function runCell(message) {
|
|
352
|
-
if (
|
|
1236
|
+
if (activeExecution !== undefined) throw new Error('kernel received overlapping cells')
|
|
353
1237
|
activeRun = message.id
|
|
354
|
-
installBindings(message)
|
|
355
1238
|
const execution = {
|
|
356
1239
|
id: message.id,
|
|
1240
|
+
exceptionOrigins: createExceptionOriginScope(),
|
|
1241
|
+
languageSemantics: message.languageSemantics,
|
|
1242
|
+
moduleTransform: message.moduleTransform,
|
|
1243
|
+
userBindingsShadowPolicy: message.userBindingsShadowPolicy,
|
|
1244
|
+
importBindingNamespaces: message.importBindingNamespaces,
|
|
1245
|
+
legacyAssignedUserBindingNames: new Set(),
|
|
357
1246
|
logs: [],
|
|
358
1247
|
open: true,
|
|
359
1248
|
outputLimited: false,
|
|
360
1249
|
logBytes: 2,
|
|
361
1250
|
maxOutputBytes: message.maxOutputBytes,
|
|
362
1251
|
valueLimits: message.valueLimits,
|
|
363
|
-
durability: message.durability === 'volatile'
|
|
364
|
-
volatileReason:
|
|
1252
|
+
durability: message.durability === 'volatile' ? 'volatile' : 'durable',
|
|
1253
|
+
volatileReason: message.volatileReason,
|
|
365
1254
|
}
|
|
366
|
-
pendingVolatileReason = undefined
|
|
367
1255
|
activeExecution = execution
|
|
368
1256
|
const cellGlobals = []
|
|
369
1257
|
const committedRedeclarations = new Set()
|
|
370
1258
|
|
|
371
1259
|
try {
|
|
372
1260
|
let completion
|
|
1261
|
+
let outcome
|
|
1262
|
+
let userBindings = { activated: [], failures: [] }
|
|
373
1263
|
try {
|
|
374
1264
|
completion = await logScope.run(execution, async () => {
|
|
1265
|
+
const namespaceError = installBindings(message)
|
|
375
1266
|
Object.defineProperty(context, message.returnSignal, {
|
|
376
1267
|
configurable: true,
|
|
377
1268
|
value: CellReturn,
|
|
378
1269
|
})
|
|
379
|
-
cellGlobals
|
|
1270
|
+
appendArray(cellGlobals, message.returnSignal)
|
|
380
1271
|
Object.defineProperty(context, message.commitSignal, {
|
|
381
1272
|
configurable: true,
|
|
382
1273
|
value(name) {
|
|
383
|
-
committedRedeclarations
|
|
1274
|
+
setAdd(committedRedeclarations, name)
|
|
384
1275
|
},
|
|
385
1276
|
})
|
|
386
|
-
cellGlobals
|
|
387
|
-
|
|
1277
|
+
appendArray(cellGlobals, message.commitSignal)
|
|
1278
|
+
if (message.rootRuntimeName !== undefined) {
|
|
1279
|
+
const nativeLexicals = setFromArray(message.rootBindings.legacyNativeLexicals)
|
|
1280
|
+
const legacyStorage = mapFromArray(filterArray(message.rootBindings.legacyLexicals,
|
|
1281
|
+
name => !statefulRoots.has(name)), name => [name, rootBindingStorage(name, setHas(nativeLexicals, name))])
|
|
1282
|
+
const legacyLexicals = filterArray(message.rootBindings.legacyLexicals, name => mapGet(legacyStorage, name) === 'lexical')
|
|
1283
|
+
const legacyNative = setFromArray(message.rootBindings.legacyNative)
|
|
1284
|
+
const legacyWritable = filterArray(message.rootBindings.legacyWritable, name => {
|
|
1285
|
+
const storage = mapGet(legacyStorage, name)
|
|
1286
|
+
return storage === 'lexical' || setHas(legacyNative, name)
|
|
1287
|
+
&& (storage === 'local' || storage === 'property') && writableRootProperty(name)
|
|
1288
|
+
})
|
|
1289
|
+
Object.defineProperty(context, message.rootRuntimeName, {
|
|
1290
|
+
configurable: true,
|
|
1291
|
+
value: statefulRoots.begin({ ...message.rootBindings,
|
|
1292
|
+
legacyLexicals,
|
|
1293
|
+
legacyWritable,
|
|
1294
|
+
legacyObjects: filterArray(legacyWritable, name => mapGet(legacyStorage, name) !== 'lexical'),
|
|
1295
|
+
committed(name) {
|
|
1296
|
+
setDelete(committedRedeclarations, name)
|
|
1297
|
+
setAdd(committedRedeclarations, name)
|
|
1298
|
+
} }),
|
|
1299
|
+
})
|
|
1300
|
+
appendArray(cellGlobals, message.rootRuntimeName)
|
|
1301
|
+
}
|
|
1302
|
+
const activate = message.userBindingsShadowPolicy === LIVE_USER_BINDINGS_SHADOW_POLICY
|
|
1303
|
+
? activatePerNameUserBindings : activateUserBindings
|
|
1304
|
+
userBindings = await activate(
|
|
1305
|
+
message.userBindings,
|
|
1306
|
+
setFromArray(message.shadowedUserBindingNames ?? []),
|
|
1307
|
+
message.userBindingsCwd,
|
|
1308
|
+
message.userBindingsReusePolicy,
|
|
1309
|
+
message.userBindingFailures,
|
|
1310
|
+
)
|
|
1311
|
+
if (userBindings.error !== undefined) throw userBindings.error
|
|
1312
|
+
if (namespaceError !== undefined) throw namespaceError
|
|
1313
|
+
for (let failureIndex = 0; failureIndex < userBindings.failures.length; failureIndex += 1) {
|
|
1314
|
+
const failure = userBindings.failures[failureIndex]
|
|
1315
|
+
appendText(execution, `Global binding ${jsonStringify(failure.id)} was not activated: ${failure.error}`)
|
|
1316
|
+
}
|
|
1317
|
+
const moduleLoads = message.moduleLoads ?? []
|
|
1318
|
+
for (let loadIndex = 0; loadIndex < moduleLoads.length; loadIndex += 1) {
|
|
1319
|
+
const load = moduleLoads[loadIndex]
|
|
388
1320
|
let namespace
|
|
389
1321
|
try {
|
|
390
|
-
namespace = await loadStaticModule(load)
|
|
1322
|
+
namespace = (await loadStaticModule(load)).namespace
|
|
391
1323
|
} catch (error) {
|
|
392
1324
|
throw new StaticImportFailure(error, load.position)
|
|
393
1325
|
}
|
|
@@ -396,100 +1328,114 @@ async function runCell(message) {
|
|
|
396
1328
|
configurable: true,
|
|
397
1329
|
value: namespace,
|
|
398
1330
|
})
|
|
399
|
-
cellGlobals
|
|
1331
|
+
appendArray(cellGlobals, load.global)
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
// Preload failure leaves prior aliases authoritative. Once evaluation
|
|
1335
|
+
// starts, new slots must prove their own initialization, including TDZ.
|
|
1336
|
+
execution.importBindingNamespaces = message.preparedImportBindingNamespaces
|
|
1337
|
+
execution.nativePublications = new Map()
|
|
1338
|
+
for (let loadIndex = 0; loadIndex < moduleLoads.length; loadIndex += 1) {
|
|
1339
|
+
const load = moduleLoads[loadIndex]
|
|
1340
|
+
const publications = load.nativePublications ?? []
|
|
1341
|
+
for (let publicationIndex = 0; publicationIndex < publications.length; publicationIndex += 1) {
|
|
1342
|
+
const publication = publications[publicationIndex]
|
|
1343
|
+
mapSet(execution.nativePublications, publication.name, publication)
|
|
400
1344
|
}
|
|
401
1345
|
}
|
|
402
|
-
return evaluate(message.program)
|
|
1346
|
+
return execution.exceptionOrigins.run(() => evaluate(message.program, message.returnSignal, message.asyncCompletion))
|
|
403
1347
|
})
|
|
404
|
-
activeRun = undefined
|
|
405
|
-
execution.open = false
|
|
406
|
-
const calls = [...pending.values()]
|
|
407
|
-
.filter(call => call.runId === message.id)
|
|
408
|
-
.map(call => new Promise(resolve => {
|
|
409
|
-
const originalResolve = call.resolve
|
|
410
|
-
const originalReject = call.reject
|
|
411
|
-
call.resolve = value => { originalResolve(value); resolve() }
|
|
412
|
-
call.reject = error => { originalReject(error); resolve() }
|
|
413
|
-
}))
|
|
414
|
-
if (calls.length > 0) await Promise.all(calls)
|
|
415
1348
|
} catch (error) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const detail = errorDetails(failure, activeFilename)
|
|
420
|
-
const position = error instanceof StaticImportFailure ? error.position : detail.position
|
|
421
|
-
channel.postMessage({
|
|
422
|
-
type: 'done',
|
|
423
|
-
id: message.id,
|
|
424
|
-
logs: execution.logs,
|
|
425
|
-
error: detail.message,
|
|
426
|
-
errorName: detail.name,
|
|
427
|
-
...(detail.toolName === undefined ? {} : { toolName: detail.toolName }),
|
|
428
|
-
...(error instanceof StaticImportFailure ? { moduleLoadFailed: true } : {}),
|
|
429
|
-
...(position === undefined ? {} : { position }),
|
|
430
|
-
...(detail.cause === undefined ? {} : { cause: detail.cause }),
|
|
431
|
-
...completionDurability(execution),
|
|
432
|
-
committedRedeclarations: [...committedRedeclarations],
|
|
433
|
-
})
|
|
434
|
-
return
|
|
1349
|
+
outcome = failureOutcome(error, 'execute', message.program)
|
|
1350
|
+
} finally {
|
|
1351
|
+
await closeExecution(execution)
|
|
435
1352
|
}
|
|
436
1353
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
...completionDurability(execution),
|
|
449
|
-
committedRedeclarations: [...committedRedeclarations],
|
|
450
|
-
}
|
|
451
|
-
} catch (error) {
|
|
452
|
-
const detail = messageOf(error)
|
|
453
|
-
response = {
|
|
454
|
-
type: 'done',
|
|
455
|
-
id: message.id,
|
|
456
|
-
logs: execution.logs,
|
|
457
|
-
invalidOutput: detail,
|
|
458
|
-
...completionDurability(execution),
|
|
459
|
-
committedRedeclarations: [...committedRedeclarations],
|
|
1354
|
+
if (outcome === undefined) {
|
|
1355
|
+
try {
|
|
1356
|
+
const encodedValue = completion.hasValue
|
|
1357
|
+
? encodeValue(completion.value, execution.valueLimits)
|
|
1358
|
+
: undefined
|
|
1359
|
+
outcome = {
|
|
1360
|
+
hasValue: completion.hasValue,
|
|
1361
|
+
...(encodedValue === undefined ? {} : { value: encodedValue }),
|
|
1362
|
+
}
|
|
1363
|
+
} catch (error) {
|
|
1364
|
+
outcome = failureOutcome(error, 'encode', message.program)
|
|
460
1365
|
}
|
|
461
1366
|
}
|
|
462
|
-
|
|
1367
|
+
sendCompletion(message, execution, userBindings, committedRedeclarations, outcome)
|
|
463
1368
|
} finally {
|
|
464
|
-
for (
|
|
1369
|
+
for (let index = 0; index < cellGlobals.length; index += 1) delete context[cellGlobals[index]]
|
|
465
1370
|
activeRun = undefined
|
|
466
1371
|
activeExecution = undefined
|
|
1372
|
+
execution.exceptionOrigins.close()
|
|
467
1373
|
execution.open = false
|
|
468
1374
|
}
|
|
469
1375
|
}
|
|
470
1376
|
|
|
1377
|
+
// The host stops this worker cooperatively so the release below runs while the
|
|
1378
|
+
// realms, REPL and ports it owns are still alive.
|
|
1379
|
+
parentPort.on('message', (message) => {
|
|
1380
|
+
if (message?.type !== WORKER_SHUTDOWN_REQUEST) return
|
|
1381
|
+
server.close()
|
|
1382
|
+
input.destroy()
|
|
1383
|
+
output.destroy()
|
|
1384
|
+
errorDomain.removeAllListeners('error')
|
|
1385
|
+
process.removeListener('uncaughtException', handleUncaughtException)
|
|
1386
|
+
channel.close()
|
|
1387
|
+
parentPort.postMessage({ type: WORKER_SHUTDOWN_ACKNOWLEDGEMENT })
|
|
1388
|
+
parentPort.close()
|
|
1389
|
+
})
|
|
1390
|
+
|
|
471
1391
|
channel.on('message', (message) => {
|
|
1392
|
+
if (message?.type === 'prepare') {
|
|
1393
|
+
channel.postMessage({ type: 'ready', id: message.id })
|
|
1394
|
+
return
|
|
1395
|
+
}
|
|
1396
|
+
if (message?.type === 'observe') {
|
|
1397
|
+
channel.postMessage({ type: 'observation-started', id: message.id })
|
|
1398
|
+
channel.postMessage({ type: 'observation', id: message.id,
|
|
1399
|
+
observation: observeBindings(message.names) })
|
|
1400
|
+
return
|
|
1401
|
+
}
|
|
472
1402
|
if (message?.type === 'reply') {
|
|
473
|
-
const call = pending
|
|
1403
|
+
const call = mapGet(pending, message.id)
|
|
474
1404
|
if (call === undefined || call.runId !== message.runId) return
|
|
475
|
-
pending
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
1405
|
+
mapDelete(pending, message.id)
|
|
1406
|
+
try {
|
|
1407
|
+
if (message.ok) call.resolve(decodeValue(message.value, call.valueLimits))
|
|
1408
|
+
else if (call.BoundError === undefined) {
|
|
1409
|
+
const error = new Error(message.error)
|
|
1410
|
+
if (message.cause !== undefined) error.ptcCause = message.cause
|
|
1411
|
+
call.reject(error)
|
|
1412
|
+
} else call.reject(new call.BoundError(call.member, message.error, message.cause))
|
|
1413
|
+
} catch (error) {
|
|
480
1414
|
call.reject(error)
|
|
481
|
-
}
|
|
1415
|
+
}
|
|
482
1416
|
return
|
|
483
1417
|
}
|
|
484
1418
|
if (message?.type === 'run') void runCell(message)
|
|
485
1419
|
})
|
|
486
1420
|
|
|
1421
|
+
let startupTimer
|
|
487
1422
|
try {
|
|
488
|
-
await
|
|
1423
|
+
await Promise.race([
|
|
1424
|
+
verifyEvaluation(),
|
|
1425
|
+
new Promise((_, reject) => {
|
|
1426
|
+
startupTimer = setTimeout(() => reject(new Error('REPL settlement probe timed out')), 5000)
|
|
1427
|
+
}),
|
|
1428
|
+
])
|
|
1429
|
+
replModuleRealm = (await evaluate('({Promise,stringify:value=>`${value}`,importModule:(source,options)=>import(source,options)})')).value
|
|
1430
|
+
valueObserver = createReplValueObserver(context, {
|
|
1431
|
+
awaitLexicals: await supportsAwaitLexicals(context, evaluate),
|
|
1432
|
+
})
|
|
489
1433
|
parentPort.postMessage({ type: 'ready', port: port1 }, [port1])
|
|
490
1434
|
} catch (error) {
|
|
491
1435
|
parentPort.postMessage({
|
|
492
1436
|
type: 'startup-error',
|
|
493
|
-
error: `
|
|
1437
|
+
error: `PTC runtime prerequisite failed on Node ${process.version}: ${messageOf(error)}. No user cell executed; use a DSH-supported runtime that passes the REPL conformance probe. Editing the cell cannot fix this host failure.`,
|
|
494
1438
|
})
|
|
1439
|
+
} finally {
|
|
1440
|
+
clearTimeout(startupTimer)
|
|
495
1441
|
}
|