dsh-ptc-plus 0.3.2 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +63 -80
- package/README.md +63 -80
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-repair-zh.png +0 -0
- package/assets/ptc-plus-repl-workspace-en.png +0 -0
- package/assets/ptc-plus-repl-workspace-zh.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +30827 -742
- package/compiler-amaro.cjs +5 -0
- package/compiler-amaro.cjs.map +6 -0
- package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
- package/compiler-assets.json +3 -0
- package/compiler-core.cjs +556 -0
- package/compiler-core.cjs.map +6 -0
- package/compiler-platform.cjs +2 -0
- package/compiler-platform.cjs.map +6 -0
- package/compiler-typescript.cjs +388 -0
- package/compiler-typescript.cjs.map +6 -0
- package/docs/README.md +7 -0
- package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
- package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +5 -3
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
- package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
- package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
- package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
- package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +3 -1
- package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
- package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
- package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +5 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +12 -0
- package/docs/adr/0023-global-user-bindings.md +104 -0
- package/docs/adr/0024-repl-console-observation.md +33 -0
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
- package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
- package/docs/architecture.md +77 -21
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +95 -19
- package/docs/durability-design.md +28 -10
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +76 -25
- package/docs/semantic-validation.md +29 -0
- package/docs/stateful-binding-updates-proposal.md +759 -0
- package/docs/user-bindings.en.md +66 -0
- package/docs/user-bindings.md +66 -0
- package/docs/value-wire.md +3 -3
- package/docs/verification-optimization-work.md +41 -0
- package/docs/verification.md +71 -0
- package/index.js +369 -92
- package/internal/ast-traversal.js +5 -2
- package/internal/async-local-storage-intrinsics.js +38 -0
- package/internal/binding-declaration-contract.js +8 -0
- package/internal/binding-descriptors.js +1 -0
- package/internal/binding-pattern-traversal.js +25 -0
- package/internal/binding-pattern.js +26 -29
- package/internal/binding-source-analysis.js +708 -0
- package/internal/binding-update-policy.js +72 -0
- package/internal/bounded-worker-round.js +56 -0
- package/internal/callable-definition-environment.js +42 -0
- package/internal/callable-source-catalog.js +129 -0
- package/internal/callable-source-encoding.js +47 -0
- package/internal/callable-source-facts.js +459 -0
- package/internal/callable-source-format.js +2 -0
- package/internal/callable-source-lowering.js +123 -0
- package/internal/cell-analysis-contract.js +25 -0
- package/internal/cell-analysis.js +253 -290
- package/internal/cell-completion.js +15 -0
- package/internal/cell-error.js +7 -0
- package/internal/cell-parser.js +66 -0
- package/internal/cell-rewriter.js +3 -677
- package/internal/commonjs-export-evidence.js +63 -0
- package/internal/commonjs-source-evidence.js +72 -0
- package/internal/compiler-amaro.cjs +2 -0
- package/internal/compiler-compression.js +12 -0
- package/internal/compiler-data.js +58 -0
- package/internal/compiler-descriptors.js +54 -0
- package/internal/compiler-dynamic-cache.js +48 -0
- package/internal/compiler-entry.js +35 -0
- package/internal/compiler-generated-names.js +17 -0
- package/internal/compiler-intrinsics.js +50 -0
- package/internal/compiler-module-links.js +37 -0
- package/internal/compiler-operations.js +61 -0
- package/internal/compiler-platform-factory.js +84 -0
- package/internal/compiler-platform.js +136 -0
- package/internal/compiler-record-roles.js +53 -0
- package/internal/compiler-region-output.js +259 -0
- package/internal/compiler-resource-lowering.js +103 -0
- package/internal/compiler-scope-facts.js +115 -0
- package/internal/compiler-service.js +109 -0
- package/internal/compiler-source-cache.js +32 -0
- package/internal/compiler-source-pieces.js +93 -0
- package/internal/compiler-source-regions.js +132 -0
- package/internal/compiler-storage-source.js +54 -0
- package/internal/compiler-text.js +15 -0
- package/internal/compiler-typescript.cjs +2 -0
- package/internal/config-spec.js +83 -27
- package/internal/cordis-tools-owner.js +130 -21
- package/internal/diagnostic.js +27 -1
- package/internal/direct-surface-owner.js +76 -23
- package/internal/dynamic-binding-evidence.js +16 -0
- package/internal/dynamic-environment-compiler.js +779 -0
- package/internal/dynamic-environment-integration.js +91 -0
- package/internal/dynamic-environment-runtime.js +383 -0
- package/internal/dynamic-native-calls.js +153 -0
- package/internal/dynamic-native-runtime.js +262 -0
- package/internal/dynamic-scope-analysis.js +90 -0
- package/internal/edit-transport-owner.js +27 -2
- package/internal/execution-seam-compat.js +193 -0
- package/internal/failure-reporting.js +136 -28
- package/internal/host-rpc.js +66 -0
- package/internal/isolated-worker.js +555 -0
- package/internal/kernel-child.js +139 -0
- package/internal/kernel-worker.js +1179 -233
- package/internal/language-semantics.js +14 -0
- package/internal/legacy-binding-catalog.js +48 -0
- package/internal/legacy-cell-analysis.js +600 -0
- package/internal/legacy-cell-parser.js +50 -0
- package/internal/legacy-cell-rewriter.js +631 -0
- package/internal/legacy-repl-convenience.js +291 -0
- package/internal/managed-module-operations.js +170 -0
- package/internal/module-compilation.js +322 -0
- package/internal/module-runtime-platform.js +19 -0
- package/internal/module-transform-contract.js +42 -0
- package/internal/native-await-declarations.js +68 -0
- package/internal/native-root-compilation.js +54 -0
- package/internal/native-root-dynamic.js +67 -0
- package/internal/record-utils.js +24 -15
- package/internal/recovery-tips.js +27 -15
- package/internal/rejected-cell-editor.js +13 -8
- package/internal/repl-convenience.js +2 -289
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +6 -0
- package/internal/repl-scope-normalizer.js +2091 -0
- package/internal/repl-value-observer.js +117 -0
- package/internal/rpc-contract.js +52 -0
- package/internal/runtime-bridge-owner.js +172 -28
- package/internal/runtime-config.js +48 -52
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-intrinsics.js +101 -0
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +402 -87
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +123 -0
- package/internal/session-journal.js +199 -654
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +257 -95
- package/internal/session-state.js +247 -71
- package/internal/source-position-map.js +230 -71
- package/internal/stateful-module-compiler.js +154 -0
- package/internal/stateful-module-runtime.js +303 -0
- package/internal/stateful-root-compiler.js +600 -0
- package/internal/stateful-root-runtime.js +340 -0
- package/internal/typert-codec-compat.js +28 -0
- package/internal/typescript-transform.js +31 -0
- package/internal/user-binding-authoring.js +70 -0
- package/internal/user-binding-console-worker.js +244 -0
- package/internal/user-binding-console.js +148 -0
- package/internal/user-binding-draft-projection.js +335 -0
- package/internal/user-binding-model-context.js +31 -0
- package/internal/user-binding-runner.js +56 -0
- package/internal/user-bindings-owner.js +1065 -0
- package/internal/user-bindings-store.js +293 -0
- package/internal/user-bindings.js +361 -0
- package/internal/value-wire-schema.js +28 -0
- package/internal/value-wire.js +130 -91
- package/internal/worker-client.js +57 -67
- package/internal/worker-cwd-virtualization.js +80 -53
- package/internal/worker-environment.js +65 -0
- package/internal/worker-output-relay.js +29 -0
- package/internal/worker-repl-realm.js +134 -0
- package/internal/worker-shutdown.js +14 -0
- package/package.json +76 -7
- package/src/client-activity.js +95 -53
- package/src/client-authoring-view.js +580 -0
- package/src/client-binding-review.js +230 -0
- package/src/client-bindings-data.js +29 -0
- package/src/client-catalog.js +127 -0
- package/src/client-code-editor.js +84 -0
- package/src/client-console.js +112 -0
- package/src/client-copy.js +370 -0
- package/src/client-feature-gates.js +62 -0
- package/src/client-host-compat.js +150 -0
- package/src/client-indicator-view.js +267 -0
- package/src/client-repl-view.js +292 -0
- package/src/client-rpc.js +20 -0
- package/src/client-settings-view.js +221 -0
- package/src/client-styles.js +106 -0
- package/src/client-tool-view.js +108 -0
- package/src/client-transport.js +159 -0
- package/src/client-workbench.js +521 -0
- package/src/client.js +233 -615
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { LIVE_MODULE_SEMANTICS } from './repl-rewrite-contract.js'
|
|
2
|
+
import { LEGACY_LANGUAGE_SEMANTICS, STATEFUL_LANGUAGE_SEMANTICS,
|
|
3
|
+
PROTECTED_LANGUAGE_SEMANTICS, normalizeLanguageSemantics } from './language-semantics.js'
|
|
4
|
+
import { moduleTransformForLanguage, normalizeModuleTransform } from './module-transform-contract.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Translate the public compatibility settings into the execution policy used
|
|
8
|
+
* by the compiler and journal. Keeping this mapping in one owner prevents
|
|
9
|
+
* individual lowering paths from combining legacy switches differently.
|
|
10
|
+
*/
|
|
11
|
+
export function executionPolicies(config, replayRecord = undefined) {
|
|
12
|
+
if (config.bindingUpdates !== undefined && !['stateful', 'protected'].includes(config.bindingUpdates)) {
|
|
13
|
+
throw new TypeError('ptc-plus: bindingUpdates must be stateful or protected')
|
|
14
|
+
}
|
|
15
|
+
if (replayRecord !== undefined) {
|
|
16
|
+
const bindingPolicy = Object.freeze({ ...replayRecord.bindingPolicy })
|
|
17
|
+
const rewritesEnabled = Object.freeze({ ...replayRecord.rewritePolicy })
|
|
18
|
+
const moduleSemantics = Object.freeze({ ...replayRecord.moduleSemantics })
|
|
19
|
+
const languageSemantics = normalizeLanguageSemantics(replayRecord.languageSemantics ?? LEGACY_LANGUAGE_SEMANTICS)
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
languageSemantics,
|
|
22
|
+
moduleTransform: normalizeModuleTransform(replayRecord.moduleTransform, languageSemantics),
|
|
23
|
+
bindingPolicy,
|
|
24
|
+
rewritesEnabled,
|
|
25
|
+
moduleSemantics,
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
if (config.bindingUpdates === 'stateful' && config.legacyBindingSettings !== true) {
|
|
29
|
+
return Object.freeze({
|
|
30
|
+
languageSemantics: STATEFUL_LANGUAGE_SEMANTICS,
|
|
31
|
+
moduleTransform: moduleTransformForLanguage(STATEFUL_LANGUAGE_SEMANTICS),
|
|
32
|
+
bindingPolicy: Object.freeze({ variableRedeclarations: true, functionClassRedeclarations: true }),
|
|
33
|
+
rewritesEnabled: Object.freeze({ autoRewriteImports: true, autoStripExports: true, autoSplitRedeclarations: true }),
|
|
34
|
+
moduleSemantics: LIVE_MODULE_SEMANTICS,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
if (config.bindingUpdates === 'protected' && config.legacyBindingSettings !== true) {
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
languageSemantics: PROTECTED_LANGUAGE_SEMANTICS,
|
|
40
|
+
moduleTransform: moduleTransformForLanguage(PROTECTED_LANGUAGE_SEMANTICS),
|
|
41
|
+
bindingPolicy: Object.freeze({ variableRedeclarations: false, functionClassRedeclarations: false }),
|
|
42
|
+
// Module syntax is part of the language in protected mode too. The
|
|
43
|
+
// policy protects existing names; it does not remove valid syntax.
|
|
44
|
+
rewritesEnabled: Object.freeze({ autoRewriteImports: true, autoStripExports: true, autoSplitRedeclarations: true }),
|
|
45
|
+
moduleSemantics: LIVE_MODULE_SEMANTICS,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
return Object.freeze({
|
|
49
|
+
languageSemantics: LEGACY_LANGUAGE_SEMANTICS,
|
|
50
|
+
moduleTransform: moduleTransformForLanguage(LEGACY_LANGUAGE_SEMANTICS),
|
|
51
|
+
bindingPolicy: Object.freeze({
|
|
52
|
+
variableRedeclarations: config.looseTopLevelRedeclarations === true,
|
|
53
|
+
functionClassRedeclarations: config.looseTopLevelFunctionClassRedeclarations === true,
|
|
54
|
+
}),
|
|
55
|
+
rewritesEnabled: Object.freeze({
|
|
56
|
+
autoRewriteImports: config.autoRewriteImports === true,
|
|
57
|
+
autoStripExports: config.autoStripExports === true,
|
|
58
|
+
autoSplitRedeclarations: config.autoSplitRedeclarations === true,
|
|
59
|
+
}),
|
|
60
|
+
moduleSemantics: LIVE_MODULE_SEMANTICS,
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Stable prose inputs for the model-visible REPL guidance. */
|
|
65
|
+
export function guidancePolicies(config) {
|
|
66
|
+
const policies = executionPolicies(config)
|
|
67
|
+
return Object.freeze({
|
|
68
|
+
...policies,
|
|
69
|
+
durableReplay: config.durableReplay === true,
|
|
70
|
+
cordisToolsEnabled: config.cordisToolsEnabled === true,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** How a bounded worker round ended without a result. */
|
|
2
|
+
export const ROUND_TIMEOUT = 'timeout'
|
|
3
|
+
export const ROUND_CANCELLED = 'cancelled'
|
|
4
|
+
export const ROUND_OUTPUT_LIMIT = 'output-limit'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Own one bounded worker round: captured output bytes, the wall-clock deadline,
|
|
8
|
+
* cancellation and a single settlement. Worker creation, retention and
|
|
9
|
+
* termination stay with the caller, which is told exactly once how the round
|
|
10
|
+
* ended through `settle({ ok: true, result })` or `settle({ ok: false, reason })`.
|
|
11
|
+
*/
|
|
12
|
+
export function createBoundedWorkerRound({ maxOutputBytes, maxWallMs, signal, settle }) {
|
|
13
|
+
const logs = []
|
|
14
|
+
let capturedBytes = 0
|
|
15
|
+
let ended = false
|
|
16
|
+
const end = (outcome) => {
|
|
17
|
+
if (ended) return false
|
|
18
|
+
ended = true
|
|
19
|
+
clearTimeout(timer)
|
|
20
|
+
signal?.removeEventListener('abort', abort)
|
|
21
|
+
settle(outcome)
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
24
|
+
const fail = reason => end({ ok: false, reason })
|
|
25
|
+
const abort = () => { fail(ROUND_CANCELLED) }
|
|
26
|
+
const timer = setTimeout(() => fail(ROUND_TIMEOUT), maxWallMs)
|
|
27
|
+
signal?.addEventListener('abort', abort, { once: true })
|
|
28
|
+
if (signal?.aborted === true) fail(ROUND_CANCELLED)
|
|
29
|
+
return Object.freeze({
|
|
30
|
+
logs,
|
|
31
|
+
/** Record one stream chunk; ends the round when the byte budget is exceeded. */
|
|
32
|
+
capture(channel, text) {
|
|
33
|
+
if (ended) return
|
|
34
|
+
capturedBytes += Buffer.byteLength(text, 'utf8')
|
|
35
|
+
if (capturedBytes > maxOutputBytes) { fail(ROUND_OUTPUT_LIMIT); return }
|
|
36
|
+
logs.push({ channel, text })
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* True when the byte budget is spent.
|
|
40
|
+
*
|
|
41
|
+
* `envelope` measures the object the caller returns with the captured `logs`
|
|
42
|
+
* included as a member, so log wrappers and JSON escaping count against the
|
|
43
|
+
* same budget; that is the candidate run's returned-envelope budget. Without
|
|
44
|
+
* it the raw captured bytes plus the serialized payload are measured, which is
|
|
45
|
+
* the workbench console's output budget.
|
|
46
|
+
*/
|
|
47
|
+
exceeds(payload, { envelope = false } = {}) {
|
|
48
|
+
if (envelope) {
|
|
49
|
+
return Buffer.byteLength(JSON.stringify({ ...payload, logs }), 'utf8') > maxOutputBytes
|
|
50
|
+
}
|
|
51
|
+
return capturedBytes + Buffer.byteLength(JSON.stringify(payload), 'utf8') > maxOutputBytes
|
|
52
|
+
},
|
|
53
|
+
succeed(result) { return end({ ok: true, result }) },
|
|
54
|
+
fail,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { types as t } from '@babel/core'
|
|
2
|
+
import traverseImport from '@babel/traverse'
|
|
3
|
+
|
|
4
|
+
const traverse = traverseImport.default ?? traverseImport
|
|
5
|
+
|
|
6
|
+
/** Parameters and computed keys evaluate before a callable body scope exists.
|
|
7
|
+
* Return that callable's path; an intervening callable body owns its own scope. */
|
|
8
|
+
export function callableDefinitionOwner(path) {
|
|
9
|
+
let child = path
|
|
10
|
+
while (child.parentPath && !child.parentPath.isFunction()) child = child.parentPath
|
|
11
|
+
return child.parentPath && child.key !== 'body' ? child.parentPath : undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Close definition helpers without losing surrounding suspension or lexical
|
|
15
|
+
* this/new.target. The outer owner only supports Babel's arrow conversion. */
|
|
16
|
+
export function closeDefinitionExpression(helpers, value, parameters = [], argumentsList = []) {
|
|
17
|
+
const body = t.blockStatement([...helpers, t.returnStatement(value)])
|
|
18
|
+
const factory = t.arrowFunctionExpression(parameters, body)
|
|
19
|
+
const owner = t.functionExpression(null, [], t.blockStatement([t.returnStatement(factory)]))
|
|
20
|
+
const tree = t.file(t.program([t.expressionStatement(owner)]))
|
|
21
|
+
let awaited = false, yielded = false
|
|
22
|
+
traverse(tree, { ArrowFunctionExpression(path) {
|
|
23
|
+
if (path.node !== factory) return
|
|
24
|
+
path.get('body').traverse(traverse.visitors.environmentVisitor({
|
|
25
|
+
ArrowFunctionExpression(path) { path.skip() },
|
|
26
|
+
AwaitExpression() { awaited = true },
|
|
27
|
+
YieldExpression() { yielded = true },
|
|
28
|
+
}))
|
|
29
|
+
factory.async = awaited
|
|
30
|
+
if (yielded) {
|
|
31
|
+
const converted = path.arrowFunctionToExpression()
|
|
32
|
+
converted.node.generator = true
|
|
33
|
+
}
|
|
34
|
+
path.stop()
|
|
35
|
+
} })
|
|
36
|
+
if (yielded) {
|
|
37
|
+
owner.type = 'ArrowFunctionExpression'
|
|
38
|
+
return t.yieldExpression(t.callExpression(t.callExpression(owner, []), argumentsList), true)
|
|
39
|
+
}
|
|
40
|
+
const expression = t.callExpression(factory, argumentsList)
|
|
41
|
+
return awaited ? t.awaitExpression(expression) : expression
|
|
42
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { hashText, inflateText } from './compiler-service.js'
|
|
2
|
+
import { moduleCompilerIntrinsics, moduleRuntimeIntrinsics as internal } from './compiler-intrinsics.js'
|
|
3
|
+
import { CALLABLE_SOURCE_FORMAT as FORMAT, CALLABLE_SOURCE_BLOCK_UNITS as BLOCK_UNITS } from './callable-source-format.js'
|
|
4
|
+
|
|
5
|
+
export { createCallableSourceCatalog } from './compiler-service.js'
|
|
6
|
+
|
|
7
|
+
const CACHED_BLOCKS = 8
|
|
8
|
+
const sourceKey = source => `${source.length}:${hashText(source, 'utf16le', 'base64')}`
|
|
9
|
+
|
|
10
|
+
function catalogKey(catalog) {
|
|
11
|
+
let text = ''
|
|
12
|
+
for (let index = 0; index < catalog.buffers.length; index++) {
|
|
13
|
+
const buffer = catalog.buffers[index]
|
|
14
|
+
text += `${buffer.length}:`
|
|
15
|
+
for (let block = 0; block < buffer.blocks.length; block++) {
|
|
16
|
+
text += `${buffer.blocks[block]};`
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
for (let index = 0; index < catalog.entries.length; index++) {
|
|
20
|
+
const entry = catalog.entries[index]
|
|
21
|
+
for (let item = 0; item < entry.length; item++) text += `${entry[item]}:`
|
|
22
|
+
text += ';'
|
|
23
|
+
}
|
|
24
|
+
return hashText(text)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function sameCatalog(left, right) {
|
|
28
|
+
if (left.buffers.length !== right.buffers.length || left.entries.length !== right.entries.length) return false
|
|
29
|
+
for (let index = 0; index < left.buffers.length; index++) {
|
|
30
|
+
const a = left.buffers[index], b = right.buffers[index]
|
|
31
|
+
if (a.length !== b.length || a.blocks.length !== b.blocks.length) return false
|
|
32
|
+
for (let block = 0; block < a.blocks.length; block++) if (a.blocks[block] !== b.blocks[block]) return false
|
|
33
|
+
}
|
|
34
|
+
for (let index = 0; index < left.entries.length; index++) {
|
|
35
|
+
const a = left.entries[index], b = right.entries[index]
|
|
36
|
+
if (a.length !== b.length) return false
|
|
37
|
+
for (let item = 0; item < a.length; item++) if (a[item] !== b[item]) return false
|
|
38
|
+
}
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Digests select candidates; only an exact native-source comparison owns reflection. */
|
|
43
|
+
export function createCallableSourceRegistry() {
|
|
44
|
+
const literals = new internal.Map()
|
|
45
|
+
const catalogs = new internal.Map()
|
|
46
|
+
const candidates = new internal.Map()
|
|
47
|
+
const decoded = new internal.Map()
|
|
48
|
+
let ordinal = 0
|
|
49
|
+
const blockText = encoded => {
|
|
50
|
+
let text = internal.mapGet(decoded, encoded)
|
|
51
|
+
if (text === undefined) {
|
|
52
|
+
text = inflateText(encoded, BLOCK_UNITS * 2)
|
|
53
|
+
internal.mapSet(decoded, encoded, text)
|
|
54
|
+
if (internal.mapSize(decoded) > CACHED_BLOCKS) internal.mapDelete(decoded, internal.mapFirstKey(decoded))
|
|
55
|
+
}
|
|
56
|
+
return text
|
|
57
|
+
}
|
|
58
|
+
const visitParts = (catalog, bufferIndex, start, end, visit) => {
|
|
59
|
+
const buffer = catalog.buffers[bufferIndex]
|
|
60
|
+
while (start < end) {
|
|
61
|
+
const index = internal.floor(start / BLOCK_UNITS)
|
|
62
|
+
const offset = start % BLOCK_UNITS
|
|
63
|
+
const length = internal.min(end - start, BLOCK_UNITS - offset)
|
|
64
|
+
if (visit(internal.sliceString(blockText(buffer.blocks[index]), offset, offset + length)) === false) return false
|
|
65
|
+
start += length
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const matches = (catalog, entry, native) => {
|
|
69
|
+
let offset = 0
|
|
70
|
+
const complete = visitParts(catalog, entry[0], entry[1], entry[2], text => {
|
|
71
|
+
if (!internal.startsWith(native, text, offset)) return false
|
|
72
|
+
offset += text.length
|
|
73
|
+
})
|
|
74
|
+
return complete !== false && offset === native.length
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
register(sources) {
|
|
78
|
+
const current = ++ordinal
|
|
79
|
+
if (internal.isArray(sources)) {
|
|
80
|
+
for (let index = 0; index < sources.length; index++) {
|
|
81
|
+
const entry = sources[index]
|
|
82
|
+
internal.mapSet(literals, entry[0], { original: entry[1], ordinal: current })
|
|
83
|
+
}
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
if (sources.format !== FORMAT) throw new moduleCompilerIntrinsics.TypeError('unsupported callable source catalog')
|
|
87
|
+
const key = catalogKey(sources)
|
|
88
|
+
let bucket = internal.mapGet(catalogs, key)
|
|
89
|
+
if (bucket === undefined) internal.mapSet(catalogs, key, bucket = [])
|
|
90
|
+
for (let index = 0; index < bucket.length; index++) {
|
|
91
|
+
const registration = bucket[index]
|
|
92
|
+
if (!sameCatalog(registration.catalog, sources)) continue
|
|
93
|
+
registration.ordinal = current
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
// Compiler transport values may be reused by their caller. Snapshot the
|
|
97
|
+
// compressed facts without retaining caller-owned mutable containers.
|
|
98
|
+
const catalog = { buffers: [], entries: [] }
|
|
99
|
+
for (let index = 0; index < sources.buffers.length; index++) {
|
|
100
|
+
const buffer = sources.buffers[index]
|
|
101
|
+
internal.appendArray(catalog.buffers, { length: buffer.length, blocks: internal.copyArray(buffer.blocks) })
|
|
102
|
+
}
|
|
103
|
+
for (let index = 0; index < sources.entries.length; index++) internal.appendArray(catalog.entries, internal.copyArray(sources.entries[index]))
|
|
104
|
+
const registration = { catalog, ordinal: current }
|
|
105
|
+
internal.appendArray(bucket, registration)
|
|
106
|
+
for (let index = 0; index < catalog.entries.length; index++) {
|
|
107
|
+
const entry = catalog.entries[index]
|
|
108
|
+
let entries = internal.mapGet(candidates, entry[6])
|
|
109
|
+
if (entries === undefined) internal.mapSet(candidates, entry[6], entries = [])
|
|
110
|
+
internal.appendArray(entries, { registration, entry })
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
get(native) {
|
|
114
|
+
let found = internal.mapGet(literals, native)
|
|
115
|
+
const entries = internal.mapGet(candidates, sourceKey(native))
|
|
116
|
+
if (entries !== undefined) for (let index = 0; index < entries.length; index++) {
|
|
117
|
+
const { registration, entry } = entries[index]
|
|
118
|
+
if (found !== undefined && found.ordinal > registration.ordinal) continue
|
|
119
|
+
if (matches(registration.catalog, entry, native)) found = { ...registration, entry }
|
|
120
|
+
}
|
|
121
|
+
if (found === undefined) return undefined
|
|
122
|
+
if (found.original !== undefined) return found.original
|
|
123
|
+
const entry = found.entry
|
|
124
|
+
let original = ''
|
|
125
|
+
visitParts(found.catalog, entry[3], entry[4], entry[5], text => { original += text })
|
|
126
|
+
return original
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { hashText, deflateText } from './compiler-text.js'
|
|
2
|
+
import { CALLABLE_SOURCE_FORMAT as FORMAT, CALLABLE_SOURCE_BLOCK_UNITS as BLOCK_UNITS } from './callable-source-format.js'
|
|
3
|
+
|
|
4
|
+
/** Keep windows around referenced callables. Neighbors share compression
|
|
5
|
+
* blocks; unrelated large data regions stay outside the reflection catalog. */
|
|
6
|
+
export function createCallableSourceCatalog(buffers, ranges) {
|
|
7
|
+
const selected = buffers.map(() => [])
|
|
8
|
+
const entries = ranges.map(range => [...range])
|
|
9
|
+
for (let index = 0; index < ranges.length; index++) {
|
|
10
|
+
const range = ranges[index]
|
|
11
|
+
for (const offset of [0, 3]) {
|
|
12
|
+
const source = buffers[range[offset]]
|
|
13
|
+
const start = range[offset + 1], end = range[offset + 2]
|
|
14
|
+
if (typeof source !== 'string' || !Number.isInteger(start) || !Number.isInteger(end)
|
|
15
|
+
|| start < 0 || end < start || end > source.length) throw new TypeError('invalid callable source range')
|
|
16
|
+
selected[range[offset]].push({ start, end, index, offset })
|
|
17
|
+
}
|
|
18
|
+
const source = buffers[range[0]].slice(range[1], range[2])
|
|
19
|
+
entries[index].push(`${source.length}:${hashText(source, 'utf16le', 'base64')}`)
|
|
20
|
+
}
|
|
21
|
+
const encoded = []
|
|
22
|
+
for (let index = 0; index < selected.length; index++) {
|
|
23
|
+
const intervals = []
|
|
24
|
+
for (const item of selected[index].sort((left, right) => left.start - right.start || right.end - left.end)) {
|
|
25
|
+
let interval = intervals.at(-1)
|
|
26
|
+
if (interval === undefined || item.start - interval.end > BLOCK_UNITS) {
|
|
27
|
+
interval = { start: item.start, end: item.end, items: [] }
|
|
28
|
+
intervals.push(interval)
|
|
29
|
+
}
|
|
30
|
+
interval.end = Math.max(interval.end, item.end)
|
|
31
|
+
interval.items.push(item)
|
|
32
|
+
}
|
|
33
|
+
for (const interval of intervals) {
|
|
34
|
+
const source = buffers[index].slice(interval.start, interval.end)
|
|
35
|
+
const blocks = []
|
|
36
|
+
for (let start = 0; start < source.length; start += BLOCK_UNITS) blocks.push(deflateText(source.slice(start, start + BLOCK_UNITS)))
|
|
37
|
+
for (const item of interval.items) {
|
|
38
|
+
const entry = entries[item.index]
|
|
39
|
+
entry[item.offset] = encoded.length
|
|
40
|
+
entry[item.offset + 1] -= interval.start
|
|
41
|
+
entry[item.offset + 2] -= interval.start
|
|
42
|
+
}
|
|
43
|
+
encoded.push({ length: source.length, blocks })
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { format: FORMAT, length: entries.length, buffers: encoded, entries }
|
|
47
|
+
}
|