├── .clusterfuzzlite ├── Dockerfile ├── build.sh └── project.yaml ├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── cflite_batch.yml │ ├── cflite_build.yml │ ├── cflite_cron.yml │ ├── cflite_pr.yml │ ├── format.yml │ └── main.yml ├── .gitignore ├── .spi.yml ├── .swift-format ├── Benchmarks ├── Benchmarks │ ├── MacroPlugin │ │ └── MacroPlugin.swift │ ├── MicroBench │ │ └── MicroBench.swift │ └── WishYouWereFast │ │ └── WishYouWereFast.swift ├── Package.swift ├── README.md ├── bench.py └── wasm │ ├── local.wasm │ └── local.wat ├── CI ├── Sources │ └── os-check.sh ├── build-dev-dashboard.sh └── install-wabt.sh ├── CMakeLists.txt ├── Documentation ├── ComponentModel │ ├── CanonicalABI.md │ └── SemanticsNotes.md └── RegisterMachine.md ├── Examples ├── Package.swift ├── README.md ├── Sources │ ├── Factorial │ │ └── Factorial.swift │ ├── PrintAdd │ │ └── PrintAdd.swift │ └── WASI-Hello │ │ └── Hello.swift └── wasm │ ├── factorial.wat │ ├── hello.swift │ └── hello.wasm ├── FuzzTesting ├── FailCases │ ├── FuzzDifferential │ │ ├── diff-03871720302dce5752bb1ff7c3eeb393ed633e10.wasm │ │ ├── diff-259df1650a6738711fc751c0ff56f40b0327b49e.wasm │ │ ├── diff-28b86dad836ec8190896453558e3c4a9026f13e1.wasm │ │ ├── diff-2b10b7d98e31adffad9efa84c05fcc0b5cc9b7ca.wasm │ │ ├── diff-3cefb827c90b7927cc0c576d83caf9ff5bb9c9f7.wasm │ │ ├── diff-a847f7b178038728c43d2897abdb5e1c12965bb6.wasm │ │ └── diff-af7fbc40cf82d8cac89fc63d4d56fc21d5806595.wasm │ ├── FuzzExecute │ │ ├── crash-03e19a2681cb95b538237b4f691999dcde00ab55 │ │ ├── crash-0e27f2ac7ea0e6aec7910b36d3113d3b32f97525 │ │ ├── crash-113053c0f3aa13a158502f91b56e8a16a76b0e48 │ │ ├── crash-163c5cbf427de5fd638efdaf91b714259d63a7d7 │ │ ├── crash-28cb43aabedbdeb4bbaa71c209566df5a05e04c1 │ │ ├── crash-2f873cd3b8289d46c43a73f46b9456b8a5491101 │ │ ├── crash-33b8ee16bea391aadce8840da046eaedf3d69b40 │ │ ├── crash-40cda94b13f0dc2627ebed7d3fc0fed970c5d0dd │ │ ├── crash-47de585e2edbaf20e93f82884be5e633ea4eb709 │ │ ├── crash-6709a22436451c40f283d16eefb2ed18fb3847dc │ │ ├── crash-9283d06966be2066531d47fdef0d804e2d2821c3 │ │ ├── crash-dce0c2aaecb7724dcdc7ef414d37c86685fea7f6 │ │ ├── crash-e6d6ff616328461f670b40885787dfa31d5f6cfd │ │ ├── leak-134b08dc7e91a49af18c9d16140df87ee9331eda │ │ ├── leak-c6006650182737e2b09bada7fc0774812b802917 │ │ ├── leak-cadea9052385438e85b9fbe41d7963849fefdfa2 │ │ └── oom-b6402648e0f6d2cfa0873adb73fb15a48ff9cfad │ └── FuzzTranslator │ │ ├── crash-00c03bb238a993ad521865092d1faa092ccba0fa │ │ ├── crash-2e61aac9dea72d2efe93489c1276ec1b69fb2992 │ │ ├── crash-362df2a30ead679d48492f81479924f404ce926d │ │ ├── crash-3769772d5ea06bc5e4214bf4abfb4f50feea05e6 │ │ ├── crash-4fd35c09b62a4ee7e152a2b4586d1f55635bb673 │ │ ├── crash-5f8fe66dfa8b7f12881421d7110fe834438a29d9 │ │ ├── crash-8b2f015e5de0aa1eb03af3e970169f732eeea21b │ │ ├── crash-bfca64d7e0e9d9e8ad6371a977d47c07db7bd93f │ │ ├── crash-c639d0c0e8a2667af85537f2beebd98dac42c136 │ │ ├── crash-cc3c3de2c72d1acf6dd0ed8c53e6f1d4bd0a783b │ │ ├── crash-d7b38baf235334838c29be758f6d708cdc4f4b2b │ │ ├── crash-d8d325cf3a2836ddbe2ae146ff18f554c97e42f6 │ │ ├── crash-e19eb785046f30e38732ca80ebc521fae813a6db │ │ ├── crash-fc13f28803f8114de1324454b0c995d6ed3d7460 │ │ └── oom-c6676d89e7808d8180b29eedd29c60e0b02c04dd ├── Package.swift ├── README.md ├── Sources │ ├── FuzzDifferential │ │ └── FuzzDifferential.swift │ ├── FuzzExecute │ │ └── FuzzExecute.swift │ ├── FuzzTranslator │ │ └── FuzzTranslator.swift │ ├── WasmCAPI │ │ └── include │ │ │ ├── module.modulemap │ │ │ └── wasm.h │ └── WasmKitFuzzing │ │ └── WasmKitFuzzing.swift ├── differential.py └── fuzz.py ├── LICENSE ├── NOTICE.txt ├── Package.swift ├── Plugins ├── GenerateOverlayForTesting │ └── Plugin.swift ├── WITExtractorPlugin │ └── Plugin.swift └── WITOverlayPlugin │ └── Plugin.swift ├── README.md ├── Sources ├── CLI │ ├── CLI.swift │ ├── CMakeLists.txt │ └── Commands │ │ ├── Explore.swift │ │ └── Run.swift ├── CMakeLists.txt ├── SystemExtras │ ├── CMakeLists.txt │ ├── Clock.swift │ ├── Constants.swift │ ├── FileAtOperations.swift │ ├── FileOperations.swift │ ├── Syscalls.swift │ └── Vendor │ │ ├── Exports.swift │ │ ├── Utils.swift │ │ └── WindowsSyscallAdapter.swift ├── WASI │ ├── CMakeLists.txt │ ├── Clock.swift │ ├── FileSystem.swift │ ├── GuestMemorySupport.swift │ ├── Platform │ │ ├── Directory.swift │ │ ├── Entry.swift │ │ ├── File.swift │ │ ├── PlatformTypes.swift │ │ ├── SandboxPrimitives.swift │ │ └── SandboxPrimitives │ │ │ ├── Open.swift │ │ │ └── OpenParent.swift │ ├── RandomBufferGenerator.swift │ └── WASI.swift ├── WAT │ ├── BinaryInstructionEncoder.swift │ ├── CMakeLists.txt │ ├── Docs.docc │ │ └── Docs.md │ ├── Encoder.swift │ ├── Lexer.swift │ ├── Location.swift │ ├── NameMapping.swift │ ├── ParseTextInstruction.swift │ ├── Parser.swift │ ├── Parser │ │ ├── ExpressionParser.swift │ │ ├── WastParser.swift │ │ └── WatParser.swift │ └── WAT.swift ├── WIT │ ├── AST.swift │ ├── ASTVisitor.swift │ ├── CanonicalABI │ │ ├── CanonicalABI.swift │ │ ├── CanonicalDeallocation.swift │ │ ├── CanonicalLifting.swift │ │ ├── CanonicalLoading.swift │ │ ├── CanonicalLowering.swift │ │ └── CanonicalStoring.swift │ ├── Diagnostics.swift │ ├── Lexer.swift │ ├── PackageResolver.swift │ ├── Semantics │ │ ├── NameLookup.swift │ │ ├── PackageBuilder.swift │ │ ├── RequestEvaluator.swift │ │ ├── SemanticsContext.swift │ │ ├── Type.swift │ │ └── Validation.swift │ ├── SyntaxNode.swift │ └── TextParser │ │ ├── ParseFunctionDecl.swift │ │ ├── ParseInterface.swift │ │ ├── ParseTop.swift │ │ ├── ParseTypes.swift │ │ ├── ParseWorld.swift │ │ └── Parser.swift ├── WITExtractor │ ├── Diagnostic.swift │ ├── ModuleTranslation.swift │ ├── Naming │ │ └── ConvertCase.swift │ ├── SourcePrinter.swift │ ├── SourceSummary.swift │ ├── SwiftAPIDigester.swift │ ├── TypeMapping.swift │ ├── WITBuilder.swift │ └── WITExtractor.swift ├── WITOverlayGenerator │ ├── CanonicalABI │ │ ├── CanonicalName.swift │ │ ├── CanonicalOperation.swift │ │ ├── SignatureTranslation.swift │ │ ├── StaticCanonicalDeallocation.swift │ │ ├── StaticCanonicalLifting.swift │ │ ├── StaticCanonicalLoading.swift │ │ ├── StaticCanonicalLowering.swift │ │ └── StaticCanonicalStoring.swift │ ├── DefinitionMapping.swift │ ├── GuestGenerators │ │ ├── GuestExportFunction.swift │ │ ├── GuestExportInterface.swift │ │ ├── GuestPrelude.swift │ │ └── GuestWorldGenerator.swift │ ├── HostGenerators │ │ ├── HostExportFunction.swift │ │ ├── HostExportInterface.swift │ │ ├── HostWorldGenerator.swift │ │ └── WasmKitSourcePrinter.swift │ ├── Naming │ │ ├── ConvertCase.swift │ │ ├── SwiftName.swift │ │ └── TypeName.swift │ ├── SourcePrinter.swift │ ├── TypeGenerators │ │ ├── InterfaceTypeGenerator.swift │ │ ├── TypeAccessory.swift │ │ ├── TypeDefinition.swift │ │ └── TypeGenerator.swift │ └── WITOverlayGen.swift ├── WITTool │ ├── Utilities.swift │ └── WITTool.swift ├── WasmKit │ ├── CMakeLists.txt │ ├── Component │ │ ├── CanonicalCall.swift │ │ ├── CanonicalLifting.swift │ │ ├── CanonicalLowering.swift │ │ ├── CanonicalOptions.swift │ │ └── ComponentTypes.swift │ ├── Docs.docc │ │ └── Docs.md │ ├── Engine.swift │ ├── Execution │ │ ├── ConstEvaluation.swift │ │ ├── DispatchInstruction.swift │ │ ├── EngineInterceptor.swift │ │ ├── Errors.swift │ │ ├── Execution.swift │ │ ├── Function.swift │ │ ├── IO.swift │ │ ├── Instances.swift │ │ ├── Instructions │ │ │ ├── Control.swift │ │ │ ├── Instruction.swift │ │ │ ├── InstructionSupport.swift │ │ │ ├── Memory.swift │ │ │ ├── Misc.swift │ │ │ └── Table.swift │ │ ├── NameRegistry.swift │ │ ├── Profiler.swift │ │ ├── Runtime.swift │ │ ├── SignpostTracer.swift │ │ ├── Store.swift │ │ ├── StoreAllocator.swift │ │ ├── UntypedValue.swift │ │ └── Value.swift │ ├── Imports.swift │ ├── Module.swift │ ├── ModuleParser.swift │ ├── ResourceLimiter.swift │ ├── Translator.swift │ └── Validator.swift ├── WasmKitWASI │ ├── CMakeLists.txt │ └── WASIBridgeToHost+WasmKit.swift ├── WasmParser │ ├── BinaryInstructionDecoder.swift │ ├── CMakeLists.txt │ ├── Docs.docc │ │ └── Docs.md │ ├── InstructionVisitor.swift │ ├── LEB.swift │ ├── ParsingLimits.swift │ ├── Stream │ │ ├── ByteStream.swift │ │ ├── FileHandleStream.swift │ │ └── Stream.swift │ ├── WasmParser.swift │ └── WasmTypes.swift ├── WasmTypes │ ├── CMakeLists.txt │ ├── GuestMemory.swift │ └── WasmTypes.swift ├── _CWasmKit │ ├── CMakeLists.txt │ ├── _CWasmKit.c │ └── include │ │ ├── DirectThreadedCode.inc │ │ ├── InlineCode.h │ │ ├── Platform.h │ │ ├── _CWasmKit.h │ │ └── module.modulemap └── _CabiShims │ ├── include │ ├── module.modulemap │ └── shims.h │ └── shims.c ├── Tests ├── WASITests │ ├── IntegrationTests.swift │ ├── Platform │ │ └── SandboxPrimitives │ │ │ └── OpenParentTests.swift │ ├── RandomBufferGeneratorTests.swift │ ├── TestSupport.swift │ └── WASITests.swift ├── WATTests │ ├── EncoderTests.swift │ ├── LexerTests.swift │ ├── ParserTests.swift │ ├── Spectest.swift │ └── TestSupport.swift ├── WITExtractorPluginTests │ ├── Fixtures │ │ └── PluginSmokePackage │ │ │ ├── .gitignore │ │ │ ├── Package.swift │ │ │ └── Sources │ │ │ └── PluginSmokeModule │ │ │ └── PluginSmoke.swift │ ├── PluginSmokeTests.swift │ └── TestSupport.swift ├── WITExtractorTests │ ├── ConvertCaseTests.swift │ ├── ExportFunctionTests.swift │ ├── TestSupport.swift │ └── TypeTranslationTests.swift ├── WITOverlayGeneratorTests │ ├── Compiled │ │ └── .gitkeep │ ├── EmbeddedSupport │ │ └── MinLibc.c │ ├── Fixtures │ │ ├── Char │ │ │ ├── Char.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Enum │ │ │ ├── Enum.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Flags │ │ │ ├── Flags.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Interface │ │ │ ├── Interface.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── List │ │ │ ├── List.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Naming │ │ │ ├── Naming.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Number │ │ │ ├── Number.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Option │ │ │ ├── Option.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Record │ │ │ ├── Record.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Result │ │ │ ├── Result.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Smoke │ │ │ ├── Smoke.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── String │ │ │ ├── String.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ ├── Tuple │ │ │ ├── Tuple.swift │ │ │ └── wit │ │ │ │ └── world.wit │ │ └── Variant │ │ │ ├── Variant.swift │ │ │ └── wit │ │ │ └── world.wit │ ├── Generated │ │ └── .gitkeep │ ├── HostGeneratorTests.swift │ └── Runtime │ │ ├── RuntimeSmokeTests.swift │ │ ├── RuntimeTestHarness.swift │ │ └── RuntimeTypesTests.swift ├── WITTests │ ├── LexerTests.swift │ ├── PackageResolverTests.swift │ ├── Semantics │ │ ├── NameLookupTests.swift │ │ ├── RequestEvaluatorTests.swift │ │ └── ValidationTests.swift │ └── TextParser │ │ ├── ParseFunctionDeclTests.swift │ │ ├── ParseInterfaceTests.swift │ │ ├── ParseTopTests.swift │ │ ├── ParseTypesTests.swift │ │ ├── ParseVersionTests.swift │ │ └── ParseWorldTests.swift ├── WasmKitTests │ ├── Execution │ │ ├── HostModuleTests.swift │ │ └── Runtime │ │ │ └── StoreAllocatorTests.swift │ ├── ExecutionTests.swift │ ├── ExtraSuite │ │ ├── block_type.wast │ │ ├── br_if.wast │ │ ├── const_slot.wast │ │ ├── local_cow.wast │ │ └── shr_s.wast │ ├── FuzzTranslatorRegressionTests.swift │ ├── Spectest │ │ ├── Spectest.swift │ │ └── TestCase.swift │ └── SpectestTests.swift └── WasmParserTests │ └── LEBTests.swift ├── Utilities ├── Instructions.json ├── Package.swift ├── README.md ├── Sources │ ├── GeneratedFile.swift │ ├── VMGen.swift │ ├── VMSpec │ │ ├── Immediate.swift │ │ └── Instruction.swift │ ├── WasmGen.swift │ └── main.swift └── format.py ├── Vendor ├── .gitignore ├── checkout-dependency └── dependencies.json └── cmake └── modules ├── AddSwiftHostLibrary.cmake └── CMakeLists.txt /.clusterfuzzlite/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcr.io/oss-fuzz-base/base-builder-swift:v1 2 | RUN apt-get update && apt-get install -y make autoconf automake libtool 3 | ENV SWIFT_PREFIX=/opt/swift 4 | RUN mkdir -p "$SWIFT_PREFIX" && \ 5 | curl -L https://download.swift.org/swift-6.0.1-release/ubuntu2004/swift-6.0.1-RELEASE/swift-6.0.1-RELEASE-ubuntu20.04.tar.gz | tar xz -C "$SWIFT_PREFIX" --strip-component 1 6 | ENV PATH="$SWIFT_PREFIX/usr/bin:$PATH" 7 | COPY . $SRC/wasmkit 8 | WORKDIR $SRC/wasmkit 9 | COPY .clusterfuzzlite/build.sh $SRC/ 10 | -------------------------------------------------------------------------------- /.clusterfuzzlite/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | cd FuzzTesting 4 | ./fuzz.py --verbose build --sanitizer="$SANITIZER" FuzzTranslator 5 | 6 | find .build/debug/ -maxdepth 1 -type f -name "Fuzz*" -executable -exec cp {} "$OUT/" \; 7 | 8 | -------------------------------------------------------------------------------- /.clusterfuzzlite/project.yaml: -------------------------------------------------------------------------------- 1 | language: swift 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.swift] 11 | indent_size = 4 12 | 13 | [Makefile] 14 | indent_style = tab 15 | tab_width = 4 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | 8 | - package-ecosystem: swift 9 | directory: / 10 | schedule: 11 | interval: weekly 12 | -------------------------------------------------------------------------------- /.github/workflows/cflite_batch.yml: -------------------------------------------------------------------------------- 1 | name: ClusterFuzzLite batch fuzzing 2 | on: 3 | schedule: 4 | - cron: '0 0/6 * * *' 5 | workflow_dispatch: 6 | inputs: 7 | fuzz-seconds: 8 | description: 'The total time allotted for fuzzing in seconds.' 9 | required: true 10 | default: 3600 11 | 12 | permissions: read-all 13 | jobs: 14 | BatchFuzzing: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | security-events: write 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | sanitizer: 22 | - address 23 | steps: 24 | - name: Build Fuzzers (${{ matrix.sanitizer }}) 25 | id: build 26 | uses: google/clusterfuzzlite/actions/build_fuzzers@v1 27 | with: 28 | language: swift 29 | sanitizer: ${{ matrix.sanitizer }} 30 | - name: Run Fuzzers (${{ matrix.sanitizer }}) 31 | id: run 32 | uses: google/clusterfuzzlite/actions/run_fuzzers@v1 33 | with: 34 | github-token: ${{ secrets.GITHUB_TOKEN }} 35 | fuzz-seconds: ${{ github.event.inputs.fuzz-seconds || 3600 }} 36 | mode: 'batch' 37 | sanitizer: ${{ matrix.sanitizer }} 38 | output-sarif: true 39 | storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git 40 | storage-repo-branch: main 41 | storage-repo-branch-coverage: gh-pages 42 | - name: Upload Sarif 43 | if: always() && steps.build.outcome == 'success' 44 | uses: github/codeql-action/upload-sarif@v3 45 | with: 46 | sarif_file: cifuzz-sarif/results.sarif 47 | checkout_path: cifuzz-sarif 48 | -------------------------------------------------------------------------------- /.github/workflows/cflite_build.yml: -------------------------------------------------------------------------------- 1 | name: ClusterFuzzLite continuous builds 2 | on: 3 | push: 4 | branches: 5 | - main 6 | permissions: read-all 7 | jobs: 8 | Build: 9 | runs-on: ubuntu-latest 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | sanitizer: 17 | - address 18 | steps: 19 | - name: Build Fuzzers (${{ matrix.sanitizer }}) 20 | id: build 21 | uses: google/clusterfuzzlite/actions/build_fuzzers@v1 22 | with: 23 | language: swift 24 | sanitizer: ${{ matrix.sanitizer }} 25 | upload-build: true 26 | -------------------------------------------------------------------------------- /.github/workflows/cflite_cron.yml: -------------------------------------------------------------------------------- 1 | name: ClusterFuzzLite cron tasks 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | workflow_dispatch: {} 6 | 7 | permissions: read-all 8 | jobs: 9 | Pruning: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Build Fuzzers 13 | id: build 14 | uses: google/clusterfuzzlite/actions/build_fuzzers@v1 15 | with: 16 | language: swift 17 | - name: Run Fuzzers 18 | id: run 19 | uses: google/clusterfuzzlite/actions/run_fuzzers@v1 20 | with: 21 | github-token: ${{ secrets.GITHUB_TOKEN }} 22 | fuzz-seconds: 600 23 | mode: 'prune' 24 | output-sarif: true 25 | storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git 26 | storage-repo-branch: main 27 | storage-repo-branch-coverage: gh-pages 28 | Coverage: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Build Fuzzers 32 | id: build 33 | uses: google/clusterfuzzlite/actions/build_fuzzers@v1 34 | with: 35 | language: swift 36 | sanitizer: coverage 37 | - name: Run Fuzzers 38 | id: run 39 | uses: google/clusterfuzzlite/actions/run_fuzzers@v1 40 | with: 41 | github-token: ${{ secrets.GITHUB_TOKEN }} 42 | fuzz-seconds: 600 43 | mode: 'coverage' 44 | sanitizer: 'coverage' 45 | storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git 46 | storage-repo-branch: main 47 | storage-repo-branch-coverage: gh-pages 48 | -------------------------------------------------------------------------------- /.github/workflows/cflite_pr.yml: -------------------------------------------------------------------------------- 1 | name: ClusterFuzzLite PR fuzzing 2 | on: 3 | pull_request: 4 | paths: 5 | - '**' 6 | permissions: read-all 7 | jobs: 8 | PR: 9 | name: PR Fuzzing (${{ matrix.sanitizer }} sanitizer) 10 | runs-on: ubuntu-latest 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | sanitizer: 18 | - address 19 | steps: 20 | - name: Build Fuzzers (${{ matrix.sanitizer }}) 21 | id: build 22 | uses: google/clusterfuzzlite/actions/build_fuzzers@v1 23 | with: 24 | language: swift 25 | github-token: ${{ secrets.GITHUB_TOKEN }} 26 | sanitizer: ${{ matrix.sanitizer }} 27 | storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git 28 | storage-repo-branch: main 29 | storage-repo-branch-coverage: gh-pages 30 | - name: Run Fuzzers (${{ matrix.sanitizer }}) 31 | id: run 32 | uses: google/clusterfuzzlite/actions/run_fuzzers@v1 33 | with: 34 | github-token: ${{ secrets.GITHUB_TOKEN }} 35 | fuzz-seconds: 300 36 | mode: 'code-change' 37 | sanitizer: ${{ matrix.sanitizer }} 38 | output-sarif: true 39 | storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git 40 | storage-repo-branch: main 41 | storage-repo-branch-coverage: gh-pages 42 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Format 2 | on: 3 | pull_request: 4 | 5 | jobs: 6 | format: 7 | runs-on: ubuntu-latest 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | container: 12 | image: swift:6.0.1-jammy 13 | steps: 14 | - uses: actions/checkout@v4 15 | - run: ./Utilities/format.py 16 | - name: Check for formatting changes 17 | run: | 18 | git config --global --add safe.directory "$GITHUB_WORKSPACE" 19 | git diff --exit-code || { 20 | echo "::error::The formatting changed some files. Please run \`./Utilities/format.py\` and commit the changes." 21 | exit 1 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .build 4 | *.xcodeproj 5 | .swiftpm 6 | 7 | .vscode 8 | 9 | /Tests/default.json 10 | /Tests/WITOverlayGeneratorTests/Compiled/** 11 | /Tests/WITOverlayGeneratorTests/Generated/** 12 | Tests/WITExtractorPluginTests/Fixtures/*/Package.resolved 13 | 14 | !**/.gitkeep 15 | FuzzTesting/FailCases/FuzzExecute/timeout-* 16 | 17 | Package.resolved 18 | Benchmarks/.benchmarkBaselines/ 19 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | builder: 3 | configs: 4 | - documentation_targets: 5 | - WasmKit 6 | - WasmKitWASI 7 | - WasmParser 8 | - WASI 9 | - WAT 10 | - WIT 11 | - WITExtractor 12 | - WITOverlayGenerator 13 | - WITTool 14 | -------------------------------------------------------------------------------- /.swift-format: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "lineLength": 200, 4 | "indentation": { 5 | "spaces": 4 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Benchmarks/Benchmarks/MicroBench/MicroBench.swift: -------------------------------------------------------------------------------- 1 | import Benchmark 2 | import WasmKit 3 | import WAT 4 | 5 | let benchmarks = { 6 | Benchmark("empty instantiation") { benchmark in 7 | for _ in benchmark.scaledIterations { 8 | let engine = Engine() 9 | let store = Store(engine: engine) 10 | let module = try parseWasm(bytes: wat2wasm(""" 11 | (module 12 | (func (export "_start")) 13 | ) 14 | """)) 15 | _ = try module.instantiate(store: store) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Benchmarks/Benchmarks/WishYouWereFast/WishYouWereFast.swift: -------------------------------------------------------------------------------- 1 | import Benchmark 2 | import WasmKit 3 | import WasmKitWASI 4 | import Foundation 5 | import SystemPackage 6 | 7 | let benchmarks = { 8 | let wishYouWereFast = URL(fileURLWithPath: #filePath) 9 | .deletingLastPathComponent() 10 | .deletingLastPathComponent() 11 | .deletingLastPathComponent() 12 | .deletingLastPathComponent() 13 | .appendingPathComponent("Vendor") 14 | .appendingPathComponent("wish-you-were-fast") 15 | .appendingPathComponent("wasm") 16 | .appendingPathComponent("suites") 17 | .appendingPathComponent("libsodium") 18 | 19 | let devNull = try! FileDescriptor.open("/dev/null", .readWrite) 20 | 21 | for file in try! FileManager.default.contentsOfDirectory( 22 | atPath: wishYouWereFast.path 23 | ) { 24 | guard file.hasSuffix(".wasm") else { continue } 25 | Benchmark("\(file)", configuration: .init(thresholds: [ 26 | .peakMemoryResident: .relaxed, 27 | ])) { benchmark in 28 | let engine = Engine() 29 | let store = Store(engine: engine) 30 | let module = try parseWasm( 31 | filePath: FilePath(wishYouWereFast.appendingPathComponent(file).path) 32 | ) 33 | let wasi = try WASIBridgeToHost(stdout: devNull, stderr: devNull) 34 | var imports = Imports() 35 | wasi.link(to: &imports, store: store) 36 | let instance = try module.instantiate(store: store, imports: imports) 37 | _ = try wasi.start(instance) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Benchmarks/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.8 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Benchmarks", 7 | platforms: [.macOS(.v13)], 8 | dependencies: [ 9 | .package(name: "WasmKit", path: "../"), 10 | .package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.4.0")), 11 | ] 12 | ) 13 | 14 | // Benchmark of WishYouWereFast 15 | package.targets += [ 16 | .executableTarget( 17 | name: "WishYouWereFast", 18 | dependencies: [ 19 | .product(name: "WasmKit", package: "WasmKit"), 20 | .product(name: "WasmKitWASI", package: "WasmKit"), 21 | .product(name: "Benchmark", package: "package-benchmark"), 22 | ], 23 | path: "Benchmarks/WishYouWereFast", 24 | plugins: [ 25 | .plugin(name: "BenchmarkPlugin", package: "package-benchmark") 26 | ] 27 | ), 28 | ] 29 | 30 | // Benchmark of MicroBench 31 | package.targets += [ 32 | .executableTarget( 33 | name: "MicroBench", 34 | dependencies: [ 35 | .product(name: "WAT", package: "WasmKit"), 36 | .product(name: "WasmKit", package: "WasmKit"), 37 | .product(name: "WasmKitWASI", package: "WasmKit"), 38 | .product(name: "Benchmark", package: "package-benchmark"), 39 | ], 40 | path: "Benchmarks/MicroBench", 41 | plugins: [ 42 | .plugin(name: "BenchmarkPlugin", package: "package-benchmark") 43 | ] 44 | ), 45 | ] 46 | 47 | // Benchmark of MacroPlugin 48 | package.targets += [ 49 | .executableTarget( 50 | name: "MacroPlugin", 51 | dependencies: [ 52 | .product(name: "WasmKit", package: "WasmKit"), 53 | .product(name: "WasmKitWASI", package: "WasmKit"), 54 | .product(name: "Benchmark", package: "package-benchmark"), 55 | ], 56 | path: "Benchmarks/MacroPlugin", 57 | plugins: [ 58 | .plugin(name: "BenchmarkPlugin", package: "package-benchmark") 59 | ] 60 | ), 61 | ] 62 | -------------------------------------------------------------------------------- /Benchmarks/README.md: -------------------------------------------------------------------------------- 1 | # Benchmarks 2 | 3 | This directory contains a set of benchmarks that can be used to compare the performance of the WasmKit runtime with other WebAssembly runtimes. 4 | 5 | ## Prerequisites 6 | 7 | To build benchmarks, you need to install [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) and set the `WASI_SDK_PATH` environment variable to the installation directory. 8 | 9 | ## Running the Benchmarks 10 | 11 | Run all benchmarks: 12 | 13 | ```console 14 | $ ./bench.py 15 | ``` 16 | 17 | Filtering examples: 18 | 19 | ```console 20 | $ ./bench.py --benchmark CoreMark 21 | $ ./bench.py --engine WasmKit 22 | ``` 23 | 24 | See `./bench.py --help` for more options. 25 | -------------------------------------------------------------------------------- /Benchmarks/wasm/local.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/Benchmarks/wasm/local.wasm -------------------------------------------------------------------------------- /Benchmarks/wasm/local.wat: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Lots of local operations 3 | (func (export "_start") (local $x i32) (local $i i32) 4 | (local.set $x (i32.const 42)) 5 | (loop $loop 6 | (local.set $i (i32.add (local.get $i) (i32.const 1))) 7 | (local.set $x (i32.add (local.get $x) (i32.const 1))) 8 | (br_if $loop (i32.lt_u (local.get $i) (i32.const 60000000))) 9 | ) 10 | ) 11 | ) 12 | 13 | 14 | -------------------------------------------------------------------------------- /CI/Sources/os-check.sh: -------------------------------------------------------------------------------- 1 | is_amazonlinux2() { 2 | if [ -f /etc/os-release ]; then 3 | source /etc/os-release 4 | if [ "$ID" == "amzn" ]; then 5 | return 0 6 | fi 7 | fi 8 | return 1 9 | } 10 | 11 | is_debian_family() { 12 | if [ -f /etc/os-release ]; then 13 | source /etc/os-release 14 | if [ "$ID_LIKE" == "debian" ]; then 15 | return 0 16 | fi 17 | fi 18 | return 1 19 | } 20 | -------------------------------------------------------------------------------- /CI/build-dev-dashboard.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | show_test_executable_path() { 6 | local base_path=".build/debug/WasmKitPackageTests.xctest" 7 | if [ -f "$base_path" ]; then 8 | echo "$base_path" 9 | else 10 | echo "$base_path/Contents/MacOS/WasmKitPackageTests" 11 | fi 12 | } 13 | 14 | build_coverage_html() { 15 | llvm-cov show --format=html "$(show_test_executable_path)" --instr-profile .build/debug/codecov/default.profdata -o "$1" --sources $(find Sources -type f) 16 | } 17 | 18 | mkdir -p ./.build/html 19 | build_coverage_html ./.build/html/coverage 20 | 21 | echo "Coverage report has been generated in ./.build/html/coverage" 22 | -------------------------------------------------------------------------------- /CI/install-wabt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # A CI script to install wabt for testing WAT library 4 | # 5 | 6 | set -eu -o pipefail 7 | source "$(dirname $0)/Sources/os-check.sh" 8 | 9 | install_tools() { 10 | if ! which make curl cmake ninja python3 xz > /dev/null; then 11 | apt update && apt install -y curl build-essential cmake ninja-build python3 xz-utils 12 | fi 13 | 14 | if ! which wat2wasm > /dev/null; then 15 | local build_dir=$(mktemp -d /tmp/WasmKit-wabt.XXXXXX) 16 | mkdir -p $build_dir 17 | curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.35/wabt-1.0.35.tar.xz | tar xJ --strip-components=1 -C $build_dir 18 | cmake -B $build_dir/build -GNinja -DBUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local $build_dir 19 | cmake --build $build_dir/build --target install 20 | fi 21 | 22 | echo "Use wat2wasm $(wat2wasm --version): $(which wat2wasm)" 23 | echo "Use wasm2wat $(wasm2wat --version): $(which wasm2wat)" 24 | } 25 | 26 | # Currently wabt is unavailable in amazonlinux2 27 | if is_amazonlinux2; then 28 | echo "Skip wabt installation on amazonlinux2" 29 | exit 0 30 | fi 31 | 32 | set -e 33 | 34 | install_tools 35 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19.6) 2 | 3 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) 4 | 5 | project(WasmKit LANGUAGES C Swift) 6 | 7 | set(SWIFT_VERSION 5) 8 | set(CMAKE_Swift_LANGUAGE_VERSION ${SWIFT_VERSION}) 9 | 10 | # Enable whole module optimization for Release or RelWithDebInfo builds. 11 | if(POLICY CMP0157) 12 | set(CMAKE_Swift_COMPILATION_MODE $,wholemodule,incremental>) 13 | else() 14 | add_compile_options($<$,$>:-wmo>) 15 | endif() 16 | 17 | if(CMAKE_VERSION VERSION_LESS 3.21) 18 | get_property(parent_dir DIRECTORY PROPERTY PARENT_DIRECTORY) 19 | if(NOT parent_dir) 20 | set(PROJECT_IS_TOP_LEVEL TRUE) 21 | endif() 22 | endif() 23 | 24 | # The subdirectory into which host libraries will be installed. 25 | set(SWIFT_HOST_LIBRARIES_SUBDIRECTORY "swift/host") 26 | 27 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") 28 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${SWIFT_HOST_LIBRARIES_SUBDIRECTORY}") 29 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 30 | 31 | set(CMAKE_MACOSX_RPATH YES) 32 | 33 | set(BUILD_SHARED_LIBS OFF) 34 | 35 | include(AddSwiftHostLibrary) 36 | 37 | set(CMAKE_Swift_COMPILER_TARGET ${SWIFT_HOST_TRIPLE}) 38 | 39 | if("${SWIFT_HOST_MODULE_TRIPLE}" STREQUAL "") 40 | set(module_triple_command "${CMAKE_Swift_COMPILER}" -print-target-info) 41 | if(CMAKE_Swift_COMPILER_TARGET) 42 | list(APPEND module_triple_command -target ${CMAKE_Swift_COMPILER_TARGET}) 43 | endif() 44 | execute_process(COMMAND ${module_triple_command} OUTPUT_VARIABLE target_info_json) 45 | string(JSON SWIFT_HOST_MODULE_TRIPLE GET "${target_info_json}" "target" "moduleTriple") 46 | endif() 47 | message(STATUS "Module triple: ${SWIFT_HOST_MODULE_TRIPLE}") 48 | 49 | add_compile_definitions( 50 | $<$:WASMKIT_BUILD_USING_CMAKE> 51 | ) 52 | 53 | include(FetchContent) 54 | 55 | find_package(SwiftSystem CONFIG) 56 | if(NOT SwiftSystem_FOUND) 57 | message("-- Vending SwiftSystem") 58 | FetchContent_Declare(SwiftSystem 59 | GIT_REPOSITORY https://github.com/apple/swift-system 60 | GIT_TAG 1.3.0 61 | ) 62 | FetchContent_MakeAvailable(SwiftSystem) 63 | endif() 64 | 65 | option(WASMKIT_BUILD_CLI "Build wasmkit-cli" ON) 66 | 67 | if(WASMKIT_BUILD_CLI) 68 | set(BUILD_TESTING OFF) # disable ArgumentParser tests 69 | find_package(ArgumentParser CONFIG) 70 | if(NOT ArgumentParser_FOUND) 71 | message("-- Vending ArgumentParser") 72 | FetchContent_Declare(ArgumentParser 73 | GIT_REPOSITORY https://github.com/apple/swift-argument-parser 74 | GIT_TAG 1.2.2 75 | ) 76 | FetchContent_MakeAvailable(ArgumentParser) 77 | endif() 78 | endif() 79 | 80 | add_subdirectory(Sources) 81 | add_subdirectory(cmake/modules) 82 | -------------------------------------------------------------------------------- /Examples/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.8 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Examples", 7 | platforms: [.macOS(.v10_13), .iOS(.v12)], 8 | dependencies: [ 9 | .package(path: "../") 10 | ], 11 | targets: [ 12 | .executableTarget(name: "Factorial", dependencies: [ 13 | .product(name: "WasmKit", package: "WasmKit"), 14 | .product(name: "WAT", package: "WasmKit") 15 | ]), 16 | .executableTarget(name: "PrintAdd", dependencies: [ 17 | .product(name: "WasmKit", package: "WasmKit"), 18 | .product(name: "WAT", package: "WasmKit") 19 | ]), 20 | .executableTarget(name: "WASI-Hello", dependencies: [ 21 | .product(name: "WasmKit", package: "WasmKit"), 22 | .product(name: "WasmKitWASI", package: "WasmKit"), 23 | .product(name: "WAT", package: "WasmKit"), 24 | ]), 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /Examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This directory contains a number of examples that demonstrate how to use the `WasmKit` library. 4 | -------------------------------------------------------------------------------- /Examples/Sources/Factorial/Factorial.swift: -------------------------------------------------------------------------------- 1 | /// This example demonstrates how to call a function exported by a WebAssembly module. 2 | 3 | import WasmKit 4 | import WAT 5 | import Foundation 6 | 7 | @main 8 | struct Example { 9 | static func main() throws { 10 | // Convert a WAT file to a Wasm binary, then parse it. 11 | let module = try parseWasm( 12 | bytes: try wat2wasm(String(contentsOfFile: "wasm/factorial.wat")) 13 | ) 14 | 15 | // Create a module instance from the parsed module. 16 | let engine = Engine() 17 | let store = Store(engine: engine) 18 | let instance = try module.instantiate(store: store) 19 | let input: UInt64 = 5 20 | // Invoke the exported function "fac" with a single argument. 21 | let fac = instance.exports[function: "fac"]! 22 | let result = try fac([.i64(input)]) 23 | print("fac(\(input)) = \(result[0].i64)") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Examples/Sources/PrintAdd/PrintAdd.swift: -------------------------------------------------------------------------------- 1 | /// This example demonstrates how to import a function from the host environment and call it from a WebAssembly module. 2 | 3 | import WasmKit 4 | import WAT 5 | 6 | @main 7 | struct Example { 8 | static func main() throws { 9 | // Convert a WAT file to a Wasm binary, then parse it. 10 | let module = try parseWasm( 11 | bytes: try wat2wasm( 12 | """ 13 | (module 14 | (import "printer" "print_i32" (func $print_i32 (param i32))) 15 | (func (export "print_add") (param $x i32) (param $y i32) 16 | (call $print_i32 (i32.add (local.get $x) (local.get $y))) 17 | ) 18 | ) 19 | """ 20 | ) 21 | ) 22 | 23 | // Create engine and store 24 | let engine = Engine() 25 | let store = Store(engine: engine) 26 | 27 | // Instantiate a parsed module with importing a host function 28 | let instance = try module.instantiate( 29 | store: store, 30 | // Import a host function that prints an i32 value. 31 | imports: [ 32 | "printer": [ 33 | "print_i32": Function(store: store, parameters: [.i32]) { _, args in 34 | // This function is called from "print_add" in the WebAssembly module. 35 | print(args[0]) 36 | return [] 37 | } 38 | ] 39 | ] 40 | ) 41 | // Invoke the exported function "print_add" 42 | let printAdd = instance.exports[function: "print_add"]! 43 | try printAdd([.i32(42), .i32(3)]) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Examples/Sources/WASI-Hello/Hello.swift: -------------------------------------------------------------------------------- 1 | // This example demonstrates WASI support in WasmKit. 2 | 3 | import WasmKit 4 | import WasmKitWASI 5 | import Foundation 6 | 7 | @main 8 | struct Example { 9 | static func main() throws { 10 | // Parse a WASI-compliant WebAssembly module from a file. 11 | let module = try parseWasm(filePath: "wasm/hello.wasm") 12 | 13 | // Create a WASI instance forwarding to the host environment. 14 | let wasi = try WASIBridgeToHost() 15 | // Create engine and store 16 | let engine = Engine() 17 | let store = Store(engine: engine) 18 | // Instantiate a parsed module importing WASI 19 | var imports = Imports() 20 | wasi.link(to: &imports, store: store) 21 | let instance = try module.instantiate(store: store, imports: imports) 22 | 23 | // Start the WASI command-line application. 24 | let exitCode = try wasi.start(instance) 25 | // Exit the Swift program with the WASI exit code. 26 | exit(Int32(exitCode)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/wasm/factorial.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $fac (export "fac") (param i64) (result i64) 3 | (if (result i64) (i64.eqz (local.get 0)) 4 | (then (i64.const 1)) 5 | (else 6 | (i64.mul 7 | (local.get 0) 8 | (call $fac (i64.sub (local.get 0) (i64.const 1))) 9 | ) 10 | ) 11 | ) 12 | ) 13 | ) 14 | -------------------------------------------------------------------------------- /Examples/wasm/hello.swift: -------------------------------------------------------------------------------- 1 | // Compile: swiftc ./hello.swift -o hello.wasm -target wasm32-unknown-none-wasm -enable-experimental-feature Extern -enable-experimental-feature Embedded -wmo -Xcc -fdeclspec -Xclang-linker -nostdlib -Xfrontend -disable-stack-protector -Osize 2 | // Swift version: DEVELOPMENT-SNAPSHOT-2024-06-13-a 3 | 4 | // This is a simple WASI program written in Embedded Swift. 5 | 6 | @_extern(wasm, module: "wasi_snapshot_preview1", name: "fd_write") 7 | @_extern(c) 8 | func fd_write(fd: Int32, iovs: UnsafeRawPointer, iovs_len: Int32, nwritten: UnsafeMutablePointer) -> Int32 9 | 10 | func _print(_ string: StaticString) { 11 | string.withUTF8Buffer { string in 12 | withUnsafeTemporaryAllocation(byteCount: 8, alignment: 4) { iov in 13 | let iov = iov.baseAddress! 14 | iov.advanced(by: 0).storeBytes(of: string.baseAddress!, as: UnsafeRawPointer.self) 15 | iov.advanced(by: 4).storeBytes(of: Int32(string.count), as: Int32.self) 16 | var nwritten: Int32 = 0 17 | _ = fd_write(fd: 1, iovs: iov, iovs_len: 1, nwritten: &nwritten) 18 | } 19 | } 20 | } 21 | 22 | // The entry point of this WASI program. 23 | @_expose(wasm, "_start") 24 | @_cdecl("_start") 25 | func _start() { 26 | _print("Hello, World!\n") 27 | } 28 | -------------------------------------------------------------------------------- /Examples/wasm/hello.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/Examples/wasm/hello.wasm -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzDifferential/diff-03871720302dce5752bb1ff7c3eeb393ed633e10.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzDifferential/diff-03871720302dce5752bb1ff7c3eeb393ed633e10.wasm -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzDifferential/diff-259df1650a6738711fc751c0ff56f40b0327b49e.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzDifferential/diff-259df1650a6738711fc751c0ff56f40b0327b49e.wasm -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzDifferential/diff-28b86dad836ec8190896453558e3c4a9026f13e1.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzDifferential/diff-28b86dad836ec8190896453558e3c4a9026f13e1.wasm -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzDifferential/diff-2b10b7d98e31adffad9efa84c05fcc0b5cc9b7ca.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzDifferential/diff-2b10b7d98e31adffad9efa84c05fcc0b5cc9b7ca.wasm -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzDifferential/diff-3cefb827c90b7927cc0c576d83caf9ff5bb9c9f7.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzDifferential/diff-3cefb827c90b7927cc0c576d83caf9ff5bb9c9f7.wasm -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzDifferential/diff-a847f7b178038728c43d2897abdb5e1c12965bb6.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzDifferential/diff-a847f7b178038728c43d2897abdb5e1c12965bb6.wasm -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzDifferential/diff-af7fbc40cf82d8cac89fc63d4d56fc21d5806595.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzDifferential/diff-af7fbc40cf82d8cac89fc63d4d56fc21d5806595.wasm -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-03e19a2681cb95b538237b4f691999dcde00ab55: -------------------------------------------------------------------------------- 1 | asma -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-0e27f2ac7ea0e6aec7910b36d3113d3b32f97525: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/crash-0e27f2ac7ea0e6aec7910b36d3113d3b32f97525 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-113053c0f3aa13a158502f91b56e8a16a76b0e48: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/crash-113053c0f3aa13a158502f91b56e8a16a76b0e48 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-163c5cbf427de5fd638efdaf91b714259d63a7d7: -------------------------------------------------------------------------------- 1 | asm A -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-28cb43aabedbdeb4bbaa71c209566df5a05e04c1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/crash-28cb43aabedbdeb4bbaa71c209566df5a05e04c1 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-2f873cd3b8289d46c43a73f46b9456b8a5491101: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/crash-2f873cd3b8289d46c43a73f46b9456b8a5491101 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-33b8ee16bea391aadce8840da046eaedf3d69b40: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/crash-33b8ee16bea391aadce8840da046eaedf3d69b40 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-40cda94b13f0dc2627ebed7d3fc0fed970c5d0dd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/crash-40cda94b13f0dc2627ebed7d3fc0fed970c5d0dd -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-47de585e2edbaf20e93f82884be5e633ea4eb709: -------------------------------------------------------------------------------- 1 | asm` 2 |  -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-6709a22436451c40f283d16eefb2ed18fb3847dc: -------------------------------------------------------------------------------- 1 | asm# -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-9283d06966be2066531d47fdef0d804e2d2821c3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/crash-9283d06966be2066531d47fdef0d804e2d2821c3 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-dce0c2aaecb7724dcdc7ef414d37c86685fea7f6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/crash-dce0c2aaecb7724dcdc7ef414d37c86685fea7f6 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/crash-e6d6ff616328461f670b40885787dfa31d5f6cfd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/crash-e6d6ff616328461f670b40885787dfa31d5f6cfd -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/leak-134b08dc7e91a49af18c9d16140df87ee9331eda: -------------------------------------------------------------------------------- 1 | asma -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/leak-c6006650182737e2b09bada7fc0774812b802917: -------------------------------------------------------------------------------- 1 | asm -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/leak-cadea9052385438e85b9fbe41d7963849fefdfa2: -------------------------------------------------------------------------------- 1 | asm`p 2 | A A A A A  3 |  -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzExecute/oom-b6402648e0f6d2cfa0873adb73fb15a48ff9cfad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzExecute/oom-b6402648e0f6d2cfa0873adb73fb15a48ff9cfad -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-00c03bb238a993ad521865092d1faa092ccba0fa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-00c03bb238a993ad521865092d1faa092ccba0fa -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-2e61aac9dea72d2efe93489c1276ec1b69fb2992: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-2e61aac9dea72d2efe93489c1276ec1b69fb2992 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-362df2a30ead679d48492f81479924f404ce926d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-362df2a30ead679d48492f81479924f404ce926d -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-3769772d5ea06bc5e4214bf4abfb4f50feea05e6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-3769772d5ea06bc5e4214bf4abfb4f50feea05e6 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-4fd35c09b62a4ee7e152a2b4586d1f55635bb673: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-4fd35c09b62a4ee7e152a2b4586d1f55635bb673 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-5f8fe66dfa8b7f12881421d7110fe834438a29d9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-5f8fe66dfa8b7f12881421d7110fe834438a29d9 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-8b2f015e5de0aa1eb03af3e970169f732eeea21b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-8b2f015e5de0aa1eb03af3e970169f732eeea21b -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-bfca64d7e0e9d9e8ad6371a977d47c07db7bd93f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-bfca64d7e0e9d9e8ad6371a977d47c07db7bd93f -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-c639d0c0e8a2667af85537f2beebd98dac42c136: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-c639d0c0e8a2667af85537f2beebd98dac42c136 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-cc3c3de2c72d1acf6dd0ed8c53e6f1d4bd0a783b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-cc3c3de2c72d1acf6dd0ed8c53e6f1d4bd0a783b -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-d7b38baf235334838c29be758f6d708cdc4f4b2b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-d7b38baf235334838c29be758f6d708cdc4f4b2b -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-d8d325cf3a2836ddbe2ae146ff18f554c97e42f6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-d8d325cf3a2836ddbe2ae146ff18f554c97e42f6 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-e19eb785046f30e38732ca80ebc521fae813a6db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-e19eb785046f30e38732ca80ebc521fae813a6db -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/crash-fc13f28803f8114de1324454b0c995d6ed3d7460: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/crash-fc13f28803f8114de1324454b0c995d6ed3d7460 -------------------------------------------------------------------------------- /FuzzTesting/FailCases/FuzzTranslator/oom-c6676d89e7808d8180b29eedd29c60e0b02c04dd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/FuzzTesting/FailCases/FuzzTranslator/oom-c6676d89e7808d8180b29eedd29c60e0b02c04dd -------------------------------------------------------------------------------- /FuzzTesting/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.8 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "FuzzTesting", 7 | products: [ 8 | // Discussion: Why we build libraries instead of executables linking libFuzzer? 9 | // 10 | // First, libclang_rt.fuzzer.a defines the main function for the fuzzing process 11 | // and object files given by the user are expected not to have a "main" function 12 | // to avoid conflicts. 13 | // Fortunately, SwiftPM asks the compiler frontend to define the main entrypoint as 14 | // `_main` for testing executable targets (`-entry-point-function-name`) 15 | // so object files of `executableTarget` targets are capable of being linked with 16 | // libclang_rt.fuzzer.a. 17 | // However, at link-time, SwiftPM asks the linker to rename the `_main` 18 | // symbol back to `main` for the final executable (`--defsym main=_main`) 19 | // and gold linker respects the renamed "main" symbol rather than the one defined in 20 | // libclang_rt.fuzzer.a, so the final executable does not start the fuzzing process. 21 | // 22 | // Instead of relying on the SwiftPM's linking process, we build libraries defining 23 | // fuzzing target functions and manually link them with fuzzing runtime libraries. 24 | .library(name: "FuzzTranslator", type: .static, targets: ["FuzzTranslator"]), 25 | .library(name: "FuzzExecute", type: .static, targets: ["FuzzExecute"]), 26 | // FuzzDifferential is not a libFuzzer-based target, so we build it as an executable. 27 | .executable(name: "FuzzDifferential", targets: ["FuzzDifferential"]), 28 | ], 29 | dependencies: [ 30 | .package(path: "../"), 31 | ], 32 | targets: [ 33 | .target(name: "FuzzTranslator", dependencies: [ 34 | "WasmKitFuzzing", 35 | .product(name: "WasmKit", package: "WasmKit") 36 | ]), 37 | .target(name: "FuzzExecute", dependencies: [ 38 | "WasmKitFuzzing", 39 | .product(name: "WasmKit", package: "WasmKit"), 40 | ]), 41 | .executableTarget(name: "FuzzDifferential", dependencies: [ 42 | .product(name: "WasmKit", package: "WasmKit"), 43 | .product(name: "WAT", package: "WasmKit"), 44 | "WasmCAPI", 45 | ]), 46 | .target(name: "WasmCAPI"), 47 | .target(name: "WasmKitFuzzing", dependencies: [ 48 | .product(name: "WasmKit", package: "WasmKit"), 49 | ]) 50 | ] 51 | ) 52 | -------------------------------------------------------------------------------- /FuzzTesting/README.md: -------------------------------------------------------------------------------- 1 | # Fuzz Testing 2 | 3 | This subdirectory contains some [libFuzzer](https://www.llvm.org/docs/LibFuzzer.html) fuzzing targets for WasmKit. 4 | 5 | > [!WARNING] 6 | > libFuzzer does not work with the latest Swift runtime library on macOS for some reason. Run the fuzzing targets on Linux for now. 7 | 8 | ## Requirements 9 | 10 | - [Open Source Swift Toolchain](https://swift.org/install) - Xcode toolchain does not contain fuzzing support, so you need to install the open source toolchain. 11 | - [wasm-tools](https://github.com/bytecodealliance/wasm-tools) - Required to generate random seed corpora 12 | 13 | ## libFuzzer-based Fuzzing Targets 14 | 15 | ### Running the Fuzzing Targets 16 | 17 | 1. Generate seed corpora for the fuzzing targets: 18 | ```sh 19 | ./fuzz.py seed 20 | ``` 21 | 2. Run the fuzzing targets, where `` is one of the fuzzing targets available in `./Sources` directory: 22 | ```sh 23 | ./fuzz.py run 24 | ``` 25 | 3. Once the fuzzer finds a crash, it will generate a test case in the `FailCases/` directory. 26 | 27 | 28 | ### Reproducing Crashes 29 | 30 | To reproduce a crash found by the fuzzer 31 | 32 | 1. Build the fuzzer executable: 33 | ```sh 34 | ./fuzz.py build 35 | ``` 36 | 2. Run the fuzzer executable with the test case: 37 | ```sh 38 | ./.build/debug/ 39 | ``` 40 | 41 | ## Differential Testing 42 | 43 | Generate a Wasm module with termination ensured by `wasm-tools smith` and check if WasmKit and another reference engine (e.g. Wasmtime) agree on the same result and the same memory state. 44 | 45 | 1. Build the differential testing tool: 46 | ```sh 47 | # Download and extract the Wasmtime C API library 48 | mkdir -p .build/libwasmtime && \ 49 | curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v23.0.2/wasmtime-v23.0.2-x86_64-linux-c-api.tar.xz -o - | \ 50 | tar xJ --strip-component=1 -C ./.build/libwasmtime 51 | # Build the differential testing tool with libwasmtime 52 | swift build -Xlinker -L./.build/libwasmtime/lib -Xlinker -l:libwasmtime.a --product FuzzDifferential 53 | ``` 54 | You can use any other reference engine implementing the [Wasm C API](https://github.com/WebAssembly/wasm-c-api) by replacing the `libwasmtime` library. 55 | 56 | 2. Run the differential testing tool: 57 | ```sh 58 | ./differential.py 59 | ``` 60 | -------------------------------------------------------------------------------- /FuzzTesting/Sources/FuzzExecute/FuzzExecute.swift: -------------------------------------------------------------------------------- 1 | @_spi(Fuzzing) import WasmKit 2 | import WasmKitFuzzing 3 | 4 | @_cdecl("LLVMFuzzerTestOneInput") 5 | public func FuzzCheck(_ start: UnsafePointer, _ count: Int) -> CInt { 6 | let bytes = Array(UnsafeBufferPointer(start: start, count: count)) 7 | do { 8 | let module = try WasmKit.parseWasm(bytes: bytes) 9 | let engine = WasmKit.Engine() 10 | let store = WasmKit.Store(engine: engine) 11 | store.resourceLimiter = FuzzerResourceLimiter() 12 | let instance = try module.instantiate(store: store) 13 | for export in instance.exports.values { 14 | guard case let .function(fn) = export else { 15 | continue 16 | } 17 | let type = fn.type 18 | let arguments = type.parameters.map { $0.defaultValue } 19 | _ = try fn(arguments) 20 | } 21 | } catch { 22 | // Ignore errors 23 | } 24 | return 0 25 | } 26 | 27 | extension ValueType { 28 | var defaultValue: Value { 29 | switch self { 30 | case .i32: return .i32(0) 31 | case .i64: return .i64(0) 32 | case .f32: return .f32(0) 33 | case .f64: return .f64(0) 34 | case .ref(.funcRef): return .ref(.function(0)) 35 | case .ref(.externRef): return .ref(.extern(0)) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /FuzzTesting/Sources/FuzzTranslator/FuzzTranslator.swift: -------------------------------------------------------------------------------- 1 | import WasmKit 2 | import WasmKitFuzzing 3 | 4 | @_cdecl("LLVMFuzzerTestOneInput") 5 | public func FuzzCheck(_ start: UnsafePointer, _ count: Int) -> CInt { 6 | let bytes = Array(UnsafeBufferPointer(start: start, count: count)) 7 | do { 8 | try fuzzInstantiation(bytes: bytes) 9 | } catch { 10 | // Ignore errors 11 | } 12 | return 0 13 | } 14 | -------------------------------------------------------------------------------- /FuzzTesting/Sources/WasmCAPI/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module WasmCAPI { 2 | header "wasm.h" 3 | } 4 | -------------------------------------------------------------------------------- /FuzzTesting/Sources/WasmCAPI/include/wasm.h: -------------------------------------------------------------------------------- 1 | ../../../../Vendor/wasm-c-api/include/wasm.h -------------------------------------------------------------------------------- /FuzzTesting/Sources/WasmKitFuzzing/WasmKitFuzzing.swift: -------------------------------------------------------------------------------- 1 | // This module defines utilities for fuzzing WasmKit. 2 | 3 | @_spi(Fuzzing) import WasmKit 4 | 5 | /// A resource limiter that restricts allocations to fuzzer limits. 6 | public struct FuzzerResourceLimiter: ResourceLimiter { 7 | public init() {} 8 | 9 | public func limitMemoryGrowth(to desired: Int) throws -> Bool { 10 | return desired < 1024 * 1024 * 1024 11 | } 12 | public func limitTableGrowth(to desired: Int) throws -> Bool { 13 | return desired < 1024 * 1024 14 | } 15 | } 16 | 17 | /// Check if a Wasm module can be instantiated without crashing. 18 | /// 19 | /// - Parameter bytes: The bytes of the Wasm module. 20 | public func fuzzInstantiation(bytes: [UInt8]) throws { 21 | let module = try WasmKit.parseWasm(bytes: bytes) 22 | let engine = Engine(configuration: EngineConfiguration(compilationMode: .eager)) 23 | let store = Store(engine: engine) 24 | store.resourceLimiter = FuzzerResourceLimiter() 25 | 26 | // Prepare dummy imports 27 | var imports = Imports() 28 | for importEntry in module.imports { 29 | let value: ExternalValueConvertible 30 | switch importEntry.descriptor { 31 | case .function(let typeIndex): 32 | guard typeIndex < module.types.count else { 33 | // Skip if import type index is out of bounds 34 | return 35 | } 36 | let type = module.types[Int(typeIndex)] 37 | value = Function(store: store, type: type) { _, _ in 38 | // Provide "start function" with empty results 39 | if type.results.isEmpty { return [] } 40 | fatalError("Unexpected function call") 41 | } 42 | case .global(let globalType): 43 | value = try Global(store: store, type: globalType, value: .i32(0)) 44 | case .memory(let memoryType): 45 | value = try Memory(store: store, type: memoryType) 46 | case .table(let tableType): 47 | value = try Table(store: store, type: tableType) 48 | } 49 | imports.define(module: importEntry.module, name: importEntry.name, value.externalValue) 50 | } 51 | 52 | // Instantiate the module 53 | _ = try module.instantiate(store: store, imports: imports) 54 | } 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Akio Yasui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | This product contains a derivation of the utility code from Swift System. 3 | 4 | * LICENSE (Apache License 2.0): 5 | * https://www.apache.org/licenses/LICENSE-2.0 6 | * HOMEPAGE: 7 | * https://github.com/apple/swift-system 8 | 9 | ------------------------------------------------------------------------------- 10 | This product contains a derivation of the list of Swift keywords from Swift Syntax. 11 | 12 | * LICENSE (Apache License 2.0): 13 | * https://www.apache.org/licenses/LICENSE-2.0 14 | * HOMEPAGE: 15 | * https://github.com/apple/swift-syntax 16 | -------------------------------------------------------------------------------- /Plugins/GenerateOverlayForTesting/Plugin.swift: -------------------------------------------------------------------------------- 1 | import PackagePlugin 2 | import Foundation 3 | 4 | @main 5 | struct Plugin: BuildToolPlugin { 6 | func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { 7 | let witTool = try context.tool(named: "WITTool").path 8 | let fixturesDir = target.directory.appending("Fixtures") 9 | let hostOverlayDir = context.pluginWorkDirectory.appending("GeneratedHostOverlay") 10 | return try FileManager.default.contentsOfDirectory(atPath: fixturesDir.string).compactMap { singleFixture in 11 | let outputFile = hostOverlayDir.appending(singleFixture + "HostOverlay.swift") 12 | let inputFileDir = fixturesDir.appending(singleFixture, "wit") 13 | guard FileManager.default.isDirectory(filePath: inputFileDir.string) else { return nil } 14 | 15 | let inputFiles = try FileManager.default.subpathsOfDirectory(atPath: inputFileDir.string).map { 16 | inputFileDir.appending(subpath: $0) 17 | } 18 | return Command.buildCommand( 19 | displayName: "Generating host overlay for \(singleFixture)", 20 | executable: witTool, 21 | arguments: [ 22 | "generate-overlay", "--target", "host", 23 | inputFileDir, "-o", outputFile 24 | ], 25 | inputFiles: inputFiles, 26 | outputFiles: [outputFile] 27 | ) 28 | } 29 | } 30 | } 31 | 32 | extension FileManager { 33 | internal func isDirectory(filePath: String) -> Bool { 34 | var isDirectory: ObjCBool = false 35 | let exists = self.fileExists(atPath: filePath, isDirectory: &isDirectory) 36 | return exists && isDirectory.boolValue 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Plugins/WITOverlayPlugin/Plugin.swift: -------------------------------------------------------------------------------- 1 | import PackagePlugin 2 | import Foundation 3 | 4 | @main 5 | struct Plugin: BuildToolPlugin { 6 | func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { 7 | if !target.recursiveTargetDependencies.contains(where: { $0.name == "_CabiShims" }) { 8 | Diagnostics.emit(.error, "\"_CabiShims\" must be included as a dependency") 9 | } 10 | let witTool = try context.tool(named: "WITTool").path 11 | let witDir = target.directory.appending("wit") 12 | let inputFiles = try FileManager.default.subpathsOfDirectory(atPath: witDir.string).map { 13 | witDir.appending(subpath: $0) 14 | } 15 | let outputFile = context.pluginWorkDirectory.appending("GeneratedOverlay", "\(target.name)Overlay.swift") 16 | let command = Command.buildCommand( 17 | displayName: "Generating WIT overlay for \(target.name)", 18 | executable: witTool, 19 | arguments: [ 20 | "generate-overlay", "--target", "guest", 21 | witDir, "-o", outputFile 22 | ], 23 | inputFiles: inputFiles, 24 | outputFiles: [outputFile] 25 | ) 26 | return [command] 27 | } 28 | } 29 | 30 | extension FileManager { 31 | internal func isDirectory(filePath: String) -> Bool { 32 | var isDirectory: ObjCBool = false 33 | let exists = self.fileExists(atPath: filePath, isDirectory: &isDirectory) 34 | return exists && isDirectory.boolValue 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/CLI/CLI.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | 3 | @main 4 | struct CLI: ParsableCommand { 5 | static let configuration = CommandConfiguration( 6 | commandName: "wasmkit", 7 | abstract: "WasmKit WebAssembly Runtime", 8 | version: "0.1.5", 9 | subcommands: [Run.self, Explore.self] 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /Sources/CLI/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(wasmkit-cli 2 | Commands/Run.swift 3 | Commands/Explore.swift 4 | CLI.swift 5 | ) 6 | 7 | target_link_wasmkit_libraries(wasmkit-cli PUBLIC 8 | ArgumentParser WAT WasmKitWASI) 9 | 10 | install(TARGETS wasmkit-cli 11 | RUNTIME DESTINATION bin) 12 | -------------------------------------------------------------------------------- /Sources/CLI/Commands/Explore.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | import SystemPackage 3 | @_spi(OnlyForCLI) import WasmKit 4 | 5 | struct Explore: ParsableCommand { 6 | 7 | static let configuration = CommandConfiguration( 8 | abstract: "Explore the compiled functions of a WebAssembly module", 9 | discussion: """ 10 | This command will parse a WebAssembly module and dump the compiled functions. 11 | """, 12 | // This command is just for debugging purposes, so it should be hidden by default 13 | shouldDisplay: false 14 | ) 15 | 16 | @Argument 17 | var path: String 18 | 19 | struct Stdout: TextOutputStream { 20 | func write(_ string: String) { 21 | print(string, terminator: "") 22 | } 23 | } 24 | 25 | func run() throws { 26 | let module = try parseWasm(filePath: FilePath(path)) 27 | // Instruction dumping requires token threading model for now 28 | let configuration = EngineConfiguration(threadingModel: .token) 29 | let engine = Engine(configuration: configuration) 30 | let store = Store(engine: engine) 31 | 32 | var imports: Imports = [:] 33 | for importEntry in module.imports { 34 | switch importEntry.descriptor { 35 | case .function(let typeIndex): 36 | let type = module.types[Int(typeIndex)] 37 | imports.define( 38 | module: importEntry.module, 39 | name: importEntry.name, 40 | Function(store: store, type: type) { _, _ in 41 | fatalError("unreachable") 42 | } 43 | ) 44 | default: 45 | fatalError("Import \(importEntry) not supported in explore mode yet") 46 | } 47 | } 48 | let instance = try module.instantiate(store: store, imports: imports) 49 | var stdout = Stdout() 50 | try instance.dumpFunctions(to: &stdout, module: module) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(_CWasmKit) 2 | add_subdirectory(WasmKit) 3 | add_subdirectory(WasmKitWASI) 4 | add_subdirectory(SystemExtras) 5 | add_subdirectory(WASI) 6 | add_subdirectory(WasmTypes) 7 | add_subdirectory(WasmParser) 8 | add_subdirectory(WAT) 9 | 10 | if(WASMKIT_BUILD_CLI) 11 | add_subdirectory(CLI) 12 | endif() 13 | -------------------------------------------------------------------------------- /Sources/SystemExtras/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_wasmkit_library(SystemExtras 2 | Vendor/Exports.swift 3 | Vendor/Utils.swift 4 | Vendor/WindowsSyscallAdapter.swift 5 | Clock.swift 6 | Constants.swift 7 | FileAtOperations.swift 8 | FileOperations.swift 9 | Syscalls.swift 10 | ) 11 | 12 | set(SWIFT_SYSTEM_APPLE_PLATFORMS "Darwin" "iOS" "watchOS" "tvOS" "visionOS") 13 | if(CMAKE_SYSTEM_NAME IN_LIST SWIFT_SYSTEM_APPLE_PLATFORMS) 14 | target_compile_definitions(SystemExtras PRIVATE SYSTEM_PACKAGE_DARWIN) 15 | endif() 16 | 17 | target_link_wasmkit_libraries(SystemExtras PUBLIC 18 | SystemPackage) 19 | -------------------------------------------------------------------------------- /Sources/SystemExtras/Vendor/WindowsSyscallAdapter.swift: -------------------------------------------------------------------------------- 1 | #if os(Windows) 2 | import WinSDK 3 | import SystemPackage 4 | 5 | extension Errno { 6 | public init(windowsError: DWORD) { 7 | self.init(rawValue: _mapWindowsErrorToErrno(windowsError)) 8 | } 9 | } 10 | 11 | @usableFromInline 12 | internal func _mapWindowsErrorToErrno(_ errorCode: DWORD) -> CInt { 13 | switch Int32(errorCode) { 14 | case ERROR_SUCCESS: 15 | return 0 16 | case ERROR_INVALID_FUNCTION, 17 | ERROR_INVALID_ACCESS, 18 | ERROR_INVALID_DATA, 19 | ERROR_INVALID_PARAMETER, 20 | ERROR_NEGATIVE_SEEK: 21 | return EINVAL 22 | case ERROR_FILE_NOT_FOUND, 23 | ERROR_PATH_NOT_FOUND, 24 | ERROR_INVALID_DRIVE, 25 | ERROR_NO_MORE_FILES, 26 | ERROR_BAD_NETPATH, 27 | ERROR_BAD_NET_NAME, 28 | ERROR_BAD_PATHNAME, 29 | ERROR_FILENAME_EXCED_RANGE: 30 | return ENOENT 31 | case ERROR_TOO_MANY_OPEN_FILES: 32 | return EMFILE 33 | case ERROR_ACCESS_DENIED, 34 | ERROR_CURRENT_DIRECTORY, 35 | ERROR_LOCK_VIOLATION, 36 | ERROR_NETWORK_ACCESS_DENIED, 37 | ERROR_CANNOT_MAKE, 38 | ERROR_FAIL_I24, 39 | ERROR_DRIVE_LOCKED, 40 | ERROR_SEEK_ON_DEVICE, 41 | ERROR_NOT_LOCKED, 42 | ERROR_LOCK_FAILED, 43 | ERROR_WRITE_PROTECT...ERROR_SHARING_BUFFER_EXCEEDED: 44 | return EACCES 45 | case ERROR_INVALID_HANDLE, 46 | ERROR_INVALID_TARGET_HANDLE, 47 | ERROR_DIRECT_ACCESS_HANDLE: 48 | return EBADF 49 | case ERROR_ARENA_TRASHED, 50 | ERROR_NOT_ENOUGH_MEMORY, 51 | ERROR_INVALID_BLOCK, 52 | ERROR_NOT_ENOUGH_QUOTA: 53 | return ENOMEM 54 | case ERROR_BAD_ENVIRONMENT: 55 | return E2BIG 56 | case ERROR_BAD_FORMAT, 57 | ERROR_INVALID_STARTING_CODESEG...ERROR_INFLOOP_IN_RELOC_CHAIN: 58 | return ENOEXEC 59 | case ERROR_NOT_SAME_DEVICE: 60 | return EXDEV 61 | case ERROR_FILE_EXISTS, 62 | ERROR_ALREADY_EXISTS: 63 | return EEXIST 64 | case ERROR_NO_PROC_SLOTS, 65 | ERROR_MAX_THRDS_REACHED, 66 | ERROR_NESTING_NOT_ALLOWED: 67 | return EAGAIN 68 | case ERROR_BROKEN_PIPE: 69 | return EPIPE 70 | case ERROR_DISK_FULL: 71 | return ENOSPC 72 | case ERROR_WAIT_NO_CHILDREN, 73 | ERROR_CHILD_NOT_COMPLETE: 74 | return ECHILD 75 | case ERROR_DIR_NOT_EMPTY: 76 | return ENOTEMPTY 77 | case ERROR_NO_UNICODE_TRANSLATION: 78 | return EILSEQ 79 | default: 80 | return EINVAL 81 | } 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /Sources/WASI/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_wasmkit_library(WASI 2 | Platform/SandboxPrimitives/Open.swift 3 | Platform/SandboxPrimitives/OpenParent.swift 4 | Platform/Directory.swift 5 | Platform/Entry.swift 6 | Platform/File.swift 7 | Platform/PlatformTypes.swift 8 | Platform/SandboxPrimitives.swift 9 | FileSystem.swift 10 | GuestMemorySupport.swift 11 | Clock.swift 12 | RandomBufferGenerator.swift 13 | WASI.swift 14 | ) 15 | 16 | target_link_wasmkit_libraries(WASI PUBLIC 17 | WasmTypes SystemExtras) 18 | -------------------------------------------------------------------------------- /Sources/WASI/GuestMemorySupport.swift: -------------------------------------------------------------------------------- 1 | import WasmTypes 2 | 3 | extension GuestPointee { 4 | static func readFromGuest(_ pointer: inout UnsafeGuestRawPointer) -> Self { 5 | pointer = pointer.alignedUp(toMultipleOf: Self.alignInGuest) 6 | let value = readFromGuest(pointer) 7 | pointer = pointer.advanced(by: sizeInGuest) 8 | return value 9 | } 10 | 11 | static func writeToGuest(at pointer: inout UnsafeGuestRawPointer, value: Self) { 12 | pointer = pointer.alignedUp(toMultipleOf: Self.alignInGuest) 13 | writeToGuest(at: pointer, value: value) 14 | pointer = pointer.advanced(by: sizeInGuest) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/WASI/Platform/SandboxPrimitives.swift: -------------------------------------------------------------------------------- 1 | internal enum SandboxPrimitives {} 2 | -------------------------------------------------------------------------------- /Sources/WASI/Platform/SandboxPrimitives/OpenParent.swift: -------------------------------------------------------------------------------- 1 | import SystemPackage 2 | 3 | /// Split the given path to the parent and the last component to be passed to openat 4 | /// Note: `SystemPackage.FilePath` strips explicit trailing "/" by normalization at `init`, 5 | /// so this function takes path as a `String`. 6 | internal func splitParent(path: String) -> (FilePath, FilePath.Component)? { 7 | func pathRequiresDirectory(path: String) -> Bool { 8 | return path.hasSuffix("/") || path.hasSuffix("/.") 9 | } 10 | 11 | guard !path.isEmpty else { return nil } 12 | 13 | if pathRequiresDirectory(path: path) { 14 | // Create a link to the directory itself 15 | return (FilePath(path), FilePath.Component(".")) 16 | } 17 | 18 | let filePath = FilePath(path) 19 | var components = filePath.components 20 | if let c = components.popLast() { 21 | switch c.kind { 22 | case .regular, .currentDirectory: 23 | return (FilePath(root: filePath.root, components), c) 24 | case .parentDirectory: 25 | // Create a link to the parent directory itself 26 | return (filePath, FilePath.Component(".")) 27 | } 28 | } else { 29 | fatalError("non-empty path should have at least one component") 30 | } 31 | } 32 | 33 | extension SandboxPrimitives { 34 | static func openParent(start: FileDescriptor, path: String) throws -> (FileDescriptor, String) { 35 | guard let (dirName, basename) = splitParent(path: path) else { 36 | throw WASIAbi.Errno.ENOENT 37 | } 38 | 39 | let dirFd: FileDescriptor 40 | if !dirName.isEmpty { 41 | let options: FileDescriptor.OpenOptions 42 | #if os(Windows) 43 | options = [] 44 | #else 45 | options = .directory 46 | #endif 47 | dirFd = try openAt( 48 | start: start, path: dirName, 49 | mode: .readOnly, options: options, 50 | permissions: [] 51 | ) 52 | } else { 53 | dirFd = start 54 | } 55 | return (dirFd, basename.string) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/WASI/RandomBufferGenerator.swift: -------------------------------------------------------------------------------- 1 | import SwiftShims // For swift_stdlib_random 2 | 3 | /// A type that provides random bytes. 4 | /// 5 | /// This type is similar to `RandomNumberGenerator` in Swift standard library, 6 | /// but it provides a way to fill a buffer with random bytes instead of a single 7 | /// random number. 8 | public protocol RandomBufferGenerator { 9 | 10 | /// Fills the buffer with random bytes. 11 | /// 12 | /// - Parameter buffer: The destination buffer to fill with random bytes. 13 | mutating func fill(buffer: UnsafeMutableBufferPointer) 14 | } 15 | 16 | extension RandomBufferGenerator where Self: RandomNumberGenerator { 17 | public mutating func fill(buffer: UnsafeMutableBufferPointer) { 18 | // The buffer is filled with 8 bytes at once. 19 | let count = buffer.count / 8 20 | for i in 0.. 0 { 32 | let random = self.next() 33 | withUnsafeBytes(of: random) { randomBytes in 34 | let startOffset = count * 8 35 | let destination = UnsafeMutableBufferPointer(rebasing: buffer[startOffset..<(startOffset + remaining)]) 36 | UnsafeMutableRawBufferPointer(destination).copyMemory( 37 | from: UnsafeRawBufferPointer(start: randomBytes.baseAddress, count: remaining) 38 | ) 39 | } 40 | } 41 | } 42 | } 43 | 44 | extension SystemRandomNumberGenerator: RandomBufferGenerator { 45 | public mutating func fill(buffer: UnsafeMutableBufferPointer) { 46 | guard let baseAddress = buffer.baseAddress else { return } 47 | // Directly call underlying C function of SystemRandomNumberGenerator 48 | swift_stdlib_random(baseAddress, Int(buffer.count)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/WAT/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_wasmkit_library(WAT 2 | BinaryInstructionEncoder.swift 3 | Encoder.swift 4 | Lexer.swift 5 | Location.swift 6 | NameMapping.swift 7 | ParseTextInstruction.swift 8 | Parser.swift 9 | Parser/ExpressionParser.swift 10 | Parser/WastParser.swift 11 | Parser/WatParser.swift 12 | WAT.swift 13 | ) 14 | 15 | target_link_wasmkit_libraries(WAT PUBLIC 16 | WasmParser) 17 | -------------------------------------------------------------------------------- /Sources/WAT/Docs.docc/Docs.md: -------------------------------------------------------------------------------- 1 | # ``WAT`` 2 | 3 | A library for working with WebAssembly Text format. 4 | 5 | 6 | ## Overview 7 | 8 | WAT is a library for working with WebAssembly Text format. It provides a parser and encoder from WebAssembly Text format to WebAssembly binary format. 9 | 10 | 11 | ## Usage 12 | 13 | ```swift 14 | import WAT 15 | 16 | let wat = try parseWAT(""" 17 | (module 18 | (func $add (param i32 i32) (result i32) 19 | local.get 0 20 | local.get 1 21 | i32.add) 22 | (export "add" (func $add)) 23 | ) 24 | """) 25 | 26 | let wasm = try wat.encode() 27 | ``` 28 | 29 | ### Parsing WAST 30 | 31 | WAST is an superset of WAT that includes additional directives for testing. You can parse WAST using the `parseWAST` function. 32 | 33 | ```swift 34 | import WAT 35 | 36 | var wast = try parseWAST(""" 37 | (module 38 | (func $add (param i32 i32) (result i32) 39 | local.get 0 40 | local.get 1 41 | i32.add) 42 | (export "add" (func $add)) 43 | ) 44 | (assert_return (invoke "add" (i32.const 1) (i32.const 2)) (i32.const 3)) 45 | """) 46 | 47 | while let (directive, location) = try wast.nextDirective() { 48 | print("\(location): \(directive)") 49 | } 50 | ``` 51 | 52 | 53 | ## Topics 54 | 55 | ### Parsing 56 | 57 | - ``parseWAT(_:features:)`` 58 | - ``parseWAST(_:features:)`` 59 | 60 | 61 | ### Encode to binary format 62 | 63 | - ``wat2wasm(_:features:)`` 64 | 65 | ### WAST structures 66 | 67 | - ``Wast`` 68 | - ``WastDirective`` 69 | - ``ModuleDirective`` 70 | - ``ModuleSource`` 71 | - ``WastInvoke`` 72 | - ``WastExecute`` 73 | - ``WastExpectValue`` 74 | -------------------------------------------------------------------------------- /Sources/WAT/Location.swift: -------------------------------------------------------------------------------- 1 | /// A location in a WAT source file. 2 | public struct Location: Equatable, CustomDebugStringConvertible { 3 | let index: Lexer.Index 4 | let source: String.UnicodeScalarView 5 | 6 | init(at index: Lexer.Index, in source: String.UnicodeScalarView) { 7 | self.index = index 8 | self.source = source 9 | } 10 | 11 | public static func == (lhs: Location, rhs: Location) -> Bool { 12 | return lhs.index == rhs.index 13 | } 14 | 15 | public var debugDescription: String { 16 | return "Location\(sourceLocation(at: index, in: source))" 17 | } 18 | 19 | /// Computes the line and column of the location in the source file. 20 | /// - Returns: The line and column of the location. Line is 1-indexed and column is 0-indexed. 21 | public func computeLineAndColumn() -> (line: Int, column: Int) { 22 | return sourceLocation(at: index, in: source) 23 | } 24 | } 25 | 26 | /// Returns the location of the given index in the source 27 | /// - Parameters: 28 | /// - index: The index in the source 29 | /// - source: The source string 30 | /// - Returns: The location of the index in line-column style. Line is 1-indexed and column is 0-indexed. 31 | func sourceLocation(at index: Lexer.Index, in source: String.UnicodeScalarView) -> (line: Int, column: Int) { 32 | let slice = source[.. (line: Int, column: Int)? { 15 | guard let textRange else { return nil } 16 | let position = textRange.lowerBound 17 | let linesBeforePos = sourceText[.. Diagnostic { 26 | return Diagnostic( 27 | message: "Invalid redeclaration of '\(identifier)'", 28 | textRange: textRange 29 | ) 30 | } 31 | 32 | static func expectedIdentifier(textRange: TextRange) -> Diagnostic { 33 | return Diagnostic(message: "Expected identifier", textRange: textRange) 34 | } 35 | 36 | static func cannotFindType(of identifier: String, textRange: TextRange?) -> Diagnostic { 37 | return Diagnostic(message: "Cannot find type '\(identifier)' in scope", textRange: textRange) 38 | } 39 | 40 | static func cannotFindInterface(of identifier: String, textRange: TextRange?) -> Diagnostic { 41 | return Diagnostic(message: "Cannot find interface '\(identifier)' in scope", textRange: textRange) 42 | } 43 | 44 | static func expectedResourceType(_ type: WITType, textRange: TextRange?) -> Diagnostic { 45 | return Diagnostic(message: "Non-resource type \(type)", textRange: textRange) 46 | } 47 | 48 | static func noSuchPackage(_ packageName: PackageNameSyntax, textRange: TextRange?) -> Diagnostic { 49 | return Diagnostic(message: "No such package '\(packageName)'", textRange: textRange) 50 | } 51 | 52 | static func inconsistentPackageName( 53 | _ packageName: PackageNameSyntax, 54 | existingName: PackageNameSyntax, 55 | textRange: TextRange? 56 | ) -> Diagnostic { 57 | return Diagnostic( 58 | message: "package identifier `\(packageName)` does not match previous package name of `\(existingName)`", 59 | textRange: textRange 60 | ) 61 | } 62 | 63 | static func noPackageHeader() -> Diagnostic { 64 | return Diagnostic(message: "no `package` header was found in any WIT file for this package", textRange: nil) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/WIT/Semantics/PackageBuilder.swift: -------------------------------------------------------------------------------- 1 | /// A type responsible to build a package from parsed `.wit` ASTs 2 | struct PackageBuilder { 3 | var packageName: PackageNameSyntax? 4 | var sourceFiles: [SyntaxNode] = [] 5 | 6 | mutating func append(_ ast: SyntaxNode) throws { 7 | // Check package name consistency 8 | switch (self.packageName, ast.packageId) { 9 | case (_, nil): break 10 | case (nil, let name?): 11 | self.packageName = name 12 | case (let existingName?, let newName?): 13 | guard existingName.isSamePackage(as: newName) else { 14 | throw DiagnosticError( 15 | diagnostic: .inconsistentPackageName( 16 | newName, 17 | existingName: existingName, 18 | textRange: newName.textRange 19 | ) 20 | ) 21 | } 22 | } 23 | self.sourceFiles.append(ast) 24 | } 25 | 26 | func build() throws -> PackageUnit { 27 | guard let packageName = self.packageName else { 28 | throw DiagnosticError(diagnostic: .noPackageHeader()) 29 | } 30 | return PackageUnit(packageName: packageName, sourceFiles: sourceFiles) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/WIT/Semantics/SemanticsContext.swift: -------------------------------------------------------------------------------- 1 | public struct SemanticsContext { 2 | let evaluator: Evaluator 3 | public let rootPackage: PackageUnit 4 | public let packageResolver: PackageResolver 5 | 6 | public init(rootPackage: PackageUnit, packageResolver: PackageResolver) { 7 | self.evaluator = Evaluator() 8 | self.rootPackage = rootPackage 9 | self.packageResolver = packageResolver 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/WIT/SyntaxNode.swift: -------------------------------------------------------------------------------- 1 | /// An AST node that can be wrapped by ``SyntaxNode`` as a reference 2 | public protocol SyntaxNodeProtocol {} 3 | 4 | /// An AST node with reference semantics to provide identity of a node. 5 | @dynamicMemberLookup 6 | public struct SyntaxNode: Equatable, Hashable { 7 | class Ref { 8 | fileprivate let syntax: Syntax 9 | init(syntax: Syntax) { 10 | self.syntax = syntax 11 | } 12 | } 13 | 14 | private let ref: Ref 15 | public var syntax: Syntax { ref.syntax } 16 | 17 | internal init(syntax: Syntax) { 18 | self.ref = Ref(syntax: syntax) 19 | } 20 | 21 | public subscript(dynamicMember keyPath: KeyPath) -> R { 22 | self.ref.syntax[keyPath: keyPath] 23 | } 24 | 25 | public static func == (lhs: SyntaxNode, rhs: SyntaxNode) -> Bool { 26 | return lhs.ref === rhs.ref 27 | } 28 | 29 | public func hash(into hasher: inout Hasher) { 30 | hasher.combine(ObjectIdentifier(ref)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/WIT/TextParser/ParseInterface.swift: -------------------------------------------------------------------------------- 1 | extension InterfaceSyntax { 2 | static func parse( 3 | lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax] 4 | ) throws -> SyntaxNode { 5 | try lexer.expect(.interface) 6 | let name = try Identifier.parse(lexer: &lexer) 7 | let items = try parseItems(lexer: &lexer) 8 | return .init( 9 | syntax: InterfaceSyntax( 10 | documents: documents, attributes: attributes, name: name, items: items 11 | )) 12 | } 13 | 14 | static func parseItems(lexer: inout Lexer) throws -> [InterfaceItemSyntax] { 15 | try lexer.expect(.leftBrace) 16 | var items: [InterfaceItemSyntax] = [] 17 | while true { 18 | let docs = try DocumentsSyntax.parse(lexer: &lexer) 19 | if lexer.eat(.rightBrace) { 20 | break 21 | } 22 | let attributes = try AttributeSyntax.parseItems(lexer: &lexer) 23 | items.append(try InterfaceItemSyntax.parse(lexer: &lexer, documents: docs, attributes: attributes)) 24 | } 25 | return items 26 | } 27 | } 28 | 29 | extension InterfaceItemSyntax { 30 | static func parse(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> InterfaceItemSyntax { 31 | switch lexer.peek()?.kind { 32 | case .type: 33 | return try .typeDef(.init(syntax: .parse(lexer: &lexer, documents: documents, attributes: attributes))) 34 | case .flags: 35 | return try .typeDef(.init(syntax: .parseFlags(lexer: &lexer, documents: documents, attributes: attributes))) 36 | case .enum: 37 | return try .typeDef(.init(syntax: .parseEnum(lexer: &lexer, documents: documents, attributes: attributes))) 38 | case .variant: 39 | return try .typeDef(.init(syntax: .parseVariant(lexer: &lexer, documents: documents, attributes: attributes))) 40 | case .resource: 41 | return try .typeDef(TypeDefSyntax.parseResource(lexer: &lexer, documents: documents, attributes: attributes)) 42 | case .record: 43 | return try .typeDef(.init(syntax: .parseRecord(lexer: &lexer, documents: documents, attributes: attributes))) 44 | case .union: 45 | return try .typeDef(.init(syntax: .parseUnion(lexer: &lexer, documents: documents, attributes: attributes))) 46 | case .id, .explicitId: 47 | return try .function(NamedFunctionSyntax.parse(lexer: &lexer, documents: documents)) 48 | case .use: 49 | return try .use(UseSyntax.parse(lexer: &lexer)) 50 | default: 51 | throw ParseError(description: "`import`, `export`, `include`, `use`, or type definition") 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/WIT/TextParser/Parser.swift: -------------------------------------------------------------------------------- 1 | enum Parser { 2 | static func parseList( 3 | lexer: inout Lexer, start: TokenKind, end: TokenKind, 4 | parse: (DocumentsSyntax, inout Lexer) throws -> Item 5 | ) throws -> [Item] { 6 | try lexer.expect(start) 7 | return try parseListTrailer(lexer: &lexer, end: end, parse: parse) 8 | } 9 | 10 | static func parseListTrailer( 11 | lexer: inout Lexer, end: TokenKind, 12 | parse: (DocumentsSyntax, inout Lexer) throws -> Item 13 | ) throws -> [Item] { 14 | var items: [Item] = [] 15 | while true { 16 | let docs = try DocumentsSyntax.parse(lexer: &lexer) 17 | if lexer.eat(end) { 18 | break 19 | } 20 | 21 | let item = try parse(docs, &lexer) 22 | items.append(item) 23 | 24 | guard lexer.eat(.comma) else { 25 | try lexer.expect(end) 26 | break 27 | } 28 | } 29 | return items 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/WITExtractor/Diagnostic.swift: -------------------------------------------------------------------------------- 1 | public struct Diagnostic: CustomStringConvertible { 2 | public enum Severity { 3 | case warning 4 | case error 5 | } 6 | 7 | public let message: String 8 | public let severity: Severity 9 | 10 | public var description: String { 11 | "\(severity):\(message)" 12 | } 13 | 14 | static func warning(_ messaage: String) -> Diagnostic { 15 | return Diagnostic(message: messaage, severity: .warning) 16 | } 17 | } 18 | 19 | extension Diagnostic { 20 | static func skipField(context: String, field: String, missingType: String) -> Diagnostic { 21 | .warning("Skipping \(context)/\(field) field due to missing corresponding WIT type for \"\(missingType)\"") 22 | } 23 | } 24 | 25 | final class DiagnosticCollection { 26 | private(set) var diagnostics: [Diagnostic] = [] 27 | 28 | func add(_ diagnostic: Diagnostic) { 29 | diagnostics.append(diagnostic) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/WITExtractor/SourcePrinter.swift: -------------------------------------------------------------------------------- 1 | final class SourcePrinter { 2 | private(set) var contents: String = "" 3 | private var indentLevel: Int = 0 4 | 5 | init(header: String = "") { 6 | self.contents = header 7 | } 8 | 9 | func emptyLine() { 10 | contents += "\n" 11 | } 12 | 13 | func write(line: S) { 14 | contents += "\n" + String(repeating: " ", count: indentLevel * 4) 15 | contents += line 16 | } 17 | 18 | func write(multiline: String) { 19 | for line in multiline.split(separator: "\n") { 20 | write(line: line) 21 | } 22 | } 23 | 24 | func indent() { 25 | indentLevel += 1 26 | } 27 | 28 | func unindent() { 29 | indentLevel -= 1 30 | } 31 | 32 | func indent(_ body: () throws -> Void) rethrows { 33 | indentLevel += 1 34 | try body() 35 | indentLevel -= 1 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/CanonicalABI/CanonicalName.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | enum CanonicalFunctionName { 4 | case world(String) 5 | case interface(PackageNameSyntax, interfaceName: String, id: String) 6 | 7 | var abiName: String { 8 | switch self { 9 | case .world(let id): return id 10 | case .interface(let packageName, let interfaceName, let id): 11 | return "\(packageName.namespace.text):\(packageName.name.text)/\(interfaceName)#\(id)" 12 | } 13 | } 14 | 15 | var uniqueSwiftName: String { 16 | switch self { 17 | case .world(let id): 18 | return ConvertCase.camelCase(kebab: id) 19 | case .interface(let packageName, let interfaceName, let id): 20 | return ConvertCase.camelCase(kebab: packageName.namespace.text) + "_" 21 | + ConvertCase.camelCase(kebab: packageName.name.text) + "_" 22 | + ConvertCase.camelCase(kebab: interfaceName) + "_" 23 | + ConvertCase.camelCase(kebab: id) 24 | } 25 | } 26 | 27 | var apiSwiftName: String { 28 | switch self { 29 | case .world(let id), .interface(_, _, let id): 30 | return ConvertCase.camelCase(kebab: id) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/CanonicalABI/CanonicalOperation.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | enum StaticMetaOperand: CustomStringConvertible { 4 | /// A case that represents a core value stored in the given variable name 5 | case variable(String) 6 | case literal(String) 7 | case call(_ name: String, arguments: [(String?, StaticMetaOperand)]) 8 | indirect case forceUnwrap(StaticMetaOperand) 9 | indirect case liftOptional(isSome: StaticMetaOperand, payload: StaticMetaOperand) 10 | indirect case lowerBool(StaticMetaOperand) 11 | indirect case accessField(StaticMetaOperand, name: String) 12 | 13 | static func call(_ name: String, arguments: [StaticMetaOperand]) -> StaticMetaOperand { 14 | return .call(name, arguments: arguments.map { (nil, $0) }) 15 | } 16 | 17 | var description: String { 18 | switch self { 19 | case .variable(let label): return label 20 | case .literal(let content): return content 21 | case .call(let typeName, let arguments): 22 | let arguments = arguments.map { label, operand in 23 | if let label { return "\(label): \(operand)" } 24 | return operand.description 25 | } 26 | return "\(typeName)(\(arguments.joined(separator: ", ")))" 27 | case .forceUnwrap(let operand): 28 | return "(\(operand))!" 29 | case .liftOptional(let isSome, let payload): 30 | return "\(isSome) == 0 ? nil : \(payload)" 31 | case .lowerBool(let value): 32 | return "Int32(\(value) ? 1 : 0)" 33 | case .accessField(let base, let name): 34 | return "\(base).\(name)" 35 | } 36 | } 37 | } 38 | 39 | struct StaticMetaPointer: Strideable, CustomStringConvertible { 40 | typealias Stride = Int 41 | let basePointerVar: String 42 | let offset: Int 43 | 44 | init(basePointerVar: String, offset: Int) { 45 | self.basePointerVar = basePointerVar 46 | self.offset = offset 47 | } 48 | 49 | func advanced(by n: Stride) -> StaticMetaPointer { 50 | return StaticMetaPointer(basePointerVar: basePointerVar, offset: offset + n) 51 | } 52 | 53 | func distance(to other: StaticMetaPointer) -> Int { 54 | assert(basePointerVar == other.basePointerVar) 55 | return offset - other.offset 56 | } 57 | 58 | var description: String { 59 | "\(basePointerVar).advanced(by: \(offset))" 60 | } 61 | } 62 | 63 | /// Meta view of ``WasmKit/CanonicalCallContext`` 64 | struct StaticMetaCanonicalCallContext { 65 | /// The variable name of the ``WasmKit/CanonicalCallContext`` 66 | let contextVar: String 67 | } 68 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/CanonicalABI/StaticCanonicalDeallocation.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | struct StaticCanonicalDeallocation: CanonicalDeallocation { 4 | typealias Operand = StaticMetaOperand 5 | typealias Pointer = StaticMetaPointer 6 | 7 | let printer: SourcePrinter 8 | let builder: SwiftFunctionBuilder 9 | 10 | func deallocateString(pointer: Operand, length: Operand) { 11 | printer.write(line: "Prelude.deallocateString(pointer: \(pointer), length: \(length))") 12 | } 13 | 14 | func deallocateList( 15 | pointer: Operand, length: Operand, element: WITType, 16 | deallocateElement: (Pointer) throws -> Void 17 | ) throws { 18 | let deallocElementVar = builder.variable("deallocElement") 19 | printer.write(line: "let \(deallocElementVar): (UnsafeMutableRawPointer) -> Void = {") 20 | try printer.indent { 21 | printer.write(line: "_ = $0") 22 | try deallocateElement(.init(basePointerVar: "$0", offset: 0)) 23 | } 24 | printer.write(line: "}") 25 | printer.write( 26 | line: "Prelude.deallocateList(pointer: \(pointer), length: \(length), elementSize: \(CanonicalABI.size(type: element)), deallocateElement: \(deallocElementVar))" 27 | ) 28 | } 29 | 30 | func deallocateVariantLike(discriminant: Operand, cases: [WITType?], deallocatePayload: (Int) throws -> Void) throws { 31 | printer.write(line: "switch \(discriminant) {") 32 | for (i, _) in cases.enumerated() { 33 | printer.write(line: "case \(i):") 34 | try printer.indent { 35 | try deallocatePayload(i) 36 | printer.write(line: "break") 37 | } 38 | } 39 | printer.write(line: "default: fatalError(\"invalid variant discriminant\")") 40 | printer.write(line: "}") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/CanonicalABI/StaticCanonicalLoading.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | struct StaticCanonicalLoading: CanonicalLoading { 4 | typealias Operand = StaticMetaOperand 5 | typealias Pointer = StaticMetaPointer 6 | 7 | let printer: SourcePrinter 8 | let builder: SwiftFunctionBuilder 9 | 10 | private func loadByteSwappable(at pointer: Pointer, type: String) -> Operand { 11 | let boundPointer = "\(pointer).assumingMemoryBound(to: \(type).self)" 12 | let loadedVar = builder.variable("loaded") 13 | printer.write(line: "let \(loadedVar) = \(boundPointer).pointee") 14 | return .variable(loadedVar) 15 | } 16 | 17 | private func loadUInt(at pointer: Pointer, bitWidth: Int) -> Operand { 18 | return loadByteSwappable(at: pointer, type: "UInt\(bitWidth)") 19 | } 20 | private func loadInt(at pointer: Pointer, bitWidth: Int) -> Operand { 21 | let bitPattern = loadUInt(at: pointer, bitWidth: bitWidth) 22 | return .call("Int\(bitWidth)", arguments: [("bitPattern", bitPattern)]) 23 | } 24 | private func loadFloat(at pointer: Pointer, bitWidth: Int) -> Operand { 25 | let bitPattern = loadUInt(at: pointer, bitWidth: bitWidth) 26 | return .call("Float\(bitWidth)", arguments: [("bitPattern", bitPattern)]) 27 | } 28 | 29 | func loadUInt8(at pointer: Pointer) -> Operand { 30 | loadUInt(at: pointer, bitWidth: 8) 31 | } 32 | func loadUInt16(at pointer: Pointer) -> Operand { 33 | loadUInt(at: pointer, bitWidth: 16) 34 | } 35 | func loadUInt32(at pointer: Pointer) -> Operand { 36 | loadUInt(at: pointer, bitWidth: 32) 37 | } 38 | func loadUInt64(at pointer: Pointer) -> Operand { 39 | loadUInt(at: pointer, bitWidth: 64) 40 | } 41 | func loadInt8(at pointer: Pointer) -> Operand { 42 | loadInt(at: pointer, bitWidth: 8) 43 | } 44 | func loadInt16(at pointer: Pointer) -> Operand { 45 | loadInt(at: pointer, bitWidth: 16) 46 | } 47 | func loadInt32(at pointer: Pointer) -> Operand { 48 | loadInt(at: pointer, bitWidth: 32) 49 | } 50 | func loadInt64(at pointer: Pointer) -> Operand { 51 | loadInt(at: pointer, bitWidth: 64) 52 | } 53 | func loadFloat32(at pointer: Pointer) -> Operand { 54 | loadFloat(at: pointer, bitWidth: 32) 55 | } 56 | func loadFloat64(at pointer: Pointer) -> Operand { 57 | loadFloat(at: pointer, bitWidth: 64) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/GuestGenerators/GuestExportInterface.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | struct GuestExportInterface: ASTVisitor { 4 | var printer: SourcePrinter 5 | let packageUnit: PackageUnit 6 | var packageName: PackageNameSyntax { packageUnit.packageName } 7 | let sourceFile: SyntaxNode 8 | let interface: SyntaxNode 9 | let signatureTranslation: SignatureTranslation 10 | var exportFunctions: [NamedFunctionSyntax] = [] 11 | let context: SemanticsContext 12 | let definitionMapping: DefinitionMapping 13 | 14 | mutating func generate() throws { 15 | try walk(interface) 16 | } 17 | 18 | func visit(_: T) throws {} 19 | func visitPost(_: T) throws {} 20 | 21 | var protocolName: String { 22 | get throws { 23 | try "\(ConvertCase.pascalCase(packageName.namespace))\(ConvertCase.pascalCase(packageName.name))\(ConvertCase.pascalCase(interface.name))Exports" 24 | } 25 | } 26 | 27 | func visit(_ interface: SyntaxNode) throws { 28 | try printer.write(line: "public protocol \(protocolName) {") 29 | printer.indent() 30 | } 31 | 32 | func visitPost(_ interface: SyntaxNode) throws { 33 | printer.unindent() 34 | printer.write(line: "}") 35 | 36 | for namedFunction in exportFunctions { 37 | let function = namedFunction.function 38 | try GuestExportFunction( 39 | function: function, 40 | definitionMapping: definitionMapping, 41 | name: .interface( 42 | packageName, 43 | interfaceName: interface.name.text, 44 | id: namedFunction.name.text 45 | ), 46 | implementation: "\(protocolName)Impl.\(try ConvertCase.camelCase(namedFunction.name))" 47 | ).print( 48 | typeResolver: { 49 | try context.resolveType($0, in: interface, sourceFile: sourceFile, contextPackage: packageUnit) 50 | }, printer: printer) 51 | } 52 | } 53 | 54 | mutating func visit(_ namedFunction: SyntaxNode) throws { 55 | exportFunctions.append(namedFunction.syntax) 56 | try printer.write( 57 | line: "static " 58 | + signatureTranslation.signature( 59 | function: namedFunction.function, 60 | name: namedFunction.name.text 61 | ).description 62 | ) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/HostGenerators/HostExportInterface.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | struct HostExportInterface: ASTVisitor { 4 | var printer: SourcePrinter 5 | let context: SemanticsContext 6 | let sourceFile: SyntaxNode 7 | let packageUnit: PackageUnit 8 | let interface: SyntaxNode 9 | let signatureTranslation: SignatureTranslation 10 | let definitionMapping: DefinitionMapping 11 | 12 | var structName: String { 13 | get throws { 14 | return try ConvertCase.pascalCase(interface.name) 15 | } 16 | } 17 | 18 | mutating func generate() throws { 19 | try walk(interface) 20 | } 21 | 22 | func visit(_: T) throws {} 23 | func visitPost(_: T) throws {} 24 | 25 | func visit(_ interface: SyntaxNode) throws { 26 | try printer.write(line: "struct \(structName) {") 27 | printer.indent() 28 | printer.write(line: "let instance: WasmKit.Instance") 29 | } 30 | 31 | func visitPost(_ interface: SyntaxNode) throws { 32 | printer.unindent() 33 | printer.write(line: "}") 34 | } 35 | 36 | func resolveType(_ type: TypeReprSyntax) throws -> WITType { 37 | try context.resolveType(type, in: interface, sourceFile: sourceFile, contextPackage: packageUnit) 38 | } 39 | 40 | mutating func visit(_ namedFunction: SyntaxNode) throws { 41 | let exportFunction = HostExportFunction( 42 | function: namedFunction.function, 43 | name: .interface( 44 | packageUnit.packageName, 45 | interfaceName: interface.name.text, 46 | id: namedFunction.name.text 47 | ), 48 | signatureTranslation: signatureTranslation, 49 | definitionMapping: definitionMapping 50 | ) 51 | try exportFunction.print(typeResolver: resolveType(_:), printer: printer) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/HostGenerators/WasmKitSourcePrinter.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | /// A printer responsible for printing a source code of WasmKit 4 | struct WasmKitSourcePrinter { 5 | /// Prints a core value representation 6 | /// - Parameters: 7 | /// - operand: Lowered core value representation 8 | /// - type: Core type of the operand 9 | func printNewValue( 10 | _ operand: Operand, type: CanonicalABI.CoreType 11 | ) -> String { 12 | // Integers are lowered as signed but ``WasmKit/Value`` use unsigned 13 | let rawValue: String 14 | switch type { 15 | case .i32: rawValue = "UInt32(bitPattern: \(operand))" 16 | case .i64: rawValue = "UInt64(bitPattern: \(operand))" 17 | case .f32, .f64: rawValue = "\(operand).bitPattern" 18 | } 19 | return "Value.\(type)(\(rawValue))" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/Naming/ConvertCase.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | enum ConvertCase { 4 | static func pascalCase(_ ident: Identifier) throws -> String { 5 | return pascalCase(kebab: ident.text) 6 | } 7 | 8 | static func pascalCase(kebab text: String) -> String { 9 | var id = "" 10 | for component in text.split(separator: "-") { 11 | id += component[component.startIndex].uppercased() 12 | id += component[component.index(after: component.startIndex)...] 13 | } 14 | return id 15 | } 16 | 17 | static func camelCase(_ ident: Identifier) throws -> String { 18 | return camelCase(kebab: ident.text) 19 | } 20 | 21 | static func camelCase(kebab text: String) -> String { 22 | let components = text.split(separator: "-") 23 | return camelCase(components.map(String.init)) 24 | } 25 | 26 | static func camelCase(_ components: [String]) -> String { 27 | var id = "\(components[0])" 28 | for component in components.dropFirst() { 29 | id += component[component.startIndex].uppercased() 30 | id += component[component.index(after: component.startIndex)...] 31 | } 32 | return id 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/Naming/TypeName.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | extension WITType { 4 | func qualifiedSwiftName(mapping: DefinitionMapping) throws -> String { 5 | switch self { 6 | case .record(let record): 7 | return try mapping.lookupSwiftName(record: record) 8 | case .enum(let enumTy): 9 | return try mapping.lookupSwiftName(enum: enumTy) 10 | case .flags(let flags): 11 | return try mapping.lookupSwiftName(flags: flags) 12 | case .variant(let variant): 13 | return try mapping.lookupSwiftName(variant: variant) 14 | case .list(let element): 15 | return try "[" + element.qualifiedSwiftName(mapping: mapping) + "]" 16 | case .tuple(let types): 17 | return try "(" 18 | + types.map { 19 | try $0.qualifiedSwiftName(mapping: mapping) 20 | }.joined(separator: ", ") + ")" 21 | case .string: return "String" 22 | case .option(let type): 23 | return try "Optional<\(type.qualifiedSwiftName(mapping: mapping))>" 24 | case .bool: return "Bool" 25 | case .u8: return "UInt8" 26 | case .u16: return "UInt16" 27 | case .u32: return "UInt32" 28 | case .u64: return "UInt64" 29 | case .s8: return "Int8" 30 | case .s16: return "Int16" 31 | case .s32: return "Int32" 32 | case .s64: return "Int64" 33 | case .float32: return "Float" 34 | case .float64: return "Double" 35 | case .char: return "Unicode.Scalar" 36 | case .result(let ok, let error): 37 | let successTy = try ok?.qualifiedSwiftName(mapping: mapping) ?? "Void" 38 | let failureTy = try "ComponentError<\(error?.qualifiedSwiftName(mapping: mapping) ?? "Void")>" 39 | return "Result<\(successTy), \(failureTy)>" 40 | default: fatalError("\(self) is not supported") 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/SourcePrinter.swift: -------------------------------------------------------------------------------- 1 | class SourcePrinter { 2 | var contents: String = "" 3 | var indentLevel: Int = 0 4 | 5 | init(header: String = "") { 6 | self.contents = header 7 | } 8 | 9 | func write(line: S) { 10 | contents += "\n" + String(repeating: " ", count: indentLevel * 4) 11 | contents += line 12 | } 13 | 14 | func write(multiline: String) { 15 | for line in multiline.split(separator: "\n") { 16 | write(line: line) 17 | } 18 | } 19 | 20 | func indent() { 21 | indentLevel += 1 22 | } 23 | 24 | func unindent() { 25 | indentLevel -= 1 26 | } 27 | 28 | func indent(_ body: () throws -> Void) rethrows { 29 | indentLevel += 1 30 | try body() 31 | indentLevel -= 1 32 | } 33 | } 34 | 35 | final class SwiftFunctionBuilder { 36 | private var variables: Set = [] 37 | 38 | func variable(_ name: String) -> String { 39 | if variables.insert(name).inserted { 40 | return name 41 | } 42 | var suffixedName: String 43 | var suffix = 1 44 | repeat { 45 | suffixedName = name + suffix.description 46 | suffix += 1 47 | } while !variables.insert(suffixedName).inserted 48 | return suffixedName 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/TypeGenerators/InterfaceTypeGenerator.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | /// Generate Swift type definitions from WIT types defined under `interface` 4 | struct InterfaceTypeGenerator: ASTVisitor { 5 | var printer: SourcePrinter 6 | let packageUnit: PackageUnit 7 | let interface: SyntaxNode 8 | let signatureTranslation: SignatureTranslation 9 | 10 | mutating func generate() throws { 11 | try walk(interface) 12 | } 13 | 14 | func visit(_: T) throws {} 15 | func visitPost(_: T) throws {} 16 | 17 | func visit(_ interface: SyntaxNode) throws { 18 | let packageName = packageUnit.packageName 19 | try printer.write(line: "public enum \(typeNamespace(packageName: packageName, interface: interface.name)) {") 20 | printer.indent() 21 | } 22 | func visitPost(_ interface: SyntaxNode) throws { 23 | printer.unindent() 24 | printer.write(line: "}") 25 | } 26 | 27 | mutating func visit(_ typeDef: SyntaxNode) throws { 28 | try TypeDefinition(accessLevel: .public) 29 | .print(printer: printer, signatureTranslation: signatureTranslation, typeDef: typeDef) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/TypeGenerators/TypeAccessory.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | public protocol SourceSummaryProvider { 4 | func enumCaseNames(byWITName witName: String) -> [String]? 5 | func qualifiedSwiftTypeName(byWITName witName: String) -> String? 6 | } 7 | 8 | struct EnumWitRawValueGetter { 9 | let swiftTypeName: String 10 | let fieldNames: [String] 11 | let type: EnumSyntax 12 | 13 | func print(printer: SourcePrinter) throws { 14 | let discriminantType = CanonicalABI.discriminantType(numberOfCases: UInt32(type.cases.count)) 15 | printer.write(line: "extension \(swiftTypeName) {") 16 | printer.indent { 17 | printer.write(line: "var witRawValue: \(discriminantType.swiftTypeName) {") 18 | printer.indent { 19 | printer.write(line: "switch self {") 20 | printer.indent { 21 | for (index, caseName) in fieldNames.enumerated() { 22 | printer.write(line: "case .\(SwiftName.makeName(caseName)): return \(index)") 23 | } 24 | } 25 | printer.write(line: "}") 26 | } 27 | printer.write(line: "}") 28 | 29 | printer.write(line: "init(witRawValue: \(discriminantType.swiftTypeName)) {") 30 | printer.indent { 31 | printer.write(line: "switch witRawValue {") 32 | printer.indent { 33 | for (index, caseName) in fieldNames.enumerated() { 34 | printer.write(line: "case \(index): self = .\(caseName)") 35 | } 36 | printer.write(line: "default: fatalError(\"Invalid discriminant value \\(witRawValue) for enum \(swiftTypeName)\")") 37 | } 38 | printer.write(line: "}") 39 | } 40 | printer.write(line: "}") 41 | } 42 | printer.write(line: "}") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/WITOverlayGenerator/TypeGenerators/TypeGenerator.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | 3 | struct TypeGenerator { 4 | let context: SemanticsContext 5 | 6 | func generate(printer: SourcePrinter) throws { 7 | for sourceFile in context.rootPackage.sourceFiles { 8 | for case .interface(let item) in sourceFile.items { 9 | var generator = InterfaceTypeGenerator( 10 | printer: printer, 11 | packageUnit: context.rootPackage, 12 | interface: item, 13 | signatureTranslation: SignatureTranslation(interfaceContext: nil) 14 | ) 15 | try generator.generate() 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/WITTool/Utilities.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension FileManager { 4 | internal func isDirectory(filePath: String) -> Bool { 5 | var isDirectory: ObjCBool = false 6 | let exists = self.fileExists(atPath: filePath, isDirectory: &isDirectory) 7 | return exists && isDirectory.boolValue 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/WasmKit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_wasmkit_library(WasmKit 2 | Engine.swift 3 | Imports.swift 4 | Module.swift 5 | ModuleParser.swift 6 | Translator.swift 7 | Validator.swift 8 | ResourceLimiter.swift 9 | Component/CanonicalLifting.swift 10 | Component/CanonicalLowering.swift 11 | Component/CanonicalCall.swift 12 | Component/CanonicalOptions.swift 13 | Component/ComponentTypes.swift 14 | Execution/Instructions/Control.swift 15 | Execution/Instructions/Instruction.swift 16 | Execution/Instructions/Table.swift 17 | Execution/Instructions/Memory.swift 18 | Execution/Instructions/Misc.swift 19 | Execution/Instructions/InstructionSupport.swift 20 | Execution/ConstEvaluation.swift 21 | Execution/DispatchInstruction.swift 22 | Execution/EngineInterceptor.swift 23 | Execution/Errors.swift 24 | Execution/Execution.swift 25 | Execution/Function.swift 26 | Execution/Instances.swift 27 | Execution/IO.swift 28 | Execution/NameRegistry.swift 29 | Execution/Profiler.swift 30 | Execution/Runtime.swift 31 | Execution/SignpostTracer.swift 32 | Execution/Store.swift 33 | Execution/StoreAllocator.swift 34 | Execution/UntypedValue.swift 35 | Execution/Value.swift 36 | ) 37 | 38 | target_link_wasmkit_libraries(WasmKit PUBLIC 39 | _CWasmKit WasmParser SystemExtras WasmTypes SystemPackage) 40 | -------------------------------------------------------------------------------- /Sources/WasmKit/Component/CanonicalCall.swift: -------------------------------------------------------------------------------- 1 | @_exported import WasmTypes 2 | 3 | /// Error type for canonical ABI operations. 4 | public struct CanonicalABIError: Error, CustomStringConvertible { 5 | public let description: String 6 | 7 | @_documentation(visibility: internal) 8 | public init(description: String) { 9 | self.description = description 10 | } 11 | } 12 | 13 | /// Call context for `(canon lift)` or `(canon lower)` operations. 14 | /// This type corresponds to `CallContext` in the Canonical ABI spec. 15 | /// 16 | /// > Note: 17 | /// 18 | public struct CanonicalCallContext { 19 | /// The options used for lifting or lowering operations. 20 | public let options: CanonicalOptions 21 | /// The module instance that defines the lift/lower operation. 22 | public let instance: Instance 23 | /// A reference to the guest memory. 24 | public var guestMemory: Memory { 25 | options.memory 26 | } 27 | 28 | public init(options: CanonicalOptions, instance: Instance) { 29 | self.options = options 30 | self.instance = instance 31 | } 32 | 33 | /// Call `cabi_realloc` export with the given arguments. 34 | public func realloc( 35 | old: UInt32, 36 | oldSize: UInt32, 37 | oldAlign: UInt32, 38 | newSize: UInt32 39 | ) throws -> UnsafeGuestRawPointer { 40 | guard let realloc = options.realloc else { 41 | throw CanonicalABIError(description: "Missing required \"cabi_realloc\" export") 42 | } 43 | let results = try realloc( 44 | [.i32(old), .i32(oldSize), .i32(oldAlign), .i32(newSize)] 45 | ) 46 | guard results.count == 1 else { 47 | throw CanonicalABIError(description: "\"cabi_realloc\" export should return a single value") 48 | } 49 | guard case let .i32(new) = results[0] else { 50 | throw CanonicalABIError(description: "\"cabi_realloc\" export should return an i32 value") 51 | } 52 | return UnsafeGuestRawPointer(memorySpace: guestMemory, offset: new) 53 | } 54 | } 55 | 56 | extension CanonicalCallContext { 57 | @available(*, deprecated, renamed: "instance") 58 | public var moduleInstance: Instance { 59 | return instance 60 | } 61 | 62 | @available(*, deprecated, renamed: "init(options:instance:)") 63 | public init(options: CanonicalOptions, moduleInstance: Instance, runtime: Runtime) { 64 | self.init(options: options, instance: moduleInstance) 65 | } 66 | } 67 | 68 | @available(*, deprecated, renamed: "Memory", message: "WasmKitGuestMemory has been removed; use Memory instead") 69 | public typealias WasmKitGuestMemory = Memory 70 | 71 | extension Memory { 72 | /// Creates a new memory instance from the given store and address 73 | @available(*, unavailable, message: "WasmKitGuestMemory has been removed; use Memory instead") 74 | public init(store: Store, memory: Memory) { 75 | fatalError() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/WasmKit/Component/CanonicalLifting.swift: -------------------------------------------------------------------------------- 1 | /// A namespace for the canonical lifting operations used by host side. 2 | public enum CanonicalLifting { 3 | /// Lifts a pair of a pointer and a length to a Swift Array value with the given element type. 4 | /// 5 | /// - Parameters: 6 | /// - pointer: A pointer to a guest memory region that contains the byte representation of the array value. 7 | /// - length: A length of the array value. 8 | /// - elementSize: A byte size of an element of the array value in lowered representation. 9 | /// - loadElement: A closure that loads an element from the given pointer. 10 | /// - memoryBase: A base address of the guest memory region. Can be `nil` if length is zero. 11 | /// - Returns: A lifted Swift Array value with the given element type. 12 | public static func liftList( 13 | pointer: UInt32, length: UInt32, elementSize: UInt32, 14 | loadElement: (UnsafeGuestRawPointer) throws -> Element, 15 | context: CanonicalCallContext 16 | ) throws -> [Element] { 17 | var elements = [Element]() 18 | elements.reserveCapacity(Int(elementSize)) 19 | let guestPointer = UnsafeGuestRawPointer(memorySpace: context.guestMemory, offset: pointer) 20 | for i in 0.. (pointer: UInt32, length: UInt32) { 8 | guard context.options.stringEncoding == .utf8 else { 9 | throw CanonicalABIError(description: "Unsupported string encoding: \(context.options.stringEncoding)") 10 | } 11 | let bytes = value.utf8 12 | let newBuffer = try context.realloc( 13 | old: 0, oldSize: 0, oldAlign: 1, newSize: UInt32(bytes.count) 14 | ) 15 | newBuffer.withHostPointer(count: bytes.count) { newBuffer in 16 | newBuffer.copyBytes(from: bytes) 17 | } 18 | return (newBuffer.offset, UInt32(bytes.count)) 19 | } 20 | 21 | /// Lowers a Swift Array value to a pair of a pointer and a length. 22 | /// 23 | /// - Parameters: 24 | /// - value: A Swift Array value to be lowered. 25 | /// - elementSize: A byte size of an element of the array value in lowered representation. 26 | /// - elementAlignment: A byte alignment of an element of the array value in lowered representation. 27 | /// - storeElement: A closure that stores an element to the given pointer. 28 | /// - context: A canonical call context. 29 | /// - Returns: A pair of a pointer and a length. 30 | public static func lowerList( 31 | _ value: [Element], elementSize: UInt32, elementAlignment: UInt32, 32 | storeElement: (Element, UnsafeGuestRawPointer) throws -> Void, 33 | context: CanonicalCallContext 34 | ) throws -> (pointer: UInt32, length: UInt32) { 35 | let byteLength = UInt32(value.count) * elementSize 36 | let newBuffer = try context.realloc( 37 | old: 0, oldSize: 0, oldAlign: elementAlignment, newSize: byteLength 38 | ) 39 | for (i, element) in value.enumerated() { 40 | try storeElement(element, newBuffer.advanced(by: elementSize * UInt32(i))) 41 | } 42 | return (newBuffer.offset, UInt32(value.count)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/WasmKit/Component/CanonicalOptions.swift: -------------------------------------------------------------------------------- 1 | /// A type representing a `canonopt` values supplied to currently-executing 2 | /// `canon lift` or `canon lower`. 3 | /// > Note: 4 | /// 5 | public struct CanonicalOptions { 6 | /// A type of string encoding used in the Component Model. 7 | public enum StringEncoding { 8 | /// UTF-8 9 | case utf8 10 | /// UTF-16 11 | case utf16 12 | /// Dynamic encoding of Latin-1 or UTF-16 13 | case latin1OrUTF16 14 | } 15 | 16 | /// The memory address used for lifting or lowering operations. 17 | public let memory: Memory 18 | /// The string encoding used for lifting or lowering string values. 19 | public let stringEncoding: StringEncoding 20 | /// The realloc function address used for lifting or lowering values. 21 | public let realloc: Function? 22 | /// The function address called when a lifted/lowered function returns. 23 | public let postReturn: Function? 24 | 25 | public init( 26 | memory: Memory, stringEncoding: StringEncoding, 27 | realloc: Function?, postReturn: Function? 28 | ) { 29 | self.memory = memory 30 | self.stringEncoding = stringEncoding 31 | self.realloc = realloc 32 | self.postReturn = postReturn 33 | } 34 | 35 | /// FIXME: This deriviation is wrong because the options should be determined by `(canon lift)` or `(canon lower)` 36 | /// in an encoded component at componetizing-time. (e.g. wit-component tool is one of the componetizers) 37 | /// Remove this temporary method after we will accept binary form of component file. 38 | public static func _derive(from instance: Instance, exportName: String) -> CanonicalOptions { 39 | guard case let .memory(memory) = instance.exports["memory"] else { 40 | fatalError("Missing required \"memory\" export") 41 | } 42 | return CanonicalOptions( 43 | memory: memory, stringEncoding: .utf8, 44 | realloc: instance.exportedFunction(name: "cabi_realloc"), 45 | postReturn: instance.exportedFunction(name: "cabi_post_\(exportName)")) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/WasmKit/Component/ComponentTypes.swift: -------------------------------------------------------------------------------- 1 | /// Error type raised from Component Model operations 2 | public struct ComponentError: Error { 3 | /// The content of the error 4 | public let content: Content 5 | 6 | /// Initialize a new error with the given content 7 | public init(_ content: Content) { 8 | self.content = content 9 | } 10 | } 11 | 12 | extension ComponentError: Equatable where Content: Equatable {} 13 | extension ComponentError: Hashable where Content: Hashable {} 14 | -------------------------------------------------------------------------------- /Sources/WasmKit/Execution/EngineInterceptor.swift: -------------------------------------------------------------------------------- 1 | @_documentation(visibility: internal) 2 | public protocol EngineInterceptor { 3 | func onEnterFunction(_ function: Function) 4 | func onExitFunction(_ function: Function) 5 | } 6 | 7 | /// An interceptor that multiplexes multiple interceptors 8 | @_documentation(visibility: internal) 9 | public class MultiplexingInterceptor: EngineInterceptor { 10 | private let interceptors: [EngineInterceptor] 11 | 12 | /// Creates a new multiplexing interceptor 13 | /// - Parameter interceptors: The interceptors to multiplex 14 | public init(_ interceptors: [EngineInterceptor]) { 15 | self.interceptors = interceptors 16 | } 17 | 18 | public func onEnterFunction(_ function: Function) { 19 | for interceptor in interceptors { 20 | interceptor.onEnterFunction(function) 21 | } 22 | } 23 | 24 | public func onExitFunction(_ function: Function) { 25 | for interceptor in interceptors { 26 | interceptor.onExitFunction(function) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/WasmKit/Execution/IO.swift: -------------------------------------------------------------------------------- 1 | import _CWasmKit 2 | 3 | /// Standard error output stream. 4 | struct _Stderr: TextOutputStream { 5 | 6 | func write(_ string: String) { 7 | if string.isEmpty { return } 8 | var string = string 9 | string.withUTF8 { 10 | wasmkit_fwrite_stderr($0.baseAddress!, $0.count) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/WasmKit/Execution/Instructions/Misc.swift: -------------------------------------------------------------------------------- 1 | /// > Note: 2 | /// 3 | extension Execution { 4 | mutating func globalGet(sp: Sp, immediate: Instruction.GlobalAndVRegOperand) { 5 | immediate.global.withValue { 6 | sp[immediate.reg] = $0.rawValue 7 | } 8 | } 9 | mutating func globalSet(sp: Sp, immediate: Instruction.GlobalAndVRegOperand) { 10 | let value = sp[immediate.reg] 11 | immediate.global.withValue { $0.rawValue = value } 12 | } 13 | 14 | mutating func copyStack(sp: Sp, immediate: Instruction.CopyStackOperand) { 15 | sp[immediate.dest] = sp[immediate.source] 16 | } 17 | } 18 | 19 | /// > Note: 20 | /// 21 | extension Execution { 22 | mutating func refNull(sp: Sp, immediate: Instruction.RefNullOperand) { 23 | let value: Value 24 | switch immediate.type { 25 | case .externRef: 26 | value = .ref(.extern(nil)) 27 | case .funcRef: 28 | value = .ref(.function(nil)) 29 | } 30 | sp[immediate.result] = UntypedValue(value) 31 | } 32 | mutating func refIsNull(sp: Sp, immediate: Instruction.RefIsNullOperand) { 33 | let value = sp[immediate.value] 34 | 35 | let result: Value 36 | if value.isNullRef { 37 | result = .i32(1) 38 | } else { 39 | result = .i32(0) 40 | } 41 | sp[immediate.result] = UntypedValue(result) 42 | } 43 | mutating func refFunc(sp: Sp, immediate: Instruction.RefFuncOperand) { 44 | let function = currentInstance(sp: sp).functions[Int(immediate.index)] 45 | sp[immediate.result] = UntypedValue(.ref(.function(from: function))) 46 | } 47 | } 48 | 49 | /// > Note: 50 | /// 51 | extension Execution { 52 | @inline(__always) 53 | mutating func const32(sp: Sp, immediate: Instruction.Const32Operand) { 54 | sp[immediate.result] = UntypedValue(storage32: immediate.value) 55 | } 56 | @inline(__always) 57 | mutating func const64(sp: Sp, immediate: Instruction.Const64Operand) { 58 | sp[immediate.result] = immediate.value 59 | } 60 | } 61 | 62 | /// > Note: 63 | /// 64 | extension Execution { 65 | mutating func select(sp: Sp, immediate: Instruction.SelectOperand) { 66 | let flag = sp[i32: immediate.condition] 67 | let selected = flag != 0 ? immediate.onTrue : immediate.onFalse 68 | let value = sp[selected] 69 | sp[immediate.result] = value 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sources/WasmKit/Execution/NameRegistry.swift: -------------------------------------------------------------------------------- 1 | import struct WasmParser.CustomSection 2 | import struct WasmParser.NameMap 3 | import struct WasmParser.NameSectionParser 4 | import class WasmParser.StaticByteStream 5 | 6 | struct NameRegistry { 7 | private var functionNames: [InternalFunction: String] = [:] 8 | private var materializers: [(inout NameRegistry) throws -> Void] = [] 9 | 10 | init() {} 11 | 12 | mutating func register(instance: InternalInstance, nameSection: CustomSection) throws { 13 | materializers.append { registry in 14 | let stream = StaticByteStream(bytes: Array(nameSection.bytes)) 15 | let parser = NameSectionParser(stream: stream) 16 | for result in try parser.parseAll() { 17 | switch result { 18 | case .functions(let nameMap): 19 | registry.register(instance: instance, nameMap: nameMap) 20 | } 21 | } 22 | 23 | for (name, entry) in instance.exports { 24 | // Use exported name if the function doesn't have name in name section. 25 | guard case .function(let function) = entry else { continue } 26 | guard registry.functionNames[function] == nil else { continue } 27 | registry.functionNames[function] = name 28 | } 29 | } 30 | } 31 | 32 | private mutating func register(instance: InternalInstance, nameMap: NameMap) { 33 | for (index, name) in nameMap { 34 | let addr = instance.functions[Int(index)] 35 | self.functionNames[addr] = name 36 | } 37 | } 38 | 39 | private mutating func materializeIfNeeded() throws { 40 | guard !materializers.isEmpty else { return } 41 | for materialize in materializers { 42 | try materialize(&self) 43 | } 44 | materializers = [] 45 | } 46 | 47 | mutating func lookup(_ addr: InternalFunction) throws -> String? { 48 | try materializeIfNeeded() 49 | return functionNames[addr] 50 | } 51 | 52 | mutating func symbolicate(_ function: InternalFunction) -> String { 53 | if let name = try? lookup(function) { 54 | return name 55 | } 56 | // Fallback 57 | if function.isWasm { 58 | return "wasm function[\(function.wasm.index)]" 59 | } else { 60 | return "unknown host function" 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/WasmKit/Execution/SignpostTracer.swift: -------------------------------------------------------------------------------- 1 | #if canImport(os.signpost) 2 | import os.signpost 3 | 4 | /// A `RuntimeInterceptor` that emits signposts for each function call 5 | /// - Note: This interceptor is available only on Apple platforms 6 | @_documentation(visibility: internal) 7 | @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) 8 | public class SignpostTracer: EngineInterceptor { 9 | /// The `OSSignposter` to use for emitting signposts 10 | let signposter: OSSignposter 11 | /// The stack of signpost states for each function call in progress 12 | private var signpostStates: [OSSignpostIntervalState] = [] 13 | 14 | /// Initialize a new `SignpostProfiler` with the given `OSSignposter` 15 | /// - Parameter signposter: The `OSSignposter` to use for emitting signposts 16 | public init(signposter: OSSignposter) { 17 | self.signposter = signposter 18 | } 19 | 20 | /// The name of the function call signpost 21 | private var functionCallName: StaticString { 22 | "Function Call" 23 | } 24 | 25 | public func onEnterFunction(_ function: Function) { 26 | let name = function.store.nameRegistry.symbolicate(function.handle) 27 | let state = self.signposter.beginInterval(functionCallName, "\(name)") 28 | signpostStates.append(state) 29 | } 30 | 31 | public func onExitFunction(_ function: Function) { 32 | let state = signpostStates.popLast()! 33 | self.signposter.endInterval(functionCallName, state) 34 | } 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /Sources/WasmKit/ResourceLimiter.swift: -------------------------------------------------------------------------------- 1 | /// A protocol for limiting resource allocation. 2 | public protocol ResourceLimiter { 3 | /// Limit the memory growth of the process to the specified number of bytes. 4 | /// 5 | /// - Parameter desired: The desired size of the memory in bytes. 6 | /// - Returns: `true` if the memory growth should be allowed. `false` if 7 | /// the memory growth should be denied. 8 | func limitMemoryGrowth(to desired: Int) throws -> Bool 9 | 10 | /// Limit the table growth of the process to the specified number of elements. 11 | /// 12 | /// - Parameter desired: The desired size of the table in elements. 13 | /// - Returns: `true` if the table growth should be allowed. `false` if 14 | /// the table growth should be denied. 15 | func limitTableGrowth(to desired: Int) throws -> Bool 16 | } 17 | 18 | // By default, we don't limit resource growth. 19 | extension ResourceLimiter { 20 | func limitMemoryGrowth(to desired: Int) throws -> Bool { 21 | return true 22 | } 23 | func limitTableGrowth(to desired: Int) throws -> Bool { 24 | return true 25 | } 26 | } 27 | 28 | /// A default resource limiter that doesn't limit resource growth. 29 | struct DefaultResourceLimiter: ResourceLimiter {} 30 | -------------------------------------------------------------------------------- /Sources/WasmKitWASI/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_wasmkit_library(WasmKitWASI 2 | WASIBridgeToHost+WasmKit.swift 3 | ) 4 | 5 | target_link_wasmkit_libraries(WasmKitWASI PUBLIC 6 | WasmKit WASI) 7 | -------------------------------------------------------------------------------- /Sources/WasmParser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_wasmkit_library(WasmParser 2 | Stream/ByteStream.swift 3 | Stream/FileHandleStream.swift 4 | Stream/Stream.swift 5 | BinaryInstructionDecoder.swift 6 | InstructionVisitor.swift 7 | LEB.swift 8 | ParsingLimits.swift 9 | WasmParser.swift 10 | WasmTypes.swift 11 | ) 12 | 13 | target_link_wasmkit_libraries(WasmParser PUBLIC 14 | WasmTypes SystemPackage) 15 | -------------------------------------------------------------------------------- /Sources/WasmParser/Docs.docc/Docs.md: -------------------------------------------------------------------------------- 1 | # ``WasmParser`` 2 | 3 | A WebAssembly binary parser library. 4 | 5 | ## Overview 6 | 7 | WasmParser is a library for parsing WebAssembly binary format. It provides a parser for [WebAssembly binary format](https://webassembly.github.io/spec/core/binary/index.html). 8 | 9 | 10 | ## Quick start 11 | 12 | To parse a WebAssembly binary file, you can use the `Parser` struct and its `parseNext()` method to incrementally parse the binary. 13 | 14 | ```swift 15 | import WasmParser 16 | 17 | var parser = Parser(bytes: [ 18 | 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, 19 | 0x01, 0x7e, 0x01, 0x7e, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 20 | 0x66, 0x61, 0x63, 0x00, 0x00, 0x0a, 0x17, 0x01, 0x15, 0x00, 0x20, 0x00, 21 | 0x50, 0x04, 0x7e, 0x42, 0x01, 0x05, 0x20, 0x00, 0x20, 0x00, 0x42, 0x01, 22 | 0x7d, 0x10, 0x00, 0x7e, 0x0b, 0x0b 23 | ]) 24 | 25 | while let payload = try parser.parseNext() { 26 | switch payload { 27 | case .header(let version): print("Version: \(version)") 28 | case .customSection(let customSection): print("Custom section: \(customSection)") 29 | case .typeSection(let types): print("Type section: \(types)") 30 | case .importSection(let importSection): print("Import section: \(importSection)") 31 | case .functionSection(let types): print("Function section: \(types)") 32 | case .tableSection(let tableSection): print("Table section: \(tableSection)") 33 | case .memorySection(let memorySection): print("Memory section: \(memorySection)") 34 | case .globalSection(let globalSection): print("Global section: \(globalSection)") 35 | case .exportSection(let exportSection): print("Export section: \(exportSection)") 36 | case .startSection(let functionIndex): print("Start section: \(functionIndex)") 37 | case .elementSection(let elementSection): print("Element section: \(elementSection)") 38 | case .codeSection(let codeSection): print("Code section: \(codeSection)") 39 | case .dataSection(let dataSection): print("Data section: \(dataSection)") 40 | case .dataCount(let count): print("Data count: \(count)") 41 | } 42 | } 43 | ``` 44 | 45 | ## Topics 46 | 47 | ### Parsing 48 | 49 | - ``Parser`` 50 | - ``Parser/parseNext()`` 51 | - ``NameSectionParser`` 52 | 53 | ### Visitor 54 | 55 | - ``InstructionVisitor`` 56 | - ``AnyInstructionVisitor`` 57 | 58 | ### Core Module Elements 59 | 60 | - ``Import`` 61 | - ``ImportDescriptor`` 62 | - ``Export`` 63 | - ``ExportDescriptor`` 64 | - ``Table`` 65 | - ``TableType`` 66 | - ``Global`` 67 | - ``GlobalType`` 68 | - ``Memory`` 69 | - ``MemoryType`` 70 | - ``Mutability`` 71 | - ``Limits`` 72 | - ``DataSegment`` 73 | - ``ElementSegment`` 74 | - ``Code`` 75 | - ``CustomSection`` 76 | 77 | ### Instruction Types 78 | 79 | - ``Instruction`` 80 | - ``BrTable`` 81 | - ``BlockType`` 82 | - ``MemArg`` 83 | 84 | ### Index Types 85 | 86 | - ``TypeIndex`` 87 | - ``FunctionIndex`` 88 | - ``TableIndex`` 89 | - ``GlobalIndex`` 90 | - ``MemoryIndex`` 91 | - ``ElementIndex`` 92 | - ``DataIndex`` 93 | -------------------------------------------------------------------------------- /Sources/WasmParser/LEB.swift: -------------------------------------------------------------------------------- 1 | @usableFromInline 2 | enum LEBError: Swift.Error, Equatable { 3 | case overflow 4 | case integerRepresentationTooLong 5 | case insufficientBytes 6 | } 7 | 8 | @inlinable 9 | func decodeLEB128( 10 | stream: Stream 11 | ) throws -> IntType where IntType: FixedWidthInteger, IntType: UnsignedInteger, Stream: ByteStream { 12 | let firstByte = try stream.consumeAny() 13 | var result: IntType = IntType(firstByte & 0b0111_1111) 14 | if _fastPath(firstByte & 0b1000_0000 == 0) { 15 | return result 16 | } 17 | 18 | var shift: UInt = 7 19 | 20 | while true { 21 | let byte = try stream.consumeAny() 22 | let slice = IntType(byte & 0b0111_1111) 23 | let nextShift = shift + 7 24 | if nextShift >= IntType.bitWidth, (byte >> (UInt(IntType.bitWidth) - shift)) != 0 { 25 | throw LEBError.integerRepresentationTooLong 26 | } 27 | result |= slice << shift 28 | shift = nextShift 29 | 30 | guard byte & 0b1000_0000 != 0 else { break } 31 | } 32 | 33 | return result 34 | } 35 | 36 | @inlinable 37 | func decodeLEB128( 38 | stream: Stream, bitWidth: Int = IntType.bitWidth 39 | ) throws -> IntType where IntType: FixedWidthInteger, IntType: RawSignedInteger, Stream: ByteStream { 40 | let firstByte = try stream.consumeAny() 41 | var result = IntType.Unsigned(firstByte & 0b0111_1111) 42 | if _fastPath(firstByte & 0b1000_0000 == 0) { 43 | // Interpret Int${Self.bitWidth-1} as Int${Self.bitWidth} 44 | return (IntType(bitPattern: result) << (IntType.bitWidth - 7)) >> (IntType.bitWidth - 7) 45 | } 46 | 47 | var shift: IntType = 7 48 | 49 | var byte: UInt8 50 | repeat { 51 | byte = try stream.consumeAny() 52 | 53 | let slice = IntType.Unsigned(byte & 0b0111_1111) 54 | result |= slice << shift 55 | 56 | // When we don't have enough bit width 57 | if shift > (bitWidth - 7) { 58 | let remainingBitWidth = bitWidth - Int(shift) 59 | let continuationBit = (byte & 0b1000_0000) != 0 60 | // When a next byte is expected 61 | if continuationBit { 62 | throw LEBError.integerRepresentationTooLong 63 | } 64 | 65 | let signAndDiscardingBits = Int8(bitPattern: byte << 1) >> remainingBitWidth 66 | // When meaningful bits are discarded 67 | if signAndDiscardingBits != 0 && signAndDiscardingBits != -1 { 68 | throw LEBError.overflow 69 | } 70 | return IntType(bitPattern: result) 71 | } 72 | 73 | shift += 7 74 | } while byte & 0b1000_0000 != 0 75 | 76 | // Sign flag is second high-order bit 77 | if byte & 0b0100_0000 != 0 { 78 | // Sign extend 79 | result |= IntType.Unsigned(bitPattern: ~0) << shift 80 | } 81 | 82 | return IntType(bitPattern: result) 83 | } 84 | -------------------------------------------------------------------------------- /Sources/WasmParser/ParsingLimits.swift: -------------------------------------------------------------------------------- 1 | /// Limits for parsing WebAssembly modules. 2 | @usableFromInline 3 | struct ParsingLimits { 4 | /// Maximum number of locals in a function. 5 | @usableFromInline 6 | var maxFunctionLocals: UInt64 7 | 8 | /// The default limits for parsing. 9 | static var `default`: ParsingLimits { 10 | return ParsingLimits(maxFunctionLocals: 100000) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/WasmParser/Stream/ByteStream.swift: -------------------------------------------------------------------------------- 1 | public protocol ByteStream: Stream where Element == UInt8 {} 2 | 3 | public final class StaticByteStream: ByteStream { 4 | public let bytes: ArraySlice 5 | public var currentIndex: Int 6 | 7 | public init(bytes: [UInt8]) { 8 | self.bytes = ArraySlice(bytes) 9 | currentIndex = bytes.startIndex 10 | } 11 | 12 | public init(bytes: ArraySlice) { 13 | self.bytes = bytes 14 | currentIndex = bytes.startIndex 15 | } 16 | 17 | @discardableResult 18 | public func consumeAny() throws -> UInt8 { 19 | guard bytes.indices.contains(currentIndex) else { 20 | throw StreamError.unexpectedEnd(expected: nil) 21 | } 22 | 23 | let consumed = bytes[currentIndex] 24 | currentIndex = bytes.index(after: currentIndex) 25 | return consumed 26 | } 27 | 28 | @discardableResult 29 | public func consume(_ expected: Set) throws -> UInt8 { 30 | guard bytes.indices.contains(currentIndex) else { 31 | throw StreamError.unexpectedEnd(expected: Set(expected)) 32 | } 33 | 34 | let consumed = bytes[currentIndex] 35 | guard expected.contains(consumed) else { 36 | throw StreamError.unexpected(consumed, index: currentIndex, expected: Set(expected)) 37 | } 38 | 39 | currentIndex = bytes.index(after: currentIndex) 40 | return consumed 41 | } 42 | 43 | public func consume(count: Int) throws -> ArraySlice { 44 | guard count > 0 else { return [] } 45 | let updatedIndex = currentIndex + count 46 | 47 | guard bytes.indices.contains(updatedIndex - 1) else { 48 | throw StreamError.unexpectedEnd(expected: nil) 49 | } 50 | 51 | defer { currentIndex = updatedIndex } 52 | 53 | return bytes[currentIndex.. UInt8? { 57 | guard bytes.indices.contains(currentIndex) else { 58 | return nil 59 | } 60 | return bytes[currentIndex] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/WasmParser/Stream/Stream.swift: -------------------------------------------------------------------------------- 1 | @usableFromInline 2 | enum StreamError: Swift.Error, Equatable where Element: Hashable { 3 | case unexpectedEnd(expected: Set?) 4 | case unexpected(Element, index: Int, expected: Set?) 5 | } 6 | 7 | public protocol Stream { 8 | associatedtype Element: Hashable 9 | 10 | var currentIndex: Int { get } 11 | 12 | func consumeAny() throws -> Element 13 | func consume(_ expected: Set) throws -> Element 14 | func consume(count: Int) throws -> ArraySlice 15 | 16 | func peek() throws -> Element? 17 | } 18 | 19 | extension Stream { 20 | func consume(_ expected: Element) throws -> Element { 21 | try consume(Set([expected])) 22 | } 23 | 24 | @usableFromInline 25 | func hasReachedEnd() throws -> Bool { 26 | try peek() == nil 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/WasmTypes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_wasmkit_library(WasmTypes 2 | GuestMemory.swift 3 | WasmTypes.swift 4 | ) 5 | -------------------------------------------------------------------------------- /Sources/_CWasmKit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(_CWasmKit INTERFACE) 2 | target_include_directories(_CWasmKit INTERFACE 3 | include) 4 | 5 | install(TARGETS _CWasmKit EXPORT WasmKitTargets) 6 | set_property(GLOBAL APPEND PROPERTY WASMKIT_EXPORTS _CWasmKit) 7 | -------------------------------------------------------------------------------- /Sources/_CWasmKit/_CWasmKit.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/Sources/_CWasmKit/_CWasmKit.c -------------------------------------------------------------------------------- /Sources/_CWasmKit/include/InlineCode.h: -------------------------------------------------------------------------------- 1 | // 2 | // ---------------------------------------------------------------------------- 3 | // This file is to be imported in Swift code to mix C and Swift code and 4 | // optimize them together. "static inline" functions in this file are are 5 | // included in the Swift compilation unit via ClangImporter. 6 | // ---------------------------------------------------------------------------- 7 | // 8 | 9 | #ifndef WASMKIT_INLINE_CODE_H 10 | #define WASMKIT_INLINE_CODE_H 11 | 12 | #include 13 | 14 | #include "_CWasmKit.h" 15 | 16 | #if WASMKIT_USE_DIRECT_THREADED_CODE 17 | // Inline instruction implementation written in Swift side into C handler 18 | // function. Used in DirectThreadedCode.inc. 19 | // TODO: Clang in Swift 5.10 Windows toolchain seems not to have `[[clang::always_inline]]`? 20 | # if defined(__clang__) && (__clang_major__ >= 16) && __has_attribute(always_inline) && !defined(_WIN32) 21 | // `always_inline` at statement level is available since Clang 16. 22 | # define INLINE_CALL [[clang::always_inline]] 23 | # else 24 | // Fallback to regular call without inlining. 25 | # define INLINE_CALL 26 | # endif 27 | 28 | /// Set the error result in the execution state. Used in DirectThreadedCode.inc. 29 | // 30 | /// NOTE: We use error storage in the execution state rather than `swift_error_result` 31 | /// because it's not permitted with `swiftasynccc`. 32 | SWIFT_CC(swift) void wasmkit_execution_state_set_error(void *trap, Sp sp, SWIFT_CONTEXT void *state); 33 | 34 | // Define instruction handler functions generated by 35 | // Utilities/Sources/VMGen.swift 36 | # include "DirectThreadedCode.inc" 37 | 38 | // Undefine macros only used in DirectThreadedCode.inc 39 | # undef INLINE_CALL 40 | 41 | #else 42 | 43 | /// The fallback table of instruction handlers when direct-threaded code is not 44 | /// supported. Never used when running on token-threading model. 45 | static const uintptr_t wasmkit_tc_exec_handlers[] = {}; 46 | 47 | #endif // WASMKIT_USE_DIRECT_THREADED_CODE 48 | 49 | #endif // WASMKIT_INLINE_CODE_H 50 | -------------------------------------------------------------------------------- /Sources/_CWasmKit/include/Platform.h: -------------------------------------------------------------------------------- 1 | #ifndef WASMKIT_PLATFORM_H 2 | #define WASMKIT_PLATFORM_H 3 | 4 | // NOTE: The `swiftasynccc` attribute is considered to be a Clang extension 5 | // rather than a language standard feature after LLVM 19. We check 6 | // `__has_attribute(swiftasynccc)` too for compatibility with older versions. 7 | // See https://github.com/llvm/llvm-project/pull/85347 8 | #if __has_feature(swiftasynccc) || __has_extension(swiftasynccc) 9 | # define WASMKIT_HAS_SWIFTASYNCCC 1 10 | #else 11 | # define WASMKIT_HAS_SWIFTASYNCCC 0 12 | #endif 13 | 14 | #if WASMKIT_HAS_SWIFTASYNCCC 15 | # define WASMKIT_USE_DIRECT_THREADED_CODE 1 16 | #else 17 | # define WASMKIT_USE_DIRECT_THREADED_CODE 0 18 | #endif 19 | 20 | #define SWIFT_CC_swift __attribute__((swiftcall)) 21 | #define SWIFT_CC_swiftasync __attribute__((swiftasynccall)) 22 | #define SWIFT_CC(CC) SWIFT_CC_##CC 23 | 24 | #define SWIFT_CONTEXT __attribute__((swift_context)) 25 | #define SWIFT_ERROR_RESULT __attribute__((swift_error_result)) 26 | 27 | #endif // WASMKIT_PLATFORM_H 28 | -------------------------------------------------------------------------------- /Sources/_CWasmKit/include/_CWasmKit.h: -------------------------------------------------------------------------------- 1 | #ifndef WASMKIT__CWASMKIT_H 2 | #define WASMKIT__CWASMKIT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Platform.h" 10 | 11 | // MARK: - Execution Parameters 12 | // See ExecutionContext.swift for more information about each execution 13 | // parameter. 14 | typedef uint64_t *_Nonnull Sp; 15 | typedef void *_Nullable Pc; 16 | typedef void *_Nullable Md; 17 | typedef size_t Ms; 18 | 19 | /// The function type for executing a single instruction and transitioning to 20 | /// the next instruction by tail calling. `swiftasync` calling convention is 21 | /// used to keep `state` in the context register and to force tail calling. 22 | /// 23 | /// See https://clang.llvm.org/docs/AttributeReference.html#swiftasynccall for 24 | /// more information about `swiftasynccall`. 25 | typedef SWIFT_CC(swiftasync) void (* _Nonnull wasmkit_tc_exec)( 26 | uint64_t *_Nonnull sp, Pc, Md, Ms, SWIFT_CONTEXT void *_Nullable state); 27 | 28 | /// The entry point for executing a direct-threaded interpreter loop. 29 | /// The interpreter loop is implemented as a tail-recursive function that 30 | /// executes a single instruction and transitions to the next instruction by 31 | /// tail calling. 32 | /// 33 | /// NOTE: This entry point must be implemented in C for now because of an issue 34 | /// with the ClangImporter that ignores the explicitly specified calling 35 | /// convention and it leads to a miscompilation of the tail call. 36 | /// See https://github.com/swiftlang/swift/issues/69264 37 | static inline void wasmkit_tc_start( 38 | wasmkit_tc_exec exec, Sp sp, Pc pc, Md md, Ms ms, void *_Nullable state 39 | ) { 40 | exec(sp, pc, md, ms, state); 41 | } 42 | 43 | static inline void wasmkit_fwrite_stderr(const char *_Nonnull str, size_t len) { 44 | fwrite(str, 1, len, stderr); 45 | } 46 | 47 | // MARK: - Swift Runtime Functions 48 | 49 | struct SwiftError; 50 | 51 | /// Releases the given Swift error object. 52 | static inline void wasmkit_swift_errorRelease(const void *_Nonnull object) { 53 | extern void swift_errorRelease(const struct SwiftError *_Nonnull object); 54 | swift_errorRelease(object); 55 | } 56 | 57 | #endif // WASMKIT__CWASMKIT_H 58 | -------------------------------------------------------------------------------- /Sources/_CWasmKit/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module _CWasmKit { 2 | header "_CWasmKit.h" 3 | 4 | module Platform { 5 | header "Platform.h" 6 | export * 7 | } 8 | 9 | module InlineCode { 10 | header "InlineCode.h" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/_CabiShims/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module _CabiShims { 2 | header "shims.h" 3 | } 4 | -------------------------------------------------------------------------------- /Sources/_CabiShims/include/shims.h: -------------------------------------------------------------------------------- 1 | /// FIXME: This is a hack to reference a C function from Swift with C calling convention. 2 | /// It is needed because swiftc does not support `@_cdecl` without a body. 3 | /// This declaration should be removed after rdar://115802180 will be implemented. 4 | void __wasm_call_ctors(void); 5 | -------------------------------------------------------------------------------- /Sources/_CabiShims/shims.c: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Tests/WASITests/Platform/SandboxPrimitives/OpenParentTests.swift: -------------------------------------------------------------------------------- 1 | import SystemPackage 2 | import XCTest 3 | 4 | @testable import WASI 5 | 6 | final class OpenParentTests: XCTestCase { 7 | func testSplitParent() { 8 | func XCTCheck( 9 | _ lhs: (FilePath, FilePath.Component)?, 10 | _ rhs: (FilePath, FilePath.Component)?, 11 | file: StaticString = #file, 12 | line: UInt = #line 13 | ) { 14 | switch (lhs, rhs) { 15 | case (.none, .none): return 16 | case let (.some(lhs), .some(rhs)): 17 | XCTAssertEqual(lhs.0, rhs.0, file: file, line: line) 18 | XCTAssertEqual(lhs.1, rhs.1, file: file, line: line) 19 | default: 20 | XCTFail("\(String(describing: lhs)) and \(String(describing: rhs)) are not equal", file: file, line: line) 21 | } 22 | } 23 | 24 | XCTCheck(splitParent(path: ""), nil) 25 | 26 | XCTCheck(splitParent(path: "/"), (FilePath("/"), FilePath.Component("."))) 27 | XCTCheck(splitParent(path: "/."), (FilePath("/."), FilePath.Component("."))) 28 | XCTCheck(splitParent(path: "/a"), (FilePath("/"), FilePath.Component("a"))) 29 | XCTCheck(splitParent(path: "/a/"), (FilePath("/a"), FilePath.Component("."))) 30 | XCTCheck(splitParent(path: "/a/."), (FilePath("/a/."), FilePath.Component("."))) 31 | XCTCheck(splitParent(path: "/a/.."), (FilePath("/a/.."), FilePath.Component("."))) 32 | 33 | XCTCheck(splitParent(path: "b"), (FilePath(""), FilePath.Component("b"))) 34 | XCTCheck(splitParent(path: "b/."), (FilePath("b/."), FilePath.Component("."))) 35 | XCTCheck(splitParent(path: "b/.."), (FilePath("b/.."), FilePath.Component("."))) 36 | 37 | XCTCheck(splitParent(path: "../c"), (FilePath(".."), FilePath.Component("c"))) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tests/WASITests/RandomBufferGeneratorTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import WASI 4 | 5 | final class RandomBufferGeneratorTests: XCTestCase { 6 | struct DeterministicGenerator: RandomNumberGenerator, RandomBufferGenerator { 7 | var items: [UInt64] 8 | 9 | mutating func next() -> UInt64 { 10 | items.removeFirst() 11 | } 12 | } 13 | func testDefaultFill() { 14 | var generator = DeterministicGenerator(items: [ 15 | 0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210, 0xdead_beef_badd_cafe, 16 | ]) 17 | for (bufferSize, expectedBytes): (Int, [UInt8]) in [ 18 | (10, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32]), 19 | (2, [0xfe, 0xca]), 20 | (0, []), 21 | ] { 22 | var buffer: [UInt8] = Array(repeating: 0, count: bufferSize) 23 | buffer.withUnsafeMutableBufferPointer { 24 | generator.fill(buffer: $0) 25 | } 26 | let expected: [UInt8] 27 | #if _endian(little) 28 | expected = expectedBytes 29 | #else 30 | expected = Array(expectedBytes.reversed()) 31 | #endif 32 | XCTAssertEqual(buffer, expected) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Tests/WASITests/TestSupport.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum TestSupport { 4 | struct Error: Swift.Error, CustomStringConvertible { 5 | let description: String 6 | 7 | init(description: String) { 8 | self.description = description 9 | } 10 | 11 | init(errno: Int32) { 12 | self.init(description: String(cString: strerror(errno))) 13 | } 14 | } 15 | 16 | class TemporaryDirectory { 17 | let path: String 18 | var url: URL { URL(fileURLWithPath: path) } 19 | 20 | init() throws { 21 | let tempdir = URL(fileURLWithPath: NSTemporaryDirectory()) 22 | let templatePath = tempdir.appendingPathComponent("WasmKit.XXXXXX") 23 | var template = [UInt8](templatePath.path.utf8).map({ Int8($0) }) + [Int8(0)] 24 | 25 | #if os(Windows) 26 | if _mktemp_s(&template, template.count) != 0 { 27 | throw Error(errno: errno) 28 | } 29 | if _mkdir(template) != 0 { 30 | throw Error(errno: errno) 31 | } 32 | #else 33 | if mkdtemp(&template) == nil { 34 | #if os(Android) 35 | throw Error(errno: __errno().pointee) 36 | #else 37 | throw Error(errno: errno) 38 | #endif 39 | } 40 | #endif 41 | 42 | self.path = String(cString: template) 43 | } 44 | 45 | func createDir(at relativePath: String) throws { 46 | let directoryURL = url.appendingPathComponent(relativePath) 47 | try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) 48 | } 49 | 50 | func createFile(at relativePath: String, contents: String) throws { 51 | let fileURL = url.appendingPathComponent(relativePath) 52 | guard let data = contents.data(using: .utf8) else { return } 53 | FileManager.default.createFile(atPath: fileURL.path, contents: data, attributes: nil) 54 | } 55 | 56 | func createSymlink(at relativePath: String, to target: String) throws { 57 | let linkURL = url.appendingPathComponent(relativePath) 58 | try FileManager.default.createSymbolicLink( 59 | atPath: linkURL.path, 60 | withDestinationPath: target 61 | ) 62 | } 63 | 64 | deinit { 65 | _ = try? FileManager.default.removeItem(atPath: path) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Tests/WATTests/TestSupport.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum TestSupport { 4 | struct Error: Swift.Error, CustomStringConvertible { 5 | let description: String 6 | 7 | init(description: String) { 8 | self.description = description 9 | } 10 | 11 | init(errno: Int32) { 12 | self.init(description: String(cString: strerror(errno))) 13 | } 14 | } 15 | 16 | static func withTemporaryDirectory( 17 | _ body: (String, _ shouldRetain: inout Bool) throws -> Result 18 | ) throws -> Result { 19 | let tempdir = URL(fileURLWithPath: NSTemporaryDirectory()) 20 | let templatePath = tempdir.appendingPathComponent("WasmKit.XXXXXX") 21 | var template = [UInt8](templatePath.path.utf8).map({ Int8($0) }) + [Int8(0)] 22 | 23 | #if os(Windows) 24 | if _mktemp_s(&template, template.count) != 0 { 25 | throw Error(errno: errno) 26 | } 27 | if _mkdir(template) != 0 { 28 | throw Error(errno: errno) 29 | } 30 | #else 31 | if mkdtemp(&template) == nil { 32 | #if os(Android) 33 | throw Error(errno: __errno().pointee) 34 | #else 35 | throw Error(errno: errno) 36 | #endif 37 | } 38 | #endif 39 | 40 | let path = String(cString: template) 41 | var shouldRetain = false 42 | defer { 43 | if !shouldRetain { 44 | _ = try? FileManager.default.removeItem(atPath: path) 45 | } 46 | } 47 | return try body(path, &shouldRetain) 48 | } 49 | 50 | static func lookupExecutable(_ name: String) -> URL? { 51 | let envName = "\(name.uppercased())_EXEC" 52 | if let path = ProcessInfo.processInfo.environment[envName] { 53 | let url = URL(fileURLWithPath: path).deletingLastPathComponent().appendingPathComponent(name) 54 | if FileManager.default.isExecutableFile(atPath: url.path) { 55 | return url 56 | } 57 | print("Executable path \(url.path) specified by \(envName) is not found or not executable, falling back to PATH lookup") 58 | } 59 | #if os(Windows) 60 | let pathEnvVar = "Path" 61 | let pathSeparator: Character = ";" 62 | #else 63 | let pathEnvVar = "PATH" 64 | let pathSeparator: Character = ":" 65 | #endif 66 | 67 | let paths = ProcessInfo.processInfo.environment[pathEnvVar] ?? "" 68 | let searchPaths = paths.split(separator: pathSeparator).map(String.init) 69 | for path in searchPaths { 70 | let url = URL(fileURLWithPath: path).appendingPathComponent(name) 71 | if FileManager.default.isExecutableFile(atPath: url.path) { 72 | return url 73 | } 74 | } 75 | return nil 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/WITExtractorPluginTests/Fixtures/PluginSmokePackage/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /Tests/WITExtractorPluginTests/Fixtures/PluginSmokePackage/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.8 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "PluginSmokePackage", 7 | dependencies: [ 8 | .package(path: "../../../../") 9 | ], 10 | targets: [ 11 | .target(name: "PluginSmokeModule") 12 | ] 13 | ) 14 | -------------------------------------------------------------------------------- /Tests/WITExtractorPluginTests/Fixtures/PluginSmokePackage/Sources/PluginSmokeModule/PluginSmoke.swift: -------------------------------------------------------------------------------- 1 | @_spi(WIT) 2 | public struct StructA { 3 | @_spi(WIT) public var memberB: Int 4 | 5 | @_spi(WIT) public struct NestedStructC { 6 | @_spi(WIT) public var memberD: Int 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/WITExtractorPluginTests/PluginSmokeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class PluginSmokeTests: XCTestCase { 4 | func testExtractPlugin() throws { 5 | #if WASMKIT_CI_TOOLCHAIN_NIGHTLY 6 | throw XCTSkip("XFAIL: https://github.com/swiftwasm/WasmKit/issues/184") 7 | #endif 8 | guard ProcessInfo.processInfo.environment["__XCODE_BUILT_PRODUCTS_DIR_PATHS"] == nil else { 9 | throw XCTSkip( 10 | "\"swift package resolve\" somehow fails to clone git repository only when invoking from Xcode test runner" 11 | ) 12 | } 13 | let stdout = try assertSwiftPackage( 14 | fixturePackage: "PluginSmokePackage", 15 | ["extract-wit", "--target", "PluginSmokeModule"] 16 | ) 17 | XCTAssertEqual( 18 | stdout, 19 | """ 20 | // DO NOT EDIT. 21 | // 22 | // Generated by the WITExtractor 23 | package swift:plugin-smoke-package 24 | 25 | interface plugin-smoke-module { 26 | record struct-A { 27 | member-B: s64, 28 | } 29 | 30 | record struct-A-nested-struct-C { 31 | member-D: s64, 32 | } 33 | } 34 | """) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/WITExtractorTests/ConvertCaseTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import WITExtractor 4 | 5 | class ConvertCaseTests: XCTestCase { 6 | func testPascalToKebab() { 7 | XCTAssertEqual(ConvertCase.kebabCase(identifier: "PascalCase"), "pascal-case") 8 | XCTAssertEqual(ConvertCase.kebabCase(identifier: "camelCase"), "camel-case") 9 | XCTAssertEqual(ConvertCase.kebabCase(identifier: "libXML2"), "lib-XML2") 10 | // underscore is not allowed in WIT kebab-case 11 | XCTAssertEqual(ConvertCase.kebabCase(identifier: "iso9899_1990"), "iso98991990") 12 | XCTAssertEqual(ConvertCase.kebabCase(identifier: "v4_2"), "v42") 13 | XCTAssertEqual(ConvertCase.kebabCase(identifier: "CXXConfig"), "C-X-X-config") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tests/WITExtractorTests/ExportFunctionTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class ExportFunctionTests: XCTestCase { 4 | func testNoParameter() throws { 5 | try TestSupport.assertTranslation( 6 | """ 7 | @_spi(WIT) public func noParameter() {} 8 | """, 9 | """ 10 | package swift:wasmkit 11 | 12 | interface test { 13 | no-parameter: func() 14 | } 15 | """) 16 | } 17 | 18 | func testParameters() throws { 19 | try TestSupport.assertTranslation( 20 | """ 21 | @_spi(WIT) public func withParameters(x: String, y: Int) {} 22 | """, 23 | """ 24 | package swift:wasmkit 25 | 26 | interface test { 27 | with-parameters: func(a: string, b: s64) 28 | } 29 | """) 30 | } 31 | 32 | func testResult() throws { 33 | try TestSupport.assertTranslation( 34 | """ 35 | @_spi(WIT) public func withResult() -> Int { return 0 } 36 | """, 37 | """ 38 | package swift:wasmkit 39 | 40 | interface test { 41 | with-result: func() -> s64 42 | } 43 | """) 44 | } 45 | 46 | func testMultipleResult() throws { 47 | try TestSupport.assertTranslation( 48 | """ 49 | @_spi(WIT) public func withMultipleResult() -> (x: Int, y: Int) { 50 | return (0, 1) 51 | } 52 | """, 53 | """ 54 | package swift:wasmkit 55 | 56 | interface test { 57 | with-multiple-result: func() -> (a: s64, b: s64) 58 | } 59 | """) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Compiled/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/Tests/WITOverlayGeneratorTests/Compiled/.gitkeep -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/EmbeddedSupport/MinLibc.c: -------------------------------------------------------------------------------- 1 | // This file is used to provide a "just enough" implementation of some C standard library functions 2 | // required by Embedded target builds. 3 | #include 4 | #include 5 | #define WASM_PAGE_SIZE 0x10000 6 | 7 | size_t __builtin_wasm_memory_grow(int32_t index, size_t delta); 8 | void *__builtin_memcpy(void *dest, const void *src, size_t n); 9 | 10 | static void* alignedAlloc(size_t alignment, size_t size) { 11 | size_t basePageSize = __builtin_wasm_memory_grow(0, (size + 0xffff) / 0x10000); 12 | if (basePageSize == (size_t)-1) { 13 | return NULL; 14 | } 15 | size_t base = basePageSize * WASM_PAGE_SIZE; 16 | base = (base + alignment - 1) & -alignment; 17 | return (void*)base; 18 | } 19 | 20 | /// NOTE: always allocates a new memory page by `memory.grow` 21 | int posix_memalign(void** memptr, size_t alignment, size_t size) { 22 | void* ptr = alignedAlloc(alignment, size); 23 | if (ptr == NULL) { 24 | return -1; 25 | } 26 | *memptr = ptr; 27 | return 0; 28 | } 29 | 30 | /// NOTE: always allocates a new memory page by `memory.grow` and copies the old data 31 | void* cabi_realloc(void* old, size_t oldSize, size_t align, size_t newSize) { 32 | if (old != NULL) { 33 | void* new = alignedAlloc(align, newSize); 34 | if (new != NULL) { 35 | __builtin_memcpy(new, old, oldSize < newSize ? oldSize : newSize); 36 | } 37 | return new; 38 | } else { 39 | return alignedAlloc(align, newSize); 40 | } 41 | } 42 | 43 | void *memmove(void *dest, const void *src, size_t n) { 44 | return __builtin_memcpy(dest, src, n); 45 | } 46 | 47 | void *memcpy(void *dest, const void *src, size_t n) { 48 | // `memory.copy` is safe even if `src` and `dest` overlap 49 | // > Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap. 50 | // > https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md 51 | return __builtin_memcpy(dest, src, n); 52 | } 53 | 54 | /// NOTE: does nothing as we don't manage memory chunks 55 | void free(void *ptr) {} 56 | 57 | /// NOTE: just returns the input character as is, no output is produced 58 | int putchar(int c) { 59 | return c; 60 | } 61 | 62 | /// NOTE: fills the buffer with a constant value 63 | void arc4random_buf(void *buf, size_t n) { 64 | for (size_t i = 0; i < n; i++) { 65 | ((uint8_t *)buf)[i] = (uint8_t)42; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Char/Char.swift: -------------------------------------------------------------------------------- 1 | struct CharTestWorldExportsImpl: CharTestWorldExports { 2 | static func roundtrip(v: Unicode.Scalar) -> Unicode.Scalar { v } 3 | } 4 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Char/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:char-check 2 | 3 | world char-test-world { 4 | export roundtrip: func(v: char) -> char 5 | } 6 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Enum/Enum.swift: -------------------------------------------------------------------------------- 1 | struct EnumTestWorldExportsImpl: EnumTestWorldExports { 2 | static func roundtripSingle(v: Single) -> Single { 3 | return v 4 | } 5 | 6 | static func roundtripLarge(v: Large) -> Large { 7 | return v 8 | } 9 | 10 | static func returnByPointer() -> (Binary, Binary) { 11 | return (.a, .b) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Enum/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:enum-check 2 | 3 | world enum-test-world { 4 | enum single { a } 5 | export roundtrip-single: func(v: single) -> single 6 | 7 | enum large { 8 | c000, c001, c002, c003, c004, c005, c006, c007, 9 | c008, c009, c010, c011, c012, c013, c014, c015, 10 | c016, c017, c018, c019, c020, c021, c022, c023, 11 | c024, c025, c026, c027, c028, c029, c030, c031, 12 | c032, c033, c034, c035, c036, c037, c038, c039, 13 | c040, c041, c042, c043, c044, c045, c046, c047, 14 | c048, c049, c050, c051, c052, c053, c054, c055, 15 | c056, c057, c058, c059, c060, c061, c062, c063, 16 | c064, c065, c066, c067, c068, c069, c070, c071, 17 | c072, c073, c074, c075, c076, c077, c078, c079, 18 | c080, c081, c082, c083, c084, c085, c086, c087, 19 | c088, c089, c090, c091, c092, c093, c094, c095, 20 | c096, c097, c098, c099, c100, c101, c102, c103, 21 | c104, c105, c106, c107, c108, c109, c110, c111, 22 | c112, c113, c114, c115, c116, c117, c118, c119, 23 | c120, c121, c122, c123, c124, c125, c126, c127, 24 | c128, c129, c130, c131, c132, c133, c134, c135, 25 | c136, c137, c138, c139, c140, c141, c142, c143, 26 | c144, c145, c146, c147, c148, c149, c150, c151, 27 | c152, c153, c154, c155, c156, c157, c158, c159, 28 | c160, c161, c162, c163, c164, c165, c166, c167, 29 | c168, c169, c170, c171, c172, c173, c174, c175, 30 | c176, c177, c178, c179, c180, c181, c182, c183, 31 | c184, c185, c186, c187, c188, c189, c190, c191, 32 | c192, c193, c194, c195, c196, c197, c198, c199, 33 | c200, c201, c202, c203, c204, c205, c206, c207, 34 | c208, c209, c210, c211, c212, c213, c214, c215, 35 | c216, c217, c218, c219, c220, c221, c222, c223, 36 | c224, c225, c226, c227, c228, c229, c230, c231, 37 | c232, c233, c234, c235, c236, c237, c238, c239, 38 | c240, c241, c242, c243, c244, c245, c246, c247, 39 | c248, c249, c250, c251, c252, c253, c254, c255, 40 | c256, 41 | } 42 | 43 | export roundtrip-large: func(v: large) -> large 44 | 45 | enum binary { a, b } 46 | export return-by-pointer: func() -> tuple 47 | } 48 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Flags/Flags.swift: -------------------------------------------------------------------------------- 1 | struct FlagsTestWorldExportsImpl: FlagsTestWorldExports { 2 | static func roundtripSingle(v: Single) -> Single { 3 | return v 4 | } 5 | 6 | static func roundtripManyU8(v: ManyU8) -> ManyU8 { v } 7 | static func roundtripManyU16(v: ManyU16) -> ManyU16 { v } 8 | static func roundtripManyU32(v: ManyU32) -> ManyU32 { v } 9 | static func roundtripManyU64(v: ManyU64) -> ManyU64 { v } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Flags/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:flags-check 2 | 3 | world flags-test-world { 4 | flags single { a } 5 | export roundtrip-single: func(v: single) -> single 6 | 7 | flags many-u8 { 8 | f00, f01, f02, f03, f04, f05, f06, f07, 9 | } 10 | export roundtrip-many-u8: func(v: many-u8) -> many-u8 11 | 12 | 13 | flags many-u16 { 14 | f00, f01, f02, f03, f04, f05, f06, f07, 15 | f08, f09, f10, f11, f12, f13, f14, f15, 16 | } 17 | export roundtrip-many-u16: func(v: many-u16) -> many-u16 18 | 19 | flags many-u32 { 20 | f00, f01, f02, f03, f04, f05, f06, f07, 21 | f08, f09, f10, f11, f12, f13, f14, f15, 22 | f16, f17, f18, f19, f20, f21, f22, f23, 23 | f24, f25, f26, f27, f28, f29, f30, f31, 24 | } 25 | export roundtrip-many-u32: func(v: many-u32) -> many-u32 26 | 27 | flags many-u64 { 28 | f00, f01, f02, f03, f04, f05, f06, f07, 29 | f08, f09, f10, f11, f12, f13, f14, f15, 30 | f16, f17, f18, f19, f20, f21, f22, f23, 31 | f24, f25, f26, f27, f28, f29, f30, f31, 32 | f32, f33, f34, f35, f36, f37, f38, f39, 33 | f40, f41, f42, f43, f44, f45, f46, f47, 34 | f48, f49, f50, f51, f52, f53, f54, f55, 35 | f56, f57, f58, f59, f60, f61, f62, f63, 36 | } 37 | export roundtrip-many-u64: func(v: many-u64) -> many-u64 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Interface/Interface.swift: -------------------------------------------------------------------------------- 1 | struct InterfaceTestWorldExportsImpl: InterfaceTestWorldExports { 2 | static func roundtripT1(v: T1) -> T1 { v } 3 | } 4 | 5 | struct TestInterfaceCheckIfaceFuncsExportsImpl: TestInterfaceCheckIfaceFuncsExports { 6 | static func roundtripU8(v: UInt8) -> UInt8 { v } 7 | } 8 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Interface/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:interface-check 2 | 3 | interface types { 4 | type my-type = u8 5 | } 6 | 7 | interface iface-funcs { 8 | roundtrip-u8: func(v: u8) -> u8 9 | } 10 | 11 | world interface-test-world { 12 | use types.{my-type} 13 | type t1 = my-type 14 | 15 | export roundtrip-t1: func(v: t1) -> t1 16 | export iface-funcs 17 | } 18 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/List/List.swift: -------------------------------------------------------------------------------- 1 | struct ListTestWorldExportsImpl: ListTestWorldExports { 2 | static func returnEmpty() -> [UInt32] { 3 | return [] 4 | } 5 | 6 | static func roundtrip(v: [UInt8]) -> [UInt8] { 7 | return v 8 | } 9 | 10 | static func roundtripNonPod(v: [String]) -> [String] { 11 | return v 12 | } 13 | 14 | static func roundtripListList(v: [[String]]) -> [[String]] { 15 | return v 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/List/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:list-check 2 | 3 | world list-test-world { 4 | export return-empty: func() -> list 5 | export roundtrip: func(v: list) -> list 6 | export roundtrip-non-pod: func(v: list) -> list 7 | export roundtrip-list-list: func(v: list>) -> list> 8 | } 9 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Naming/Naming.swift: -------------------------------------------------------------------------------- 1 | struct NamingTestWorldExportsImpl: NamingTestWorldExports { 2 | static func roundtripRecord(v: KeywordingRecord) -> KeywordingRecord { v } 3 | static func roundtripEnum(v: KeywordingEnum) -> KeywordingEnum { v } 4 | static func roundtripVariant(v: KeywordingVariant) -> KeywordingVariant { v } 5 | static func roundtripFlags(v: KeywordingFlags) -> KeywordingFlags { v } 6 | } 7 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Naming/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:naming-check 2 | 3 | world naming-test-world { 4 | record keywording-record { 5 | %static: bool, 6 | dynamic: bool, 7 | await: bool, 8 | } 9 | 10 | enum keywording-enum { 11 | struct, internal, 12 | } 13 | 14 | variant keywording-variant { 15 | case, init, 16 | } 17 | 18 | flags keywording-flags { 19 | if, for, 20 | } 21 | 22 | export roundtrip-record: func(v: keywording-record) -> keywording-record 23 | export roundtrip-enum: func(v: keywording-enum) -> keywording-enum 24 | export roundtrip-variant: func(v: keywording-variant) -> keywording-variant 25 | export roundtrip-flags: func(v: keywording-flags) -> keywording-flags 26 | } 27 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Number/Number.swift: -------------------------------------------------------------------------------- 1 | struct NumberTestWorldExportsImpl: NumberTestWorldExports { 2 | static func roundtripBool(v: Bool) -> Bool { v } 3 | static func roundtripU8(v: UInt8) -> UInt8 { v } 4 | static func roundtripU16(v: UInt16) -> UInt16 { v } 5 | static func roundtripU32(v: UInt32) -> UInt32 { v } 6 | static func roundtripU64(v: UInt64) -> UInt64 { v } 7 | static func roundtripS8(v: Int8) -> Int8 { v } 8 | static func roundtripS16(v: Int16) -> Int16 { v } 9 | static func roundtripS32(v: Int32) -> Int32 { v } 10 | static func roundtripS64(v: Int64) -> Int64 { v } 11 | static func roundtripFloat32(v: Float) -> Float { v } 12 | static func roundtripFloat64(v: Double) -> Double { v } 13 | 14 | static func retptrU8() -> (UInt8, UInt8) { (1, 2) } 15 | static func retptrU16() -> (UInt16, UInt16) { (1, 2) } 16 | static func retptrU32() -> (UInt32, UInt32) { (1, 2) } 17 | static func retptrU64() -> (UInt64, UInt64) { (1, 2) } 18 | static func retptrS8() -> (Int8, Int8) { (1, -2) } 19 | static func retptrS16() -> (Int16, Int16) { (1, -2) } 20 | static func retptrS32() -> (Int32, Int32) { (1, -2) } 21 | static func retptrS64() -> (Int64, Int64) { (1, -2) } 22 | } 23 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Number/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:number-check 2 | 3 | world number-test-world { 4 | export roundtrip-bool: func(v: bool) -> bool 5 | 6 | export roundtrip-u8: func(v: u8) -> u8 7 | export roundtrip-u16: func(v: u16) -> u16 8 | export roundtrip-u32: func(v: u32) -> u32 9 | export roundtrip-u64: func(v: u64) -> u64 10 | 11 | export roundtrip-s8: func(v: s8) -> s8 12 | export roundtrip-s16: func(v: s16) -> s16 13 | export roundtrip-s32: func(v: s32) -> s32 14 | export roundtrip-s64: func(v: s64) -> s64 15 | 16 | export roundtrip-float32: func(v: float32) -> float32 17 | export roundtrip-float64: func(v: float64) -> float64 18 | 19 | export retptr-u8: func() -> tuple 20 | export retptr-u16: func() -> tuple 21 | export retptr-u32: func() -> tuple 22 | export retptr-u64: func() -> tuple 23 | 24 | export retptr-s8: func() -> tuple 25 | export retptr-s16: func() -> tuple 26 | export retptr-s32: func() -> tuple 27 | export retptr-s64: func() -> tuple 28 | } 29 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Option/Option.swift: -------------------------------------------------------------------------------- 1 | struct OptionTestWorldExportsImpl: OptionTestWorldExports { 2 | static func returnNone() -> UInt8? { 3 | return nil 4 | } 5 | static func returnOptionF32() -> Float? { 6 | return 1 / 2.0 7 | } 8 | static func returnOptionTypedef() -> OptionTypedef { 9 | return .some(42) 10 | } 11 | static func returnSomeNone() -> UInt32?? { 12 | return .some(nil) 13 | } 14 | static func returnSomeSome() -> UInt32?? { 15 | return .some(.some(33_550_336)) 16 | } 17 | static func roundtrip(v: UInt32?) -> UInt32? { 18 | return v 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Option/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:option-check 2 | 3 | world option-test-world { 4 | export return-none: func() -> option 5 | export return-option-f32: func() -> option 6 | 7 | type option-typedef = option 8 | export return-option-typedef: func() -> option-typedef 9 | 10 | export return-some-none: func() -> option> 11 | export return-some-some: func() -> option> 12 | 13 | export roundtrip: func(v: option) -> option 14 | } 15 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Record/Record.swift: -------------------------------------------------------------------------------- 1 | struct RecordTestWorldExportsImpl: RecordTestWorldExports { 2 | static func returnEmpty() -> RecordEmpty { 3 | return RecordEmpty() 4 | } 5 | static func roundtripEmpty(v: RecordEmpty) -> RecordEmpty { 6 | return v 7 | } 8 | static func returnPadded() -> RecordPadded { 9 | return RecordPadded(f1: 28, f2: 496) 10 | } 11 | static func roundtripPadded(v: RecordPadded) -> RecordPadded { 12 | return v 13 | } 14 | 15 | static func checkIdentFlat(v: RecordIdentFlat) -> RecordIdentFlat { v } 16 | 17 | static func checkIdentLoadstore(v: RecordIdentLoadstore) -> RecordIdentLoadstore { v } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Record/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:record-check 2 | 3 | world record-test-world { 4 | record record-empty {} 5 | export return-empty: func() -> record-empty 6 | export roundtrip-empty: func(v: record-empty) -> record-empty 7 | 8 | record record-padded { 9 | f1: u8, 10 | f2: u32, 11 | } 12 | export return-padded: func() -> record-padded 13 | export roundtrip-padded: func(v: record-padded) -> record-padded 14 | 15 | record record-ident-flat { 16 | my-field: u8, 17 | } 18 | 19 | export check-ident-flat: func(v: record-ident-flat) -> record-ident-flat 20 | 21 | record record-ident-loadstore { 22 | my-field: u8, 23 | } 24 | 25 | export check-ident-loadstore: func(v: record-ident-loadstore) -> record-ident-loadstore 26 | } 27 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Result/Result.swift: -------------------------------------------------------------------------------- 1 | struct ResultTestWorldExportsImpl: ResultTestWorldExports { 2 | static func roundtripResult(v: Result>) -> Result> { 3 | return v 4 | } 5 | static func roundtripResultOk(v: Result>) -> Result> { 6 | return v 7 | } 8 | static func roundtripResultOkError(v: Result>) -> Result> { 9 | return v 10 | } 11 | static func roundtripResultError(v: Result>) -> Result> { 12 | return v 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Result/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:result-check 2 | 3 | world result-test-world { 4 | export roundtrip-result: func(v: result) -> result 5 | export roundtrip-result-ok: func(v: result) -> result 6 | export roundtrip-result-ok-error: func(v: result) -> result 7 | export roundtrip-result-error: func(v: result<_, string>) -> result<_, string> 8 | } 9 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Smoke/Smoke.swift: -------------------------------------------------------------------------------- 1 | struct SmokeTestWorldExportsImpl: SmokeTestWorldExports { 2 | static func hello() {} 3 | } 4 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Smoke/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:smoke 2 | 3 | world smoke-test-world { 4 | export hello: func() 5 | } 6 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/String/String.swift: -------------------------------------------------------------------------------- 1 | struct StringTestWorldExportsImpl: StringTestWorldExports { 2 | static func returnEmpty() -> String { 3 | return "" 4 | } 5 | 6 | static func roundtrip(v: String) -> String { 7 | return v 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/String/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:string-check 2 | 3 | world string-test-world { 4 | export return-empty: func() -> string 5 | export roundtrip: func(v: string) -> string 6 | } 7 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Tuple/Tuple.swift: -------------------------------------------------------------------------------- 1 | struct TupleTestWorldExportsImpl: TupleTestWorldExports { 2 | static func roundtrip(v: (Bool, UInt32)) -> (Bool, UInt32) { v } 3 | } 4 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Tuple/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:tuple-check 2 | 3 | world tuple-test-world { 4 | export roundtrip: func(v: tuple) -> tuple 5 | } 6 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Variant/Variant.swift: -------------------------------------------------------------------------------- 1 | struct VariantTestWorldExportsImpl: VariantTestWorldExports { 2 | static func returnSingle() -> Single { 3 | return .a(33_550_336) 4 | } 5 | 6 | static func returnLarge() -> Large { 7 | return .c256(42) 8 | } 9 | static func roundtripLarge(v: Large) -> Large { 10 | return v 11 | } 12 | 13 | static func roundtripResult(v: Result>) -> Result> { 14 | return v 15 | } 16 | static func roundtripResultOk(v: Result>) -> Result> { 17 | return v 18 | } 19 | static func roundtripResultOkError(v: Result>) -> Result> { 20 | return v 21 | } 22 | static func roundtripResultError(v: Result>) -> Result> { 23 | return v 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Fixtures/Variant/wit/world.wit: -------------------------------------------------------------------------------- 1 | package test:variant-check 2 | 3 | world variant-test-world { 4 | variant single { a(u32) } 5 | export return-single: func() -> single 6 | variant large { 7 | c000, c001, c002, c003, c004, c005, c006, c007, 8 | c008, c009, c010, c011, c012, c013, c014, c015, 9 | c016, c017, c018, c019, c020, c021, c022, c023, 10 | c024, c025, c026, c027, c028, c029, c030, c031, 11 | c032, c033, c034, c035, c036, c037, c038, c039, 12 | c040, c041, c042, c043, c044, c045, c046, c047, 13 | c048, c049, c050, c051, c052, c053, c054, c055, 14 | c056, c057, c058, c059, c060, c061, c062, c063, 15 | c064, c065, c066, c067, c068, c069, c070, c071, 16 | c072, c073, c074, c075, c076, c077, c078, c079, 17 | c080, c081, c082, c083, c084, c085, c086, c087, 18 | c088, c089, c090, c091, c092, c093, c094, c095, 19 | c096, c097, c098, c099, c100, c101, c102, c103, 20 | c104, c105, c106, c107, c108, c109, c110, c111, 21 | c112, c113, c114, c115, c116, c117, c118, c119, 22 | c120, c121, c122, c123, c124, c125, c126, c127, 23 | c128, c129, c130, c131, c132, c133, c134, c135, 24 | c136, c137, c138, c139, c140, c141, c142, c143, 25 | c144, c145, c146, c147, c148, c149, c150, c151, 26 | c152, c153, c154, c155, c156, c157, c158, c159, 27 | c160, c161, c162, c163, c164, c165, c166, c167, 28 | c168, c169, c170, c171, c172, c173, c174, c175, 29 | c176, c177, c178, c179, c180, c181, c182, c183, 30 | c184, c185, c186, c187, c188, c189, c190, c191, 31 | c192, c193, c194, c195, c196, c197, c198, c199, 32 | c200, c201, c202, c203, c204, c205, c206, c207, 33 | c208, c209, c210, c211, c212, c213, c214, c215, 34 | c216, c217, c218, c219, c220, c221, c222, c223, 35 | c224, c225, c226, c227, c228, c229, c230, c231, 36 | c232, c233, c234, c235, c236, c237, c238, c239, 37 | c240, c241, c242, c243, c244, c245, c246, c247, 38 | c248, c249, c250, c251, c252, c253, c254, c255, 39 | c256(u32), 40 | } 41 | export return-large: func() -> large 42 | export roundtrip-large: func(v: large) -> large 43 | 44 | export roundtrip-result: func(v: result) -> result 45 | export roundtrip-result-ok: func(v: result) -> result 46 | export roundtrip-result-ok-error: func(v: result) -> result 47 | export roundtrip-result-error: func(v: result<_, string>) -> result<_, string> 48 | } 49 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Generated/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/WasmKit/16683fd9337f19eb26c0d30a67a1fd7b745e32f0/Tests/WITOverlayGeneratorTests/Generated/.gitkeep -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/HostGeneratorTests.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | import XCTest 3 | 4 | @testable import WITOverlayGenerator 5 | 6 | class HostGeneratorTests: XCTestCase { 7 | // Host generators are already executed before running this test suite by SwiftPM build tool plugin, 8 | // but execute again here to collect coverage data. 9 | func testGenerateFromFixtures() throws { 10 | #if os(Android) 11 | throw XCTSkip("unable to run spectest on Android due to missing files on emulator") 12 | #endif 13 | let fixturesDir = RuntimeTestHarness.testsDirectory.appendingPathComponent("Fixtures") 14 | for fixture in try FileManager.default.contentsOfDirectory(atPath: fixturesDir.path) { 15 | let inputFileDir = fixturesDir.appendingPathComponent(fixture).appendingPathComponent("wit") 16 | guard FileManager.default.isDirectory(filePath: inputFileDir.path) else { continue } 17 | let (mainPackage, packageResolver) = try PackageResolver.parse( 18 | directory: inputFileDir.path, 19 | loader: LocalFileLoader() 20 | ) 21 | let context = SemanticsContext(rootPackage: mainPackage, packageResolver: packageResolver) 22 | _ = try WITOverlayGenerator.generateHost(context: context) 23 | } 24 | } 25 | } 26 | 27 | extension FileManager { 28 | internal func isDirectory(filePath: String) -> Bool { 29 | var isDirectory: ObjCBool = false 30 | let exists = self.fileExists(atPath: filePath, isDirectory: &isDirectory) 31 | return exists && isDirectory.boolValue 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/WITOverlayGeneratorTests/Runtime/RuntimeSmokeTests.swift: -------------------------------------------------------------------------------- 1 | import WIT 2 | import XCTest 3 | 4 | @testable import WITOverlayGenerator 5 | 6 | class RuntimeSmokeTests: XCTestCase { 7 | func testCallExportByGuest() throws { 8 | var harness = try RuntimeTestHarness(fixture: "Smoke") 9 | try harness.build(link: SmokeTestWorld.link) { (instance) in 10 | let component = SmokeTestWorld(instance: instance) 11 | _ = try component.hello() 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/WITTests/Semantics/RequestEvaluatorTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import WIT 4 | 5 | class RequestEvaluatorTests: XCTestCase { 6 | struct MyRequest: EvaluationRequest { 7 | let key: Int 8 | let evaluate: (Evaluator) throws -> T 9 | 10 | init(key: Int, evaluate: @escaping (Evaluator) throws -> T = { _ in }) { 11 | self.key = key 12 | self.evaluate = evaluate 13 | } 14 | 15 | func evaluate(evaluator: Evaluator) throws -> T { 16 | return try self.evaluate(evaluator) 17 | } 18 | 19 | static func == (lhs: MyRequest, rhs: MyRequest) -> Bool { 20 | return lhs.key == rhs.key 21 | } 22 | 23 | func hash(into hasher: inout Hasher) { 24 | hasher.combine(key) 25 | } 26 | } 27 | 28 | struct MyError: Error {} 29 | 30 | func testMemoization() throws { 31 | var invoked = 0 32 | let request = MyRequest(key: 1) { _ in 33 | invoked += 1 34 | return invoked 35 | } 36 | let evaluator = Evaluator() 37 | XCTAssertEqual(try evaluator.evaluate(request: request), 1) 38 | // Ensure it returns the same value 39 | XCTAssertEqual(try evaluator.evaluate(request: request), 1) 40 | // Ensure the evaluation method is called only once 41 | XCTAssertEqual(invoked, 1) 42 | } 43 | 44 | func testCycleDetection() throws { 45 | let evaluator = Evaluator() 46 | var anyError: Error? 47 | XCTAssertThrowsError( 48 | try evaluator.evaluate( 49 | request: MyRequest( 50 | key: 1, 51 | evaluate: { 52 | try $0.evaluate( 53 | request: MyRequest( 54 | key: 2, 55 | evaluate: { 56 | try $0.evaluate(request: MyRequest(key: 1)) // again request key=1 57 | })) 58 | })), 59 | "CyclicalRequestError expected", 60 | { anyError = $0 } 61 | ) 62 | 63 | let cyclicalRequestError = try XCTUnwrap(anyError as? Evaluator.CyclicalRequestError) 64 | XCTAssertEqual(cyclicalRequestError.activeRequests.count, 3) 65 | } 66 | 67 | func testThrowingRequest() throws { 68 | var invoked = 0 69 | let request = MyRequest(key: 1) { _ in 70 | invoked += 1 71 | throw MyError() 72 | } 73 | let evaluator = Evaluator() 74 | XCTAssertThrowsError(try evaluator.evaluate(request: request)) 75 | XCTAssertThrowsError(try evaluator.evaluate(request: request)) 76 | // Ensure the evaluation method is called only once even though 77 | // the request throws error 78 | XCTAssertEqual(invoked, 1) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Tests/WITTests/TextParser/ParseFunctionDeclTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import WIT 4 | 5 | class ParseFunctionDeclTests: XCTestCase { 6 | 7 | func parse(_ text: String) throws -> ResourceFunctionSyntax { 8 | var lexer = Lexer(cursor: .init(input: text)) 9 | return try ResourceFunctionSyntax.parse( 10 | lexer: &lexer, 11 | documents: DocumentsSyntax(comments: []), 12 | attributes: [] 13 | ) 14 | } 15 | 16 | func testFunction() throws { 17 | var lexer = Lexer(cursor: .init(input: "func(x: u8) -> u16")) 18 | let f = try FunctionSyntax.parse(lexer: &lexer) 19 | XCTAssertEqual(f.parameters.count, 1) 20 | guard case .anon(.u16) = f.results else { 21 | XCTFail("expected anon but got \(f.results)") 22 | return 23 | } 24 | } 25 | 26 | func testFunctionNamedReturn() throws { 27 | var lexer = Lexer(cursor: .init(input: "func() -> (a: u8, b: u16)")) 28 | let f = try FunctionSyntax.parse(lexer: &lexer) 29 | guard case .named(let results) = f.results else { 30 | XCTFail("expected anon but got \(f.results)") 31 | return 32 | } 33 | XCTAssertEqual(results.count, 2) 34 | } 35 | 36 | func testResourceFunction() throws { 37 | let f = try parse("%foo: func() -> bool") 38 | guard case .method(let method) = f else { 39 | XCTFail("expected method but got \(f)") 40 | return 41 | } 42 | XCTAssertEqual(method.name.text, "foo") 43 | } 44 | 45 | func testResourceFunctionStatic() throws { 46 | let f = try parse("foo: static func() -> bool") 47 | guard case .static(let method) = f else { 48 | XCTFail("expected method but got \(f)") 49 | return 50 | } 51 | XCTAssertEqual(method.name.text, "foo") 52 | } 53 | 54 | func testResourceFunctionConstructor() throws { 55 | let f = try parse("constructor(a: u8, b: u16)") 56 | guard case .constructor(let ctor) = f else { 57 | XCTFail("expected method but got \(f)") 58 | return 59 | } 60 | XCTAssertEqual(ctor.name.text, "constructor") 61 | XCTAssertEqual(ctor.function.parameters.count, 2) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tests/WITTests/TextParser/ParseVersionTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import WIT 4 | 5 | class ParseVersionTests: XCTestCase { 6 | 7 | func parse(_ text: String) throws -> Version? { 8 | var lexer = Lexer(cursor: .init(input: text)) 9 | return try Version.parse(lexer: &lexer) 10 | } 11 | 12 | func testParse() throws { 13 | do { 14 | let version = try XCTUnwrap(parse("1.0.0")) 15 | XCTAssertEqual(version.major, 1) 16 | XCTAssertEqual(version.minor, 0) 17 | XCTAssertEqual(version.patch, 0) 18 | } 19 | do { 20 | let version = try XCTUnwrap(parse("12.222.4444")) 21 | XCTAssertEqual(version.major, 12) 22 | XCTAssertEqual(version.minor, 222) 23 | XCTAssertEqual(version.patch, 4444) 24 | } 25 | } 26 | 27 | func testParseInvalid() throws { 28 | XCTAssertThrowsError(try parse("0.0.01")) 29 | XCTAssertThrowsError(try parse("0.0.x")) 30 | XCTAssertThrowsError(try parse("0.0.0-")) 31 | XCTAssertThrowsError(try parse("0.0.0-+")) 32 | } 33 | 34 | func testParsePrerelease() throws { 35 | do { 36 | let version = try XCTUnwrap(parse("1.0.0-alpha")) 37 | XCTAssertEqual(version.prerelease, "alpha") 38 | } 39 | do { 40 | let version = try XCTUnwrap(parse("1.0.0-alpha.1")) 41 | XCTAssertEqual(version.prerelease, "alpha.1") 42 | } 43 | do { 44 | let version = try XCTUnwrap(parse("1.0.0-0.3.7")) 45 | XCTAssertEqual(version.prerelease, "0.3.7") 46 | } 47 | do { 48 | let version = try XCTUnwrap(parse("1.0.0-x-y-z.--")) 49 | XCTAssertEqual(version.prerelease, "x-y-z.--") 50 | } 51 | } 52 | 53 | func testParseBuildMetadata() throws { 54 | do { 55 | let version = try XCTUnwrap(parse("1.0.0-alpha+001")) 56 | XCTAssertEqual(version.prerelease, "alpha") 57 | XCTAssertEqual(version.buildMetadata, "001") 58 | } 59 | do { 60 | let version = try XCTUnwrap(parse("1.0.0+20130313144700")) 61 | XCTAssertEqual(version.prerelease, nil) 62 | XCTAssertEqual(version.buildMetadata, "20130313144700") 63 | } 64 | do { 65 | let version = try XCTUnwrap(parse("1.0.0-beta+exp.sha.5114f85")) 66 | XCTAssertEqual(version.prerelease, "beta") 67 | XCTAssertEqual(version.buildMetadata, "exp.sha.5114f85") 68 | } 69 | do { 70 | let version = try XCTUnwrap(parse("1.0.0+21AF26D3----117B344092BD")) 71 | XCTAssertEqual(version.prerelease, nil) 72 | XCTAssertEqual(version.buildMetadata, "21AF26D3----117B344092BD") 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Tests/WasmKitTests/Execution/HostModuleTests.swift: -------------------------------------------------------------------------------- 1 | import WAT 2 | import XCTest 3 | 4 | @testable import WasmKit 5 | @testable import WasmParser 6 | 7 | final class HostModuleTests: XCTestCase { 8 | func testImportMemory() throws { 9 | let engine = Engine() 10 | let store = Store(engine: engine) 11 | let memoryType = MemoryType(min: 1, max: nil) 12 | let memory = try WasmKit.Memory(store: store, type: memoryType) 13 | let imports: Imports = [ 14 | "env": ["memory": memory] 15 | ] 16 | 17 | let module = try parseWasm( 18 | bytes: wat2wasm( 19 | """ 20 | (module 21 | (import "env" "memory" (memory 1)) 22 | ) 23 | """)) 24 | XCTAssertNoThrow(try module.instantiate(store: store, imports: imports)) 25 | // Ensure the allocated address is valid 26 | _ = memory.data 27 | } 28 | 29 | func testReentrancy() throws { 30 | let engine = Engine() 31 | let store = Store(engine: engine) 32 | let voidSignature = WasmTypes.FunctionType(parameters: [], results: []) 33 | let module = try parseWasm( 34 | bytes: wat2wasm( 35 | """ 36 | (module 37 | (import "env" "bar" (func $bar)) 38 | (import "env" "qux" (func $qux)) 39 | (func (export "foo") 40 | (call $bar) 41 | (call $bar) 42 | (call $bar) 43 | ) 44 | (func (export "baz") 45 | (call $qux) 46 | ) 47 | ) 48 | """) 49 | ) 50 | 51 | var isExecutingFoo = false 52 | var isQuxCalled = false 53 | let imports: Imports = [ 54 | "env": [ 55 | "bar": Function(store: store, type: voidSignature) { caller, _ in 56 | // Ensure "invoke" executes instructions under the current call 57 | XCTAssertFalse(isExecutingFoo, "bar should not be called recursively") 58 | isExecutingFoo = true 59 | defer { isExecutingFoo = false } 60 | let foo = try XCTUnwrap(caller.instance?.exportedFunction(name: "baz")) 61 | _ = try foo() 62 | return [] 63 | }, 64 | "qux": Function(store: store, type: voidSignature) { caller, _ in 65 | XCTAssertTrue(isExecutingFoo) 66 | isQuxCalled = true 67 | return [] 68 | }, 69 | ] 70 | ] 71 | let instance = try module.instantiate(store: store, imports: imports) 72 | // Check foo(wasm) -> bar(host) -> baz(wasm) -> qux(host) 73 | let foo = try XCTUnwrap(instance.exports[function: "foo"]) 74 | try foo() 75 | XCTAssertTrue(isQuxCalled) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/WasmKitTests/Execution/Runtime/StoreAllocatorTests.swift: -------------------------------------------------------------------------------- 1 | import WAT 2 | import WasmParser 3 | import XCTest 4 | 5 | @testable import WasmKit 6 | 7 | final class StoreAllocatorTests: XCTestCase { 8 | func testBumpAllocatorDeallocates() { 9 | class NonTrivialEntity {} 10 | weak var weakEntity: NonTrivialEntity? 11 | do { 12 | let allocator = BumpAllocator(initialCapacity: 2) 13 | do { 14 | let entity = NonTrivialEntity() 15 | // Allocate space placing non-trivial entity 16 | // This `allocate` call should retain the entity 17 | _ = allocator.allocate(initializing: entity) 18 | // Ensure that the initial page is full 19 | _ = allocator.allocate(initializing: entity) 20 | _ = allocator.allocate(initializing: entity) 21 | weakEntity = entity 22 | } 23 | // The entity is still alive because the allocator retains it 24 | XCTAssertNotNil(weakEntity) 25 | } 26 | // The entity should be deallocated when the allocator is deallocated 27 | XCTAssertNil(weakEntity) 28 | } 29 | 30 | func testStoreAllocatorLeak() throws { 31 | weak var weakAllocator: StoreAllocator? 32 | do { 33 | let module = try parseWasm( 34 | bytes: wat2wasm( 35 | """ 36 | (module 37 | (memory (;0;) 0) 38 | (export "a" (memory 0))) 39 | """)) 40 | let engine = Engine() 41 | let store = Store(engine: engine) 42 | _ = try module.instantiate(store: store) 43 | weakAllocator = store.allocator 44 | } 45 | XCTAssertNil(weakAllocator) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/WasmKitTests/ExtraSuite/br_if.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "brif_not_taken") (param i32) (result i32) 3 | (block (result i32) 4 | local.get 0 5 | i32.const 0 6 | br_if 0 7 | local.get 0 8 | i32.add 9 | ) 10 | ) 11 | ) 12 | 13 | (assert_return (invoke "brif_not_taken" (i32.const 3)) (i32.const 6)) 14 | 15 | (module 16 | (func (export "check") 17 | (local i64 i32 f32) 18 | i64.const 4 19 | ;; use non-const nor local instruction to force 20 | ;; leaving local.set at runtime 21 | i64.clz 22 | 23 | i32.const 0 24 | i32.eqz 25 | ;; popping the i32.eqz should invalidate the relinking state 26 | br_if 0 27 | local.set 0 28 | unreachable) 29 | ) 30 | 31 | 32 | (invoke "check") 33 | -------------------------------------------------------------------------------- /Tests/WasmKitTests/ExtraSuite/const_slot.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "check-limit") (param i32) (result i32) 3 | ;; Enough to reach the limit of constant slots 4 | (drop (i32.const 1)) 5 | (drop (i32.const 2)) 6 | (drop (i32.const 3)) 7 | (drop (i32.const 4)) 8 | (drop (i32.const 5)) 9 | (drop (i32.const 6)) 10 | (i32.add (local.get 0) (i32.add (i32.const 1) (i32.const 1))) 11 | ) 12 | (func (export "invalidate-relink") (result i32) 13 | (local i32) 14 | (i32.add (i32.const 1) (i32.const 1)) 15 | ;; This constant should invalidate the relinking 16 | ;; connection of i32.add even though it doesn't 17 | ;; emit its own instruction. 18 | (i32.const 0) 19 | (local.set 0) 20 | (drop) ;; drop i32.add 21 | (local.get 0) 22 | ) 23 | ) 24 | 25 | (assert_return (invoke "check-limit" (i32.const 3)) (i32.const 5)) 26 | (assert_return (invoke "invalidate-relink") (i32.const 0)) 27 | 28 | -------------------------------------------------------------------------------- /Tests/WasmKitTests/ExtraSuite/local_cow.wast: -------------------------------------------------------------------------------- 1 | ;; This test file checks Copy-on-Write behavior of the local-source values 2 | 3 | (module 4 | (func (export "local_source_block_param") (param i32) (result i32) 5 | (local.get 0) 6 | (block (param i32) (result i32)) 7 | ) 8 | ) 9 | 10 | (assert_return (invoke "local_source_block_param" (i32.const 3)) (i32.const 3)) 11 | 12 | (module 13 | (func (export "local_source_loop_param") (param i32) (result i32) 14 | (local.get 0) 15 | (loop (param i32) (result i32)) 16 | ) 17 | ) 18 | 19 | (assert_return (invoke "local_source_loop_param" (i32.const 3)) (i32.const 3)) 20 | 21 | (module 22 | (func (export "check") (result i32) 23 | (local i32 i32) 24 | i32.const 42 25 | local.set 0 26 | block (result i32) 27 | i32.const 0 28 | br 0 29 | local.get 0 30 | end 31 | local.set 1 32 | local.get 1 33 | ) 34 | ) 35 | 36 | (assert_return (invoke "check") (i32.const 0)) 37 | 38 | (module 39 | (func (export "invalidate-relink") (param i32 i32) (result i32) 40 | (i32.add (i32.const 1) (i32.const 42)) 41 | ;; This local.get should invalidate the relinking 42 | ;; connection of i32.add even though it doesn't 43 | ;; emit its own instruction. 44 | (local.get 1) 45 | (local.set 0) 46 | (drop) ;; drop i32.add 47 | (local.get 0) 48 | ) 49 | ) 50 | 51 | (assert_return (invoke "invalidate-relink" (i32.const 1) (i32.const 2)) (i32.const 2)) 52 | 53 | (module 54 | (global (mut i32) (i32.const 4)) 55 | (func (export "check") (result i32) 56 | (local i32) 57 | global.get 0 58 | drop 59 | local.get 0 60 | block 61 | i32.const 1 62 | br_if 0 63 | i32.const 0 64 | local.set 0 65 | end 66 | ) 67 | ) 68 | 69 | (assert_return (invoke "check") (i32.const 0)) 70 | 71 | (module 72 | (global (mut i32) (i32.const 4)) 73 | (func (export "check") (result i32) 74 | (local i32) 75 | global.get 0 76 | drop 77 | local.get 0 78 | i32.const 1 79 | if 80 | i32.const 1 81 | br_if 0 82 | i32.const 0 83 | local.set 0 84 | end 85 | ) 86 | ) 87 | 88 | (assert_return (invoke "check") (i32.const 0)) 89 | -------------------------------------------------------------------------------- /Tests/WasmKitTests/ExtraSuite/shr_s.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "i64.shr_s") (param i64) (param i64) (result i64) 3 | (i64.shr_s (local.get 0) (local.get 1)) 4 | ) 5 | ) 6 | 7 | (assert_return (invoke "i64.shr_s" (i64.const -1309934030728401938) (i64.const -1309934030728401938)) (i64.const -18616)) 8 | 9 | -------------------------------------------------------------------------------- /Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift: -------------------------------------------------------------------------------- 1 | import WasmKit 2 | import WasmKitFuzzing 3 | import XCTest 4 | 5 | final class FuzzTranslatorRegressionTests: XCTestCase { 6 | func testRunAll() throws { 7 | #if os(Android) 8 | throw XCTSkip("Test skipped due to absolute path #filePath unavailable on emulator") 9 | #endif 10 | let sourceRoot = URL(fileURLWithPath: #filePath) 11 | .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() 12 | let failCasesDir = 13 | sourceRoot 14 | .appendingPathComponent("FuzzTesting/FailCases/FuzzTranslator") 15 | 16 | for file in try FileManager.default.contentsOfDirectory(atPath: failCasesDir.path) { 17 | let path = failCasesDir.appendingPathComponent(file).path 18 | print("Fuzz regression test: \(path.dropFirst(sourceRoot.path.count + 1))") 19 | let data = try Data(contentsOf: URL(fileURLWithPath: path)) 20 | do { 21 | try WasmKitFuzzing.fuzzInstantiation(bytes: Array(data)) 22 | } catch { 23 | // Skip exceptions without crash 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/WasmKitTests/SpectestTests.swift: -------------------------------------------------------------------------------- 1 | import WasmKit 2 | import XCTest 3 | 4 | @available(macOS 11, iOS 14.0, watchOS 7.0, tvOS 14.0, *) 5 | final class SpectestTests: XCTestCase { 6 | static let projectDir = URL(fileURLWithPath: #filePath) 7 | .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() 8 | static let testsuite = 9 | projectDir 10 | .appendingPathComponent("Vendor/testsuite") 11 | static var testPaths: [String] { 12 | [ 13 | Self.testsuite.path, 14 | Self.testsuite.appendingPathComponent("proposals/memory64").path, 15 | Self.testsuite.appendingPathComponent("proposals/tail-call").path, 16 | Self.projectDir.appendingPathComponent("Tests/WasmKitTests/ExtraSuite").path, 17 | ] 18 | } 19 | 20 | /// Run all the tests in the spectest suite. 21 | func testRunAll() async throws { 22 | #if os(Android) 23 | throw XCTSkip("unable to run spectest on Android due to missing files on emulator") 24 | #endif 25 | let defaultConfig = EngineConfiguration() 26 | let ok = try await spectest( 27 | path: Self.testPaths, 28 | include: [], 29 | exclude: [], 30 | parallel: true, 31 | configuration: defaultConfig 32 | ) 33 | XCTAssertTrue(ok) 34 | } 35 | 36 | func testRunAllWithTokenThreading() async throws { 37 | #if os(Android) 38 | throw XCTSkip("unable to run spectest on Android due to missing files on emulator") 39 | #endif 40 | let defaultConfig = EngineConfiguration() 41 | guard defaultConfig.threadingModel != .token else { return } 42 | // Sanity check that non-default threading models work. 43 | var config = defaultConfig 44 | config.threadingModel = .token 45 | let ok = try await spectest( 46 | path: Self.testPaths, 47 | parallel: true, 48 | configuration: config 49 | ) 50 | XCTAssertTrue(ok) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Utilities/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.9 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "WasmKitDevUtils", 7 | platforms: [.macOS(.v13)], 8 | targets: [ 9 | .executableTarget(name: "WasmKitDevUtils", path: "Sources"), 10 | ] 11 | ) 12 | -------------------------------------------------------------------------------- /Utilities/README.md: -------------------------------------------------------------------------------- 1 | # WasmKit Development Utilities 2 | 3 | This directory contains a set of utilities that are useful for developing WasmKit. 4 | 5 | ## Usage 6 | 7 | Re-generates all the auto-generated files 8 | 9 | ```console 10 | $ swift run WasmKitDevUtils 11 | ``` 12 | 13 | Re-generates only the internal VM instruction related files 14 | 15 | ```console 16 | $ swift run WasmKitDevUtils vmgen 17 | ``` 18 | 19 | Re-generates only the Core Wasm instruction related files based on `Instructions.json`. 20 | 21 | ```console 22 | $ swift run WasmKitDevUtils wasmgen 23 | ``` 24 | -------------------------------------------------------------------------------- /Utilities/Sources/GeneratedFile.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A unit of code-generation output file. 4 | struct GeneratedFile { 5 | let pathComponents: [String] 6 | let content: String 7 | 8 | init(_ pathComponents: [String], _ content: String) { 9 | self.pathComponents = pathComponents 10 | self.content = content 11 | } 12 | 13 | func writeIfChanged(sourceRoot: URL) throws { 14 | let subPath = pathComponents.joined(separator: "/") 15 | let path = sourceRoot.appendingPathComponent(subPath) 16 | // Write the content only if the file does not exist or the content is different 17 | let shouldWrite: Bool 18 | if !FileManager.default.fileExists(atPath: path.path) { 19 | shouldWrite = true 20 | } else { 21 | let existingContent = try String(contentsOf: path) 22 | shouldWrite = existingContent != content 23 | } 24 | 25 | if shouldWrite { 26 | try content.write(to: path, atomically: true, encoding: .utf8) 27 | print("\u{001B}[1;33mUpdated\u{001B}[0;0m \(subPath)") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Utilities/Sources/main.swift: -------------------------------------------------------------------------------- 1 | import func Foundation.exit 2 | 3 | var arguments = CommandLine.arguments 4 | 5 | struct Subcommand { 6 | let name: String 7 | let description: String 8 | let handler: ([String]) throws -> Void 9 | } 10 | 11 | func main() throws { 12 | 13 | let subcommands: [Subcommand] = [ 14 | Subcommand(name: "vmgen", description: "Generate internal instruction code", handler: VMGen.main), 15 | Subcommand(name: "wasmgen", description: "Generate wasm instruction code", handler: WasmGen.main), 16 | ] 17 | 18 | func printAvailableSubcommands() { 19 | print("Available subcommands:") 20 | for subcommand in subcommands { 21 | print(" \(subcommand.name): \(subcommand.description)") 22 | } 23 | } 24 | 25 | guard arguments.count > 1 else { 26 | for subcommand in subcommands { 27 | try subcommand.handler([subcommand.name]) 28 | } 29 | return 30 | } 31 | 32 | let subcommandName = arguments[1] 33 | guard let subcommand = subcommands.first(where: { $0.name == subcommandName }) else { 34 | print("Unknown subcommand: \(subcommandName)") 35 | printAvailableSubcommands() 36 | exit(1) 37 | } 38 | 39 | try subcommand.handler(Array(arguments.dropFirst(1))) 40 | 41 | } 42 | 43 | try main() 44 | -------------------------------------------------------------------------------- /Utilities/format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import subprocess 4 | import os 5 | 6 | SOURCE_ROOT = os.path.relpath(os.path.join(os.path.dirname(__file__), "..")) 7 | 8 | 9 | def run(arguments): 10 | print("Running: " + " ".join(arguments)) 11 | subprocess.run(arguments, check=True) 12 | 13 | 14 | def build_swift_format(): 15 | # Build swift-format 16 | package_path = os.path.join(SOURCE_ROOT, "Vendor", "swift-format") 17 | bin_path = os.path.join(package_path, ".build", "release", "swift-format") 18 | if os.path.exists(bin_path): 19 | return bin_path 20 | 21 | run(["./Vendor/checkout-dependency", "swift-format"]) 22 | 23 | run([ 24 | "swift", "build", "-c", "release", 25 | "--package-path", package_path]) 26 | return bin_path 27 | 28 | 29 | def main(): 30 | targets = [] 31 | for targets_dir in ["Sources", "Tests"]: 32 | targets_path = os.path.join(SOURCE_ROOT, targets_dir) 33 | for target in os.listdir(targets_path): 34 | if not os.path.isdir(os.path.join(targets_path, target)): 35 | continue 36 | targets.append(os.path.join(targets_dir, target)) 37 | 38 | # NOTE: SystemExtras is not included in the list of targets because it 39 | # follows swift-system style conventions, which is different from 40 | # swift-format. 41 | targets.remove(os.path.join("Sources", "SystemExtras")) 42 | 43 | swift_format = build_swift_format() 44 | 45 | arguments = [ 46 | swift_format, "format", "--in-place", "--recursive", "--parallel" 47 | ] 48 | for target in targets: 49 | arguments.append(os.path.join(SOURCE_ROOT, target)) 50 | arguments.append(os.path.join(SOURCE_ROOT, "Package.swift")) 51 | run(arguments) 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /Vendor/.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | !checkout-dependency 3 | !dependencies.json 4 | -------------------------------------------------------------------------------- /Vendor/checkout-dependency: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # This script checks out dependency repositories from dependencies.json 4 | # 5 | # Usage: 6 | # checkout-dependency 7 | 8 | import os 9 | import sys 10 | import json 11 | import subprocess 12 | 13 | class CheckoutDependency: 14 | 15 | def __init__(self, dependencies_file): 16 | self.dependencies_file = dependencies_file 17 | self.dependencies = json.load(open(dependencies_file)) 18 | 19 | def checkout(self, dependency_name): 20 | dependency = self.dependencies.get(dependency_name) 21 | 22 | if dependency is None: 23 | print(f"Dependency '{dependency_name}' not found in {self.dependencies_file}") 24 | print(f"Available dependencies: {', '.join(self.dependencies.keys())}") 25 | sys.exit(1) 26 | 27 | dependency_path = os.path.join(os.path.dirname(__file__), dependency_name) 28 | if os.path.exists(dependency_path): 29 | print(f"Dependency '{dependency_name}' already exists at {dependency_path}") 30 | else: 31 | print(f"Cloning '{dependency_name}' to {dependency_path}") 32 | subprocess.run(["git", "clone", dependency["repository"], dependency_path], check=True) 33 | 34 | print(f"Checking out '{dependency_name}' to {dependency['revision']}") 35 | subprocess.run(["git", "-C", dependency_path, "checkout", dependency["revision"]], check=True) 36 | 37 | def checkout_category(self, category): 38 | for dependency_name, dependency in self.dependencies.items(): 39 | if category in dependency.get("categories", []): 40 | self.checkout(dependency_name) 41 | 42 | def checkout_all(self): 43 | for dependency_name in self.dependencies.keys(): 44 | self.checkout(dependency_name) 45 | 46 | 47 | def main(): 48 | import argparse 49 | dependencies_file = os.path.join(os.path.dirname(__file__), "dependencies.json") 50 | checkout_dependency = CheckoutDependency(dependencies_file) 51 | 52 | parser = argparse.ArgumentParser(description="Checkout dependency repositories") 53 | available_dependencies = ", ".join(checkout_dependency.dependencies.keys()) 54 | parser.add_argument("names", nargs="*", help=f"Available dependencies: {available_dependencies}") 55 | parser.add_argument("--all", action="store_true", help="Checkout all dependencies") 56 | parser.add_argument("--category", action="append", dest="categories", 57 | default=["default"], 58 | help="Checkout dependencies by category") 59 | 60 | args = parser.parse_args() 61 | 62 | if args.names: 63 | for dependency_name in args.names: 64 | checkout_dependency.checkout(dependency_name) 65 | elif args.all: 66 | checkout_dependency.checkout_all() 67 | elif args.categories: 68 | for category in args.categories: 69 | checkout_dependency.checkout_category(category) 70 | 71 | 72 | if __name__ == "__main__": 73 | main() 74 | -------------------------------------------------------------------------------- /Vendor/dependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "testsuite": { 3 | "repository": "https://github.com/WebAssembly/testsuite.git", 4 | "revision": "53da17c0936a23f68f97cde4f9346a0a374dc35f", 5 | "categories": ["default"] 6 | }, 7 | "wasi-testsuite": { 8 | "repository": "https://github.com/WebAssembly/wasi-testsuite.git", 9 | "revision": "c9c751586fd86b321d595bbef13f2c7403cfdbc5", 10 | "categories": ["default"] 11 | }, 12 | "wasm-c-api": { 13 | "repository": "https://github.com/WebAssembly/wasm-c-api.git", 14 | "revision": "2ce1367c9d1271c83fb63bef26d896a2f290cd23", 15 | "categories": ["default"] 16 | }, 17 | "swift-format": { 18 | "repository": "https://github.com/swiftlang/swift-format.git", 19 | "revision": "c7a8b752e35d96577d1e9191d27cd9ea57dce2f2", 20 | "categories": ["default"] 21 | }, 22 | "wish-you-were-fast": { 23 | "repository": "https://github.com/composablesys/wish-you-were-fast.git", 24 | "revision": "65c968685d66eea3ae88ef747df95210690c6b46", 25 | "categories": ["benchmark"] 26 | }, 27 | "swift-stringify-macro.wasm": { 28 | "repository": "https://github.com/kateinoigakukun/swift-stringify-macro.wasm.git", 29 | "revision": "576ef7ff3b9713d75519eab0cb1f2b738220b636", 30 | "categories": ["benchmark"] 31 | }, 32 | "coremark": { 33 | "repository": "https://github.com/eembc/coremark.git", 34 | "revision": "d5fad6bd094899101a4e5fd53af7298160ced6ab", 35 | "categories": ["benchmark"] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cmake/modules/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(PROJECT_IS_TOP_LEVEL) 2 | export(EXPORT WasmKitTargets 3 | FILE ${CMAKE_CURRENT_BINARY_DIR}/WasmKitConfig.cmake 4 | NAMESPACE WasmKit::) 5 | endif() 6 | --------------------------------------------------------------------------------