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