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