space-data-module-sdk 0.8.4 → 0.8.5
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 +117 -20
- package/bin/space-data-module.js +263 -0
- package/docs/.nojekyll +1 -0
- package/docs/AGENTS.md +34 -0
- package/docs/architecture.svg +141 -0
- package/docs/browser-wasmedge-isomorphic.md +179 -0
- package/docs/flatsql-streaming-standard.md +341 -0
- package/docs/gpu-module-abi.md +193 -0
- package/docs/index.html +125 -0
- package/docs/isomorphic-pthreads.md +188 -0
- package/docs/isomorphic-sdn-runtime-plan.md +167 -0
- package/docs/language-runtime-matrix.md +82 -0
- package/docs/manifest-structure.svg +114 -0
- package/docs/module-bundle-runtime-plan.md +19 -0
- package/docs/module-lifecycle.svg +70 -0
- package/docs/module-publication-standard.md +540 -0
- package/docs/node-red-default-node-parity.md +88 -0
- package/docs/protocol-installation.md +196 -0
- package/docs/secrets-capability.md +152 -0
- package/docs/styles.css +288 -0
- package/docs/testing-harness.md +408 -0
- package/docs/tri-runtime-parity.md +132 -0
- package/docs/wallet-callback.html +2 -0
- package/package.json +25 -6
- package/schemas/HttpRequestAbi.fbs +41 -0
- package/schemas/HttpResponseAbi.fbs +36 -0
- package/schemas/PluginManifest.fbs +2 -1
- package/src/AGENTS.md +1 -1
- package/src/app/codec.js +548 -0
- package/src/app/index.js +6 -0
- package/src/browser.js +5 -0
- package/src/bundle/constants.js +1 -0
- package/src/bundle/index.js +15 -0
- package/src/bundle/sigdomain.js +179 -0
- package/src/bundle/signing.js +767 -0
- package/src/bundle/wasm.js +3 -2
- package/src/capabilities.js +3 -0
- package/src/compat/index.js +7 -0
- package/src/compat/sdnLegacy.js +449 -0
- package/src/compiler/compileModule.js +556 -55
- package/src/compiler/flatcSupport.js +41 -27
- package/src/compiler/index.d.ts +2 -0
- package/src/compiler/index.js +18 -0
- package/src/compiler/invokeGlue.js +1212 -183
- package/src/compiler/pthreadArtifactGuard.js +910 -0
- package/src/compiler/sdnShimGenerator.js +323 -0
- package/src/compiler/wasiThreadsToolchain.js +150 -0
- package/src/compliance/index.js +11 -1
- package/src/compliance/legacyWildcardPorts.js +228 -0
- package/src/compliance/pluginCompliance.js +1000 -96
- package/src/field-stream/index.d.ts +31 -0
- package/src/field-stream/index.js +119 -0
- package/src/flow/dependencyStreamBridge.js +234 -0
- package/src/flow/flatsqlLinkShim.js +304 -0
- package/src/flow/flowCodec.js +107 -0
- package/src/flow/flowCompiler.js +2900 -0
- package/src/flow/flowRuntimeHost.js +877 -0
- package/src/flow/index.d.ts +117 -0
- package/src/flow/index.js +31 -0
- package/src/flow/isomorphicFlowHost.js +384 -0
- package/src/flow/jsFlowRuntime.js +275 -0
- package/src/flow/normalize.js +294 -0
- package/src/flow/runtime-src/flow_runtime.cpp +1699 -0
- package/src/flow/vendor/sdn-flow/MethodRegistry.js +400 -0
- package/src/flow/vendor/sdn-flow/constants.js +39 -0
- package/src/flow/vendor/sdn-flow/index.js +29 -0
- package/src/flow/vendor/sdn-flow/normalize.js +506 -0
- package/src/flow/vendor/sdn-flow/wasmCompatibility.js +189 -0
- package/src/generated/http/cpp/HttpRequestAbi_generated.h +285 -0
- package/src/generated/http/cpp/HttpResponseAbi_generated.h +192 -0
- package/src/generated/http/sdn/http/http-header.js +53 -0
- package/src/generated/http/sdn/http/http-header.ts +67 -0
- package/src/generated/http/sdn/http/http-request.js +132 -0
- package/src/generated/http/sdn/http/http-request.ts +179 -0
- package/src/generated/http/sdn/http/http-response.js +144 -0
- package/src/generated/http/sdn/http/http-response.ts +168 -0
- package/src/generated/http/sdn/http.js +11 -0
- package/src/generated/http/sdn/http.ts +4 -0
- package/src/generated/orbpro/flow/backpressure-policy.d.ts +12 -0
- package/src/generated/orbpro/flow/backpressure-policy.d.ts.map +1 -0
- package/src/generated/orbpro/flow/backpressure-policy.js +17 -0
- package/src/generated/orbpro/flow/backpressure-policy.js.map +1 -0
- package/src/generated/orbpro/flow/backpressure-policy.ts +15 -0
- package/src/generated/orbpro/flow/flow-bundle-manifest.d.ts +142 -0
- package/src/generated/orbpro/flow/flow-bundle-manifest.d.ts.map +1 -0
- package/src/generated/orbpro/flow/flow-bundle-manifest.js +304 -0
- package/src/generated/orbpro/flow/flow-bundle-manifest.js.map +1 -0
- package/src/generated/orbpro/flow/flow-bundle-manifest.ts +399 -0
- package/src/generated/orbpro/flow/flow-edge.d.ts +113 -0
- package/src/generated/orbpro/flow/flow-edge.d.ts.map +1 -0
- package/src/generated/orbpro/flow/flow-edge.js +221 -0
- package/src/generated/orbpro/flow/flow-edge.js.map +1 -0
- package/src/generated/orbpro/flow/flow-edge.ts +292 -0
- package/src/generated/orbpro/flow/flow-node.d.ts +80 -0
- package/src/generated/orbpro/flow/flow-node.d.ts.map +1 -0
- package/src/generated/orbpro/flow/flow-node.js +158 -0
- package/src/generated/orbpro/flow/flow-node.js.map +1 -0
- package/src/generated/orbpro/flow/flow-node.ts +202 -0
- package/src/generated/orbpro/flow/flow-program.d.ts +164 -0
- package/src/generated/orbpro/flow/flow-program.d.ts.map +1 -0
- package/src/generated/orbpro/flow/flow-program.js +350 -0
- package/src/generated/orbpro/flow/flow-program.js.map +1 -0
- package/src/generated/orbpro/flow/flow-program.ts +477 -0
- package/src/generated/orbpro/flow/flow-trigger.d.ts +108 -0
- package/src/generated/orbpro/flow/flow-trigger.d.ts.map +1 -0
- package/src/generated/orbpro/flow/flow-trigger.js +207 -0
- package/src/generated/orbpro/flow/flow-trigger.js.map +1 -0
- package/src/generated/orbpro/flow/flow-trigger.ts +276 -0
- package/src/generated/orbpro/flow/node-kind.d.ts +13 -0
- package/src/generated/orbpro/flow/node-kind.d.ts.map +1 -0
- package/src/generated/orbpro/flow/node-kind.js +16 -0
- package/src/generated/orbpro/flow/node-kind.js.map +1 -0
- package/src/generated/orbpro/{invoke.ts → flow/node-kind.ts} +12 -2
- package/src/generated/orbpro/flow/plugin-version-binding.d.ts +52 -0
- package/src/generated/orbpro/flow/plugin-version-binding.d.ts.map +1 -0
- package/src/generated/orbpro/flow/plugin-version-binding.js +85 -0
- package/src/generated/orbpro/flow/plugin-version-binding.js.map +1 -0
- package/src/generated/orbpro/flow/plugin-version-binding.ts +121 -0
- package/src/generated/orbpro/flow/schema-hash-binding.d.ts +70 -0
- package/src/generated/orbpro/flow/schema-hash-binding.d.ts.map +1 -0
- package/src/generated/orbpro/flow/schema-hash-binding.js +143 -0
- package/src/generated/orbpro/flow/schema-hash-binding.js.map +1 -0
- package/src/generated/orbpro/flow/schema-hash-binding.ts +189 -0
- package/src/generated/orbpro/flow/trigger-binding.d.ts +71 -0
- package/src/generated/orbpro/flow/trigger-binding.d.ts.map +1 -0
- package/src/generated/orbpro/flow/trigger-binding.js +142 -0
- package/src/generated/orbpro/flow/trigger-binding.js.map +1 -0
- package/src/generated/orbpro/flow/trigger-binding.ts +181 -0
- package/src/generated/orbpro/flow/trigger-kind.d.ts +14 -0
- package/src/generated/orbpro/flow/trigger-kind.d.ts.map +1 -0
- package/src/generated/orbpro/flow/trigger-kind.js +17 -0
- package/src/generated/orbpro/flow/trigger-kind.js.map +1 -0
- package/src/generated/orbpro/flow/trigger-kind.ts +17 -0
- package/src/generated/orbpro/flow.d.ts +21 -0
- package/src/generated/orbpro/flow.d.ts.map +1 -0
- package/src/generated/orbpro/flow.js +23 -0
- package/src/generated/orbpro/flow.js.map +1 -0
- package/src/generated/orbpro/flow.ts +24 -0
- package/src/generated/orbpro/manifest/capability-kind.d.ts +2 -1
- package/src/generated/orbpro/manifest/capability-kind.js +1 -0
- package/src/generated/orbpro/manifest/capability-kind.ts +1 -0
- package/src/generated/orbpro/plugin/stream-invoke-request.d.ts +93 -0
- package/src/generated/orbpro/plugin/stream-invoke-request.js +160 -0
- package/src/generated/orbpro/plugin/stream-invoke-request.ts +211 -0
- package/src/generated/orbpro/plugin/stream-invoke-response.d.ts +99 -0
- package/src/generated/orbpro/plugin/stream-invoke-response.js +175 -0
- package/src/generated/orbpro/plugin/stream-invoke-response.ts +226 -0
- package/src/generated/orbpro/plugin.js +8 -0
- package/src/generated/orbpro/stream.d.ts +12 -0
- package/src/generated/orbpro/stream.js +14 -0
- package/src/generated/orbpro/stream.ts +15 -0
- package/src/generated/spacedatastandards/plg/EntryFunction.d.ts +52 -0
- package/src/generated/spacedatastandards/plg/EntryFunction.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/EntryFunction.js +106 -0
- package/src/generated/spacedatastandards/plg/EntryFunction.ts +162 -0
- package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.d.ts +90 -0
- package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.js +189 -0
- package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.ts +270 -0
- package/src/generated/spacedatastandards/plg/PLG.d.ts +492 -0
- package/src/generated/spacedatastandards/plg/PLG.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLG.js +1139 -0
- package/src/generated/spacedatastandards/plg/PLG.ts +1600 -0
- package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.d.ts +63 -0
- package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.js +138 -0
- package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.ts +189 -0
- package/src/generated/spacedatastandards/plg/PLGBuildArtifact.d.ts +41 -0
- package/src/generated/spacedatastandards/plg/PLGBuildArtifact.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGBuildArtifact.js +104 -0
- package/src/generated/spacedatastandards/plg/PLGBuildArtifact.ts +151 -0
- package/src/generated/spacedatastandards/plg/PLGFlowEdge.d.ts +68 -0
- package/src/generated/spacedatastandards/plg/PLGFlowEdge.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGFlowEdge.js +124 -0
- package/src/generated/spacedatastandards/plg/PLGFlowEdge.ts +184 -0
- package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.d.ts +58 -0
- package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.js +126 -0
- package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.ts +161 -0
- package/src/generated/spacedatastandards/plg/PLGFlowNode.d.ts +82 -0
- package/src/generated/spacedatastandards/plg/PLGFlowNode.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGFlowNode.js +164 -0
- package/src/generated/spacedatastandards/plg/PLGFlowNode.ts +244 -0
- package/src/generated/spacedatastandards/plg/PLGFlowTrigger.d.ts +55 -0
- package/src/generated/spacedatastandards/plg/PLGFlowTrigger.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGFlowTrigger.js +105 -0
- package/src/generated/spacedatastandards/plg/PLGFlowTrigger.ts +162 -0
- package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.d.ts +42 -0
- package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.js +83 -0
- package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.ts +127 -0
- package/src/generated/spacedatastandards/plg/PLGHostCapability.d.ts +36 -0
- package/src/generated/spacedatastandards/plg/PLGHostCapability.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGHostCapability.js +90 -0
- package/src/generated/spacedatastandards/plg/PLGHostCapability.ts +127 -0
- package/src/generated/spacedatastandards/plg/PLGMethodManifest.d.ts +53 -0
- package/src/generated/spacedatastandards/plg/PLGMethodManifest.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGMethodManifest.js +153 -0
- package/src/generated/spacedatastandards/plg/PLGMethodManifest.ts +210 -0
- package/src/generated/spacedatastandards/plg/PLGPortManifest.d.ts +70 -0
- package/src/generated/spacedatastandards/plg/PLGPortManifest.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGPortManifest.js +149 -0
- package/src/generated/spacedatastandards/plg/PLGPortManifest.ts +212 -0
- package/src/generated/spacedatastandards/plg/PLGProtocolSpec.d.ts +73 -0
- package/src/generated/spacedatastandards/plg/PLGProtocolSpec.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGProtocolSpec.js +199 -0
- package/src/generated/spacedatastandards/plg/PLGProtocolSpec.ts +292 -0
- package/src/generated/spacedatastandards/plg/PLGTimerSpec.d.ts +40 -0
- package/src/generated/spacedatastandards/plg/PLGTimerSpec.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PLGTimerSpec.js +103 -0
- package/src/generated/spacedatastandards/plg/PLGTimerSpec.ts +148 -0
- package/src/generated/spacedatastandards/plg/PluginCapability.d.ts +41 -0
- package/src/generated/spacedatastandards/plg/PluginCapability.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PluginCapability.js +82 -0
- package/src/generated/spacedatastandards/plg/{plugin-capability.ts → PluginCapability.ts} +55 -18
- package/src/generated/spacedatastandards/plg/PluginDependency.d.ts +42 -0
- package/src/generated/spacedatastandards/plg/PluginDependency.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/PluginDependency.js +80 -0
- package/src/generated/spacedatastandards/plg/PluginDependency.ts +124 -0
- package/src/generated/spacedatastandards/plg/TAB.d.ts +85 -0
- package/src/generated/spacedatastandards/plg/TAB.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/TAB.js +173 -0
- package/src/generated/spacedatastandards/plg/TAB.ts +228 -0
- package/src/generated/spacedatastandards/plg/bufferMutability.d.ts +18 -0
- package/src/generated/spacedatastandards/plg/bufferMutability.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/bufferMutability.js +20 -0
- package/src/generated/spacedatastandards/plg/bufferMutability.ts +23 -0
- package/src/generated/spacedatastandards/plg/bufferOwnership.d.ts +18 -0
- package/src/generated/spacedatastandards/plg/bufferOwnership.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/bufferOwnership.js +20 -0
- package/src/generated/spacedatastandards/plg/bufferOwnership.ts +23 -0
- package/src/generated/spacedatastandards/plg/drainBehavior.d.ts +20 -0
- package/src/generated/spacedatastandards/plg/drainBehavior.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/drainBehavior.js +22 -0
- package/src/generated/spacedatastandards/plg/drainBehavior.ts +25 -0
- package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.d.ts +10 -0
- package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.js +12 -0
- package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.ts +13 -0
- package/src/generated/spacedatastandards/plg/hostCapabilityKind.d.ts +51 -0
- package/src/generated/spacedatastandards/plg/hostCapabilityKind.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/hostCapabilityKind.js +53 -0
- package/src/generated/spacedatastandards/plg/hostCapabilityKind.ts +55 -0
- package/src/generated/spacedatastandards/plg/invokeSurfaceKind.d.ts +15 -0
- package/src/generated/spacedatastandards/plg/invokeSurfaceKind.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/invokeSurfaceKind.js +17 -0
- package/src/generated/spacedatastandards/plg/invokeSurfaceKind.ts +19 -0
- package/src/generated/spacedatastandards/plg/main.d.ts +29 -0
- package/src/generated/spacedatastandards/plg/main.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/main.js +30 -0
- package/src/generated/spacedatastandards/plg/main.ts +32 -0
- package/src/generated/spacedatastandards/plg/payloadWireFormat.d.ts +17 -0
- package/src/generated/spacedatastandards/plg/payloadWireFormat.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/payloadWireFormat.js +19 -0
- package/src/generated/spacedatastandards/plg/payloadWireFormat.ts +21 -0
- package/src/generated/spacedatastandards/plg/pluginCategory.d.ts +82 -0
- package/src/generated/spacedatastandards/plg/pluginCategory.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/pluginCategory.js +84 -0
- package/src/generated/spacedatastandards/plg/pluginCategory.ts +103 -0
- package/src/generated/spacedatastandards/plg/publicationState.d.ts +18 -0
- package/src/generated/spacedatastandards/plg/publicationState.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/publicationState.js +20 -0
- package/src/generated/spacedatastandards/plg/purchaseTier.d.ts +18 -0
- package/src/generated/spacedatastandards/plg/purchaseTier.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/purchaseTier.js +20 -0
- package/src/host/AGENTS.md +2 -2
- package/src/host/abi.js +53 -66
- package/src/host/browserHost.js +355 -8
- package/src/host/cpp/keyslotClient.hpp +231 -0
- package/src/host/cpp/secretsClient.hpp +583 -0
- package/src/host/hostcallWire.js +183 -0
- package/src/host/index.js +2 -0
- package/src/host/isomorphicLoader.js +44 -7
- package/src/host/nodeHost.js +227 -8
- package/src/host/sabHostcallChannel.js +206 -0
- package/src/host/timerDriver.js +203 -0
- package/src/host/wasiShim.js +41 -1
- package/src/host/wasiThreadBrowserWorker.mjs +48 -0
- package/src/host/wasiThreadHost.js +393 -0
- package/src/host/wasiThreadWorker.mjs +32 -0
- package/src/host/wasiThreadWorkerRuntime.js +93 -0
- package/src/http/index.js +339 -0
- package/src/index.d.ts +716 -10
- package/src/index.js +4 -0
- package/src/invoke/codec.js +1202 -128
- package/src/invoke/index.js +5 -1
- package/src/licensing/index.d.ts +2 -0
- package/src/licensing/index.js +2 -0
- package/src/licensing/records.js +287 -8
- package/src/manifest/codec.js +62 -14
- package/src/manifest/index.js +2 -0
- package/src/manifest/legacyToPlg.js +164 -25
- package/src/manifest/normalize.js +1 -0
- package/src/manifest/plgCodec.js +1303 -62
- package/src/manifest/typeRefs.js +162 -24
- package/src/runtime/compiledRuntimeAbi.js +47 -0
- package/src/runtime/compiledRuntimeAbi.json +33 -0
- package/src/runtime/index.d.ts +32 -0
- package/src/runtime/index.js +1 -0
- package/src/runtime-host/index.js +3 -0
- package/src/runtime-host/methodRegistry.js +55 -0
- package/src/standards/index.js +200 -56
- package/src/standards/sharedCatalog.js +16 -0
- package/src/testing/browserModuleHarness.js +604 -31
- package/src/testing/docker/wasmedge-parity.Dockerfile +39 -0
- package/src/testing/index.d.ts +151 -0
- package/src/testing/index.js +21 -0
- package/src/testing/native/wasmedge_emscripten_pthread_runner.c +25 -1
- package/src/testing/parityBrowserRunner.js +138 -0
- package/src/testing/parityHarness.js +616 -0
- package/src/testing/parityLanes.js +537 -0
- package/src/testing/processInvoke.js +29 -21
- package/src/testing/streamInvokeCodec.js +10 -175
- package/src/testing/wasmedgePin.json +6 -0
- package/src/testing/workerModuleHarness.js +242 -0
- package/src/testing/workerModuleHarnessWorker.js +176 -0
- package/src/transport/index.js +3 -0
- package/src/transport/pki.js +299 -11
- package/src/transport/records.js +16 -7
- package/src/utils/ecdsaDer.js +106 -0
- package/src/utils/wasmCrypto.js +0 -43
- package/templates/gpu-module/README.md +48 -0
- package/templates/gpu-module/include/space_data_gpu_abi.h +122 -0
- package/templates/gpu-module/manifest.json +84 -0
- package/schemas/PluginInvokeRequest.fbs +0 -18
- package/schemas/PluginInvokeResponse.fbs +0 -30
- package/schemas/spacedatastandards/PLG.fbs +0 -191
- package/src/generated/orbpro/invoke/plugin-invoke-request.d.ts +0 -51
- package/src/generated/orbpro/invoke/plugin-invoke-request.d.ts.map +0 -1
- package/src/generated/orbpro/invoke/plugin-invoke-request.js +0 -131
- package/src/generated/orbpro/invoke/plugin-invoke-request.js.map +0 -1
- package/src/generated/orbpro/invoke/plugin-invoke-request.ts +0 -173
- package/src/generated/orbpro/invoke/plugin-invoke-response.d.ts +0 -76
- package/src/generated/orbpro/invoke/plugin-invoke-response.d.ts.map +0 -1
- package/src/generated/orbpro/invoke/plugin-invoke-response.js +0 -184
- package/src/generated/orbpro/invoke/plugin-invoke-response.js.map +0 -1
- package/src/generated/orbpro/invoke/plugin-invoke-response.ts +0 -243
- package/src/generated/orbpro/invoke.d.ts +0 -3
- package/src/generated/orbpro/invoke.js +0 -5
- package/src/generated/spacedatastandards/plg/entry-function.js +0 -86
- package/src/generated/spacedatastandards/plg/entry-function.ts +0 -119
- package/src/generated/spacedatastandards/plg/index.js +0 -16
- package/src/generated/spacedatastandards/plg/index.ts +0 -11
- package/src/generated/spacedatastandards/plg/plg.js +0 -657
- package/src/generated/spacedatastandards/plg/plg.ts +0 -924
- package/src/generated/spacedatastandards/plg/plugin-capability.js +0 -66
- package/src/generated/spacedatastandards/plg/plugin-category.js +0 -15
- package/src/generated/spacedatastandards/plg/plugin-category.ts +0 -53
- package/src/generated/spacedatastandards/plg/plugin-dependency.js +0 -63
- package/src/generated/spacedatastandards/plg/plugin-dependency.ts +0 -86
- package/src/generated/spacedatastandards/plg/publication-state.js +0 -9
- package/src/generated/spacedatastandards/plg/purchase-tier.js +0 -9
- /package/src/generated/spacedatastandards/plg/{publication-state.ts → publicationState.ts} +0 -0
- /package/src/generated/spacedatastandards/plg/{purchase-tier.ts → purchaseTier.ts} +0 -0
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
// Isomorphic-pthreads guardrail: the enforced flag set + the emitted-artifact
|
|
2
|
+
// validator. `space-data-module-sdk` is the source of truth for isomorphic
|
|
3
|
+
// pthreads module artifacts, so a build that claims pthreads but does not emit a
|
|
4
|
+
// shared-memory/atomics wasm must FAIL the compile here rather than ship.
|
|
5
|
+
//
|
|
6
|
+
// See docs/isomorphic-pthreads.md.
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
decodeUnsignedLeb128,
|
|
10
|
+
listWasmCustomSections,
|
|
11
|
+
parseWasmModuleSections,
|
|
12
|
+
} from "../bundle/wasm.js";
|
|
13
|
+
|
|
14
|
+
// Mirrors ModuleThreadModel.EMSCRIPTEN_PTHREADS in compileModule.js. Kept as a
|
|
15
|
+
// local literal to avoid an import cycle (compileModule imports this module).
|
|
16
|
+
// NOTE: the enum value string is historical; this model now compiles to a
|
|
17
|
+
// wasi-threads artifact (NOT an Emscripten Web-Worker build). See
|
|
18
|
+
// docs/isomorphic-pthreads.md and wasiThreadsToolchain.js.
|
|
19
|
+
const EMSCRIPTEN_PTHREADS = "emscripten-pthreads";
|
|
20
|
+
|
|
21
|
+
// The non-bypassable final-LINK flag set for the isomorphic-pthreads model.
|
|
22
|
+
//
|
|
23
|
+
// These are the wasm-ld / clang flags that produce a wasi-threads artifact:
|
|
24
|
+
// -pthread select the threads runtime (wasi-threads libc/libc++)
|
|
25
|
+
// -matomics -mbulk-memory enable the atomics + bulk-memory target features
|
|
26
|
+
// -Wl,--import-memory import the (shared) memory from the host (env.memory)
|
|
27
|
+
// -Wl,--shared-memory make that memory shared (SharedArrayBuffer / atomics)
|
|
28
|
+
// -Wl,--max-memory=2GiB required maximum for a shared, growable memory
|
|
29
|
+
//
|
|
30
|
+
// These deliberately do NOT use Emscripten `-s` settings. Emscripten `-pthread`
|
|
31
|
+
// (even with -s STANDALONE_WASM=1) emits the browser-only Web Worker model
|
|
32
|
+
// (env.__pthread_create_js + _emscripten_* mailbox/postMessage imports, no wasi
|
|
33
|
+
// thread-spawn) which CANNOT thread under WasmEdge. `-mthreads` is likewise not
|
|
34
|
+
// used (a MinGW driver flag, not a wasm target feature). The thread guarantee is
|
|
35
|
+
// delivered by these flags AND validated on the emitted artifact by
|
|
36
|
+
// assertPthreadArtifact() below (shared memory + atomics + wasi.thread-spawn
|
|
37
|
+
// import + wasi_thread_start export, and NO Emscripten thread hooks).
|
|
38
|
+
// The ONE sanctioned clang target for every SDK-built guest, threaded or not.
|
|
39
|
+
// assertSequentialArtifact holds the sequential model to it so that the model
|
|
40
|
+
// cannot be used to slip off the isomorphic toolchain via SDN_WASI_TARGET.
|
|
41
|
+
export const SANCTIONED_WASI_TARGET = "wasm32-wasip1-threads";
|
|
42
|
+
|
|
43
|
+
// Link flags a wasi-sequential final link must NEVER carry.
|
|
44
|
+
//
|
|
45
|
+
// The one that MATTERS is `--import-memory`: it makes the guest depend on a
|
|
46
|
+
// host-supplied memory it has no wasi-threads contract to receive, which is
|
|
47
|
+
// precisely what WasmEdge refuses to instantiate.
|
|
48
|
+
//
|
|
49
|
+
// `--shared-memory` is listed too, but NOT because shared memory is a defect —
|
|
50
|
+
// on `wasm32-wasip1-threads` a DECLARED shared memory is the normal, expected
|
|
51
|
+
// driver output for a sequential artifact (the triple implies atomics; see
|
|
52
|
+
// assertSequentialArtifact). Passing it EXPLICITLY is rejected only as a
|
|
53
|
+
// configuration smell: it signals someone reaching for the threaded profile in
|
|
54
|
+
// a sequential link, and it is free to catch at flag time.
|
|
55
|
+
const SEQUENTIAL_FORBIDDEN_LINK_FLAGS = Object.freeze([
|
|
56
|
+
"-Wl,--shared-memory",
|
|
57
|
+
"-Wl,--import-memory",
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The mirror of assertPthreadFlagsPresent: after the final-link args are
|
|
62
|
+
* assembled for the wasi-sequential model, confirm none of the thread-memory
|
|
63
|
+
* flags survived. A future edit that reuses PTHREAD_FINAL_LINK_FLAGS for the
|
|
64
|
+
* sequential profile fails here, loudly, instead of silently shipping a guest
|
|
65
|
+
* that needs cross-origin isolation to load in a browser.
|
|
66
|
+
*
|
|
67
|
+
* @param {string[]} args assembled final-link argument list.
|
|
68
|
+
* @param {{ threadModel?: string }} [context]
|
|
69
|
+
*/
|
|
70
|
+
export function assertSequentialFlagsAbsent(args, context = {}) {
|
|
71
|
+
const present = SEQUENTIAL_FORBIDDEN_LINK_FLAGS.filter((flag) =>
|
|
72
|
+
(Array.isArray(args) ? args : []).includes(flag),
|
|
73
|
+
);
|
|
74
|
+
if (present.length > 0) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`wasi-sequential final-link args must not contain ${present.join(" ")} ` +
|
|
77
|
+
`(threadModel ${JSON.stringify(context.threadModel ?? "wasi-sequential")}). ` +
|
|
78
|
+
"--import-memory gives the guest a memory it has no wasi-threads contract to " +
|
|
79
|
+
"receive, which is what WasmEdge refuses to instantiate. --shared-memory is " +
|
|
80
|
+
"rejected as a configuration smell, NOT because shared memory is wrong: a " +
|
|
81
|
+
"declared shared memory is the expected driver output on this triple.",
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return args;
|
|
85
|
+
}
|
|
86
|
+
const SEQUENTIAL_MIRROR_PTHREAD_MODEL = "emscripten-pthreads";
|
|
87
|
+
|
|
88
|
+
export const PTHREAD_FINAL_LINK_FLAGS = Object.freeze([
|
|
89
|
+
"-pthread",
|
|
90
|
+
"-matomics",
|
|
91
|
+
"-mbulk-memory",
|
|
92
|
+
"-Wl,--import-memory",
|
|
93
|
+
"-Wl,--shared-memory",
|
|
94
|
+
"-Wl,--max-memory=2147483648",
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
// The bare flags that must be present in the assembled final-link args for the
|
|
98
|
+
// pthreads model.
|
|
99
|
+
const PTHREAD_REQUIRED_BARE_FLAGS = Object.freeze([
|
|
100
|
+
"-pthread",
|
|
101
|
+
"-matomics",
|
|
102
|
+
"-mbulk-memory",
|
|
103
|
+
"-Wl,--import-memory",
|
|
104
|
+
"-Wl,--shared-memory",
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
// A `-Wl,--max-memory=<n>` flag (any value) must also be present.
|
|
108
|
+
const PTHREAD_MAX_MEMORY_PREFIX = "-Wl,--max-memory=";
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Defensive invariant: after the final-link args are assembled, confirm that the
|
|
112
|
+
* pthreads model still carries every mandated flag. This makes it impossible for
|
|
113
|
+
* a future edit to silently drop a thread-enabling flag — the compile fails loud
|
|
114
|
+
* instead.
|
|
115
|
+
*
|
|
116
|
+
* @param {string[]} args assembled final-link argument list.
|
|
117
|
+
* @param {{ threadModel?: string }} [context]
|
|
118
|
+
*/
|
|
119
|
+
export function assertPthreadFlagsPresent(args, context = {}) {
|
|
120
|
+
if (context.threadModel !== EMSCRIPTEN_PTHREADS) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const argList = Array.isArray(args) ? args.map((value) => String(value)) : [];
|
|
124
|
+
const missing = [];
|
|
125
|
+
for (const flag of PTHREAD_REQUIRED_BARE_FLAGS) {
|
|
126
|
+
if (!argList.includes(flag)) {
|
|
127
|
+
missing.push(flag);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (!argList.some((value) => value.startsWith(PTHREAD_MAX_MEMORY_PREFIX))) {
|
|
131
|
+
missing.push(`${PTHREAD_MAX_MEMORY_PREFIX}<bytes>`);
|
|
132
|
+
}
|
|
133
|
+
if (missing.length > 0) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
"Pthreads final-link flag assembler dropped mandated flags: " +
|
|
136
|
+
`${missing.join(", ")}. The emscripten-pthreads model must always link ` +
|
|
137
|
+
`with ${PTHREAD_FINAL_LINK_FLAGS.join(" ")}.`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const VALTYPE_BYTES = new Set([0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x70, 0x6f]);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Skip a LEB128-encoded integer (signedness is irrelevant for skipping — only
|
|
146
|
+
* the continuation bits matter). Bounds-checked.
|
|
147
|
+
*/
|
|
148
|
+
function skipLeb(bytes, cursor, end) {
|
|
149
|
+
let position = cursor;
|
|
150
|
+
while (position < end) {
|
|
151
|
+
const byte = bytes[position++];
|
|
152
|
+
if ((byte & 0x80) === 0) {
|
|
153
|
+
return position;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
throw new Error("LEB128 immediate ran past end of function body.");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Decode a LEB128 value AND return the next offset (bounds-checked). */
|
|
160
|
+
function readLeb(bytes, cursor, end) {
|
|
161
|
+
if (cursor >= end) {
|
|
162
|
+
throw new Error("LEB128 immediate ran past end of function body.");
|
|
163
|
+
}
|
|
164
|
+
const info = decodeUnsignedLeb128(bytes, cursor);
|
|
165
|
+
if (info.nextOffset > end) {
|
|
166
|
+
throw new Error("LEB128 immediate ran past end of function body.");
|
|
167
|
+
}
|
|
168
|
+
return info;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function skipBlockType(bytes, cursor, end) {
|
|
172
|
+
if (cursor >= end) {
|
|
173
|
+
throw new Error("Block type ran past end of function body.");
|
|
174
|
+
}
|
|
175
|
+
const byte = bytes[cursor];
|
|
176
|
+
// 0x40 (empty), single-byte value types, and single-byte type indices all
|
|
177
|
+
// consume exactly one byte; only a multi-byte (continuation-bit) type index
|
|
178
|
+
// needs a full LEB skip.
|
|
179
|
+
if ((byte & 0x80) !== 0) {
|
|
180
|
+
return skipLeb(bytes, cursor, end);
|
|
181
|
+
}
|
|
182
|
+
return cursor + 1;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function skipMemArg(bytes, cursor, end) {
|
|
186
|
+
const alignInfo = readLeb(bytes, cursor, end);
|
|
187
|
+
let position = alignInfo.nextOffset;
|
|
188
|
+
// Multi-memory / memory64: align bit 6 flags a following memory index.
|
|
189
|
+
if ((alignInfo.value & 0x40) !== 0) {
|
|
190
|
+
position = skipLeb(bytes, position, end);
|
|
191
|
+
}
|
|
192
|
+
return skipLeb(bytes, position, end);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function skipVec(bytes, cursor, end, bytesPerElement) {
|
|
196
|
+
const countInfo = readLeb(bytes, cursor, end);
|
|
197
|
+
let position = countInfo.nextOffset;
|
|
198
|
+
for (let i = 0; i < countInfo.value; i += 1) {
|
|
199
|
+
if (bytesPerElement === "leb") {
|
|
200
|
+
position = skipLeb(bytes, position, end);
|
|
201
|
+
} else {
|
|
202
|
+
position += bytesPerElement;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (position > end) {
|
|
206
|
+
throw new Error("Vector immediate ran past end of function body.");
|
|
207
|
+
}
|
|
208
|
+
return position;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Walk one function body and count genuine 0xFE-prefixed atomic instructions.
|
|
213
|
+
* This is a real instruction decoder — NOT a byte scan — so `0xFE` bytes that
|
|
214
|
+
* appear inside i32.const / LEB128 / memarg immediates are never miscounted.
|
|
215
|
+
*/
|
|
216
|
+
function countAtomicsInFunctionBody(bytes, bodyStart, bodyEnd) {
|
|
217
|
+
let cursor = bodyStart;
|
|
218
|
+
// Local declarations: vec of (count, valtype).
|
|
219
|
+
const localVecCount = readLeb(bytes, cursor, bodyEnd);
|
|
220
|
+
cursor = localVecCount.nextOffset;
|
|
221
|
+
for (let i = 0; i < localVecCount.value; i += 1) {
|
|
222
|
+
cursor = skipLeb(bytes, cursor, bodyEnd); // count
|
|
223
|
+
if (cursor >= bodyEnd) {
|
|
224
|
+
throw new Error("Local declaration ran past end of function body.");
|
|
225
|
+
}
|
|
226
|
+
// valtype: usually one byte; concrete heap types are a LEB.
|
|
227
|
+
if ((bytes[cursor] & 0x80) !== 0) {
|
|
228
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
229
|
+
} else {
|
|
230
|
+
cursor += 1;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let atomics = 0;
|
|
235
|
+
while (cursor < bodyEnd) {
|
|
236
|
+
const opcode = bytes[cursor++];
|
|
237
|
+
switch (opcode) {
|
|
238
|
+
// Control / parametric with no immediates.
|
|
239
|
+
case 0x00: // unreachable
|
|
240
|
+
case 0x01: // nop
|
|
241
|
+
case 0x05: // else
|
|
242
|
+
case 0x0b: // end
|
|
243
|
+
case 0x0f: // return
|
|
244
|
+
case 0x1a: // drop
|
|
245
|
+
case 0x1b: // select
|
|
246
|
+
break;
|
|
247
|
+
case 0x02: // block
|
|
248
|
+
case 0x03: // loop
|
|
249
|
+
case 0x04: // if
|
|
250
|
+
cursor = skipBlockType(bytes, cursor, bodyEnd);
|
|
251
|
+
break;
|
|
252
|
+
case 0x0c: // br
|
|
253
|
+
case 0x0d: // br_if
|
|
254
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
255
|
+
break;
|
|
256
|
+
case 0x0e: {
|
|
257
|
+
// br_table: vec<labelidx> + default labelidx
|
|
258
|
+
cursor = skipVec(bytes, cursor, bodyEnd, "leb");
|
|
259
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case 0x10: // call
|
|
263
|
+
case 0x12: // return_call
|
|
264
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
265
|
+
break;
|
|
266
|
+
case 0x11: // call_indirect
|
|
267
|
+
case 0x13: // return_call_indirect
|
|
268
|
+
cursor = skipLeb(bytes, cursor, bodyEnd); // typeidx
|
|
269
|
+
cursor = skipLeb(bytes, cursor, bodyEnd); // tableidx
|
|
270
|
+
break;
|
|
271
|
+
case 0x1c: // select t*
|
|
272
|
+
cursor = skipVec(bytes, cursor, bodyEnd, 1);
|
|
273
|
+
break;
|
|
274
|
+
case 0x20: // local.get
|
|
275
|
+
case 0x21: // local.set
|
|
276
|
+
case 0x22: // local.tee
|
|
277
|
+
case 0x23: // global.get
|
|
278
|
+
case 0x24: // global.set
|
|
279
|
+
case 0x25: // table.get
|
|
280
|
+
case 0x26: // table.set
|
|
281
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
282
|
+
break;
|
|
283
|
+
case 0x3f: // memory.size
|
|
284
|
+
case 0x40: // memory.grow
|
|
285
|
+
cursor = skipLeb(bytes, cursor, bodyEnd); // memidx (reserved)
|
|
286
|
+
break;
|
|
287
|
+
case 0x41: // i32.const
|
|
288
|
+
case 0x42: // i64.const
|
|
289
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
290
|
+
break;
|
|
291
|
+
case 0x43: // f32.const
|
|
292
|
+
cursor += 4;
|
|
293
|
+
break;
|
|
294
|
+
case 0x44: // f64.const
|
|
295
|
+
cursor += 8;
|
|
296
|
+
break;
|
|
297
|
+
case 0xd0: // ref.null
|
|
298
|
+
if (cursor < bodyEnd && (bytes[cursor] & 0x80) !== 0) {
|
|
299
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
300
|
+
} else {
|
|
301
|
+
cursor += 1;
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
case 0xd1: // ref.is_null
|
|
305
|
+
break;
|
|
306
|
+
case 0xd2: // ref.func
|
|
307
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
308
|
+
break;
|
|
309
|
+
case 0xfc: {
|
|
310
|
+
// Bulk-memory / saturating-truncation / table ops.
|
|
311
|
+
const sub = readLeb(bytes, cursor, bodyEnd);
|
|
312
|
+
cursor = sub.nextOffset;
|
|
313
|
+
switch (sub.value) {
|
|
314
|
+
case 0: case 1: case 2: case 3:
|
|
315
|
+
case 4: case 5: case 6: case 7:
|
|
316
|
+
break; // trunc_sat: no immediates
|
|
317
|
+
case 8: // memory.init dataidx memidx
|
|
318
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
319
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
320
|
+
break;
|
|
321
|
+
case 9: // data.drop
|
|
322
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
323
|
+
break;
|
|
324
|
+
case 10: // memory.copy memidx memidx
|
|
325
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
326
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
327
|
+
break;
|
|
328
|
+
case 11: // memory.fill memidx
|
|
329
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
330
|
+
break;
|
|
331
|
+
case 12: // table.init elemidx tableidx
|
|
332
|
+
case 14: // table.copy tableidx tableidx
|
|
333
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
334
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
335
|
+
break;
|
|
336
|
+
case 13: // elem.drop
|
|
337
|
+
case 15: // table.grow
|
|
338
|
+
case 16: // table.size
|
|
339
|
+
case 17: // table.fill
|
|
340
|
+
cursor = skipLeb(bytes, cursor, bodyEnd);
|
|
341
|
+
break;
|
|
342
|
+
default:
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
case 0xfd: {
|
|
348
|
+
// SIMD (not emitted by our pthreads builds, but decode defensively).
|
|
349
|
+
const sub = readLeb(bytes, cursor, bodyEnd);
|
|
350
|
+
cursor = sub.nextOffset;
|
|
351
|
+
const op = sub.value;
|
|
352
|
+
if (op <= 0x0b) {
|
|
353
|
+
cursor = skipMemArg(bytes, cursor, bodyEnd); // v128 load/store
|
|
354
|
+
} else if (op === 0x0c || op === 0x0d) {
|
|
355
|
+
cursor += 16; // v128.const / i8x16.shuffle
|
|
356
|
+
} else if (op >= 0x15 && op <= 0x22) {
|
|
357
|
+
cursor += 1; // extract/replace lane
|
|
358
|
+
} else if (op >= 0x54 && op <= 0x5b) {
|
|
359
|
+
cursor = skipMemArg(bytes, cursor, bodyEnd);
|
|
360
|
+
cursor += 1; // load/store lane
|
|
361
|
+
} else if (op === 0x5c || op === 0x5d) {
|
|
362
|
+
cursor = skipMemArg(bytes, cursor, bodyEnd); // load32/64_zero
|
|
363
|
+
}
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
case 0xfe: {
|
|
367
|
+
// Atomics / threads.
|
|
368
|
+
const sub = readLeb(bytes, cursor, bodyEnd);
|
|
369
|
+
cursor = sub.nextOffset;
|
|
370
|
+
atomics += 1;
|
|
371
|
+
if (sub.value === 0x03) {
|
|
372
|
+
cursor += 1; // atomic.fence: one reserved byte, no memarg
|
|
373
|
+
} else {
|
|
374
|
+
cursor = skipMemArg(bytes, cursor, bodyEnd);
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
default:
|
|
379
|
+
if (opcode >= 0x28 && opcode <= 0x3e) {
|
|
380
|
+
// Memory load/store family (i32.load .. i64.store32): each carries a
|
|
381
|
+
// memarg (align + offset). Skipping this is what prevents 0xFE bytes
|
|
382
|
+
// inside memory-offset immediates from being miscounted as atomics.
|
|
383
|
+
cursor = skipMemArg(bytes, cursor, bodyEnd);
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
// All remaining MVP numeric/comparison/conversion opcodes
|
|
387
|
+
// (0x45..0xc4, sign-extension, etc.) take no immediates.
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
if (cursor > bodyEnd) {
|
|
391
|
+
throw new Error("Instruction immediate ran past end of function body.");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return atomics;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function countAtomicInstructions(parsed) {
|
|
398
|
+
const codeSection = parsed.sections.find((section) => section.id === 10);
|
|
399
|
+
if (!codeSection) {
|
|
400
|
+
return 0;
|
|
401
|
+
}
|
|
402
|
+
const bytes = parsed.bytes;
|
|
403
|
+
let total = 0;
|
|
404
|
+
try {
|
|
405
|
+
let cursor = codeSection.payloadStart;
|
|
406
|
+
const funcCount = readLeb(bytes, cursor, codeSection.payloadEnd);
|
|
407
|
+
cursor = funcCount.nextOffset;
|
|
408
|
+
for (let i = 0; i < funcCount.value && cursor < codeSection.payloadEnd; i += 1) {
|
|
409
|
+
const sizeInfo = readLeb(bytes, cursor, codeSection.payloadEnd);
|
|
410
|
+
const bodyStart = sizeInfo.nextOffset;
|
|
411
|
+
const bodyEnd = bodyStart + sizeInfo.value;
|
|
412
|
+
if (bodyEnd > codeSection.payloadEnd) {
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
total += countAtomicsInFunctionBody(bytes, bodyStart, bodyEnd);
|
|
417
|
+
} catch {
|
|
418
|
+
// Best-effort per function: skip an undecodable body rather than crash.
|
|
419
|
+
}
|
|
420
|
+
cursor = bodyEnd;
|
|
421
|
+
}
|
|
422
|
+
} catch {
|
|
423
|
+
// Truncated / malformed code section: return best-effort count.
|
|
424
|
+
}
|
|
425
|
+
return total;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function readLimits(bytes, cursor, end) {
|
|
429
|
+
const flags = bytes[cursor];
|
|
430
|
+
let position = cursor + 1;
|
|
431
|
+
const minInfo = decodeUnsignedLeb128(bytes, position);
|
|
432
|
+
position = minInfo.nextOffset;
|
|
433
|
+
let max = null;
|
|
434
|
+
if ((flags & 0x01) !== 0) {
|
|
435
|
+
const maxInfo = decodeUnsignedLeb128(bytes, position);
|
|
436
|
+
position = maxInfo.nextOffset;
|
|
437
|
+
max = maxInfo.value;
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
nextOffset: position,
|
|
441
|
+
limits: {
|
|
442
|
+
flags,
|
|
443
|
+
min: minInfo.value,
|
|
444
|
+
max,
|
|
445
|
+
shared: (flags & 0x02) !== 0,
|
|
446
|
+
growable: max === null || max > minInfo.value,
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const textDecoder = new TextDecoder();
|
|
452
|
+
|
|
453
|
+
function collectImportedMemories(parsed) {
|
|
454
|
+
const section = parsed.sections.find((s) => s.id === 2);
|
|
455
|
+
if (!section) {
|
|
456
|
+
return [];
|
|
457
|
+
}
|
|
458
|
+
const bytes = parsed.bytes;
|
|
459
|
+
const memories = [];
|
|
460
|
+
let cursor = section.payloadStart;
|
|
461
|
+
const countInfo = decodeUnsignedLeb128(bytes, cursor);
|
|
462
|
+
cursor = countInfo.nextOffset;
|
|
463
|
+
for (let i = 0; i < countInfo.value; i += 1) {
|
|
464
|
+
const modLen = decodeUnsignedLeb128(bytes, cursor);
|
|
465
|
+
cursor = modLen.nextOffset;
|
|
466
|
+
const moduleName = textDecoder.decode(bytes.subarray(cursor, cursor + modLen.value));
|
|
467
|
+
cursor += modLen.value;
|
|
468
|
+
const nameLen = decodeUnsignedLeb128(bytes, cursor);
|
|
469
|
+
cursor = nameLen.nextOffset;
|
|
470
|
+
const importName = textDecoder.decode(bytes.subarray(cursor, cursor + nameLen.value));
|
|
471
|
+
cursor += nameLen.value;
|
|
472
|
+
const kind = bytes[cursor++];
|
|
473
|
+
if (kind === 0x00) {
|
|
474
|
+
cursor = decodeUnsignedLeb128(bytes, cursor).nextOffset; // typeidx
|
|
475
|
+
} else if (kind === 0x01) {
|
|
476
|
+
cursor += 1; // reftype
|
|
477
|
+
const limits = readLimits(bytes, cursor, section.payloadEnd);
|
|
478
|
+
cursor = limits.nextOffset;
|
|
479
|
+
} else if (kind === 0x02) {
|
|
480
|
+
const limits = readLimits(bytes, cursor, section.payloadEnd);
|
|
481
|
+
cursor = limits.nextOffset;
|
|
482
|
+
memories.push({
|
|
483
|
+
source: "import",
|
|
484
|
+
module: moduleName,
|
|
485
|
+
name: importName,
|
|
486
|
+
...limits.limits,
|
|
487
|
+
});
|
|
488
|
+
} else if (kind === 0x03) {
|
|
489
|
+
cursor += 1; // valtype
|
|
490
|
+
cursor += 1; // mutability
|
|
491
|
+
} else {
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return memories;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function collectDeclaredMemories(parsed) {
|
|
499
|
+
const section = parsed.sections.find((s) => s.id === 5);
|
|
500
|
+
if (!section) {
|
|
501
|
+
return [];
|
|
502
|
+
}
|
|
503
|
+
const bytes = parsed.bytes;
|
|
504
|
+
const memories = [];
|
|
505
|
+
let cursor = section.payloadStart;
|
|
506
|
+
const countInfo = decodeUnsignedLeb128(bytes, cursor);
|
|
507
|
+
cursor = countInfo.nextOffset;
|
|
508
|
+
for (let i = 0; i < countInfo.value; i += 1) {
|
|
509
|
+
const limits = readLimits(bytes, cursor, section.payloadEnd);
|
|
510
|
+
cursor = limits.nextOffset;
|
|
511
|
+
memories.push({ source: "declared", ...limits.limits });
|
|
512
|
+
}
|
|
513
|
+
return memories;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const IMPORT_KIND_NAMES = ["function", "table", "memory", "global"];
|
|
517
|
+
|
|
518
|
+
function collectAllImports(parsed) {
|
|
519
|
+
const section = parsed.sections.find((s) => s.id === 2);
|
|
520
|
+
if (!section) {
|
|
521
|
+
return [];
|
|
522
|
+
}
|
|
523
|
+
const bytes = parsed.bytes;
|
|
524
|
+
const imports = [];
|
|
525
|
+
let cursor = section.payloadStart;
|
|
526
|
+
const countInfo = decodeUnsignedLeb128(bytes, cursor);
|
|
527
|
+
cursor = countInfo.nextOffset;
|
|
528
|
+
for (let i = 0; i < countInfo.value; i += 1) {
|
|
529
|
+
const modLen = decodeUnsignedLeb128(bytes, cursor);
|
|
530
|
+
cursor = modLen.nextOffset;
|
|
531
|
+
const moduleName = textDecoder.decode(bytes.subarray(cursor, cursor + modLen.value));
|
|
532
|
+
cursor += modLen.value;
|
|
533
|
+
const nameLen = decodeUnsignedLeb128(bytes, cursor);
|
|
534
|
+
cursor = nameLen.nextOffset;
|
|
535
|
+
const importName = textDecoder.decode(bytes.subarray(cursor, cursor + nameLen.value));
|
|
536
|
+
cursor += nameLen.value;
|
|
537
|
+
const kind = bytes[cursor++];
|
|
538
|
+
imports.push({ module: moduleName, name: importName, kind: IMPORT_KIND_NAMES[kind] ?? kind });
|
|
539
|
+
if (kind === 0x00) {
|
|
540
|
+
cursor = decodeUnsignedLeb128(bytes, cursor).nextOffset; // typeidx
|
|
541
|
+
} else if (kind === 0x01) {
|
|
542
|
+
cursor += 1; // reftype
|
|
543
|
+
const limits = readLimits(bytes, cursor, section.payloadEnd);
|
|
544
|
+
cursor = limits.nextOffset;
|
|
545
|
+
} else if (kind === 0x02) {
|
|
546
|
+
const limits = readLimits(bytes, cursor, section.payloadEnd);
|
|
547
|
+
cursor = limits.nextOffset;
|
|
548
|
+
} else if (kind === 0x03) {
|
|
549
|
+
cursor += 2; // valtype + mutability
|
|
550
|
+
} else {
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return imports;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function collectExportNames(parsed) {
|
|
558
|
+
const section = parsed.sections.find((s) => s.id === 7);
|
|
559
|
+
if (!section) {
|
|
560
|
+
return [];
|
|
561
|
+
}
|
|
562
|
+
const bytes = parsed.bytes;
|
|
563
|
+
const names = [];
|
|
564
|
+
let cursor = section.payloadStart;
|
|
565
|
+
const countInfo = decodeUnsignedLeb128(bytes, cursor);
|
|
566
|
+
cursor = countInfo.nextOffset;
|
|
567
|
+
for (let i = 0; i < countInfo.value; i += 1) {
|
|
568
|
+
const nameLen = decodeUnsignedLeb128(bytes, cursor);
|
|
569
|
+
cursor = nameLen.nextOffset;
|
|
570
|
+
names.push(textDecoder.decode(bytes.subarray(cursor, cursor + nameLen.value)));
|
|
571
|
+
cursor += nameLen.value;
|
|
572
|
+
cursor += 1; // export kind
|
|
573
|
+
cursor = decodeUnsignedLeb128(bytes, cursor).nextOffset; // index
|
|
574
|
+
}
|
|
575
|
+
return names;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// The WasmEdge/wasi thread-spawn host contract: the guest imports a
|
|
579
|
+
// `thread-spawn` function and exports `wasi_thread_start`.
|
|
580
|
+
function isWasiThreadSpawnImport(entry) {
|
|
581
|
+
return (
|
|
582
|
+
(entry.module === "wasi" || entry.module === "wasi_snapshot_preview1") &&
|
|
583
|
+
entry.name === "thread-spawn"
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Emscripten's browser-only Web Worker thread hooks. Their presence means the
|
|
588
|
+
// artifact threads via JS postMessage workers and CANNOT spawn threads under
|
|
589
|
+
// WasmEdge — it must be rejected as an isomorphic-pthreads artifact.
|
|
590
|
+
function isEmscriptenThreadHook(entry) {
|
|
591
|
+
if (entry.module !== "env") {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
return (
|
|
595
|
+
entry.name === "__pthread_create_js" ||
|
|
596
|
+
/pthread_create/.test(entry.name) ||
|
|
597
|
+
/^_?emscripten_.*(thread|mailbox|main_thread|postmessage)/i.test(entry.name)
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function readDeclaredFeatures(wasmBytes) {
|
|
602
|
+
let customSections;
|
|
603
|
+
try {
|
|
604
|
+
customSections = listWasmCustomSections(wasmBytes);
|
|
605
|
+
} catch {
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
const featureSection = customSections.find(
|
|
609
|
+
(section) => section.name === "target_features",
|
|
610
|
+
);
|
|
611
|
+
if (!featureSection) {
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
const bytes = featureSection.dataBytes;
|
|
615
|
+
const features = [];
|
|
616
|
+
try {
|
|
617
|
+
let cursor = 0;
|
|
618
|
+
const countInfo = decodeUnsignedLeb128(bytes, cursor);
|
|
619
|
+
cursor = countInfo.nextOffset;
|
|
620
|
+
for (let i = 0; i < countInfo.value; i += 1) {
|
|
621
|
+
cursor += 1; // prefix ('+', '-', '=')
|
|
622
|
+
const nameLen = decodeUnsignedLeb128(bytes, cursor);
|
|
623
|
+
cursor = nameLen.nextOffset;
|
|
624
|
+
features.push(
|
|
625
|
+
textDecoder.decode(bytes.subarray(cursor, cursor + nameLen.value)),
|
|
626
|
+
);
|
|
627
|
+
cursor += nameLen.value;
|
|
628
|
+
}
|
|
629
|
+
} catch {
|
|
630
|
+
return features.length > 0 ? features : null;
|
|
631
|
+
}
|
|
632
|
+
return features;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Parse a compiled wasm artifact and report its threading features, including
|
|
637
|
+
* the isomorphic wasi-threads host contract.
|
|
638
|
+
*
|
|
639
|
+
* @param {Uint8Array|ArrayBuffer|ArrayBufferView} wasmBytes
|
|
640
|
+
* @returns {{
|
|
641
|
+
* hasSharedMemory: boolean,
|
|
642
|
+
* sharedMemory: object|null,
|
|
643
|
+
* memories: object[],
|
|
644
|
+
* usesAtomics: boolean,
|
|
645
|
+
* atomicInstructionCount: number,
|
|
646
|
+
* declaredFeatures: string[]|null,
|
|
647
|
+
* isGrowableSharedMemory: boolean,
|
|
648
|
+
* hasWasiThreadSpawnImport: boolean,
|
|
649
|
+
* hasWasiThreadStartExport: boolean,
|
|
650
|
+
* emscriptenThreadHooks: string[],
|
|
651
|
+
* isIsomorphicPthreads: boolean,
|
|
652
|
+
* }}
|
|
653
|
+
*/
|
|
654
|
+
export function analyzeWasmThreadFeatures(wasmBytes) {
|
|
655
|
+
const parsed = parseWasmModuleSections(wasmBytes);
|
|
656
|
+
const memories = [
|
|
657
|
+
...collectImportedMemories(parsed),
|
|
658
|
+
...collectDeclaredMemories(parsed),
|
|
659
|
+
];
|
|
660
|
+
const sharedMemory = memories.find((memory) => memory.shared) ?? null;
|
|
661
|
+
const atomicInstructionCount = countAtomicInstructions(parsed);
|
|
662
|
+
const declaredFeatures = readDeclaredFeatures(parsed.bytes);
|
|
663
|
+
const usesAtomics =
|
|
664
|
+
atomicInstructionCount > 0 ||
|
|
665
|
+
(Array.isArray(declaredFeatures) && declaredFeatures.includes("atomics"));
|
|
666
|
+
const imports = collectAllImports(parsed);
|
|
667
|
+
const exportNames = collectExportNames(parsed);
|
|
668
|
+
const hasWasiThreadSpawnImport = imports.some(isWasiThreadSpawnImport);
|
|
669
|
+
const hasWasiThreadStartExport = exportNames.includes("wasi_thread_start");
|
|
670
|
+
const emscriptenThreadHooks = imports
|
|
671
|
+
.filter(isEmscriptenThreadHook)
|
|
672
|
+
.map((entry) => `${entry.module}.${entry.name}`);
|
|
673
|
+
const hasSharedMemory = sharedMemory !== null;
|
|
674
|
+
return {
|
|
675
|
+
hasSharedMemory,
|
|
676
|
+
sharedMemory,
|
|
677
|
+
memories,
|
|
678
|
+
usesAtomics,
|
|
679
|
+
atomicInstructionCount,
|
|
680
|
+
declaredFeatures,
|
|
681
|
+
isGrowableSharedMemory: hasSharedMemory && sharedMemory.growable === true,
|
|
682
|
+
hasWasiThreadSpawnImport,
|
|
683
|
+
hasWasiThreadStartExport,
|
|
684
|
+
emscriptenThreadHooks,
|
|
685
|
+
isIsomorphicPthreads:
|
|
686
|
+
hasSharedMemory &&
|
|
687
|
+
usesAtomics &&
|
|
688
|
+
hasWasiThreadSpawnImport &&
|
|
689
|
+
hasWasiThreadStartExport &&
|
|
690
|
+
emscriptenThreadHooks.length === 0,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Reject a compiled wasm artifact that claims the isomorphic-pthreads thread
|
|
696
|
+
* model but is not a real wasi-threads artifact. To pass, the emitted wasm MUST:
|
|
697
|
+
* - declare/import a shared memory (shared limits flag), and
|
|
698
|
+
* - use atomics (atomic instructions in the code section), and
|
|
699
|
+
* - import the wasi `thread-spawn` host function, and
|
|
700
|
+
* - export `wasi_thread_start`, and
|
|
701
|
+
* - NOT import Emscripten's browser-only Web Worker thread hooks
|
|
702
|
+
* (env.__pthread_create_js / env._emscripten_* mailbox/postMessage), which
|
|
703
|
+
* cannot spawn threads under WasmEdge.
|
|
704
|
+
*
|
|
705
|
+
* Shared memory + atomics alone are necessary but NOT sufficient — an Emscripten
|
|
706
|
+
* `-pthread` browser build has both yet threads only via JS workers. Returns the
|
|
707
|
+
* analysis on success; throws a clear, actionable error otherwise.
|
|
708
|
+
*
|
|
709
|
+
* @param {Uint8Array|ArrayBuffer|ArrayBufferView} wasmBytes
|
|
710
|
+
* @param {{ source?: string }} [options]
|
|
711
|
+
*/
|
|
712
|
+
export function assertPthreadArtifact(wasmBytes, options = {}) {
|
|
713
|
+
const source = options.source ? String(options.source) : "emitted wasm";
|
|
714
|
+
let analysis;
|
|
715
|
+
try {
|
|
716
|
+
analysis = analyzeWasmThreadFeatures(wasmBytes);
|
|
717
|
+
} catch (error) {
|
|
718
|
+
throw new Error(
|
|
719
|
+
`pthreads artifact validation failed for ${source}: could not parse the ` +
|
|
720
|
+
`emitted wasm (${error instanceof Error ? error.message : String(error)}).`,
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
const failures = [];
|
|
724
|
+
if (!analysis.hasSharedMemory) {
|
|
725
|
+
failures.push(
|
|
726
|
+
"it declares no shared memory (a threaded module must import or declare a " +
|
|
727
|
+
"memory with the shared limits flag)",
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
if (!analysis.usesAtomics) {
|
|
731
|
+
failures.push(
|
|
732
|
+
"it uses no atomics (no atomic/threads instructions were found in the code section)",
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
if (!analysis.hasWasiThreadSpawnImport) {
|
|
736
|
+
failures.push(
|
|
737
|
+
"it does not import the wasi `thread-spawn` host function (WasmEdge cannot " +
|
|
738
|
+
"spawn guest threads without the wasi-threads contract)",
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
if (!analysis.hasWasiThreadStartExport) {
|
|
742
|
+
failures.push(
|
|
743
|
+
"it does not export `wasi_thread_start` (the wasi-threads entry a host " +
|
|
744
|
+
"invokes to run a spawned thread)",
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
if (analysis.emscriptenThreadHooks.length > 0) {
|
|
748
|
+
failures.push(
|
|
749
|
+
"it imports Emscripten's browser-only Web Worker thread hooks " +
|
|
750
|
+
`(${analysis.emscriptenThreadHooks.join(", ")}) — this is a JS-worker ` +
|
|
751
|
+
"build that cannot thread under WasmEdge, not an isomorphic artifact",
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
if (failures.length > 0) {
|
|
755
|
+
throw new Error(
|
|
756
|
+
`isomorphic-pthreads artifact validation REJECTED ${source}: the build ` +
|
|
757
|
+
"claims the pthreads thread model but the emitted wasm is not a valid " +
|
|
758
|
+
`wasi-threads shared-memory/atomics artifact — ${failures.join("; ")}. ` +
|
|
759
|
+
"This build must not ship.",
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
return analysis;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* The MIRROR of assertPthreadArtifact, for the wasi-sequential thread model.
|
|
767
|
+
*
|
|
768
|
+
* A module that declares itself sequential is making a permanent architectural
|
|
769
|
+
* claim: this guest can never spawn a thread. That claim buys it a
|
|
770
|
+
* self-contained artifact (its own memory, no wasi-threads contract) which is
|
|
771
|
+
* what lets an inherently-sequential guest instantiate under WasmEdge at all —
|
|
772
|
+
* an artifact that imports `env.memory` without exporting `wasi_thread_start`
|
|
773
|
+
* is an INCOMPLETE wasi-threads contract and WasmEdge refuses it with
|
|
774
|
+
* "unknown import: env.memory".
|
|
775
|
+
*
|
|
776
|
+
* So the guard is the exact inverse of the pthreads one. To pass, the emitted
|
|
777
|
+
* wasm MUST:
|
|
778
|
+
* - declare NO shared memory (a sequential guest owns an unshared memory), and
|
|
779
|
+
* - NOT import the wasi `thread-spawn` host function, and
|
|
780
|
+
* - NOT export `wasi_thread_start`, and
|
|
781
|
+
* - NOT import Emscripten's browser-only Web Worker thread hooks.
|
|
782
|
+
*
|
|
783
|
+
* It must ALSO have been produced by the sanctioned target. Without that check
|
|
784
|
+
* `SDN_WASI_TARGET` would be an unpoliced route to a plain `wasm32-wasip1`
|
|
785
|
+
* object: the sequential model would become a way to leave the isomorphic
|
|
786
|
+
* toolchain entirely, which is the opposite of its purpose.
|
|
787
|
+
*
|
|
788
|
+
* @param {Uint8Array|ArrayBuffer|ArrayBufferView} wasmBytes
|
|
789
|
+
* @param {{ source?: string, target?: string }} [options]
|
|
790
|
+
*/
|
|
791
|
+
export function assertSequentialArtifact(wasmBytes, options = {}) {
|
|
792
|
+
const source = options.source ? String(options.source) : "emitted wasm";
|
|
793
|
+
let analysis;
|
|
794
|
+
try {
|
|
795
|
+
analysis = analyzeWasmThreadFeatures(wasmBytes);
|
|
796
|
+
} catch (error) {
|
|
797
|
+
throw new Error(
|
|
798
|
+
`wasi-sequential artifact validation failed for ${source}: could not parse the ` +
|
|
799
|
+
`emitted wasm (${error instanceof Error ? error.message : String(error)}).`,
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const failures = [];
|
|
804
|
+
|
|
805
|
+
// WHY THIS GUARD REJECTS AN IMPORTED MEMORY BUT NOT THE SHARED LIMITS BIT.
|
|
806
|
+
//
|
|
807
|
+
// READ THIS FIRST: on `wasm32-wasip1-threads` a DECLARED shared memory is the
|
|
808
|
+
// NORMAL, EXPECTED output for a sequential artifact — it is not a defect and
|
|
809
|
+
// not a hole. "Self-contained" here means the guest OWNS its memory; it does
|
|
810
|
+
// NOT mean "has no SharedArrayBuffer dependency". Those are different claims,
|
|
811
|
+
// and conflating them is what re-opens this argument.
|
|
812
|
+
//
|
|
813
|
+
// Read this before tightening the guard to "no shared memory" — on the
|
|
814
|
+
// mandated target that change is UNSATISFIABLE, and here is the measured
|
|
815
|
+
// reason rather than an assertion.
|
|
816
|
+
//
|
|
817
|
+
// ROOT CAUSE: the `wasm32-wasip1-threads` TRIPLE implies the `atomics` target
|
|
818
|
+
// feature. `wasm-objdump -x` on an object compiled for that triple with
|
|
819
|
+
// NEITHER `-matomics` NOR `-pthread` still reports
|
|
820
|
+
// `target_features: [+] atomics`. wasm-ld then infers a shared memory from
|
|
821
|
+
// that feature, so the DECLARED memory carries limits flags 0x03 with no
|
|
822
|
+
// `--shared-memory` anywhere on the link line. `--no-shared-memory` is not a
|
|
823
|
+
// wasm-ld flag ("unknown argument"). So "reject any shared limits bit" and
|
|
824
|
+
// "require target wasm32-wasip1-threads" cannot both hold — every artifact on
|
|
825
|
+
// that target would be rejected.
|
|
826
|
+
//
|
|
827
|
+
// Measured three ways on clang 22.1.2 + homebrew wasi-libc, all giving a
|
|
828
|
+
// DECLARED memory with flags 0x03: (a) threads-libc objects with no atomics
|
|
829
|
+
// flags; (b) the same objects with `-matomics -pthread`; (c) `--shared-memory`
|
|
830
|
+
// passed WITHOUT `--import-memory`.
|
|
831
|
+
//
|
|
832
|
+
// ARBITRATED 2026-07-28 on the SDK's OWN resolved toolchain (the exact
|
|
833
|
+
// clang/sysroot/resource-dir that resolveWasiThreadsToolchain picks), because
|
|
834
|
+
// an architecture review initially measured the opposite and required a
|
|
835
|
+
// "no shared memory" clause. Three independent re-runs:
|
|
836
|
+
// 1. bare `--target=wasm32-wasip1-threads`, zero feature flags
|
|
837
|
+
// -> `+atomics` present in target_features. Triple-implies-atomics holds.
|
|
838
|
+
// 2. driver-default link of `int main(){}` on that triple
|
|
839
|
+
// -> DECLARED shared memory (0x03) with no shared/import flag anywhere.
|
|
840
|
+
// 3. the review's own counter-artifacts were found to LACK `+atomics`,
|
|
841
|
+
// i.e. they had been built on the NON-threads triple that the same
|
|
842
|
+
// review's target clause forbids — so the counter-measurement did not
|
|
843
|
+
// apply to the mandated target.
|
|
844
|
+
// The "no shared memory" clause was therefore DROPPED as
|
|
845
|
+
// unsatisfiable-as-measured. Recorded here so nobody re-derives it from
|
|
846
|
+
// first principles and re-breaks the model.
|
|
847
|
+
//
|
|
848
|
+
// WHAT THE GUARD DOES ENFORCE, and why it is the property that matters:
|
|
849
|
+
// an artifact that IMPORTS `env.memory` while exporting no `wasi_thread_start`
|
|
850
|
+
// is an incomplete wasi-threads contract, and WasmEdge refuses to instantiate
|
|
851
|
+
// it — "unknown import: env.memory", reproduced on both the native 0.16.4
|
|
852
|
+
// binary and the pinned Docker image. A module that OWNS and exports its
|
|
853
|
+
// memory instantiates cleanly in every runtime.
|
|
854
|
+
//
|
|
855
|
+
// The residual exposure — a module-declared shared memory implies a
|
|
856
|
+
// SharedArrayBuffer / COOP-COEP dependency in the browser — is REAL, but it is
|
|
857
|
+
// a property of the mandated target itself and is shared by EVERY SDK guest,
|
|
858
|
+
// so this guard cannot remove it. Tracked as its own task because it decides
|
|
859
|
+
// whether browser-served modules need cross-origin isolation.
|
|
860
|
+
//
|
|
861
|
+
// What IS fixable here is a misconfiguration: explicitly passing
|
|
862
|
+
// `--shared-memory` / `--import-memory` into a sequential link. wasm-ld
|
|
863
|
+
// accepts `--shared-memory` even WITHOUT `--import-memory`, and an
|
|
864
|
+
// artifact-only check cannot tell that apart from the driver's own doing.
|
|
865
|
+
// assertSequentialFlagsAbsent() rejects it at flag time, which is the layer
|
|
866
|
+
// where the intent actually exists.
|
|
867
|
+
const importedMemories = analysis.memories.filter((memory) => memory.source === "import");
|
|
868
|
+
if (importedMemories.length > 0) {
|
|
869
|
+
failures.push(
|
|
870
|
+
"it IMPORTS its memory — a sequential guest must declare and export its own " +
|
|
871
|
+
"memory, because importing one without exporting `wasi_thread_start` is an " +
|
|
872
|
+
"incomplete wasi-threads contract that WasmEdge refuses to instantiate",
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
if (analysis.hasWasiThreadSpawnImport) {
|
|
876
|
+
failures.push(
|
|
877
|
+
"it imports the wasi `thread-spawn` host function — a guest that can spawn " +
|
|
878
|
+
`threads must declare threadModel "${SEQUENTIAL_MIRROR_PTHREAD_MODEL}", not wasi-sequential`,
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
if (analysis.hasWasiThreadStartExport) {
|
|
882
|
+
failures.push(
|
|
883
|
+
"it exports `wasi_thread_start` — that is the wasi-threads entry point, " +
|
|
884
|
+
"which a sequential guest must not have",
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
if (analysis.emscriptenThreadHooks.length > 0) {
|
|
888
|
+
failures.push(
|
|
889
|
+
"it imports Emscripten's browser-only Web Worker thread hooks " +
|
|
890
|
+
`(${analysis.emscriptenThreadHooks.join(", ")})`,
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const target = options.target === undefined ? null : String(options.target);
|
|
895
|
+
if (target !== null && target !== SANCTIONED_WASI_TARGET) {
|
|
896
|
+
failures.push(
|
|
897
|
+
`it was built for target "${target}" rather than "${SANCTIONED_WASI_TARGET}" — ` +
|
|
898
|
+
"the sequential model is a concurrency exemption, NOT a toolchain exemption, " +
|
|
899
|
+
"and must not become an unpoliced route off the isomorphic target (check SDN_WASI_TARGET)",
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (failures.length > 0) {
|
|
904
|
+
throw new Error(
|
|
905
|
+
`wasi-sequential artifact validation REJECTED ${source}: the build claims the ` +
|
|
906
|
+
`sequential thread model but — ${failures.join("; ")}. This build must not ship.`,
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
return analysis;
|
|
910
|
+
}
|