semantifold 0.3.0 → 0.4.0
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.md +58 -15
- package/build/index.d.ts +48 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +57 -8
- package/build/src/artifact-path.d.ts +23 -1
- package/build/src/artifact-path.d.ts.map +1 -1
- package/build/src/artifact-path.js +45 -3
- package/build/src/artifacts.d.ts.map +1 -1
- package/build/src/artifacts.js +66 -1
- package/build/src/backends/c-validation.d.ts.map +1 -1
- package/build/src/backends/c-validation.js +7 -3
- package/build/src/backends/cpp-validation.d.ts.map +1 -1
- package/build/src/backends/cpp-validation.js +7 -3
- package/build/src/backends/csharp.js +7 -1
- package/build/src/backends/dart-runtime.d.ts +2 -0
- package/build/src/backends/dart-runtime.d.ts.map +1 -0
- package/build/src/backends/dart-runtime.js +41 -0
- package/build/src/backends/dart.d.ts +20 -0
- package/build/src/backends/dart.d.ts.map +1 -0
- package/build/src/backends/dart.js +321 -0
- package/build/src/backends/effects.d.ts +60 -0
- package/build/src/backends/effects.d.ts.map +1 -0
- package/build/src/backends/effects.js +746 -0
- package/build/src/backends/go.js +18 -4
- package/build/src/backends/identifiers.d.ts +36 -4
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +154 -10
- package/build/src/backends/index.d.ts.map +1 -1
- package/build/src/backends/index.js +27 -4
- package/build/src/backends/ios.d.ts +68 -0
- package/build/src/backends/ios.d.ts.map +1 -0
- package/build/src/backends/ios.js +1503 -0
- package/build/src/backends/java.d.ts.map +1 -1
- package/build/src/backends/java.js +429 -36
- package/build/src/backends/javascript.d.ts +6 -0
- package/build/src/backends/javascript.d.ts.map +1 -1
- package/build/src/backends/javascript.js +380 -24
- package/build/src/backends/kotlin-runtime.d.ts +3 -0
- package/build/src/backends/kotlin-runtime.d.ts.map +1 -0
- package/build/src/backends/kotlin-runtime.js +29 -0
- package/build/src/backends/kotlin.d.ts +23 -0
- package/build/src/backends/kotlin.d.ts.map +1 -0
- package/build/src/backends/kotlin.js +136 -0
- package/build/src/backends/native-validation.d.ts +2 -2
- package/build/src/backends/native-validation.d.ts.map +1 -1
- package/build/src/backends/native-validation.js +2 -2
- package/build/src/backends/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +39 -7
- package/build/src/backends/php-stdlib-support.d.ts +19 -0
- package/build/src/backends/php-stdlib-support.d.ts.map +1 -0
- package/build/src/backends/php-stdlib-support.js +227 -0
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +448 -33
- package/build/src/backends/program.d.ts +81 -0
- package/build/src/backends/program.d.ts.map +1 -0
- package/build/src/backends/program.js +1308 -0
- package/build/src/backends/python.js +7 -1
- package/build/src/backends/ruby.d.ts.map +1 -1
- package/build/src/backends/ruby.js +310 -24
- package/build/src/backends/rust-runtime.d.ts +7 -0
- package/build/src/backends/rust-runtime.d.ts.map +1 -0
- package/build/src/backends/rust-runtime.js +32 -0
- package/build/src/backends/rust-validation.d.ts +9 -0
- package/build/src/backends/rust-validation.d.ts.map +1 -0
- package/build/src/backends/rust-validation.js +78 -0
- package/build/src/backends/rust.d.ts +24 -0
- package/build/src/backends/rust.d.ts.map +1 -0
- package/build/src/backends/rust.js +344 -0
- package/build/src/backends/scalars.d.ts +14 -6
- package/build/src/backends/scalars.d.ts.map +1 -1
- package/build/src/backends/scalars.js +167 -17
- package/build/src/backends/shared.d.ts +23 -4
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +2158 -62
- package/build/src/backends/stdlib-support.d.ts +14 -0
- package/build/src/backends/stdlib-support.d.ts.map +1 -0
- package/build/src/backends/stdlib-support.js +237 -0
- package/build/src/backends/swift-runtime.d.ts +3 -0
- package/build/src/backends/swift-runtime.d.ts.map +1 -0
- package/build/src/backends/swift-runtime.js +14 -0
- package/build/src/backends/swift.d.ts +8 -0
- package/build/src/backends/swift.d.ts.map +1 -0
- package/build/src/backends/swift.js +127 -0
- package/build/src/backends/typescript.d.ts.map +1 -1
- package/build/src/backends/typescript.js +344 -24
- package/build/src/backends/wasm.d.ts +43 -0
- package/build/src/backends/wasm.d.ts.map +1 -0
- package/build/src/backends/wasm.js +2164 -0
- package/build/src/backends/writer.d.ts +235 -4
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +468 -3
- package/build/src/diagnostic.d.ts +4 -3
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +5 -3
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +5 -3
- package/build/src/frontends/cpp.d.ts.map +1 -1
- package/build/src/frontends/cpp.js +5 -3
- package/build/src/frontends/csharp.d.ts.map +1 -1
- package/build/src/frontends/csharp.js +7 -3
- package/build/src/frontends/dart.d.ts +10 -0
- package/build/src/frontends/dart.d.ts.map +1 -0
- package/build/src/frontends/dart.js +913 -0
- package/build/src/frontends/index.d.ts +3 -1
- package/build/src/frontends/index.d.ts.map +1 -1
- package/build/src/frontends/index.js +15 -5
- package/build/src/frontends/java.d.ts +110 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +1725 -70
- package/build/src/frontends/javascript-typescript.d.ts +69 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +1825 -93
- package/build/src/frontends/kotlin.d.ts +10 -0
- package/build/src/frontends/kotlin.d.ts.map +1 -0
- package/build/src/frontends/kotlin.js +685 -0
- package/build/src/frontends/php.d.ts +118 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +1583 -76
- package/build/src/frontends/program.d.ts +74 -0
- package/build/src/frontends/program.d.ts.map +1 -0
- package/build/src/frontends/program.js +989 -0
- package/build/src/frontends/ruby.d.ts +68 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +1446 -101
- package/build/src/frontends/rust-parser.d.ts +11 -0
- package/build/src/frontends/rust-parser.d.ts.map +1 -0
- package/build/src/frontends/rust-parser.js +15 -0
- package/build/src/frontends/rust.d.ts +14 -0
- package/build/src/frontends/rust.d.ts.map +1 -0
- package/build/src/frontends/rust.js +715 -0
- package/build/src/frontends/scalars.d.ts +10 -0
- package/build/src/frontends/scalars.d.ts.map +1 -1
- package/build/src/frontends/scalars.js +28 -2
- package/build/src/frontends/swift.d.ts +10 -0
- package/build/src/frontends/swift.d.ts.map +1 -0
- package/build/src/frontends/swift.js +860 -0
- package/build/src/frontends/types.d.ts +186 -0
- package/build/src/frontends/types.d.ts.map +1 -0
- package/build/src/frontends/types.js +634 -0
- package/build/src/language-registry.d.ts +46 -4
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +167 -13
- package/build/src/mapping.d.ts +2 -2
- package/build/src/mapping.d.ts.map +1 -1
- package/build/src/mapping.js +31 -10
- package/build/src/semantic/capabilities.d.ts +66 -0
- package/build/src/semantic/capabilities.d.ts.map +1 -0
- package/build/src/semantic/capabilities.js +649 -0
- package/build/src/semantic/effects.d.ts +26 -0
- package/build/src/semantic/effects.d.ts.map +1 -0
- package/build/src/semantic/effects.js +764 -0
- package/build/src/semantic/generics.d.ts +23 -0
- package/build/src/semantic/generics.d.ts.map +1 -0
- package/build/src/semantic/generics.js +61 -0
- package/build/src/semantic/lifetime.d.ts +37 -0
- package/build/src/semantic/lifetime.d.ts.map +1 -0
- package/build/src/semantic/lifetime.js +527 -0
- package/build/src/semantic/provenance.d.ts +9 -2
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +462 -20
- package/build/src/semantic/scalars.d.ts +11 -0
- package/build/src/semantic/scalars.d.ts.map +1 -1
- package/build/src/semantic/scalars.js +16 -1
- package/build/src/semantic/stdlib.d.ts +254 -0
- package/build/src/semantic/stdlib.d.ts.map +1 -0
- package/build/src/semantic/stdlib.js +880 -0
- package/build/src/semantic/types.d.ts +2952 -202
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +688 -15
- package/build/src/semantic/validate.d.ts +85 -5
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +2576 -98
- package/build/src/stdlib-facades.d.ts +304 -0
- package/build/src/stdlib-facades.d.ts.map +1 -0
- package/build/src/stdlib-facades.js +1167 -0
- package/build/src/stdlib-negotiation.d.ts +69 -0
- package/build/src/stdlib-negotiation.d.ts.map +1 -0
- package/build/src/stdlib-negotiation.js +403 -0
- package/build/src/stdlib-providers.d.ts +72 -0
- package/build/src/stdlib-providers.d.ts.map +1 -0
- package/build/src/stdlib-providers.js +339 -0
- package/build/src/toolchains.d.ts +55 -1
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +15 -4
- package/docs/architecture.md +45 -13
- package/docs/dart.md +60 -0
- package/docs/goals.md +3 -3
- package/docs/ios.md +121 -0
- package/docs/kotlin.md +42 -0
- package/docs/language-support.md +231 -26
- package/docs/multi-file-modules.md +52 -0
- package/docs/parser-qualification.md +121 -8
- package/docs/plans/2026-09-05-standard-library-portability.md +10 -10
- package/docs/rust.md +88 -0
- package/docs/source-maps.md +15 -7
- package/docs/standard-library-portability.md +39 -8
- package/docs/swift.md +44 -0
- package/docs/testing.md +366 -4
- package/docs/webassembly.md +52 -0
- package/index.js +63 -8
- package/node_modules/semantifold-tree-sitter-legacy-internal/README.md +2 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/LICENSE +21 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/README.md +45 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/binding.gyp +30 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding.cc +20 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding_test.js +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.d.ts +28 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.js +11 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/grammar.js +1630 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/package.json +64 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/highlights.scm +159 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/injections.scm +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/tags.scm +60 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/grammar.json +9405 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/node-types.json +5399 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/parser.c +192569 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/scanner.c +393 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/alloc.h +54 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/array.h +290 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/parser.h +266 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter-rust.wasm +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter.json +53 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/package.json +3 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/src/c.js +5 -4
- package/package.json +5 -2
- package/src/artifact-path.js +47 -2
- package/src/artifacts.js +67 -0
- package/src/backends/c-validation.js +6 -1
- package/src/backends/cpp-validation.js +6 -1
- package/src/backends/csharp.js +3 -0
- package/src/backends/dart-runtime.js +41 -0
- package/src/backends/dart.js +326 -0
- package/src/backends/effects.js +705 -0
- package/src/backends/go.js +14 -2
- package/src/backends/identifiers.js +160 -9
- package/src/backends/index.js +27 -2
- package/src/backends/ios.js +1612 -0
- package/src/backends/java.js +454 -36
- package/src/backends/javascript.js +389 -23
- package/src/backends/kotlin-runtime.js +29 -0
- package/src/backends/kotlin.js +141 -0
- package/src/backends/native-validation.js +1 -1
- package/src/backends/ordered-expressions.js +40 -6
- package/src/backends/php-stdlib-support.js +239 -0
- package/src/backends/php.js +469 -32
- package/src/backends/program.js +1407 -0
- package/src/backends/python.js +3 -0
- package/src/backends/ruby.js +326 -23
- package/src/backends/rust-runtime.js +34 -0
- package/src/backends/rust-validation.js +75 -0
- package/src/backends/rust.js +335 -0
- package/src/backends/scalars.js +141 -16
- package/src/backends/shared.js +2304 -124
- package/src/backends/stdlib-support.js +234 -0
- package/src/backends/swift-runtime.js +14 -0
- package/src/backends/swift.js +132 -0
- package/src/backends/typescript.js +349 -23
- package/src/backends/wasm.js +2208 -0
- package/src/backends/writer.js +520 -2
- package/src/diagnostic.js +4 -2
- package/src/frontends/c.js +4 -2
- package/src/frontends/cpp.js +4 -2
- package/src/frontends/csharp.js +4 -2
- package/src/frontends/dart.js +963 -0
- package/src/frontends/index.js +14 -4
- package/src/frontends/java.js +1977 -69
- package/src/frontends/javascript-typescript.js +2046 -102
- package/src/frontends/kotlin.js +713 -0
- package/src/frontends/php.js +1725 -79
- package/src/frontends/program.js +1062 -0
- package/src/frontends/ruby.js +1624 -98
- package/src/frontends/rust-parser.js +18 -0
- package/src/frontends/rust.js +714 -0
- package/src/frontends/scalars.js +31 -1
- package/src/frontends/swift.js +893 -0
- package/src/frontends/types.js +750 -0
- package/src/language-registry.js +172 -12
- package/src/mapping.js +33 -9
- package/src/semantic/capabilities.js +671 -0
- package/src/semantic/effects.js +737 -0
- package/src/semantic/generics.js +58 -0
- package/src/semantic/lifetime.js +517 -0
- package/src/semantic/provenance.js +412 -19
- package/src/semantic/scalars.js +17 -0
- package/src/semantic/stdlib.js +921 -0
- package/src/semantic/types.js +765 -14
- package/src/semantic/validate.js +2934 -209
- package/src/stdlib-facades.js +1211 -0
- package/src/stdlib-negotiation.js +446 -0
- package/src/stdlib-providers.js +377 -0
- package/src/toolchains.js +16 -3
|
@@ -0,0 +1,1407 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {isDenseArray} from "../array.js"
|
|
4
|
+
import {isSafeArtifactPath, isSafeSourcePath} from "../artifact-path.js"
|
|
5
|
+
import {createGeneratedArtifactSet} from "../artifacts.js"
|
|
6
|
+
import {SemantifoldDiagnostic, unsupportedCapability, unsupportedRole} from "../diagnostic.js"
|
|
7
|
+
import {parseProgramSource} from "../frontends/program.js"
|
|
8
|
+
import {languageRegistry} from "../language-registry.js"
|
|
9
|
+
import {finalizeMapping, toSourceMapV3} from "../mapping.js"
|
|
10
|
+
import {createCoordinateIndex, indexedPointAt, moduleLocation} from "../semantic/location.js"
|
|
11
|
+
import {semanticEntries} from "../semantic/provenance.js"
|
|
12
|
+
import {listStdlibModules, parseStdlibVersionRange} from "../semantic/stdlib.js"
|
|
13
|
+
import {moduleUncheckedErrorEffects} from "../semantic/validate.js"
|
|
14
|
+
import {negotiateStdlibProviders} from "../stdlib-negotiation.js"
|
|
15
|
+
import {stdlibFacadeProgramDescriptor, stdlibFacadeRegistry} from "../stdlib-facades.js"
|
|
16
|
+
import {stdlibProviderRegistry} from "../stdlib-providers.js"
|
|
17
|
+
import {preflightEffectCapabilities} from "./effects.js"
|
|
18
|
+
import {validateTargetBindingIdentifier, validateTargetIdentifier, validateTargetTypeIdentifier} from "./identifiers.js"
|
|
19
|
+
import {generateStdlibProviderContent} from "./stdlib-support.js"
|
|
20
|
+
import {validateBackendModule} from "./shared.js"
|
|
21
|
+
import {programImportName, SourceWriter} from "./writer.js"
|
|
22
|
+
|
|
23
|
+
/** @type {Readonly<Set<import("../semantic/types.js").SemanticLanguage>>} */
|
|
24
|
+
const programTargets = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
25
|
+
/** @type {Readonly<Set<import("../semantic/types.js").BackendLanguage>>} */
|
|
26
|
+
const programApplicationTargets = new Set(["ios"])
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validates and prepares a complete semantic program for a non-text backend without allocating a writer.
|
|
30
|
+
* @param {object} input - Backend preflight request.
|
|
31
|
+
* @param {import("../semantic/types.js").SemanticLanguage} input.backendLanguage - Existing semantic backend profile to enforce.
|
|
32
|
+
* @param {import("../semantic/types.js").BackendLanguage} input.diagnosticLanguage - Public target identity for capability failures.
|
|
33
|
+
* @param {import("../semantic/types.js").SemanticProgram} input.program - Complete candidate program.
|
|
34
|
+
* @param {Readonly<Set<import("../semantic/types.js").SemanticLanguage>>} [input.sourceLanguages] - Accepted source provenance languages.
|
|
35
|
+
* @returns {{modules: import("../semantic/types.js").SemanticModule[], program: import("../semantic/types.js").SemanticProgram, sources: {content: string, filename: string, language?: import("../semantic/types.js").SemanticLanguage}[]}} Prepared immutable-input views.
|
|
36
|
+
*/
|
|
37
|
+
export function preflightSemanticProgram({backendLanguage, diagnosticLanguage, program: candidate, sourceLanguages}) {
|
|
38
|
+
try {
|
|
39
|
+
const program = validateProgram(candidate, backendLanguage, sourceLanguages)
|
|
40
|
+
const modules = prepareEmissionModules(program, backendLanguage)
|
|
41
|
+
|
|
42
|
+
validateProgramSourceOwnership(program, backendLanguage)
|
|
43
|
+
const sources = program.sources.map((source) => {
|
|
44
|
+
if (source.content === null) invalidProgramGeneration(`Program source '${source.filename}' has no retained content.`)
|
|
45
|
+
|
|
46
|
+
return {content: source.content, filename: source.filename, ...(source.language ? {language: source.language} : {})}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
return {modules, program, sources}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (error instanceof SemantifoldDiagnostic && error.code == "UNSUPPORTED_CAPABILITY" &&
|
|
52
|
+
error.language != diagnosticLanguage) {
|
|
53
|
+
throw new SemantifoldDiagnostic({
|
|
54
|
+
cause: error,
|
|
55
|
+
code: error.code,
|
|
56
|
+
language: diagnosticLanguage,
|
|
57
|
+
location: error.location,
|
|
58
|
+
message: error.detail
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
throw error
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generates a complete deterministic mapped artifact set for one semantic program.
|
|
67
|
+
* @param {object} input - Program generation request.
|
|
68
|
+
* @param {import("../semantic/types.js").BackendLanguage} input.language - Registered program target.
|
|
69
|
+
* @param {import("../semantic/types.js").SemanticProgram} input.program - Complete semantic program.
|
|
70
|
+
* @param {"text" | "application"} [input.role] - Explicit artifact role; omitted preserves text generation.
|
|
71
|
+
* @param {import("../semantic/types.js").IosApplicationConfigurationInput} [input.configuration] - iOS application configuration.
|
|
72
|
+
* @param {import("../semantic/types.js").IosApplicationAssetInput[]} [input.assets] - Exact iOS caller assets.
|
|
73
|
+
* @returns {import("../semantic/types.js").GeneratedArtifactSet} Transactionally validated artifact set.
|
|
74
|
+
*/
|
|
75
|
+
export function generateProgramArtifacts(input) {
|
|
76
|
+
if (!isPlainObject(input) || typeof input.language != "string") invalidProgramGeneration("Program generation requires a target language and program.")
|
|
77
|
+
const role = input.role ?? "text"
|
|
78
|
+
|
|
79
|
+
if (role != "text" && role != "application") invalidProgramGeneration("Program artifact role must be 'text' or 'application'.")
|
|
80
|
+
languageRegistry.record(input.language)
|
|
81
|
+
if (role == "application") {
|
|
82
|
+
if (!programApplicationTargets.has(input.language)) unsupportedRole(input.language, "multi-file application backend")
|
|
83
|
+
const backend = languageRegistry.resolve(input.language, "applicationBackend")
|
|
84
|
+
|
|
85
|
+
return createGeneratedArtifactSet(backend(input))
|
|
86
|
+
}
|
|
87
|
+
if (!programTargets.has(/** @type {import("../semantic/types.js").SemanticLanguage} */ (input.language))) {
|
|
88
|
+
unsupportedRole(input.language, "multi-file text backend")
|
|
89
|
+
}
|
|
90
|
+
const language = /** @type {import("../semantic/types.js").SemanticLanguage} */ (input.language)
|
|
91
|
+
const program = validateProgram(input.program, language)
|
|
92
|
+
const linking = planStdlibLinking(program, language)
|
|
93
|
+
const emissionModules = prepareEmissionModules(program, language, linking)
|
|
94
|
+
validateProgramSourceOwnership(program, language)
|
|
95
|
+
const paths = planModulePaths(program, language, linking)
|
|
96
|
+
const sources = program.sources.map((source) => {
|
|
97
|
+
if (source.content === null) invalidProgramGeneration(`Program source '${source.filename}' has no retained content.`)
|
|
98
|
+
|
|
99
|
+
return {content: source.content, filename: source.filename, ...(source.language ? {language: source.language} : {})}
|
|
100
|
+
})
|
|
101
|
+
const backend = /** @type {typeof import("./javascript.js").generateJavaScript} */ (
|
|
102
|
+
languageRegistry.resolve(language, "textBackend"))
|
|
103
|
+
const mediaType = languageRegistry.record(language).mediaType
|
|
104
|
+
/** @type {import("../semantic/types.js").GeneratedSetArtifact[]} */
|
|
105
|
+
const artifacts = language == "javascript" || language == "typescript" ? [{
|
|
106
|
+
content: "{\n \"type\": \"module\"\n}\n",
|
|
107
|
+
contentKind: /** @type {const} */ ("text"),
|
|
108
|
+
mediaType: "application/json",
|
|
109
|
+
ownership: /** @type {const} */ ("generated"),
|
|
110
|
+
path: "package.json",
|
|
111
|
+
provenance: {
|
|
112
|
+
kind: /** @type {const} */ ("synthetic"),
|
|
113
|
+
reason: "Declares the generated JavaScript-family artifact set as canonical ESM.",
|
|
114
|
+
relatedOrigins: []
|
|
115
|
+
},
|
|
116
|
+
role: "manifest"
|
|
117
|
+
}] : []
|
|
118
|
+
|
|
119
|
+
if (linking && language != "java") {
|
|
120
|
+
for (const provider of linking.negotiation.providers) {
|
|
121
|
+
const record = /** @type {import("../stdlib-providers.js").StdlibProviderRecord} */ (linking.records.get(provider.module))
|
|
122
|
+
|
|
123
|
+
artifacts.push({
|
|
124
|
+
content: generateStdlibProviderContent(/** @type {"php" | "ruby" | "javascript" | "typescript"} */ (language),
|
|
125
|
+
[...provider.operations], provider.module),
|
|
126
|
+
contentKind: /** @type {const} */ ("text"),
|
|
127
|
+
mediaType: record.artifact.mediaType,
|
|
128
|
+
ownership: /** @type {const} */ ("generated"),
|
|
129
|
+
path: record.artifact.path,
|
|
130
|
+
provenance: {
|
|
131
|
+
kind: /** @type {const} */ ("synthetic"),
|
|
132
|
+
reason: `semantifold-stdlib-provider:${record.identity}`,
|
|
133
|
+
relatedOrigins: []
|
|
134
|
+
},
|
|
135
|
+
role: /** @type {const} */ ("support")
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (const emissionModule of emissionModules) {
|
|
141
|
+
const moduleId = /** @type {string} */ (Reflect.get(emissionModule, "id"))
|
|
142
|
+
const filename = /** @type {string} */ (paths.get(moduleId))
|
|
143
|
+
const writer = new SourceWriter({
|
|
144
|
+
filename,
|
|
145
|
+
language,
|
|
146
|
+
module: emissionModule,
|
|
147
|
+
program,
|
|
148
|
+
programPaths: paths,
|
|
149
|
+
sources,
|
|
150
|
+
...(linking && emissionModule.capabilities !== undefined ? {
|
|
151
|
+
stdlibProviderEntries: Object.fromEntries((linking.usedOperationsByModule.get(moduleId) ?? []).map(({module, operation}) => [
|
|
152
|
+
qualifiedOperationKey(module, operation),
|
|
153
|
+
/** @type {string} */ (linking.records.get(module)?.nativeEntries[operation])
|
|
154
|
+
])),
|
|
155
|
+
stdlibProviderImports: language == "java" || language == "javascript" || language == "typescript" ?
|
|
156
|
+
linking.providerImportsByModule.get(moduleId) : undefined,
|
|
157
|
+
stdlibProviderPaths: linking.providerPathsByModule.get(moduleId),
|
|
158
|
+
...(language == "java" ? {
|
|
159
|
+
stdlibProviderOwnerModule: /** @type {string} */ (linking.ownerModuleId),
|
|
160
|
+
stdlibProviderShims: moduleId == linking.ownerModuleId ? linking.usedOperations.map(({operation}) => operation) : undefined
|
|
161
|
+
} : {})
|
|
162
|
+
} : {})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
backend(emissionModule, writer)
|
|
166
|
+
const mapping = finalizeMapping(writer.finish())
|
|
167
|
+
|
|
168
|
+
artifacts.push({
|
|
169
|
+
content: mapping.generated.content,
|
|
170
|
+
contentKind: /** @type {const} */ ("text"),
|
|
171
|
+
mediaType: /** @type {string} */ (mediaType),
|
|
172
|
+
ownership: /** @type {const} */ ("generated"),
|
|
173
|
+
path: filename,
|
|
174
|
+
provenance: {
|
|
175
|
+
kind: /** @type {const} */ ("text"),
|
|
176
|
+
mapping,
|
|
177
|
+
sourceMap: toSourceMapV3(mapping),
|
|
178
|
+
sourceMapFilename: `${filename}.map`
|
|
179
|
+
},
|
|
180
|
+
role: /** @type {import("../semantic/types.js").GeneratedArtifactRole} */ (
|
|
181
|
+
moduleId == program.entryModule ? "entry" : Reflect.get(emissionModule, "stdlibFacade") ? "support" : "source")
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return createGeneratedArtifactSet({
|
|
186
|
+
artifacts,
|
|
187
|
+
...(linking || program.stdlibFacades ? {metadata: stdlibLinkMetadata(linking, paths, program.stdlibFacades)} : {}),
|
|
188
|
+
target: language
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @typedef StdlibProgramLinking
|
|
194
|
+
* @property {import("../stdlib-negotiation.js").StdlibNegotiationResult} negotiation - Negotiated provider closure.
|
|
195
|
+
* @property {Map<string, import("../stdlib-providers.js").StdlibProviderRecord>} records - Materialized provider records by canonical module.
|
|
196
|
+
* @property {QualifiedStdlibOperation[]} usedOperations - Program-wide used canonical operations in stable order.
|
|
197
|
+
* @property {Map<string, QualifiedStdlibOperation[]>} usedOperationsByModule - Used canonical operations by program module identity.
|
|
198
|
+
* @property {Map<string, string[]>} providerImportsByModule - Ordered provider import names by module identity.
|
|
199
|
+
* @property {Map<string, string[]>} providerPathsByModule - Provider artifact closure by program module identity.
|
|
200
|
+
* @property {string | null} ownerModuleId - Java target: first operation-using module identity hosting the shared support; null for artifact-materializing targets.
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @typedef QualifiedStdlibOperation
|
|
205
|
+
* @property {string} module - Canonical module identity.
|
|
206
|
+
* @property {string} operation - Canonical operation name.
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Plans the linked stdlib provider for one program, or reports that no operation is used.
|
|
211
|
+
* Java resolves the same negotiated provider record but hosts its support in the first
|
|
212
|
+
* operation-using module instead of a separate provider artifact.
|
|
213
|
+
* @param {import("../semantic/types.js").SemanticProgram} program - Validated semantic program.
|
|
214
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
215
|
+
* @returns {StdlibProgramLinking | null} Linking plan, or null when nothing is used.
|
|
216
|
+
*/
|
|
217
|
+
function planStdlibLinking(program, language) {
|
|
218
|
+
const {programWide, usedOperationsByModule} = collectUsedEffectOperations(program)
|
|
219
|
+
|
|
220
|
+
if (programWide.length == 0) return null
|
|
221
|
+
const contracts = program.stdlibContracts ?? [/** @type {{identity: string, contractVersion?: string}} */ (program.stdlibContract)]
|
|
222
|
+
const contractsByIdentity = new Map(contracts.map((contract) => [contract.identity, contract]))
|
|
223
|
+
const requirementsByModule = new Map()
|
|
224
|
+
|
|
225
|
+
for (const qualified of programWide) {
|
|
226
|
+
const requirement = requirementsByModule.get(qualified.module) ?? []
|
|
227
|
+
|
|
228
|
+
requirement.push(qualified.operation)
|
|
229
|
+
requirementsByModule.set(qualified.module, requirement)
|
|
230
|
+
}
|
|
231
|
+
const negotiation = negotiateStdlibProviders({
|
|
232
|
+
requirements: [...requirementsByModule].map(([module, operations]) => {
|
|
233
|
+
const contract = contractsByIdentity.get(module)
|
|
234
|
+
|
|
235
|
+
if (!contract) throw new SemantifoldDiagnostic({
|
|
236
|
+
code: "STDLIB_LINK_FAILURE",
|
|
237
|
+
language,
|
|
238
|
+
message: `Reachable canonical operation '${module}.${operations[0]}' has no declared standard-library contract.`
|
|
239
|
+
})
|
|
240
|
+
return {module, operations, ...(contract.contractVersion !== undefined ? {range: contract.contractVersion} : {})}
|
|
241
|
+
}),
|
|
242
|
+
target: language
|
|
243
|
+
})
|
|
244
|
+
/** @type {Map<string, import("../stdlib-providers.js").StdlibProviderRecord>} */
|
|
245
|
+
const records = new Map()
|
|
246
|
+
|
|
247
|
+
for (const selected of negotiation.modules) {
|
|
248
|
+
const candidates = stdlibProviderRegistry.providersFor(language, selected.identity, selected.version)
|
|
249
|
+
|
|
250
|
+
if (candidates.length != 1) {
|
|
251
|
+
throw new SemantifoldDiagnostic({
|
|
252
|
+
code: "STDLIB_LINK_FAILURE",
|
|
253
|
+
language,
|
|
254
|
+
message: `Expected exactly one registered stdlib provider for '${selected.identity}' on '${language}'.`
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
records.set(selected.identity, /** @type {import("../stdlib-providers.js").StdlibProviderRecord} */ (candidates[0]))
|
|
258
|
+
}
|
|
259
|
+
const ownerModuleId = language == "java"
|
|
260
|
+
? program.modules.map((module) => module.id).find((id) => (usedOperationsByModule.get(id) ?? []).length > 0)
|
|
261
|
+
: null
|
|
262
|
+
|
|
263
|
+
if (ownerModuleId === undefined) throw new SemantifoldDiagnostic({
|
|
264
|
+
code: "STDLIB_LINK_FAILURE",
|
|
265
|
+
language,
|
|
266
|
+
message: "Java program linking requires one module using canonical operations."
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
negotiation,
|
|
271
|
+
ownerModuleId,
|
|
272
|
+
providerImportsByModule: collectProviderImports(program, negotiation, records, usedOperationsByModule, language, ownerModuleId),
|
|
273
|
+
providerPathsByModule: collectProviderPaths(negotiation, records, usedOperationsByModule),
|
|
274
|
+
records,
|
|
275
|
+
usedOperations: [...programWide],
|
|
276
|
+
usedOperationsByModule
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Selects the exact provider/type dependency closure required by each emitting program module.
|
|
282
|
+
* @param {import("../stdlib-negotiation.js").StdlibNegotiationResult} negotiation - Negotiated program closure.
|
|
283
|
+
* @param {Map<string, import("../stdlib-providers.js").StdlibProviderRecord>} records - Selected provider records.
|
|
284
|
+
* @param {Map<string, QualifiedStdlibOperation[]>} usedOperationsByModule - Reachable operations by program module.
|
|
285
|
+
* @returns {Map<string, string[]>} Ordered provider paths by program module.
|
|
286
|
+
*/
|
|
287
|
+
function collectProviderPaths(negotiation, records, usedOperationsByModule) {
|
|
288
|
+
const paths = new Map()
|
|
289
|
+
|
|
290
|
+
for (const [moduleId, operations] of usedOperationsByModule) {
|
|
291
|
+
const selected = new Set(operations.map(({module}) => module))
|
|
292
|
+
let changed = true
|
|
293
|
+
|
|
294
|
+
while (changed) {
|
|
295
|
+
changed = false
|
|
296
|
+
for (const module of [...selected]) {
|
|
297
|
+
for (const dependency of records.get(module)?.dependencies ?? []) {
|
|
298
|
+
if (!selected.has(dependency.module)) {
|
|
299
|
+
selected.add(dependency.module)
|
|
300
|
+
changed = true
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
paths.set(moduleId, negotiation.providers.filter(({module}) => selected.has(module)).map(({module}) =>
|
|
306
|
+
/** @type {import("../stdlib-providers.js").StdlibProviderRecord} */ (records.get(module)).artifact.path))
|
|
307
|
+
}
|
|
308
|
+
return paths
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Collects the canonical operations invoked by each program module in stable order.
|
|
313
|
+
* @param {import("../semantic/types.js").SemanticProgram} program - Validated semantic program.
|
|
314
|
+
* @returns {{programWide: QualifiedStdlibOperation[], usedOperationsByModule: Map<string, QualifiedStdlibOperation[]>}} Used operations.
|
|
315
|
+
*/
|
|
316
|
+
function collectUsedEffectOperations(program) {
|
|
317
|
+
/** @type {Map<string, QualifiedStdlibOperation[]>} */
|
|
318
|
+
const usedOperationsByModule = new Map()
|
|
319
|
+
/** @type {Map<string, QualifiedStdlibOperation>} */
|
|
320
|
+
const programWide = new Map()
|
|
321
|
+
/** @type {Map<string, {body: unknown, module: import("../semantic/types.js").SemanticProgramModule}>} */
|
|
322
|
+
const bodies = new Map()
|
|
323
|
+
|
|
324
|
+
for (const module of program.modules) {
|
|
325
|
+
for (const declaration of module.functions) bodies.set(/** @type {string} */ (declaration.id), {body: declaration.body, module})
|
|
326
|
+
for (const declaration of module.classes ?? []) {
|
|
327
|
+
bodies.set(/** @type {string} */ (declaration.constructor.id), {body: declaration.constructor.body, module})
|
|
328
|
+
for (const method of declaration.methods) bodies.set(/** @type {string} */ (method.id), {body: method.body, module})
|
|
329
|
+
}
|
|
330
|
+
usedOperationsByModule.set(module.id, [])
|
|
331
|
+
}
|
|
332
|
+
const entryModule = /** @type {import("../semantic/types.js").SemanticProgramModule} */ (
|
|
333
|
+
program.modules.find(({id}) => id == program.entryModule))
|
|
334
|
+
/** @type {{body: unknown, module: import("../semantic/types.js").SemanticProgramModule}[]} */
|
|
335
|
+
const pending = [{body: entryModule.entryPoint?.body, module: entryModule}]
|
|
336
|
+
const reachedDeclarations = new Set()
|
|
337
|
+
|
|
338
|
+
while (pending.length > 0) {
|
|
339
|
+
const current = /** @type {{body: unknown, module: import("../semantic/types.js").SemanticProgramModule}} */ (pending.shift())
|
|
340
|
+
const operationNames = new Map(current.module.capabilities?.flatMap((capability) =>
|
|
341
|
+
capability.operations.map((operation) => [operation.id, {module: capability.authorityId, operation: operation.name}])) ?? [])
|
|
342
|
+
const seen = new Set()
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Visits one reachable semantic subtree.
|
|
346
|
+
* @param {unknown} value - Candidate semantic subtree.
|
|
347
|
+
*/
|
|
348
|
+
const visit = (value) => {
|
|
349
|
+
if (!value || typeof value != "object" || seen.has(value)) return
|
|
350
|
+
seen.add(value)
|
|
351
|
+
|
|
352
|
+
if (!Array.isArray(value)) {
|
|
353
|
+
const kind = Reflect.get(value, "kind")
|
|
354
|
+
|
|
355
|
+
if (kind == "EffectCallExpression") {
|
|
356
|
+
const operation = Reflect.get(value, "operation")
|
|
357
|
+
|
|
358
|
+
if (typeof operation == "string") {
|
|
359
|
+
const qualified = operationNames.get(operation)
|
|
360
|
+
|
|
361
|
+
if (qualified !== undefined) {
|
|
362
|
+
const key = qualifiedOperationKey(qualified.module, qualified.operation)
|
|
363
|
+
const moduleOperations = /** @type {QualifiedStdlibOperation[]} */ (usedOperationsByModule.get(current.module.id))
|
|
364
|
+
|
|
365
|
+
if (!moduleOperations.some((item) => qualifiedOperationKey(item.module, item.operation) == key)) moduleOperations.push(qualified)
|
|
366
|
+
programWide.set(key, qualified)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const declarationId = isPlainObject(Reflect.get(value, "resolution"))
|
|
371
|
+
? Reflect.get(Reflect.get(value, "resolution"), "declarationId") : undefined
|
|
372
|
+
|
|
373
|
+
if (typeof declarationId == "string" && !reachedDeclarations.has(declarationId)) {
|
|
374
|
+
const declaration = bodies.get(declarationId)
|
|
375
|
+
|
|
376
|
+
if (declaration) {
|
|
377
|
+
reachedDeclarations.add(declarationId)
|
|
378
|
+
pending.push(declaration)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
for (const [key, child] of Object.entries(value)) {
|
|
382
|
+
if (["capabilities", "location", "provenance", "resolution", "sourceProvenance"].includes(key)) continue
|
|
383
|
+
visit(child)
|
|
384
|
+
}
|
|
385
|
+
} else for (const child of value) visit(child)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
visit(current.body)
|
|
389
|
+
}
|
|
390
|
+
for (const operations of usedOperationsByModule.values()) {
|
|
391
|
+
operations.sort((left, right) => qualifiedOperationKey(left.module, left.operation)
|
|
392
|
+
.localeCompare(qualifiedOperationKey(right.module, right.operation), "en"))
|
|
393
|
+
}
|
|
394
|
+
return {programWide: [...programWide.values()].sort((left, right) => qualifiedOperationKey(left.module, left.operation)
|
|
395
|
+
.localeCompare(qualifiedOperationKey(right.module, right.operation), "en")), usedOperationsByModule}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Builds an unambiguous internal key for one canonical operation authority.
|
|
400
|
+
* @param {string} module - Canonical module identity.
|
|
401
|
+
* @param {string} operation - Canonical operation name.
|
|
402
|
+
* @returns {string} Qualified internal key.
|
|
403
|
+
*/
|
|
404
|
+
function qualifiedOperationKey(module, operation) {
|
|
405
|
+
return `${module}\u0000${operation}`
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Collects the ordered provider import names each module must pull from the linked provider.
|
|
410
|
+
* Java modules list referenced capability type names only; the owner lists none.
|
|
411
|
+
* @param {import("../semantic/types.js").SemanticProgram} program - Validated semantic program.
|
|
412
|
+
* @param {import("../stdlib-negotiation.js").StdlibNegotiationResult} negotiation - Negotiated closure.
|
|
413
|
+
* @param {Map<string, import("../stdlib-providers.js").StdlibProviderRecord>} records - Provider records by canonical module.
|
|
414
|
+
* @param {Map<string, QualifiedStdlibOperation[]>} usedOperationsByModule - Used canonical operations by module identity.
|
|
415
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
416
|
+
* @param {string | null} [ownerModuleId] - Java owner module identity.
|
|
417
|
+
* @returns {Map<string, string[]>} Ordered provider import names by module identity.
|
|
418
|
+
*/
|
|
419
|
+
function collectProviderImports(program, negotiation, records, usedOperationsByModule, language, ownerModuleId = null) {
|
|
420
|
+
/** @type {Map<string, string[]>} */
|
|
421
|
+
const providerImportsByModule = new Map()
|
|
422
|
+
|
|
423
|
+
for (const module of program.modules) {
|
|
424
|
+
if (module.capabilities === undefined) continue
|
|
425
|
+
const used = new Set((usedOperationsByModule.get(module.id) ?? []).map(({module: owner, operation}) =>
|
|
426
|
+
qualifiedOperationKey(owner, operation)))
|
|
427
|
+
/** @type {Set<string>} */
|
|
428
|
+
const surface = new Set()
|
|
429
|
+
/** @type {Map<string, string>} */
|
|
430
|
+
const resourceNameById = new Map()
|
|
431
|
+
|
|
432
|
+
/** @type {Map<string, string>} */
|
|
433
|
+
const declarationNames = new Map((module.records ?? []).map((declaration) => [/** @type {string} */ (declaration.id), declaration.name]))
|
|
434
|
+
|
|
435
|
+
for (const declaration of module.errors ?? []) {
|
|
436
|
+
declarationNames.set(/** @type {string} */ (declaration.id), declaration.name)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
for (const capability of module.capabilities) {
|
|
440
|
+
for (const resource of capability.resources) {
|
|
441
|
+
surface.add(resource.name)
|
|
442
|
+
resourceNameById.set(resource.id, resource.name)
|
|
443
|
+
}
|
|
444
|
+
for (const failure of capability.failures) {
|
|
445
|
+
surface.add(failure.name)
|
|
446
|
+
declarationNames.set(failure.id, failure.name)
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
const referenced = new Set()
|
|
450
|
+
const seen = new Set()
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Visits one semantic value.
|
|
454
|
+
* @param {unknown} value - Candidate semantic subtree.
|
|
455
|
+
* @returns {void}
|
|
456
|
+
*/
|
|
457
|
+
const visit = (value) => {
|
|
458
|
+
if (!value || typeof value != "object" || seen.has(value)) return
|
|
459
|
+
seen.add(value)
|
|
460
|
+
|
|
461
|
+
if (!Array.isArray(value)) {
|
|
462
|
+
const kind = Reflect.get(value, "kind")
|
|
463
|
+
|
|
464
|
+
if (kind == "OwnedResourceType") {
|
|
465
|
+
const resourceId = Reflect.get(value, "resourceId")
|
|
466
|
+
|
|
467
|
+
if (typeof resourceId == "string") {
|
|
468
|
+
const name = resourceNameById.get(resourceId)
|
|
469
|
+
|
|
470
|
+
if (name !== undefined) referenced.add(name)
|
|
471
|
+
}
|
|
472
|
+
} else if (kind == "ReferenceType" || kind == "OwnedReferenceType" || kind == "ErrorType") {
|
|
473
|
+
const declarationId = Reflect.get(value, "declarationId")
|
|
474
|
+
|
|
475
|
+
if (typeof declarationId == "string") {
|
|
476
|
+
const name = declarationNames.get(declarationId)
|
|
477
|
+
|
|
478
|
+
if (name !== undefined && surface.has(name)) referenced.add(name)
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
for (const [key, child] of Object.entries(value)) {
|
|
482
|
+
if (key == "capabilities" || key == "location" || key == "sourceProvenance") continue
|
|
483
|
+
visit(child)
|
|
484
|
+
}
|
|
485
|
+
} else for (const child of value) visit(child)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
visit(module)
|
|
489
|
+
const names = []
|
|
490
|
+
|
|
491
|
+
if (language != "java" || module.id != ownerModuleId) {
|
|
492
|
+
for (const capability of module.capabilities) {
|
|
493
|
+
for (const resource of capability.resources) if (referenced.has(resource.name)) names.push(resource.name)
|
|
494
|
+
for (const failure of capability.failures) if (referenced.has(failure.name)) names.push(failure.name)
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (language != "java") {
|
|
498
|
+
for (const provider of negotiation.providers) {
|
|
499
|
+
const record = records.get(provider.module)
|
|
500
|
+
|
|
501
|
+
for (const operation of provider.operations) {
|
|
502
|
+
if (used.has(qualifiedOperationKey(provider.module, operation))) {
|
|
503
|
+
names.push(/** @type {string} */ (record?.nativeEntries[operation]))
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
providerImportsByModule.set(module.id, names)
|
|
510
|
+
}
|
|
511
|
+
return providerImportsByModule
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Builds the truthful generated stdlib link metadata for one negotiated closure.
|
|
516
|
+
* Java reports the owner module file as the provider carrier instead of a separate artifact.
|
|
517
|
+
* @param {StdlibProgramLinking | null} linking - Linking plan.
|
|
518
|
+
* @param {Map<string, string>} paths - Planned artifact path by module identity.
|
|
519
|
+
* @param {import("../semantic/types.js").StdlibFacadeProgramDescriptor | undefined} facades - Selected source facade closure.
|
|
520
|
+
* @returns {Record<string, unknown>} Plain JSON metadata.
|
|
521
|
+
*/
|
|
522
|
+
function stdlibLinkMetadata(linking, paths, facades) {
|
|
523
|
+
return {
|
|
524
|
+
...(facades ? {facades} : {}),
|
|
525
|
+
modules: (linking?.negotiation.modules ?? []).map((item) => ({
|
|
526
|
+
identity: item.identity,
|
|
527
|
+
operations: [...item.operations],
|
|
528
|
+
version: item.version
|
|
529
|
+
})),
|
|
530
|
+
providers: (linking?.negotiation.providers ?? []).map((item) => {
|
|
531
|
+
const record = linking?.records.get(item.module)
|
|
532
|
+
const artifactPath = linking?.ownerModuleId !== null && linking?.ownerModuleId !== undefined
|
|
533
|
+
? /** @type {string} */ (paths.get(linking.ownerModuleId))
|
|
534
|
+
: record?.artifact.path
|
|
535
|
+
|
|
536
|
+
return {
|
|
537
|
+
artifact: {mediaType: record?.artifact.mediaType, path: artifactPath},
|
|
538
|
+
dependencies: (record?.dependencies ?? []).map((dependency) => ({module: dependency.module, range: dependency.range})),
|
|
539
|
+
identity: item.identity,
|
|
540
|
+
module: item.module,
|
|
541
|
+
nativeEntries: {...item.nativeEntries},
|
|
542
|
+
operations: [...item.operations],
|
|
543
|
+
target: item.target,
|
|
544
|
+
version: item.version
|
|
545
|
+
}
|
|
546
|
+
}),
|
|
547
|
+
schema: "SemantifoldStdlibLink",
|
|
548
|
+
version: 1
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Validates every mandatory semantic location against its owning module source before writer allocation.
|
|
554
|
+
* @param {import("../semantic/types.js").SemanticProgram} program - Structurally and semantically validated program.
|
|
555
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
556
|
+
* @returns {void}
|
|
557
|
+
*/
|
|
558
|
+
function validateProgramSourceOwnership(program, language) {
|
|
559
|
+
const sources = new Map(program.sources.map((source) => [source.filename, {
|
|
560
|
+
...source,
|
|
561
|
+
coordinates: createCoordinateIndex(/** @type {string} */ (source.content))
|
|
562
|
+
}]))
|
|
563
|
+
|
|
564
|
+
for (const module of program.modules) {
|
|
565
|
+
const source = sources.get(module.sourceFilename)
|
|
566
|
+
|
|
567
|
+
if (!source) unsupportedCapability(language, "semantic program module source ownership", undefined)
|
|
568
|
+
const fallback = moduleLocation(source.filename, /** @type {string} */ (source.content))
|
|
569
|
+
|
|
570
|
+
for (const entry of semanticEntries(module)) {
|
|
571
|
+
const subject = entry.node.kind == "Module" ? "module" : entry.node.kind == "Import" ? "import" :
|
|
572
|
+
entry.node.kind == "Export" ? "export" : "semantic node"
|
|
573
|
+
|
|
574
|
+
if (!("location" in entry.node)) {
|
|
575
|
+
if (subject != "semantic node") unsupportedCapability(language, `semantic program ${subject} location`, fallback)
|
|
576
|
+
continue
|
|
577
|
+
}
|
|
578
|
+
const location = entry.node.location
|
|
579
|
+
|
|
580
|
+
if (sourceOwnsLocation(source, location)) continue
|
|
581
|
+
|
|
582
|
+
unsupportedCapability(language, `semantic program ${subject} location source ownership`, fallback)
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Checks a normalized UTF-16 location against one exact registered source without throwing.
|
|
589
|
+
* @param {{filename: string, content: string | null, coordinates: ReturnType<typeof createCoordinateIndex>}} source - Owning source.
|
|
590
|
+
* @param {unknown} candidate - Candidate location.
|
|
591
|
+
* @returns {boolean} Whether the source owns the location.
|
|
592
|
+
*/
|
|
593
|
+
function sourceOwnsLocation(source, candidate) {
|
|
594
|
+
if (!isPlainObject(candidate) || candidate.filename != source.filename || !isPlainObject(candidate.start) ||
|
|
595
|
+
!isPlainObject(candidate.end) || !Number.isInteger(candidate.start.offset) || !Number.isInteger(candidate.end.offset) ||
|
|
596
|
+
!Number.isInteger(candidate.start.line) || !Number.isInteger(candidate.start.column) ||
|
|
597
|
+
!Number.isInteger(candidate.end.line) || !Number.isInteger(candidate.end.column)) return false
|
|
598
|
+
const startOffset = /** @type {number} */ (candidate.start.offset)
|
|
599
|
+
const endOffset = /** @type {number} */ (candidate.end.offset)
|
|
600
|
+
|
|
601
|
+
if (startOffset < 0 || endOffset < startOffset || endOffset > source.coordinates.source.length) return false
|
|
602
|
+
const expectedStart = indexedPointAt(source.coordinates, startOffset)
|
|
603
|
+
const expectedEnd = indexedPointAt(source.coordinates, endOffset)
|
|
604
|
+
|
|
605
|
+
return samePoint(candidate.start, expectedStart) && samePoint(candidate.end, expectedEnd)
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Compares an untrusted point with one canonical indexed point.
|
|
610
|
+
* @param {Record<string, unknown>} candidate - Candidate point.
|
|
611
|
+
* @param {import("../semantic/types.js").SourcePoint} expected - Canonical point.
|
|
612
|
+
* @returns {boolean} Whether every coordinate agrees.
|
|
613
|
+
*/
|
|
614
|
+
function samePoint(candidate, expected) {
|
|
615
|
+
return candidate.offset == expected.offset && candidate.line == expected.line && candidate.column == expected.column
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Collects nominal failure identities referenced by executable/type nodes outside the module's authority declarations.
|
|
620
|
+
* @param {import("../semantic/types.js").SemanticProgramModule} module - Program module.
|
|
621
|
+
* @returns {Set<string>} Referenced failure declaration identities.
|
|
622
|
+
*/
|
|
623
|
+
function collectReferencedFailureIds(module) {
|
|
624
|
+
/** @type {Set<string>} */
|
|
625
|
+
const ids = new Set()
|
|
626
|
+
const seen = new Set()
|
|
627
|
+
/**
|
|
628
|
+
* Visits one semantic subtree for referenced failures.
|
|
629
|
+
* @param {unknown} value - Candidate semantic subtree.
|
|
630
|
+
*/
|
|
631
|
+
const visit = (value) => {
|
|
632
|
+
if (!value || typeof value != "object" || seen.has(value)) return
|
|
633
|
+
seen.add(value)
|
|
634
|
+
if (!Array.isArray(value)) {
|
|
635
|
+
if (Reflect.get(value, "kind") == "ErrorType" && typeof Reflect.get(value, "declarationId") == "string") {
|
|
636
|
+
ids.add(Reflect.get(value, "declarationId"))
|
|
637
|
+
}
|
|
638
|
+
const failureIds = Reflect.get(value, "failureIds")
|
|
639
|
+
|
|
640
|
+
if (Array.isArray(failureIds)) for (const id of failureIds) if (typeof id == "string") ids.add(id)
|
|
641
|
+
for (const [key, child] of Object.entries(value)) {
|
|
642
|
+
if (["capabilities", "location", "provenance", "sourceProvenance"].includes(key)) continue
|
|
643
|
+
visit(child)
|
|
644
|
+
}
|
|
645
|
+
} else for (const child of value) visit(child)
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
visit({classes: module.classes, entryPoint: module.entryPoint, functions: module.functions})
|
|
649
|
+
return ids
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Creates and validates every backend-shaped module before any writer is allocated.
|
|
654
|
+
* @param {import("../semantic/types.js").SemanticProgram} program - Validated semantic program.
|
|
655
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
656
|
+
* @param {StdlibProgramLinking | null} [linking] - Planned stdlib provider link.
|
|
657
|
+
* @returns {import("../semantic/types.js").SemanticModule[]} Emission views in dependency order.
|
|
658
|
+
*/
|
|
659
|
+
function prepareEmissionModules(program, language, linking = null) {
|
|
660
|
+
/** @type {Map<string, import("../semantic/types.js").FunctionDeclaration | import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").ErrorDeclaration>} */
|
|
661
|
+
const declarations = new Map()
|
|
662
|
+
|
|
663
|
+
for (const module of program.modules) {
|
|
664
|
+
for (const declaration of module.records ?? []) declarations.set(/** @type {string} */ (declaration.id), declaration)
|
|
665
|
+
for (const declaration of module.classes ?? []) declarations.set(/** @type {string} */ (declaration.id), declaration)
|
|
666
|
+
for (const declaration of module.errors ?? []) declarations.set(/** @type {string} */ (declaration.id), declaration)
|
|
667
|
+
for (const declaration of module.functions) declarations.set(/** @type {string} */ (declaration.id), declaration)
|
|
668
|
+
}
|
|
669
|
+
const modules = program.modules.map((module) => /** @type {import("../semantic/types.js").SemanticModule} */ ({
|
|
670
|
+
...module,
|
|
671
|
+
entryPoint: module.entryPoint ?? {
|
|
672
|
+
body: {kind: /** @type {const} */ ("Block"), location: module.location, statements: []},
|
|
673
|
+
kind: /** @type {const} */ ("EntryPoint"),
|
|
674
|
+
location: module.location
|
|
675
|
+
}
|
|
676
|
+
}))
|
|
677
|
+
/** @type {Map<string, Set<string>>} */
|
|
678
|
+
const callEffectsByDeclarationId = new Map()
|
|
679
|
+
|
|
680
|
+
for (const module of modules) {
|
|
681
|
+
const programModule = /** @type {import("../semantic/types.js").SemanticProgramModule} */ (/** @type {unknown} */ (module))
|
|
682
|
+
const visibleFunctions = new Map()
|
|
683
|
+
const visibleCallEffects = new Map()
|
|
684
|
+
const visibleRecords = new Map()
|
|
685
|
+
const visibleClasses = new Map()
|
|
686
|
+
const visibleErrors = new Map()
|
|
687
|
+
const externalDeclarationIds = new Set()
|
|
688
|
+
|
|
689
|
+
const neededFailureIds = collectReferencedFailureIds(programModule)
|
|
690
|
+
const localFailureIds = new Set(programModule.capabilities?.flatMap(({failures}) => failures.map(({id}) => id)) ?? [])
|
|
691
|
+
|
|
692
|
+
for (const owner of program.modules) {
|
|
693
|
+
if (owner.id == programModule.id) continue
|
|
694
|
+
for (const capability of owner.capabilities ?? []) {
|
|
695
|
+
for (const failure of capability.failures) {
|
|
696
|
+
if (!localFailureIds.has(failure.id) && neededFailureIds.has(failure.id)) visibleErrors.set(failure.id, failure)
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
for (const imported of programModule.imports) {
|
|
702
|
+
const declaration = declarations.get(imported.declarationId)
|
|
703
|
+
|
|
704
|
+
if (!declaration) unsupportedCapability(language, "program import without a declaration", imported.location)
|
|
705
|
+
externalDeclarationIds.add(imported.declarationId)
|
|
706
|
+
if (imported.symbolKind == "function") {
|
|
707
|
+
visibleFunctions.set(imported.localName, /** @type {import("../semantic/types.js").FunctionDeclaration} */ (declaration))
|
|
708
|
+
visibleCallEffects.set(imported.localName, new Set(callEffectsByDeclarationId.get(imported.declarationId) ?? []))
|
|
709
|
+
} else if (imported.symbolKind == "record") {
|
|
710
|
+
visibleRecords.set(imported.declarationId, /** @type {import("../semantic/types.js").RecordDeclaration} */ (declaration))
|
|
711
|
+
} else if (imported.symbolKind == "class") {
|
|
712
|
+
visibleClasses.set(imported.declarationId, /** @type {import("../semantic/types.js").ClassDeclaration} */ (declaration))
|
|
713
|
+
} else {
|
|
714
|
+
visibleErrors.set(imported.declarationId, /** @type {import("../semantic/types.js").ErrorDeclaration} */ (declaration))
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (module.capabilities !== undefined || !linking) {
|
|
718
|
+
preflightEffectCapabilities(module, language, linking ? {usedOperations: (linking.usedOperationsByModule.get(programModule.id) ?? [])
|
|
719
|
+
.map(({operation}) => operation)} : {})
|
|
720
|
+
}
|
|
721
|
+
validateBackendModule(module, language, {
|
|
722
|
+
externalDeclarationIds,
|
|
723
|
+
program: true,
|
|
724
|
+
visibleCallEffects,
|
|
725
|
+
visibleClasses,
|
|
726
|
+
visibleErrors,
|
|
727
|
+
visibleFunctions,
|
|
728
|
+
visibleRecords
|
|
729
|
+
})
|
|
730
|
+
const localCallEffects = moduleUncheckedErrorEffects(module, {callEffects: visibleCallEffects, functions: visibleFunctions})
|
|
731
|
+
|
|
732
|
+
for (const declaration of module.functions) {
|
|
733
|
+
callEffectsByDeclarationId.set(/** @type {string} */ (declaration.id), new Set(localCallEffects.get(declaration.name) ?? []))
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return modules
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Validates the closed parser-neutral program graph before any target emission.
|
|
742
|
+
* @param {unknown} candidate - Candidate semantic program.
|
|
743
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
744
|
+
* @param {Readonly<Set<import("../semantic/types.js").SemanticLanguage>>} [sourceLanguages] - Accepted source languages.
|
|
745
|
+
* @returns {import("../semantic/types.js").SemanticProgram} Validated program.
|
|
746
|
+
*/
|
|
747
|
+
function validateProgram(candidate, language, sourceLanguages = programTargets) {
|
|
748
|
+
if (!isPlainObject(candidate) || candidate.kind != "Program" || typeof candidate.entryModule != "string" ||
|
|
749
|
+
!isDenseArray(candidate.modules) || candidate.modules.length == 0 || !isDenseArray(candidate.sources) || candidate.sources.length == 0) {
|
|
750
|
+
invalidProgramGeneration("Malformed semantic program.")
|
|
751
|
+
}
|
|
752
|
+
const program = /** @type {import("../semantic/types.js").SemanticProgram} */ (candidate)
|
|
753
|
+
const modules = new Map()
|
|
754
|
+
/** @type {Map<string, {declaration: import("../semantic/types.js").FunctionDeclaration | import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").ErrorDeclaration, module: import("../semantic/types.js").SemanticProgramModule}>} */
|
|
755
|
+
const declarations = new Map()
|
|
756
|
+
/** @type {import("../semantic/types.js").SemanticProgramModule[]} */
|
|
757
|
+
const capabilityModules = []
|
|
758
|
+
let entryCount = 0
|
|
759
|
+
const sourceIds = new Set()
|
|
760
|
+
const sourceFilenames = new Set()
|
|
761
|
+
|
|
762
|
+
for (const source of program.sources) {
|
|
763
|
+
if (!isPlainObject(source) || typeof source.id != "string" || sourceIds.has(source.id) ||
|
|
764
|
+
typeof source.filename != "string" || !isSafeSourcePath(source.filename) || sourceFilenames.has(source.filename) ||
|
|
765
|
+
typeof source.content != "string" || typeof source.language != "string" || !sourceLanguages.has(source.language)) {
|
|
766
|
+
invalidProgramGeneration("Malformed or duplicate semantic program source registry.")
|
|
767
|
+
}
|
|
768
|
+
sourceIds.add(source.id)
|
|
769
|
+
sourceFilenames.add(source.filename)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
for (const module of program.modules) {
|
|
773
|
+
if (!isPlainObject(module) || module.kind != "Module" || typeof module.id != "string" ||
|
|
774
|
+
!/^[a-z][a-z0-9_]*(?:[.-][a-z][a-z0-9_]*)*$/u.test(module.id) || modules.has(module.id) ||
|
|
775
|
+
!isDenseArray(module.functions) || module.records !== undefined && !isDenseArray(module.records) ||
|
|
776
|
+
module.errors !== undefined && !isDenseArray(module.errors) ||
|
|
777
|
+
!isDenseArray(module.imports) || !isDenseArray(module.exports)) {
|
|
778
|
+
unsupportedCapability(language, "malformed or duplicate semantic program module", module?.location)
|
|
779
|
+
}
|
|
780
|
+
if (Reflect.get(module, "classes") !== undefined && (!isDenseArray(module.classes) ||
|
|
781
|
+
Reflect.get(module, "stdlibFacade") === undefined)) {
|
|
782
|
+
unsupportedCapability(language, "Task 033 reference classes are not supported by semantic program generation",
|
|
783
|
+
Reflect.get(module, "classes")?.[0]?.location ?? module.location)
|
|
784
|
+
}
|
|
785
|
+
if (Reflect.get(module, "capabilities") !== undefined) {
|
|
786
|
+
if (!isDenseArray(module.capabilities) || module.capabilities.length == 0) {
|
|
787
|
+
unsupportedCapability(language, "malformed or empty Task 034 capability graph", module.location)
|
|
788
|
+
}
|
|
789
|
+
capabilityModules.push(module)
|
|
790
|
+
}
|
|
791
|
+
if (typeof module.sourceFilename != "string" || !sourceFilenames.has(module.sourceFilename)) {
|
|
792
|
+
unsupportedCapability(language, "semantic program module source identity", module.location)
|
|
793
|
+
}
|
|
794
|
+
if (module.entryPoint) {
|
|
795
|
+
entryCount++
|
|
796
|
+
if (module.id != program.entryModule) unsupportedCapability(language, "entry point on an unselected module", module.entryPoint.location)
|
|
797
|
+
}
|
|
798
|
+
for (const declaration of [...module.records ?? [], ...module.classes ?? [], ...module.errors ?? [], ...module.functions]) {
|
|
799
|
+
if (!isPlainObject(declaration) || typeof declaration.id != "string" ||
|
|
800
|
+
!declaration.id.startsWith(`${module.id}#`) || declarations.has(declaration.id)) {
|
|
801
|
+
unsupportedCapability(language, "duplicate or unstable program declaration identity", declaration?.location ?? module.location)
|
|
802
|
+
}
|
|
803
|
+
declarations.set(declaration.id, {declaration, module})
|
|
804
|
+
}
|
|
805
|
+
for (const imported of module.imports) {
|
|
806
|
+
if (!isPlainObject(imported) || imported.kind != "Import" || typeof imported.moduleId != "string" ||
|
|
807
|
+
typeof imported.importedName != "string" || typeof imported.localName != "string" ||
|
|
808
|
+
typeof imported.declarationId != "string" || !["function", "record", "class", "error"].includes(imported.symbolKind) ||
|
|
809
|
+
typeof imported.typeOnly != "boolean") {
|
|
810
|
+
unsupportedCapability(language, "malformed semantic program import", imported?.location ?? module.location)
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
for (const exported of module.exports) {
|
|
814
|
+
if (!isPlainObject(exported) || exported.kind != "Export" || typeof exported.exportedName != "string" ||
|
|
815
|
+
typeof exported.declarationId != "string" || !["function", "record", "class", "error"].includes(exported.symbolKind)) {
|
|
816
|
+
unsupportedCapability(language, "malformed semantic program export", exported?.location ?? module.location)
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
modules.set(module.id, module)
|
|
820
|
+
}
|
|
821
|
+
if (entryCount != 1 || !modules.get(program.entryModule)?.entryPoint) {
|
|
822
|
+
unsupportedCapability(language, "program without exactly one selected entry point", modules.get(program.entryModule)?.location)
|
|
823
|
+
}
|
|
824
|
+
const selectedModule = /** @type {import("../semantic/types.js").SemanticProgramModule} */ (modules.get(program.entryModule))
|
|
825
|
+
const selectedEntry = selectedModule.entryPoint
|
|
826
|
+
|
|
827
|
+
if (!isPlainObject(selectedEntry?.body) || !isDenseArray(selectedEntry.body.statements) || selectedEntry.body.statements.length == 0) {
|
|
828
|
+
unsupportedCapability(language, "program without a dense non-empty selected entry point block",
|
|
829
|
+
selectedEntry?.location ?? selectedModule.location)
|
|
830
|
+
}
|
|
831
|
+
const earlier = new Set()
|
|
832
|
+
|
|
833
|
+
for (const module of program.modules) {
|
|
834
|
+
const localTargetNames = new Set([...module.records ?? [], ...module.classes ?? [], ...module.errors ?? [], ...module.functions]
|
|
835
|
+
.map(({name}) => targetName(language, name)))
|
|
836
|
+
const importedTargetNames = new Set()
|
|
837
|
+
const localJavaClass = module.records?.[0]?.name ?? module.classes?.[0]?.name ?? module.errors?.[0]?.name ??
|
|
838
|
+
(module.id == program.entryModule ? "Main" : moduleClassName(module.id))
|
|
839
|
+
const javaImportClasses = new Map(language == "java" ? [[localJavaClass, module.id]] : [])
|
|
840
|
+
|
|
841
|
+
for (const imported of module.imports) {
|
|
842
|
+
const resolved = declarations.get(imported.declarationId)
|
|
843
|
+
const remoteExport = resolved?.module.exports.find((item) =>
|
|
844
|
+
item.declarationId == imported.declarationId && item.exportedName == imported.importedName)
|
|
845
|
+
|
|
846
|
+
if (!resolved || resolved.module.id != imported.moduleId || !earlier.has(imported.moduleId) ||
|
|
847
|
+
!remoteExport) {
|
|
848
|
+
unsupportedCapability(language, "unresolved, private, cyclic, or out-of-order program import", imported.location ?? module.location)
|
|
849
|
+
}
|
|
850
|
+
const declarationKind = resolved.declaration.kind == "FunctionDeclaration" ? "function" :
|
|
851
|
+
resolved.declaration.kind == "RecordDeclaration" ? "record" :
|
|
852
|
+
resolved.declaration.kind == "ClassDeclaration" ? "class" : "error"
|
|
853
|
+
|
|
854
|
+
if (imported.symbolKind != remoteExport.symbolKind || imported.symbolKind != declarationKind ||
|
|
855
|
+
imported.typeOnly && declarationKind != "record" && declarationKind != "class") {
|
|
856
|
+
unsupportedCapability(language, "program import declaration kind mismatch", imported.location ?? module.location)
|
|
857
|
+
}
|
|
858
|
+
const importName = programImportName(program, module, imported)
|
|
859
|
+
|
|
860
|
+
if (language != "ruby" && language != "java") {
|
|
861
|
+
if (imported.symbolKind != "function") validateTargetTypeIdentifier(language, importName, imported.location)
|
|
862
|
+
else validateTargetBindingIdentifier(language, importName, "import", imported.location)
|
|
863
|
+
}
|
|
864
|
+
const name = targetName(language, importName)
|
|
865
|
+
|
|
866
|
+
if (language != "ruby" && language != "java" && (localTargetNames.has(name) || importedTargetNames.has(name))) {
|
|
867
|
+
unsupportedCapability(language, `target import-name collision '${importName}'`, imported.location)
|
|
868
|
+
}
|
|
869
|
+
if (language == "java") {
|
|
870
|
+
const importedClass = imported.symbolKind == "function" ? moduleClassName(resolved.module.id) : resolved.declaration.name
|
|
871
|
+
const existingOwner = javaImportClasses.get(importedClass)
|
|
872
|
+
|
|
873
|
+
if (existingOwner && existingOwner != resolved.module.id) {
|
|
874
|
+
unsupportedCapability(language, `target import-class collision '${importedClass}'`, imported.location)
|
|
875
|
+
}
|
|
876
|
+
javaImportClasses.set(importedClass, resolved.module.id)
|
|
877
|
+
}
|
|
878
|
+
if (language == "ruby") {
|
|
879
|
+
const importedModule = moduleClassName(resolved.module.id)
|
|
880
|
+
|
|
881
|
+
if (localTargetNames.has(importedModule)) {
|
|
882
|
+
unsupportedCapability(language, `target import-module collision '${importedModule}'`, imported.location)
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
importedTargetNames.add(name)
|
|
886
|
+
}
|
|
887
|
+
const valueDeclarationIds = new Set(module.imports.filter(({typeOnly}) => !typeOnly).map(({declarationId}) => declarationId))
|
|
888
|
+
const typeOnlyDeclarationIds = new Set(module.imports
|
|
889
|
+
.filter(({declarationId, typeOnly}) => typeOnly && !valueDeclarationIds.has(declarationId))
|
|
890
|
+
.map(({declarationId}) => declarationId))
|
|
891
|
+
const valueConstruction = findTypeOnlyValueConstruction(module, typeOnlyDeclarationIds)
|
|
892
|
+
|
|
893
|
+
if (valueConstruction) {
|
|
894
|
+
unsupportedCapability(language, "type-only program import used as a value", valueConstruction.location ?? module.location)
|
|
895
|
+
}
|
|
896
|
+
const exportNames = new Set()
|
|
897
|
+
|
|
898
|
+
for (const exported of module.exports) {
|
|
899
|
+
const resolved = declarations.get(exported.declarationId)
|
|
900
|
+
const name = targetName(language, exported.exportedName)
|
|
901
|
+
|
|
902
|
+
if (language == "javascript" || language == "typescript") {
|
|
903
|
+
validateTargetIdentifier(language, exported.exportedName, "export", exported.location ?? module.location)
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
if (!resolved || resolved.module.id != module.id || exportNames.has(name)) {
|
|
907
|
+
unsupportedCapability(language, `unresolved or colliding target export '${exported.exportedName}'`, exported.location ?? module.location)
|
|
908
|
+
}
|
|
909
|
+
const declarationKind = resolved.declaration.kind == "FunctionDeclaration" ? "function" :
|
|
910
|
+
resolved.declaration.kind == "RecordDeclaration" ? "record" :
|
|
911
|
+
resolved.declaration.kind == "ClassDeclaration" ? "class" : "error"
|
|
912
|
+
|
|
913
|
+
if (exported.symbolKind != declarationKind) {
|
|
914
|
+
unsupportedCapability(language, "program export declaration kind mismatch", exported.location ?? module.location)
|
|
915
|
+
}
|
|
916
|
+
if (language != "javascript" && language != "typescript" && exported.exportedName != resolved.declaration.name) {
|
|
917
|
+
unsupportedCapability(language, `export alias '${exported.exportedName}' without an equivalent target binding`,
|
|
918
|
+
exported.location ?? resolved.declaration.location)
|
|
919
|
+
}
|
|
920
|
+
exportNames.add(name)
|
|
921
|
+
}
|
|
922
|
+
if (language == "php") {
|
|
923
|
+
const exportedDeclarationIds = new Set(module.exports.map(({declarationId}) => declarationId))
|
|
924
|
+
const privateDeclaration = [...module.records ?? [], ...module.classes ?? [], ...module.errors ?? [], ...module.functions].find(({id}) =>
|
|
925
|
+
typeof id == "string" && !exportedDeclarationIds.has(id))
|
|
926
|
+
|
|
927
|
+
if (privateDeclaration) {
|
|
928
|
+
unsupportedCapability(language, `non-exported declaration '${privateDeclaration.name}' without target-private module visibility`,
|
|
929
|
+
privateDeclaration.location)
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
earlier.add(module.id)
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
validateProgramStdlibContract(program, language, capabilityModules)
|
|
936
|
+
validateProgramStdlibFacades(program, language)
|
|
937
|
+
|
|
938
|
+
return program
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Validates compiler-selected facade descriptors, executable sources, module markers, and native identity evidence.
|
|
943
|
+
* @param {import("../semantic/types.js").SemanticProgram} program - Candidate program.
|
|
944
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language for diagnostics.
|
|
945
|
+
* @returns {void}
|
|
946
|
+
*/
|
|
947
|
+
function validateProgramStdlibFacades(program, language) {
|
|
948
|
+
const descriptor = Reflect.get(program, "stdlibFacades")
|
|
949
|
+
const markedModules = program.modules.filter((module) => Reflect.get(module, "stdlibFacade") !== undefined)
|
|
950
|
+
const facadeSources = program.sources.filter((source) => Reflect.get(source, "ownership") == "facade")
|
|
951
|
+
const evidencedImports = program.modules.flatMap((module) => module.imports.filter((item) => Reflect.get(item, "stdlibFacade") !== undefined))
|
|
952
|
+
|
|
953
|
+
if (descriptor === undefined) {
|
|
954
|
+
if (markedModules.length > 0 || facadeSources.length > 0 || evidencedImports.length > 0) {
|
|
955
|
+
invalidFacadeDescriptor(language, "Facade modules, sources, or import evidence require a complete program facade descriptor.")
|
|
956
|
+
}
|
|
957
|
+
return
|
|
958
|
+
}
|
|
959
|
+
if (!isPlainObject(descriptor) || descriptor.schema != "SemantifoldStdlibFacades" || descriptor.version != 1 ||
|
|
960
|
+
typeof descriptor.language != "string" || !programTargets.has(descriptor.language) || !isDenseArray(descriptor.modules) ||
|
|
961
|
+
descriptor.modules.length == 0) {
|
|
962
|
+
invalidFacadeDescriptor(language, "The program facade descriptor is malformed.")
|
|
963
|
+
}
|
|
964
|
+
const facadeLanguage = /** @type {import("../semantic/types.js").SemanticLanguage} */ (descriptor.language)
|
|
965
|
+
const records = descriptor.modules.map((item) => {
|
|
966
|
+
if (!isPlainObject(item) || typeof item.identity != "string" || typeof item.version != "string") {
|
|
967
|
+
invalidFacadeDescriptor(language, "The program facade module descriptor is malformed.")
|
|
968
|
+
}
|
|
969
|
+
return stdlibFacadeRegistry.resolveIdentity(item.identity, facadeLanguage, item.version)
|
|
970
|
+
})
|
|
971
|
+
const expected = stdlibFacadeProgramDescriptor(facadeLanguage, records)
|
|
972
|
+
|
|
973
|
+
if (!equalPlainData(descriptor, expected)) {
|
|
974
|
+
invalidFacadeDescriptor(language, "The program facade descriptor does not match the registered versioned facade closure.")
|
|
975
|
+
}
|
|
976
|
+
const selectedRecords = new Map()
|
|
977
|
+
|
|
978
|
+
for (const record of records) {
|
|
979
|
+
if (selectedRecords.has(record.identity)) {
|
|
980
|
+
invalidFacadeDescriptor(language, `Facade '${record.identity}' is selected more than once.`)
|
|
981
|
+
}
|
|
982
|
+
for (const dependency of record.dependencies) {
|
|
983
|
+
const resolved = stdlibFacadeRegistry.resolveIdentity(dependency.identity, facadeLanguage, dependency.range)
|
|
984
|
+
const selected = selectedRecords.get(resolved.identity)
|
|
985
|
+
|
|
986
|
+
if (!selected || selected.version != resolved.version) {
|
|
987
|
+
invalidFacadeDescriptor(language, `Facade '${record.identity}' dependency '${resolved.identity}@${resolved.version}' is missing or out of order.`)
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
selectedRecords.set(record.identity, record)
|
|
991
|
+
}
|
|
992
|
+
if (markedModules.length != records.length) {
|
|
993
|
+
invalidFacadeDescriptor(language, "The program facade descriptor does not match its executable semantic modules.")
|
|
994
|
+
}
|
|
995
|
+
if (program.sources.some((source) => source.language != facadeLanguage ||
|
|
996
|
+
!["application", "facade"].includes(String(Reflect.get(source, "ownership"))))) {
|
|
997
|
+
invalidFacadeDescriptor(language, "Facade programs require one source language and explicit application/facade ownership.")
|
|
998
|
+
}
|
|
999
|
+
const selectedIdentities = new Set(records.map(({identity}) => identity))
|
|
1000
|
+
const registeredModules = reparseRegisteredFacadeModules(program, facadeLanguage, language)
|
|
1001
|
+
|
|
1002
|
+
for (let index = 0; index < records.length; index += 1) {
|
|
1003
|
+
const record = records[index]
|
|
1004
|
+
const module = markedModules[index]
|
|
1005
|
+
const marker = Reflect.get(module, "stdlibFacade")
|
|
1006
|
+
|
|
1007
|
+
if (!isPlainObject(marker) || Object.keys(marker).sort().join(",") != "identity,version" ||
|
|
1008
|
+
marker.identity != record.identity || marker.version != record.version || module.id != record.source.id ||
|
|
1009
|
+
module.sourceFilename != record.source.filename) {
|
|
1010
|
+
invalidFacadeDescriptor(language, `Facade '${record.identity}' semantic module identity is not canonical.`)
|
|
1011
|
+
}
|
|
1012
|
+
const source = program.sources.find(({filename}) => filename == record.source.filename)
|
|
1013
|
+
|
|
1014
|
+
if (!source || source.content != record.source.content || Reflect.get(source, "ownership") != "facade") {
|
|
1015
|
+
invalidFacadeDescriptor(language, `Facade '${record.identity}' executable source does not match its registered definition.`)
|
|
1016
|
+
}
|
|
1017
|
+
const registeredModule = registeredModules.get(record.identity)
|
|
1018
|
+
|
|
1019
|
+
if (!registeredModule || !equalPlainData(module, registeredModule)) {
|
|
1020
|
+
invalidFacadeAuthority(language,
|
|
1021
|
+
`Facade '${record.identity}' semantic module does not match its registered executable source.`, module.location)
|
|
1022
|
+
}
|
|
1023
|
+
const allowedOperations = new Set(record.requirements.flatMap(({operations}) => operations))
|
|
1024
|
+
const usedOperations = collectDeclaredEffectOperations(module)
|
|
1025
|
+
|
|
1026
|
+
if (allowedOperations.size != usedOperations.size || [...allowedOperations].some((operation) => !usedOperations.has(operation))) {
|
|
1027
|
+
invalidFacadeAuthority(language, `Facade '${record.identity}' semantic body does not match its declared canonical requirements.`, module.location)
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
if (facadeSources.length != records.length) {
|
|
1031
|
+
invalidFacadeDescriptor(language, "The program contains an unselected compiler-owned facade source.")
|
|
1032
|
+
}
|
|
1033
|
+
if (evidencedImports.length == 0) {
|
|
1034
|
+
invalidFacadeDescriptor(language, "The selected facade closure has no parser-proved native import evidence.")
|
|
1035
|
+
}
|
|
1036
|
+
for (const imported of evidencedImports) {
|
|
1037
|
+
const evidence = Reflect.get(imported, "stdlibFacade")
|
|
1038
|
+
|
|
1039
|
+
if (!isPlainObject(evidence) || Object.keys(evidence).sort().join(",") !=
|
|
1040
|
+
"facadeIdentity,facadeVersion,nativeModule,nativeSymbol,schema,version" ||
|
|
1041
|
+
evidence.schema != "SemantifoldStdlibFacadeResolution" || evidence.version != 1 ||
|
|
1042
|
+
typeof evidence.facadeIdentity != "string" || typeof evidence.facadeVersion != "string" ||
|
|
1043
|
+
typeof evidence.nativeModule != "string" || typeof evidence.nativeSymbol != "string") {
|
|
1044
|
+
invalidFacadeDescriptor(language, "A facade import has malformed native identity evidence.")
|
|
1045
|
+
}
|
|
1046
|
+
const record = stdlibFacadeRegistry.resolveFacade(facadeLanguage, evidence.nativeModule, evidence.nativeSymbol,
|
|
1047
|
+
evidence.facadeVersion)
|
|
1048
|
+
|
|
1049
|
+
if (record.identity != evidence.facadeIdentity || !selectedIdentities.has(record.identity) || imported.moduleId != record.source.id ||
|
|
1050
|
+
imported.importedName != evidence.nativeSymbol) {
|
|
1051
|
+
invalidFacadeDescriptor(language, "A facade import does not match its registered native module and symbol identity.")
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Collects every canonical operation present in one validated facade implementation.
|
|
1058
|
+
* This checks the facade's complete declared authority independently from program reachability.
|
|
1059
|
+
* @param {import("../semantic/types.js").SemanticProgramModule} module - Facade module.
|
|
1060
|
+
* @returns {Set<string>} Used operation names.
|
|
1061
|
+
*/
|
|
1062
|
+
function collectDeclaredEffectOperations(module) {
|
|
1063
|
+
const operationNames = new Map(module.capabilities?.flatMap(({operations}) =>
|
|
1064
|
+
operations.map((operation) => [operation.id, operation.name])) ?? [])
|
|
1065
|
+
const used = new Set()
|
|
1066
|
+
const seen = new Set()
|
|
1067
|
+
/**
|
|
1068
|
+
* Visits one facade semantic subtree.
|
|
1069
|
+
* @param {unknown} value - Candidate semantic subtree.
|
|
1070
|
+
*/
|
|
1071
|
+
const visit = (value) => {
|
|
1072
|
+
if (!value || typeof value != "object" || seen.has(value)) return
|
|
1073
|
+
seen.add(value)
|
|
1074
|
+
if (!Array.isArray(value)) {
|
|
1075
|
+
const operation = Reflect.get(value, "kind") == "EffectCallExpression" ? Reflect.get(value, "operation") : undefined
|
|
1076
|
+
|
|
1077
|
+
if (typeof operation == "string" && operationNames.has(operation)) used.add(/** @type {string} */ (operationNames.get(operation)))
|
|
1078
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1079
|
+
if (["capabilities", "location", "provenance", "resolution", "sourceProvenance"].includes(key)) continue
|
|
1080
|
+
visit(child)
|
|
1081
|
+
}
|
|
1082
|
+
} else for (const child of value) visit(child)
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
visit({classes: module.classes, entryPoint: module.entryPoint, functions: module.functions})
|
|
1086
|
+
return used
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* Recompiles the registry-selected facade closure from retained caller inputs and registered facade source.
|
|
1091
|
+
* This parser-backed reference graph is created during validation, before provider planning or writer allocation.
|
|
1092
|
+
* @param {import("../semantic/types.js").SemanticProgram} program - Untrusted mutable semantic program.
|
|
1093
|
+
* @param {import("../semantic/types.js").SemanticLanguage} facadeLanguage - Selected source-language facade profile.
|
|
1094
|
+
* @param {import("../semantic/types.js").SemanticLanguage} diagnosticLanguage - Target language for boundary diagnostics.
|
|
1095
|
+
* @returns {Map<string, import("../semantic/types.js").SemanticProgramModule>} Canonical semantic modules by facade identity.
|
|
1096
|
+
*/
|
|
1097
|
+
function reparseRegisteredFacadeModules(program, facadeLanguage, diagnosticLanguage) {
|
|
1098
|
+
const applicationModules = program.modules.filter((module) => Reflect.get(module, "stdlibFacade") === undefined)
|
|
1099
|
+
const modulesByFilename = new Map(applicationModules.map((module) => [module.sourceFilename, module]))
|
|
1100
|
+
const applicationSources = program.sources.filter((source) => Reflect.get(source, "ownership") == "application").map((source) => {
|
|
1101
|
+
const module = modulesByFilename.get(source.filename)
|
|
1102
|
+
|
|
1103
|
+
if (!module) invalidFacadeDescriptor(diagnosticLanguage,
|
|
1104
|
+
`Application source '${source.filename}' has no matching semantic module for facade verification.`)
|
|
1105
|
+
return {
|
|
1106
|
+
filename: source.filename,
|
|
1107
|
+
id: /** @type {import("../semantic/types.js").SemanticProgramModule} */ (module).id,
|
|
1108
|
+
language: facadeLanguage,
|
|
1109
|
+
source: /** @type {string} */ (source.content)
|
|
1110
|
+
}
|
|
1111
|
+
})
|
|
1112
|
+
|
|
1113
|
+
if (applicationSources.length != applicationModules.length) {
|
|
1114
|
+
invalidFacadeDescriptor(diagnosticLanguage, "Facade verification requires one retained application source per semantic module.")
|
|
1115
|
+
}
|
|
1116
|
+
const reparsed = parseProgramSource({entryModule: program.entryModule, sources: applicationSources})
|
|
1117
|
+
const modules = new Map()
|
|
1118
|
+
|
|
1119
|
+
for (const module of reparsed.modules) {
|
|
1120
|
+
if (!module.stdlibFacade) continue
|
|
1121
|
+
if (modules.has(module.stdlibFacade.identity)) {
|
|
1122
|
+
invalidFacadeDescriptor(diagnosticLanguage, `Registered facade '${module.stdlibFacade.identity}' reparsed more than once.`)
|
|
1123
|
+
}
|
|
1124
|
+
modules.set(module.stdlibFacade.identity, module)
|
|
1125
|
+
}
|
|
1126
|
+
return modules
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* Compares closed plain JSON-shaped data without coercion or key-order assumptions.
|
|
1131
|
+
* @param {unknown} left - Candidate value.
|
|
1132
|
+
* @param {unknown} right - Expected value.
|
|
1133
|
+
* @returns {boolean} Exact recursive equivalence.
|
|
1134
|
+
*/
|
|
1135
|
+
function equalPlainData(left, right) {
|
|
1136
|
+
if (left === right) return true
|
|
1137
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
1138
|
+
return Array.isArray(left) && Array.isArray(right) && isDenseArray(left) && isDenseArray(right) &&
|
|
1139
|
+
left.length == right.length && left.every((value, index) => equalPlainData(value, right[index]))
|
|
1140
|
+
}
|
|
1141
|
+
if (!isPlainObject(left) || !isPlainObject(right)) return false
|
|
1142
|
+
const leftKeys = Object.keys(left).sort()
|
|
1143
|
+
const rightKeys = Object.keys(right).sort()
|
|
1144
|
+
|
|
1145
|
+
return leftKeys.length == rightKeys.length && leftKeys.every((key, index) =>
|
|
1146
|
+
key == rightKeys[index] && equalPlainData(left[key], right[key]))
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/**
|
|
1150
|
+
* Throws one stable facade artifact-boundary diagnostic.
|
|
1151
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
1152
|
+
* @param {string} message - Diagnostic detail.
|
|
1153
|
+
* @returns {never} Always throws.
|
|
1154
|
+
*/
|
|
1155
|
+
function invalidFacadeDescriptor(language, message) {
|
|
1156
|
+
throw new SemantifoldDiagnostic({code: "STDLIB_FACADE_DESCRIPTOR_INVALID", language, message})
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Throws when a facade semantic body exceeds or omits its registry-declared authority.
|
|
1161
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
1162
|
+
* @param {string} message - Diagnostic detail.
|
|
1163
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Facade source location.
|
|
1164
|
+
* @returns {never} Always throws.
|
|
1165
|
+
*/
|
|
1166
|
+
function invalidFacadeAuthority(language, message, location) {
|
|
1167
|
+
throw new SemantifoldDiagnostic({code: "STDLIB_FACADE_AUTHORITY_FORGED", language, location, message})
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* Validates the program stdlib link descriptor against its capability modules.
|
|
1172
|
+
* @param {import("../semantic/types.js").SemanticProgram} program - Candidate program.
|
|
1173
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
1174
|
+
* @param {import("../semantic/types.js").SemanticProgramModule[]} capabilityModules - Modules carrying capabilities.
|
|
1175
|
+
* @returns {void}
|
|
1176
|
+
*/
|
|
1177
|
+
function validateProgramStdlibContract(program, language, capabilityModules) {
|
|
1178
|
+
const contract = Reflect.get(program, "stdlibContract")
|
|
1179
|
+
const contracts = Reflect.get(program, "stdlibContracts")
|
|
1180
|
+
|
|
1181
|
+
if (capabilityModules.length == 0) {
|
|
1182
|
+
if (contract !== undefined || contracts !== undefined) {
|
|
1183
|
+
throw new SemantifoldDiagnostic({
|
|
1184
|
+
code: "STDLIB_LINK_FAILURE",
|
|
1185
|
+
language,
|
|
1186
|
+
message: "A declared standard-library contract requires capability modules."
|
|
1187
|
+
})
|
|
1188
|
+
}
|
|
1189
|
+
return
|
|
1190
|
+
}
|
|
1191
|
+
if (contract !== undefined && contracts !== undefined) {
|
|
1192
|
+
throw new SemantifoldDiagnostic({
|
|
1193
|
+
code: "STDLIB_LINK_FAILURE",
|
|
1194
|
+
language,
|
|
1195
|
+
message: "A program cannot declare both singular and plural standard-library contract descriptors."
|
|
1196
|
+
})
|
|
1197
|
+
}
|
|
1198
|
+
if (contract === undefined && contracts === undefined) {
|
|
1199
|
+
unsupportedCapability(language, "program modules with capabilities require a declared standard-library contract",
|
|
1200
|
+
capabilityModules[0].location)
|
|
1201
|
+
}
|
|
1202
|
+
const declared = contracts === undefined ? [contract] : contracts
|
|
1203
|
+
|
|
1204
|
+
if (!isDenseArray(declared) || declared.length == 0) {
|
|
1205
|
+
throw new SemantifoldDiagnostic({
|
|
1206
|
+
code: "STDLIB_LINK_FAILURE",
|
|
1207
|
+
language,
|
|
1208
|
+
message: "The declared standard-library contract descriptor list is malformed or empty."
|
|
1209
|
+
})
|
|
1210
|
+
}
|
|
1211
|
+
const identities = new Set()
|
|
1212
|
+
|
|
1213
|
+
for (const descriptor of declared) {
|
|
1214
|
+
if (!isPlainObject(descriptor) || Object.keys(descriptor).some((key) => key != "identity" && key != "contractVersion")) {
|
|
1215
|
+
throw new SemantifoldDiagnostic({
|
|
1216
|
+
code: "STDLIB_LINK_FAILURE",
|
|
1217
|
+
language,
|
|
1218
|
+
message: "The declared standard-library contract descriptor is malformed."
|
|
1219
|
+
})
|
|
1220
|
+
}
|
|
1221
|
+
const identity = descriptor.identity
|
|
1222
|
+
|
|
1223
|
+
if (typeof identity != "string" || identity.length == 0 || identities.has(identity) ||
|
|
1224
|
+
!listStdlibModules().some(({identity: registered}) => registered == identity)) {
|
|
1225
|
+
throw new SemantifoldDiagnostic({
|
|
1226
|
+
code: "STDLIB_LINK_FAILURE",
|
|
1227
|
+
language,
|
|
1228
|
+
message: `Declared standard-library contract '${String(identity)}' is duplicate or not a registered canonical module.`
|
|
1229
|
+
})
|
|
1230
|
+
}
|
|
1231
|
+
if (descriptor.contractVersion !== undefined && parseStdlibVersionRange(descriptor.contractVersion) === null) {
|
|
1232
|
+
throw new SemantifoldDiagnostic({
|
|
1233
|
+
code: "STDLIB_LINK_FAILURE",
|
|
1234
|
+
language,
|
|
1235
|
+
message: `Declared standard-library contract version '${String(descriptor.contractVersion)}' is not canonical.`
|
|
1236
|
+
})
|
|
1237
|
+
}
|
|
1238
|
+
identities.add(identity)
|
|
1239
|
+
}
|
|
1240
|
+
for (const module of capabilityModules) {
|
|
1241
|
+
for (const capability of module.capabilities ?? []) {
|
|
1242
|
+
if (!isPlainObject(capability) || typeof capability.authorityId != "string" || !identities.has(capability.authorityId)) {
|
|
1243
|
+
throw new SemantifoldDiagnostic({
|
|
1244
|
+
code: "STDLIB_LINK_FAILURE",
|
|
1245
|
+
language,
|
|
1246
|
+
message: `Capability authority '${String(Reflect.get(capability, "authorityId"))}' does not match a declared standard-library contract.`
|
|
1247
|
+
})
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* Finds a record construction that crosses a type-only import edge.
|
|
1255
|
+
* @param {import("../semantic/types.js").SemanticProgramModule} module - Importing semantic module.
|
|
1256
|
+
* @param {Set<string>} typeOnlyDeclarationIds - Imported identities unavailable in value positions.
|
|
1257
|
+
* @returns {import("../semantic/types.js").RecordConstruction | undefined} Invalid construction when present.
|
|
1258
|
+
*/
|
|
1259
|
+
function findTypeOnlyValueConstruction(module, typeOnlyDeclarationIds) {
|
|
1260
|
+
if (typeOnlyDeclarationIds.size == 0) return undefined
|
|
1261
|
+
const seen = new WeakSet()
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* Visits one semantic value.
|
|
1265
|
+
* @param {unknown} value - Candidate semantic subtree.
|
|
1266
|
+
* @returns {import("../semantic/types.js").RecordConstruction | undefined} Invalid construction when present.
|
|
1267
|
+
*/
|
|
1268
|
+
function visit(value) {
|
|
1269
|
+
if (!value || typeof value != "object" || seen.has(value)) return undefined
|
|
1270
|
+
seen.add(value)
|
|
1271
|
+
const object = /** @type {Record<string, unknown>} */ (value)
|
|
1272
|
+
|
|
1273
|
+
if (object.kind == "RecordConstruction" && isPlainObject(object.record) &&
|
|
1274
|
+
typeof object.record.declarationId == "string" && typeOnlyDeclarationIds.has(object.record.declarationId)) {
|
|
1275
|
+
return /** @type {import("../semantic/types.js").RecordConstruction} */ (value)
|
|
1276
|
+
}
|
|
1277
|
+
for (const child of Object.values(object)) {
|
|
1278
|
+
const found = visit(child)
|
|
1279
|
+
|
|
1280
|
+
if (found) return found
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
return undefined
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
return visit({entryPoint: module.entryPoint, functions: module.functions})
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* Plans all paths before emission and rejects exact/case/file-directory collisions.
|
|
1291
|
+
* @param {import("../semantic/types.js").SemanticProgram} program - Validated program.
|
|
1292
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
1293
|
+
* @param {StdlibProgramLinking | null} [linking] - Planned stdlib provider link.
|
|
1294
|
+
* @returns {Map<string, string>} Path by module identity.
|
|
1295
|
+
*/
|
|
1296
|
+
function planModulePaths(program, language, linking = null) {
|
|
1297
|
+
const extension = language == "javascript" ? ".js" : language == "typescript" ? ".ts" : language == "ruby" ? ".rb" :
|
|
1298
|
+
language == "php" ? ".php" : ".java"
|
|
1299
|
+
const paths = new Map()
|
|
1300
|
+
const folded = new Set()
|
|
1301
|
+
const targetModuleNames = new Set()
|
|
1302
|
+
const providerPaths = linking ? linking.negotiation.providers.map(({module}) =>
|
|
1303
|
+
/** @type {import("../stdlib-providers.js").StdlibProviderRecord} */ (linking.records.get(module)).artifact.path) : []
|
|
1304
|
+
const foldedProviders = new Map()
|
|
1305
|
+
|
|
1306
|
+
for (const providerPath of providerPaths) {
|
|
1307
|
+
const key = providerPath.toLocaleLowerCase("en-US")
|
|
1308
|
+
|
|
1309
|
+
if (!isSafeArtifactPath(providerPath) || folded.has(key)) {
|
|
1310
|
+
throw new SemantifoldDiagnostic({code: "STDLIB_NAME_COLLISION", language,
|
|
1311
|
+
message: `Stdlib provider artifact path '${providerPath}' is unsafe or colliding.`})
|
|
1312
|
+
}
|
|
1313
|
+
folded.add(key)
|
|
1314
|
+
foldedProviders.set(providerPath.toLocaleLowerCase("en-US").replaceAll(".", "/"), providerPath)
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
for (const module of program.modules) {
|
|
1318
|
+
let path = `${module.id.replaceAll(".", "/")}${extension}`
|
|
1319
|
+
const providerPath = foldedProviders.get(path.toLocaleLowerCase("en-US").replaceAll(".", "/"))
|
|
1320
|
+
|
|
1321
|
+
if (providerPath !== undefined) {
|
|
1322
|
+
throw new SemantifoldDiagnostic({
|
|
1323
|
+
code: "STDLIB_NAME_COLLISION",
|
|
1324
|
+
language,
|
|
1325
|
+
message: `Module path '${path}' collides with the stdlib provider artifact '${providerPath}'.`
|
|
1326
|
+
})
|
|
1327
|
+
}
|
|
1328
|
+
const targetModuleName = moduleClassName(module.id)
|
|
1329
|
+
|
|
1330
|
+
if (language == "php" || language == "ruby") {
|
|
1331
|
+
const key = language == "php" ? targetModuleName.toLocaleLowerCase("en-US") : targetModuleName
|
|
1332
|
+
|
|
1333
|
+
if (targetModuleNames.has(key)) {
|
|
1334
|
+
unsupportedCapability(language, `target module-name collision '${targetModuleName}'`, module.location)
|
|
1335
|
+
}
|
|
1336
|
+
targetModuleNames.add(key)
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
if (language == "php") {
|
|
1340
|
+
validateTargetIdentifier(language, targetModuleName, "module", module.location)
|
|
1341
|
+
validateTargetTypeIdentifier(language, targetModuleName, module.location)
|
|
1342
|
+
}
|
|
1343
|
+
if (language == "ruby") validateTargetTypeIdentifier(language, targetModuleName, module.location)
|
|
1344
|
+
|
|
1345
|
+
if (language == "java") {
|
|
1346
|
+
for (const segment of module.id.split(".")) validateTargetIdentifier(language, segment, "module", module.location)
|
|
1347
|
+
const nominalCount = (module.records?.length ?? 0) + (module.classes?.length ?? 0) + (module.errors?.length ?? 0)
|
|
1348
|
+
|
|
1349
|
+
if (module.id.includes("-") || nominalCount > 1 ||
|
|
1350
|
+
nominalCount > 0 && (module.functions.length > 0 || module.entryPoint)) {
|
|
1351
|
+
unsupportedCapability(language, "Java module layout requiring multiple public declarations in one semantic module", module.location)
|
|
1352
|
+
}
|
|
1353
|
+
const className = module.records?.[0]?.name ?? module.classes?.[0]?.name ?? module.errors?.[0]?.name ??
|
|
1354
|
+
(module.id == program.entryModule ? "Main" : targetModuleName)
|
|
1355
|
+
|
|
1356
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(className)) {
|
|
1357
|
+
unsupportedCapability(language, `invalid Java public class name '${className}'`, module.location)
|
|
1358
|
+
}
|
|
1359
|
+
if (module.id != program.entryModule) validateTargetTypeIdentifier(language, className, module.location)
|
|
1360
|
+
path = `semantifold/generated/${module.id.replaceAll(".", "/")}/${className}.java`
|
|
1361
|
+
}
|
|
1362
|
+
const key = path.toLocaleLowerCase("en-US")
|
|
1363
|
+
|
|
1364
|
+
if (!isSafeArtifactPath(path) || folded.has(key)) unsupportedCapability(language, `unsafe or colliding target path '${path}'`, module.location)
|
|
1365
|
+
folded.add(key)
|
|
1366
|
+
paths.set(module.id, path)
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
return paths
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* Converts a logical module identity to its canonical target type segment.
|
|
1374
|
+
* @param {string} id - Logical module identity.
|
|
1375
|
+
* @returns {string} Target class name.
|
|
1376
|
+
*/
|
|
1377
|
+
function moduleClassName(id) {
|
|
1378
|
+
return id.split(/[._-]+/u).map((part) => `${part[0].toUpperCase()}${part.slice(1)}`).join("")
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
/**
|
|
1382
|
+
* Normalizes a declaration or binding name for target collision checks.
|
|
1383
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target.
|
|
1384
|
+
* @param {string} name - Name.
|
|
1385
|
+
* @returns {string} Collision key.
|
|
1386
|
+
*/
|
|
1387
|
+
function targetName(language, name) {
|
|
1388
|
+
return language == "php" ? name.toLocaleLowerCase("en-US") : name
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
/**
|
|
1392
|
+
* Reports whether a candidate is a direct plain object.
|
|
1393
|
+
* @param {unknown} value - Candidate.
|
|
1394
|
+
* @returns {value is Record<string, unknown>} Whether the value is a plain object.
|
|
1395
|
+
*/
|
|
1396
|
+
function isPlainObject(value) {
|
|
1397
|
+
return Boolean(value) && typeof value == "object" && !Array.isArray(value) && Object.getPrototypeOf(value) == Object.prototype
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
/**
|
|
1401
|
+
* Raises a stable malformed program-generation request diagnostic.
|
|
1402
|
+
* @param {string} message - Stable failure.
|
|
1403
|
+
* @returns {never} Always throws.
|
|
1404
|
+
*/
|
|
1405
|
+
function invalidProgramGeneration(message) {
|
|
1406
|
+
throw new SemantifoldDiagnostic({code: "INVALID_PROGRAM", language: "program", message})
|
|
1407
|
+
}
|