diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile new file mode 100644 index 00000000..7d8ea7fa --- /dev/null +++ b/.clusterfuzzlite/Dockerfile @@ -0,0 +1,9 @@ +FROM gcr.io/oss-fuzz-base/base-builder-swift:v1 +RUN apt-get update && apt-get install -y make autoconf automake libtool +ENV SWIFT_PREFIX=/opt/swift +RUN mkdir -p "$SWIFT_PREFIX" && \ + 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 +ENV PATH="$SWIFT_PREFIX/usr/bin:$PATH" +COPY . $SRC/wasmkit +WORKDIR $SRC/wasmkit +COPY .clusterfuzzlite/build.sh $SRC/ diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh new file mode 100755 index 00000000..c0ee408b --- /dev/null +++ b/.clusterfuzzlite/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash -eu + +cd FuzzTesting +./fuzz.py --verbose build --sanitizer="$SANITIZER" FuzzTranslator + +find .build/debug/ -maxdepth 1 -type f -name "Fuzz*" -executable -exec cp {} "$OUT/" \; + diff --git a/.clusterfuzzlite/project.yaml b/.clusterfuzzlite/project.yaml new file mode 100644 index 00000000..310574ef --- /dev/null +++ b/.clusterfuzzlite/project.yaml @@ -0,0 +1 @@ +language: swift diff --git a/.github/workflows/cflite_batch.yml b/.github/workflows/cflite_batch.yml new file mode 100644 index 00000000..0051186c --- /dev/null +++ b/.github/workflows/cflite_batch.yml @@ -0,0 +1,47 @@ +name: ClusterFuzzLite batch fuzzing +on: + schedule: + - cron: '0 0/6 * * *' + workflow_dispatch: + inputs: + fuzz-seconds: + description: 'The total time allotted for fuzzing in seconds.' + required: true + default: 3600 + +permissions: read-all +jobs: + BatchFuzzing: + runs-on: ubuntu-latest + permissions: + security-events: write + strategy: + fail-fast: false + matrix: + sanitizer: + - address + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + language: swift + sanitizer: ${{ matrix.sanitizer }} + - name: Run Fuzzers (${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: ${{ github.event.inputs.fuzz-seconds || 3600 }} + mode: 'batch' + sanitizer: ${{ matrix.sanitizer }} + output-sarif: true + storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git + storage-repo-branch: main + storage-repo-branch-coverage: gh-pages + - name: Upload Sarif + if: always() && steps.build.outcome == 'success' + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: cifuzz-sarif/results.sarif + checkout_path: cifuzz-sarif diff --git a/.github/workflows/cflite_build.yml b/.github/workflows/cflite_build.yml new file mode 100644 index 00000000..feecbf71 --- /dev/null +++ b/.github/workflows/cflite_build.yml @@ -0,0 +1,25 @@ +name: ClusterFuzzLite continuous builds +on: + push: + branches: + - main +permissions: read-all +jobs: + Build: + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} + cancel-in-progress: true + strategy: + fail-fast: false + matrix: + sanitizer: + - address + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + language: swift + sanitizer: ${{ matrix.sanitizer }} + upload-build: true diff --git a/.github/workflows/cflite_cron.yml b/.github/workflows/cflite_cron.yml new file mode 100644 index 00000000..d74d5f89 --- /dev/null +++ b/.github/workflows/cflite_cron.yml @@ -0,0 +1,47 @@ +name: ClusterFuzzLite cron tasks +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: {} + +permissions: read-all +jobs: + Pruning: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + language: swift + - name: Run Fuzzers + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 600 + mode: 'prune' + output-sarif: true + storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git + storage-repo-branch: main + storage-repo-branch-coverage: gh-pages + Coverage: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + language: swift + sanitizer: coverage + - name: Run Fuzzers + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 600 + mode: 'coverage' + sanitizer: 'coverage' + storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git + storage-repo-branch: main + storage-repo-branch-coverage: gh-pages diff --git a/.github/workflows/cflite_pr.yml b/.github/workflows/cflite_pr.yml new file mode 100644 index 00000000..1af001b5 --- /dev/null +++ b/.github/workflows/cflite_pr.yml @@ -0,0 +1,41 @@ +name: ClusterFuzzLite PR fuzzing +on: + pull_request: + paths: + - '**' +permissions: read-all +jobs: + PR: + name: PR Fuzzing (${{ matrix.sanitizer }} sanitizer) + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} + cancel-in-progress: true + strategy: + fail-fast: false + matrix: + sanitizer: + - address + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + language: swift + github-token: ${{ secrets.GITHUB_TOKEN }} + sanitizer: ${{ matrix.sanitizer }} + storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git + storage-repo-branch: main + storage-repo-branch-coverage: gh-pages + - name: Run Fuzzers (${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 300 + mode: 'code-change' + sanitizer: ${{ matrix.sanitizer }} + output-sarif: true + storage-repo: https://${{ secrets.SWIFTWASM_BOT_GITHUB_TOKEN }}@github.com/swiftwasm/wasmkit-fuzz-corpora.git + storage-repo-branch: main + storage-repo-branch-coverage: gh-pages diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index a7114f3c..28c6ce29 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -5,6 +5,9 @@ on: jobs: format: runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true container: image: swift:6.0.1-jammy steps: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 02cd9422..ad42ca63 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,6 +5,10 @@ on: push: branches: [main] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: build-macos: strategy: @@ -13,15 +17,19 @@ jobs: # Swift 5.8.1 - os: macos-13 xcode: Xcode_14.3.1 - development-toolchain-tag: swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a - wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi.artifactbundle.zip" - wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi + development-toolchain-tag: swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a + wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi.artifactbundle.zip" + wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi + wasi-swift-sdk-checksum: "229cd9d3b0ed582c7ef7c3064888ad78764e4743b5a770df92554a94513f53fb" + test-args: "" # Swift 5.9.0 - os: macos-13 xcode: Xcode_15.0.1 - development-toolchain-tag: swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a - wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi.artifactbundle.zip" - wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi + development-toolchain-tag: swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a + wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi.artifactbundle.zip" + wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi + wasi-swift-sdk-checksum: "229cd9d3b0ed582c7ef7c3064888ad78764e4743b5a770df92554a94513f53fb" + test-args: "--sanitize address" runs-on: ${{ matrix.os }} name: "build-macos (${{ matrix.xcode }})" @@ -35,7 +43,7 @@ jobs: curl -L "$development_toolchain_download" -o $pkg sudo installer -pkg $pkg -target / echo "toolchain-path=$toolchain_path" >> $GITHUB_OUTPUT - "$toolchain_path/usr/bin/swift" sdk install "${{ matrix.wasi-swift-sdk-download }}" + "$toolchain_path/usr/bin/swift" sdk install "${{ matrix.wasi-swift-sdk-download }}" --checksum "${{ matrix.wasi-swift-sdk-checksum }}" wasi_sdk_path=$("$toolchain_path/usr/bin/swift" sdk configure --show-configuration "${{ matrix.wasi-swift-sdk-id }}" wasm32-unknown-wasi | grep sdkRootPath: | cut -d: -f2) echo "wasi-swift-sdk-path=$(dirname $wasi_sdk_path)" >> $GITHUB_OUTPUT @@ -52,7 +60,7 @@ jobs: } EOS - run: ./Vendor/checkout-dependency - - run: swift test --sanitize address + - run: swift test ${{ matrix.test-args }} build-xcode: runs-on: macos-15 @@ -68,31 +76,43 @@ jobs: strategy: matrix: include: - - swift: "5.8-focal" - development-toolchain-download: "https://download.swift.org/development/ubuntu2004/swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a/swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a-ubuntu20.04.tar.gz" - wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi.artifactbundle.zip" - wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi - - swift: "5.8-amazonlinux2" - development-toolchain-download: "https://download.swift.org/development/amazonlinux2/swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a/swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a-amazonlinux2.tar.gz" - wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi.artifactbundle.zip" - wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi - - swift: "5.9-focal" - development-toolchain-download: "https://download.swift.org/development/ubuntu2004/swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a/swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a-ubuntu20.04.tar.gz" - wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi.artifactbundle.zip" - wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi - - swift: "5.9-amazonlinux2" - development-toolchain-download: "https://download.swift.org/development/amazonlinux2/swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a/swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a-amazonlinux2.tar.gz" - wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi.artifactbundle.zip" - wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi - - runs-on: ubuntu-20.04 + - swift: "swift:5.8-focal" + development-toolchain-download: "https://download.swift.org/development/ubuntu2004/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a-ubuntu20.04.tar.gz" + wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi.artifactbundle.zip" + wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi + wasi-swift-sdk-checksum: "229cd9d3b0ed582c7ef7c3064888ad78764e4743b5a770df92554a94513f53fb" + - swift: "swift:5.9-focal" + development-toolchain-download: "https://download.swift.org/development/ubuntu2004/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a-ubuntu20.04.tar.gz" + wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi.artifactbundle.zip" + wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi + wasi-swift-sdk-checksum: "229cd9d3b0ed582c7ef7c3064888ad78764e4743b5a770df92554a94513f53fb" + - swift: "swift:5.9-amazonlinux2" + development-toolchain-download: "https://download.swift.org/development/amazonlinux2/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a-amazonlinux2.tar.gz" + wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi.artifactbundle.zip" + wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi + wasi-swift-sdk-checksum: "229cd9d3b0ed582c7ef7c3064888ad78764e4743b5a770df92554a94513f53fb" + - swift: "swift:6.0-focal" + development-toolchain-download: "https://download.swift.org/development/ubuntu2004/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a-ubuntu20.04.tar.gz" + wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi.artifactbundle.zip" + wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi + wasi-swift-sdk-checksum: "229cd9d3b0ed582c7ef7c3064888ad78764e4743b5a770df92554a94513f53fb" + test-args: "--enable-code-coverage" + build-dev-dashboard: true + - swift: "swiftlang/swift:nightly-main-jammy" + development-toolchain-download: "https://download.swift.org/development/ubuntu2204/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a/swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a-ubuntu22.04.tar.gz" + wasi-swift-sdk-download: "https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi.artifactbundle.zip" + wasi-swift-sdk-id: DEVELOPMENT-SNAPSHOT-2024-10-15-a-wasm32-unknown-wasi + wasi-swift-sdk-checksum: "229cd9d3b0ed582c7ef7c3064888ad78764e4743b5a770df92554a94513f53fb" + test-args: "-Xswiftc -DWASMKIT_CI_TOOLCHAIN_NIGHTLY" + + runs-on: ubuntu-22.04 name: "build-linux (${{ matrix.swift }})" steps: - uses: actions/checkout@v4 - name: Configure container run: | - docker run -dit --name build-container -v $PWD:/workspace -w /workspace swift:${{ matrix.swift }} + docker run -dit --name build-container -v $PWD:/workspace -w /workspace ${{ matrix.swift }} echo 'docker exec -i build-container "$@"' > ./build-exec chmod +x ./build-exec @@ -103,7 +123,7 @@ jobs: ./build-exec mkdir -p "$toolchain_path" curl -L ${{ matrix.development-toolchain-download }} | ./build-exec tar xz --strip-component 1 -C "$toolchain_path" echo "toolchain-path=$toolchain_path" >> $GITHUB_OUTPUT - ./build-exec "$toolchain_path/usr/bin/swift" sdk install "${{ matrix.wasi-swift-sdk-download }}" + ./build-exec "$toolchain_path/usr/bin/swift" sdk install "${{ matrix.wasi-swift-sdk-download }}" --checksum "${{ matrix.wasi-swift-sdk-checksum }}" wasi_sdk_path=$(./build-exec "$toolchain_path/usr/bin/swift" sdk configure --show-configuration "${{ matrix.wasi-swift-sdk-id }}" wasm32-unknown-wasi | grep sdkRootPath: | cut -d: -f2) echo "wasi-swift-sdk-path=$(dirname $wasi_sdk_path)" >> $GITHUB_OUTPUT @@ -118,7 +138,32 @@ jobs: EOS - run: ./build-exec ./CI/install-wabt.sh - run: ./Vendor/checkout-dependency - - run: ./build-exec swift test + - run: ./build-exec swift test ${{ matrix.test-args }} + + - if: matrix.build-dev-dashboard + run: ./build-exec ./CI/build-dev-dashboard.sh + - if: matrix.build-dev-dashboard + name: Relax artifact permissions + run: sudo chown -R $USER .build/html + - if: matrix.build-dev-dashboard + id: deployment + uses: actions/upload-pages-artifact@v3 + with: + path: .build/html + + deploy-dev-dashboard: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + needs: build-linux + permissions: + id-token: write + pages: write + steps: + - id: deployment + uses: actions/deploy-pages@v4 build-musl: runs-on: ubuntu-22.04 @@ -144,13 +189,20 @@ jobs: - name: Build (aarch64-swift-linux-musl) run: ./build-exec swift build --swift-sdk aarch64-swift-linux-musl + build-android: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Run Tests on Android emulator + uses: skiptools/swift-android-action@v2 + build-windows: runs-on: windows-latest steps: - uses: compnerd/gha-setup-swift@main with: - branch: swift-5.10.1-release - tag: 5.10.1-RELEASE + branch: swift-6.0.3-release + tag: 6.0.3-RELEASE - uses: actions/checkout@v4 - run: python3 ./Vendor/checkout-dependency # FIXME: CMake build is failing on CI due to "link: extra operand '/OUT:lib\\libXXXX.a'" error @@ -163,10 +215,21 @@ jobs: # - run: cmake -G Ninja -B .build/cmake # - run: cmake --build .build/cmake # Run tests with SwiftPM - - run: swift test + - name: Run tests with SwiftPM + run: | + # Workaround for https://github.com/compnerd/swift-build/issues/909 + $Win10SdkRoot = Get-ItemPropertyValue ` + -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots" ` + -Name "KitsRoot10" + $WinSDKVersion = "10.0.22621.0" + $ExtraFlags = @("-Xswiftc", "-windows-sdk-version", "-Xswiftc", "${WinSDKVersion}", + "-Xswiftc", "-windows-sdk-root", "-Xswiftc", "${Win10SdkRoot}", + "-Xbuild-tools-swiftc", "-windows-sdk-version", "-Xbuild-tools-swiftc", "${WinSDKVersion}", + "-Xbuild-tools-swiftc", "-windows-sdk-root", "-Xbuild-tools-swiftc", "${Win10SdkRoot}") + swift test @ExtraFlags build-cmake: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 container: image: swift:5.8-focal steps: diff --git a/.gitignore b/.gitignore index afd88e92..1ced1881 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ Tests/WITExtractorPluginTests/Fixtures/*/Package.resolved FuzzTesting/FailCases/FuzzExecute/timeout-* Package.resolved +Benchmarks/.benchmarkBaselines/ diff --git a/Benchmarks/Benchmarks/MacroPlugin/MacroPlugin.swift b/Benchmarks/Benchmarks/MacroPlugin/MacroPlugin.swift new file mode 100644 index 00000000..5373a975 --- /dev/null +++ b/Benchmarks/Benchmarks/MacroPlugin/MacroPlugin.swift @@ -0,0 +1,234 @@ +import Benchmark +import WasmKit +import SystemPackage +import Foundation +import WasmKitWASI + +let benchmarks = { + let macrosDir = FilePath(#filePath) + .removingLastComponent() + .removingLastComponent() + .removingLastComponent() + .removingLastComponent() + .pushing("Vendor/swift-stringify-macro.wasm/Sources") + + let handshakeMessage = """ + { + "getCapability":{ + "capability":{ + "protocolVersion":7 + } + } + } + """ + let expandMessages = [ + "StringifyMacros.wasm": """ + { + "expandFreestandingMacro":{ + "discriminator":"$s7Example0015mainswift_tzEGbfMX2_6_33_B384B672EB89465DCC67528E23350CF9Ll9stringifyfMf_", + "lexicalContext":[ + + ], + "macro":{ + "moduleName":"StringifyMacros", + "name":"stringify", + "typeName":"StringifyMacro" + }, + "macroRole":"expression", + "syntax":{ + "kind":"expression", + "location":{ + "column":7, + "fileID":"Example/main.swift", + "fileName":"", + "line":3, + "offset":24 + }, + "source":"#stringify(1 + 1)" + } + } + } + """, + "FoundationMacros.wasm": """ + { + "expandFreestandingMacro":{ + "discriminator":"$s7Example0015mainswift_tzEGbfMX2_6_33_B384B672EB89465DCC67528E23350CF9Ll9stringifyfMf_", + "lexicalContext":[ + + ], + "macro":{ + "moduleName":"FoundationMacros", + "name":"Expression", + "typeName":"ExpressionMacro" + }, + "macroRole":"expression", + "syntax":{ + "kind":"expression", + "location":{ + "column":7, + "fileID":"Example/main.swift", + "fileName":"", + "line":3, + "offset":24 + }, + "source":"#Expression { $0 + 1 }" + } + } + } + """, + "TestingMacros.wasm": """ + { + "expandFreestandingMacro":{ + "discriminator":"$s7Example0015mainswift_tzEGbfMX2_6_33_B384B672EB89465DCC67528E23350CF9Ll9stringifyfMf_", + "lexicalContext":[ + + ], + "macro":{ + "moduleName":"TestingMacros", + "name":"expect", + "typeName":"ExpectMacro" + }, + "macroRole":"expression", + "syntax":{ + "kind":"expression", + "location":{ + "column":7, + "fileID":"Example/main.swift", + "fileName":"", + "line":3, + "offset":24 + }, + "source":"#expect(1 == 2)" + } + } + } + """, + "MMIOMacros.wasm": """ + { + "expandAttachedMacro": { + "attributeSyntax": { + "kind": "attribute", + "location": { + "column": 12, + "fileID": "MMIOMacrosExample/main.swift", + "fileName": "swift-mmio/Sources/MMIOMacrosExample/main.swift", + "line": 1, + "offset": 11 + }, + "source": "\n@RegisterBlock " + }, + "declSyntax": { + "kind": "declaration", + "location": { + "column": 12, + "fileID": "MMIOMacrosExample/main.swift", + "fileName": "swift-mmio/Sources/MMIOMacrosExample/main.swift", + "line": 1, + "offset": 11 + }, + "source": "\n@RegisterBlock struct Example0 {}" + }, + "discriminator": "$s17MMIOMacrosExample8Example013RegisterBlockfMm_", + "lexicalContext": [], + "macro": { + "moduleName": "MMIOMacros", + "name": "RegisterBlock ", + "typeName": "RegisterBlockMacro" + }, + "macroRole": "member" + } + } + """ + ] + + for file in try! FileManager.default.contentsOfDirectory( + atPath: macrosDir.string + ) { + guard file.hasSuffix(".wasm") else { continue } + + struct Setup { + let hostToPlugin: FileDescriptor + let pluginToHost: FileDescriptor + let pump: Function + let expandMessage: String + + init(filePath: FilePath, expandMessage: String) throws { + let engine = Engine() + let store = Store(engine: engine) + let module = try parseWasm(filePath: filePath) + + let hostToPluginPipes = try FileDescriptor.pipe() + let pluginToHostPipes = try FileDescriptor.pipe() + let bridge = try WASIBridgeToHost( + stdin: hostToPluginPipes.readEnd, + stdout: pluginToHostPipes.writeEnd, + stderr: .standardError + ) + var imports = Imports() + bridge.link(to: &imports, store: store) + let instance = try module.instantiate(store: store, imports: imports) + try instance.exports[function: "_start"]!() + let pump = instance.exports[function: "swift_wasm_macro_v1_pump"]! + + self.hostToPlugin = hostToPluginPipes.writeEnd + self.pluginToHost = pluginToHostPipes.readEnd + self.pump = pump + self.expandMessage = expandMessage + } + + func writeMessage(_ message: String) throws { + let bytes = message.utf8 + try withUnsafeBytes(of: UInt64(bytes.count).littleEndian) { + _ = try hostToPlugin.writeAll($0) + } + try hostToPlugin.writeAll(bytes) + } + func readMessage() throws -> [UInt8] { + let lengthRaw = try withUnsafeTemporaryAllocation(of: UInt8.self, capacity: 8) { buffer in + let lengthCount = try pluginToHost.read(into: UnsafeMutableRawBufferPointer(buffer)) + guard lengthCount == 8 else { fatalError() } + return buffer.withMemoryRebound(to: UInt64.self, \.baseAddress!.pointee) + } + let length = Int(UInt64(littleEndian: lengthRaw)) + return try [UInt8](unsafeUninitializedCapacity: length) { buffer, size in + let received = try pluginToHost.read(into: UnsafeMutableRawBufferPointer(buffer)) + guard received == length else { + fatalError() + } + size = received + } + } + + func tick() throws { + try writeMessage(expandMessage) + try pump() + _ = try readMessage() + } + } + + guard let expandMessage = expandMessages[file] else { + fatalError("Expand message definition not found for \(file)") + } + + Benchmark("Startup \(file)") { benchmark in + let setup = try Setup(filePath: macrosDir.appending(file), expandMessage: expandMessage) + try setup.writeMessage(handshakeMessage) + try setup.tick() + } + + Benchmark("Expand \(file)") { benchmark, setup in + try setup.tick() + } setup: { () -> Setup in + let setup = try Setup( + filePath: macrosDir.appending(file), + expandMessage: expandMessage + ) + try setup.writeMessage(handshakeMessage) + + // Warmup + try setup.tick() + + return setup + } + } +} diff --git a/Benchmarks/Benchmarks/MicroBench/MicroBench.swift b/Benchmarks/Benchmarks/MicroBench/MicroBench.swift new file mode 100644 index 00000000..8c6b0e5f --- /dev/null +++ b/Benchmarks/Benchmarks/MicroBench/MicroBench.swift @@ -0,0 +1,18 @@ +import Benchmark +import WasmKit +import WAT + +let benchmarks = { + Benchmark("empty instantiation") { benchmark in + for _ in benchmark.scaledIterations { + let engine = Engine() + let store = Store(engine: engine) + let module = try parseWasm(bytes: wat2wasm(""" + (module + (func (export "_start")) + ) + """)) + _ = try module.instantiate(store: store) + } + } +} diff --git a/Benchmarks/Benchmarks/WishYouWereFast/WishYouWereFast.swift b/Benchmarks/Benchmarks/WishYouWereFast/WishYouWereFast.swift new file mode 100644 index 00000000..5e9eb9a5 --- /dev/null +++ b/Benchmarks/Benchmarks/WishYouWereFast/WishYouWereFast.swift @@ -0,0 +1,40 @@ +import Benchmark +import WasmKit +import WasmKitWASI +import Foundation +import SystemPackage + +let benchmarks = { + let wishYouWereFast = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .appendingPathComponent("Vendor") + .appendingPathComponent("wish-you-were-fast") + .appendingPathComponent("wasm") + .appendingPathComponent("suites") + .appendingPathComponent("libsodium") + + let devNull = try! FileDescriptor.open("/dev/null", .readWrite) + + for file in try! FileManager.default.contentsOfDirectory( + atPath: wishYouWereFast.path + ) { + guard file.hasSuffix(".wasm") else { continue } + Benchmark("\(file)", configuration: .init(thresholds: [ + .peakMemoryResident: .relaxed, + ])) { benchmark in + let engine = Engine() + let store = Store(engine: engine) + let module = try parseWasm( + filePath: FilePath(wishYouWereFast.appendingPathComponent(file).path) + ) + let wasi = try WASIBridgeToHost(stdout: devNull, stderr: devNull) + var imports = Imports() + wasi.link(to: &imports, store: store) + let instance = try module.instantiate(store: store, imports: imports) + _ = try wasi.start(instance) + } + } +} diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift new file mode 100644 index 00000000..d0d6dc18 --- /dev/null +++ b/Benchmarks/Package.swift @@ -0,0 +1,61 @@ +// swift-tools-version: 5.8 + +import PackageDescription + +let package = Package( + name: "Benchmarks", + platforms: [.macOS(.v13)], + dependencies: [ + .package(name: "WasmKit", path: "../"), + .package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.4.0")), + ] +) + +// Benchmark of WishYouWereFast +package.targets += [ + .executableTarget( + name: "WishYouWereFast", + dependencies: [ + .product(name: "WasmKit", package: "WasmKit"), + .product(name: "WasmKitWASI", package: "WasmKit"), + .product(name: "Benchmark", package: "package-benchmark"), + ], + path: "Benchmarks/WishYouWereFast", + plugins: [ + .plugin(name: "BenchmarkPlugin", package: "package-benchmark") + ] + ), +] + +// Benchmark of MicroBench +package.targets += [ + .executableTarget( + name: "MicroBench", + dependencies: [ + .product(name: "WAT", package: "WasmKit"), + .product(name: "WasmKit", package: "WasmKit"), + .product(name: "WasmKitWASI", package: "WasmKit"), + .product(name: "Benchmark", package: "package-benchmark"), + ], + path: "Benchmarks/MicroBench", + plugins: [ + .plugin(name: "BenchmarkPlugin", package: "package-benchmark") + ] + ), +] + +// Benchmark of MacroPlugin +package.targets += [ + .executableTarget( + name: "MacroPlugin", + dependencies: [ + .product(name: "WasmKit", package: "WasmKit"), + .product(name: "WasmKitWASI", package: "WasmKit"), + .product(name: "Benchmark", package: "package-benchmark"), + ], + path: "Benchmarks/MacroPlugin", + plugins: [ + .plugin(name: "BenchmarkPlugin", package: "package-benchmark") + ] + ), +] diff --git a/Benchmarks/bench.py b/Benchmarks/bench.py index 8bbb120a..c831d59f 100755 --- a/Benchmarks/bench.py +++ b/Benchmarks/bench.py @@ -71,6 +71,8 @@ def __init__(self): def add_dir(subpath): path = os.path.join(suites_path, subpath) + if not os.path.exists(path): + return for filename in os.listdir(path): if filename.endswith(".wasm"): targets.append(os.path.join(path, filename)) diff --git a/CI/build-dev-dashboard.sh b/CI/build-dev-dashboard.sh new file mode 100755 index 00000000..a38456ac --- /dev/null +++ b/CI/build-dev-dashboard.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -euo pipefail + +show_test_executable_path() { + local base_path=".build/debug/WasmKitPackageTests.xctest" + if [ -f "$base_path" ]; then + echo "$base_path" + else + echo "$base_path/Contents/MacOS/WasmKitPackageTests" + fi +} + +build_coverage_html() { + llvm-cov show --format=html "$(show_test_executable_path)" --instr-profile .build/debug/codecov/default.profdata -o "$1" --sources $(find Sources -type f) +} + +mkdir -p ./.build/html +build_coverage_html ./.build/html/coverage + +echo "Coverage report has been generated in ./.build/html/coverage" diff --git a/FuzzTesting/FailCases/FuzzTranslator/crash-2e61aac9dea72d2efe93489c1276ec1b69fb2992 b/FuzzTesting/FailCases/FuzzTranslator/crash-2e61aac9dea72d2efe93489c1276ec1b69fb2992 new file mode 100644 index 00000000..0230e496 Binary files /dev/null and b/FuzzTesting/FailCases/FuzzTranslator/crash-2e61aac9dea72d2efe93489c1276ec1b69fb2992 differ diff --git a/FuzzTesting/Package.swift b/FuzzTesting/Package.swift index e7bd9bb7..66390fc5 100644 --- a/FuzzTesting/Package.swift +++ b/FuzzTesting/Package.swift @@ -1,12 +1,29 @@ -// swift-tools-version: 5.10 +// swift-tools-version: 5.8 import PackageDescription let package = Package( name: "FuzzTesting", products: [ + // Discussion: Why we build libraries instead of executables linking libFuzzer? + // + // First, libclang_rt.fuzzer.a defines the main function for the fuzzing process + // and object files given by the user are expected not to have a "main" function + // to avoid conflicts. + // Fortunately, SwiftPM asks the compiler frontend to define the main entrypoint as + // `_main` for testing executable targets (`-entry-point-function-name`) + // so object files of `executableTarget` targets are capable of being linked with + // libclang_rt.fuzzer.a. + // However, at link-time, SwiftPM asks the linker to rename the `_main` + // symbol back to `main` for the final executable (`--defsym main=_main`) + // and gold linker respects the renamed "main" symbol rather than the one defined in + // libclang_rt.fuzzer.a, so the final executable does not start the fuzzing process. + // + // Instead of relying on the SwiftPM's linking process, we build libraries defining + // fuzzing target functions and manually link them with fuzzing runtime libraries. .library(name: "FuzzTranslator", type: .static, targets: ["FuzzTranslator"]), .library(name: "FuzzExecute", type: .static, targets: ["FuzzExecute"]), + // FuzzDifferential is not a libFuzzer-based target, so we build it as an executable. .executable(name: "FuzzDifferential", targets: ["FuzzDifferential"]), ], dependencies: [ @@ -14,9 +31,11 @@ let package = Package( ], targets: [ .target(name: "FuzzTranslator", dependencies: [ + "WasmKitFuzzing", .product(name: "WasmKit", package: "WasmKit") ]), .target(name: "FuzzExecute", dependencies: [ + "WasmKitFuzzing", .product(name: "WasmKit", package: "WasmKit"), ]), .executableTarget(name: "FuzzDifferential", dependencies: [ @@ -25,12 +44,8 @@ let package = Package( "WasmCAPI", ]), .target(name: "WasmCAPI"), + .target(name: "WasmKitFuzzing", dependencies: [ + .product(name: "WasmKit", package: "WasmKit"), + ]) ] ) - -let libFuzzerTargets = ["FuzzTranslator", "FuzzExecute"] - -for target in package.targets { - guard libFuzzerTargets.contains(target.name) else { continue } - target.swiftSettings = [.unsafeFlags(["-Xfrontend", "-sanitize=fuzzer,address"])] -} diff --git a/FuzzTesting/Sources/FuzzExecute/FuzzExecute.swift b/FuzzTesting/Sources/FuzzExecute/FuzzExecute.swift index f739a61c..3e19556a 100644 --- a/FuzzTesting/Sources/FuzzExecute/FuzzExecute.swift +++ b/FuzzTesting/Sources/FuzzExecute/FuzzExecute.swift @@ -1,13 +1,5 @@ @_spi(Fuzzing) import WasmKit - -struct FuzzerResourceLimiter: ResourceLimiter { - func limitMemoryGrowth(to desired: Int) throws -> Bool { - return desired < 1024 * 1024 * 1024 - } - func limitTableGrowth(to desired: Int) throws -> Bool { - return desired < 1024 * 1024 - } -} +import WasmKitFuzzing @_cdecl("LLVMFuzzerTestOneInput") public func FuzzCheck(_ start: UnsafePointer, _ count: Int) -> CInt { diff --git a/FuzzTesting/Sources/FuzzTranslator/FuzzTranslator.swift b/FuzzTesting/Sources/FuzzTranslator/FuzzTranslator.swift index 80ffb15b..0423eee6 100644 --- a/FuzzTesting/Sources/FuzzTranslator/FuzzTranslator.swift +++ b/FuzzTesting/Sources/FuzzTranslator/FuzzTranslator.swift @@ -1,11 +1,11 @@ import WasmKit +import WasmKitFuzzing @_cdecl("LLVMFuzzerTestOneInput") public func FuzzCheck(_ start: UnsafePointer, _ count: Int) -> CInt { let bytes = Array(UnsafeBufferPointer(start: start, count: count)) do { - var module = try WasmKit.parseWasm(bytes: bytes) - try module.materializeAll() + try fuzzInstantiation(bytes: bytes) } catch { // Ignore errors } diff --git a/FuzzTesting/Sources/WasmKitFuzzing/WasmKitFuzzing.swift b/FuzzTesting/Sources/WasmKitFuzzing/WasmKitFuzzing.swift new file mode 100644 index 00000000..899e1758 --- /dev/null +++ b/FuzzTesting/Sources/WasmKitFuzzing/WasmKitFuzzing.swift @@ -0,0 +1,54 @@ +// This module defines utilities for fuzzing WasmKit. + +@_spi(Fuzzing) import WasmKit + +/// A resource limiter that restricts allocations to fuzzer limits. +public struct FuzzerResourceLimiter: ResourceLimiter { + public init() {} + + public func limitMemoryGrowth(to desired: Int) throws -> Bool { + return desired < 1024 * 1024 * 1024 + } + public func limitTableGrowth(to desired: Int) throws -> Bool { + return desired < 1024 * 1024 + } +} + +/// Check if a Wasm module can be instantiated without crashing. +/// +/// - Parameter bytes: The bytes of the Wasm module. +public func fuzzInstantiation(bytes: [UInt8]) throws { + let module = try WasmKit.parseWasm(bytes: bytes) + let engine = Engine(configuration: EngineConfiguration(compilationMode: .eager)) + let store = Store(engine: engine) + store.resourceLimiter = FuzzerResourceLimiter() + + // Prepare dummy imports + var imports = Imports() + for importEntry in module.imports { + let value: ExternalValueConvertible + switch importEntry.descriptor { + case .function(let typeIndex): + guard typeIndex < module.types.count else { + // Skip if import type index is out of bounds + return + } + let type = module.types[Int(typeIndex)] + value = Function(store: store, type: type) { _, _ in + // Provide "start function" with empty results + if type.results.isEmpty { return [] } + fatalError("Unexpected function call") + } + case .global(let globalType): + value = try Global(store: store, type: globalType, value: .i32(0)) + case .memory(let memoryType): + value = try Memory(store: store, type: memoryType) + case .table(let tableType): + value = try Table(store: store, type: tableType) + } + imports.define(module: importEntry.module, name: importEntry.name, value.externalValue) + } + + // Instantiate the module + _ = try module.instantiate(store: store, imports: imports) +} diff --git a/FuzzTesting/fuzz.py b/FuzzTesting/fuzz.py index fd790230..11e16388 100755 --- a/FuzzTesting/fuzz.py +++ b/FuzzTesting/fuzz.py @@ -46,6 +46,8 @@ def main(): build_parser = subparsers.add_parser('build', help='Build the fuzzer') build_parser.add_argument( 'target_name', type=str, help='Name of the target', choices=available_targets) + build_parser.add_argument( + '--sanitizer', type=str, default='address') build_parser.set_defaults(func=build) run_parser = subparsers.add_parser('run', help='Run the fuzzer') @@ -96,16 +98,35 @@ def executable_path(target_name: str) -> str: def build(args, runner: CommandRunner): print(f'Building fuzzer for {args.target_name}') - runner.run([ - 'swift', 'build', '--product', args.target_name - ], check=True) + driver_flags = [] + if args.sanitizer == 'coverage': + driver_flags += [ + '-profile-generate', '-profile-coverage-mapping', + '-sanitize=fuzzer' + ] + else: + driver_flags += [f'-sanitize=fuzzer,{args.sanitizer}'] + + build_args = [ + 'swift', 'build', '--product', args.target_name, + ] + for driver_flag in driver_flags: + build_args += ['-Xswiftc', driver_flag] + + runner.run(build_args, check=True) print('Building fuzzer executable') + # See "Discussion" in Package.swift for why we need to manually link + # the library product. output = executable_path(args.target_name) - runner.run([ + link_args = [ 'swiftc', f'./.build/debug/lib{args.target_name}.a', '-g', - '-sanitize=fuzzer,address', '-o', output - ], check=True) + # Link Swift runtime statically to allow copying fuzzers to other + # machines (oss-fuzz does this) + '-static-stdlib', '-o', output + ] + link_args += driver_flags + runner.run(link_args, check=True) print('Fuzzer built successfully: ', output) diff --git a/Package.swift b/Package.swift index 983a4150..a188d6dc 100644 --- a/Package.swift +++ b/Package.swift @@ -28,6 +28,7 @@ let package = Package( .executableTarget( name: "CLI", dependencies: [ + "WAT", "WasmKit", "WasmKitWASI", .product(name: "ArgumentParser", package: "swift-argument-parser"), @@ -48,13 +49,22 @@ let package = Package( exclude: ["CMakeLists.txt"] ), .target(name: "_CWasmKit"), + .target( + name: "WasmKitFuzzing", + dependencies: ["WasmKit"], + path: "FuzzTesting/Sources/WasmKitFuzzing" + ), .testTarget( name: "WasmKitTests", - dependencies: ["WasmKit", "WAT"], + dependencies: ["WasmKit", "WAT", "WasmKitFuzzing"], exclude: ["ExtraSuite"] ), - .target(name: "WAT", dependencies: ["WasmParser"]), + .target( + name: "WAT", + dependencies: ["WasmParser"], + exclude: ["CMakeLists.txt"] + ), .testTarget(name: "WATTests", dependencies: ["WAT"]), .target( @@ -117,7 +127,7 @@ let package = Package( if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { package.dependencies += [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2"), - .package(url: "https://github.com/apple/swift-system", .upToNextMinor(from: "1.3.0")), + .package(url: "https://github.com/apple/swift-system", from: "1.3.0"), ] } else { package.dependencies += [ diff --git a/Package@swift-6.1.swift b/Package@swift-6.1.swift deleted file mode 100644 index 161f4fc0..00000000 --- a/Package@swift-6.1.swift +++ /dev/null @@ -1,161 +0,0 @@ -// swift-tools-version:999.0.0 - -@_spi(ExperimentalTraits) import PackageDescription - -import class Foundation.ProcessInfo - -let DarwinPlatforms: [Platform] -#if swift(<5.9) - DarwinPlatforms = [.macOS, .iOS, .watchOS, .tvOS] -#else - DarwinPlatforms = [.macOS, .iOS, .watchOS, .tvOS, .visionOS] -#endif - -let package = Package( - name: "WasmKit", - platforms: [.macOS(.v10_13), .iOS(.v12)], - products: [ - .executable(name: "wasmkit-cli", targets: ["CLI"]), - .library(name: "WasmKit", targets: ["WasmKit"]), - .library(name: "WasmKitWASI", targets: ["WasmKitWASI"]), - .library(name: "WASI", targets: ["WASI"]), - .library(name: "WasmParser", targets: ["WasmParser"]), - .library(name: "WAT", targets: ["WAT"]), - .library(name: "WIT", targets: ["WIT"]), - .library(name: "_CabiShims", targets: ["_CabiShims"]), - ], - traits: [ - Trait(name: "EngineStats", description: "Provides WasmKit engine execution statistics"), - ], - targets: [ - .executableTarget( - name: "CLI", - dependencies: [ - "WasmKit", - "WasmKitWASI", - .product(name: "ArgumentParser", package: "swift-argument-parser"), - .product(name: "SystemPackage", package: "swift-system"), - ], - exclude: ["CMakeLists.txt"] - ), - - .target( - name: "WasmKit", - dependencies: [ - "_CWasmKit", - "WasmParser", - "WasmTypes", - "SystemExtras", - .product(name: "SystemPackage", package: "swift-system"), - ], - exclude: ["CMakeLists.txt"] - ), - .target(name: "_CWasmKit"), - .testTarget( - name: "WasmKitTests", - dependencies: ["WasmKit", "WAT"], - exclude: ["ExtraSuite"] - ), - - .target(name: "WAT", dependencies: ["WasmParser"]), - .testTarget(name: "WATTests", dependencies: ["WAT"]), - - .target( - name: "WasmParser", - dependencies: [ - "WasmTypes", - .product(name: "SystemPackage", package: "swift-system"), - ], - exclude: ["CMakeLists.txt"] - ), - .testTarget(name: "WasmParserTests", dependencies: ["WasmParser"]), - - .target(name: "WasmTypes", exclude: ["CMakeLists.txt"]), - - .target( - name: "WasmKitWASI", - dependencies: ["WasmKit", "WASI"], - exclude: ["CMakeLists.txt"] - ), - .target( - name: "WASI", - dependencies: ["WasmTypes", "SystemExtras"], - exclude: ["CMakeLists.txt"] - ), - .testTarget(name: "WASITests", dependencies: ["WASI", "WasmKitWASI"]), - - .target( - name: "SystemExtras", - dependencies: [ - .product(name: "SystemPackage", package: "swift-system") - ], - exclude: ["CMakeLists.txt"], - swiftSettings: [ - .define("SYSTEM_PACKAGE_DARWIN", .when(platforms: DarwinPlatforms)) - ] - ), - - .executableTarget( - name: "WITTool", - dependencies: [ - "WIT", - "WITOverlayGenerator", - "WITExtractor", - .product(name: "ArgumentParser", package: "swift-argument-parser"), - ] - ), - - .target(name: "WIT"), - .testTarget(name: "WITTests", dependencies: ["WIT"]), - - .target(name: "WITOverlayGenerator", dependencies: ["WIT"]), - .target(name: "_CabiShims"), - - .target(name: "WITExtractor"), - .testTarget(name: "WITExtractorTests", dependencies: ["WITExtractor", "WIT"]), - ], - swiftLanguageModes: [.v5] -) - -if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { - package.dependencies += [ - .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2"), - .package(url: "https://github.com/apple/swift-system", .upToNextMinor(from: "1.3.0")), - ] -} else { - package.dependencies += [ - .package(path: "../swift-argument-parser"), - .package(path: "../swift-system"), - ] -} - -#if !os(Windows) - // Add build tool plugins only for non-Windows platforms - package.products.append(contentsOf: [ - .plugin(name: "WITOverlayPlugin", targets: ["WITOverlayPlugin"]), - .plugin(name: "WITExtractorPlugin", targets: ["WITExtractorPlugin"]), - ]) - - package.targets.append(contentsOf: [ - .plugin(name: "WITOverlayPlugin", capability: .buildTool(), dependencies: ["WITTool"]), - .plugin(name: "GenerateOverlayForTesting", capability: .buildTool(), dependencies: ["WITTool"]), - .testTarget( - name: "WITOverlayGeneratorTests", - dependencies: ["WITOverlayGenerator", "WasmKit", "WasmKitWASI"], - exclude: ["Fixtures", "Compiled", "Generated", "EmbeddedSupport"], - plugins: [.plugin(name: "GenerateOverlayForTesting")] - ), - .plugin( - name: "WITExtractorPlugin", - capability: .command( - intent: .custom(verb: "extract-wit", description: "Extract WIT definition from Swift module"), - permissions: [] - ), - dependencies: ["WITTool"] - ), - .testTarget( - name: "WITExtractorPluginTests", - exclude: ["Fixtures"] - ), - ]) -#endif diff --git a/Plugins/WITExtractorPlugin/Plugin.swift b/Plugins/WITExtractorPlugin/Plugin.swift index 0a394463..fbd56df3 100644 --- a/Plugins/WITExtractorPlugin/Plugin.swift +++ b/Plugins/WITExtractorPlugin/Plugin.swift @@ -48,7 +48,10 @@ struct Plugin: CommandPlugin { "--package-name", context.package.displayName, "--wit-output-path", witOutputPath.string, "--swift-output-path", swiftOutputPath.string, - "-I", buildPath.string + "-I", buildPath.string, + // SwiftPM 6.0 and later emits swiftmodule files into a separate directory + // https://github.com/swiftlang/swift-package-manager/pull/7212 + "-I", buildPath.appending(["Modules"]).string, ] if let sdk { arguments += ["-sdk", sdk] @@ -74,12 +77,28 @@ struct Plugin: CommandPlugin { return nil } for line in contents.split(separator: "\n") { - let prefix = " executable: \"" - if line.hasPrefix(prefix), line.hasSuffix("/swiftc\"") { - let pathStart = line.index(line.startIndex, offsetBy: prefix.count) - let pathEnd = line.index(before: line.endIndex) - let executablePath = line[pathStart.. Module { + if filePath.extension == "wat", #available(macOS 11.0, iOS 14.0, macCatalyst 14.0, tvOS 14.0, visionOS 1.0, watchOS 7.0, *) { + let fileHandle = try FileDescriptor.open(filePath, .readOnly) + defer { try? fileHandle.close() } + + let size = try fileHandle.seek(offset: 0, from: .end) + + let wat = try String(unsafeUninitializedCapacity: Int(size)) { + try fileHandle.read(fromAbsoluteOffset: 0, into: .init($0)) + } + return try WasmKit.parseWasm(bytes: wat2wasm(wat)) + } else { + return try WasmKit.parseWasm(filePath: filePath) + } + } + /// Derives the runtime interceptor based on the command line arguments func deriveInterceptor() throws -> (interceptor: EngineInterceptor?, finalize: () -> Void) { var interceptors: [EngineInterceptor] = [] diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index 98073f35..1f773267 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(SystemExtras) add_subdirectory(WASI) add_subdirectory(WasmTypes) add_subdirectory(WasmParser) +add_subdirectory(WAT) if(WASMKIT_BUILD_CLI) add_subdirectory(CLI) diff --git a/Sources/SystemExtras/Clock.swift b/Sources/SystemExtras/Clock.swift index 64cefc5e..b0282d20 100644 --- a/Sources/SystemExtras/Clock.swift +++ b/Sources/SystemExtras/Clock.swift @@ -6,6 +6,9 @@ import Glibc #elseif canImport(Musl) import CSystem import Musl +#elseif canImport(Android) +import CSystem +import Android #elseif os(Windows) import CSystem import ucrt @@ -28,7 +31,7 @@ public struct Clock: RawRepresentable { } extension Clock { - #if os(Linux) + #if os(Linux) || os(Android) @_alwaysEmitIntoClient public static var boottime: Clock { Clock(rawValue: CLOCK_BOOTTIME) } #endif @@ -38,7 +41,7 @@ extension Clock { public static var rawMonotonic: Clock { Clock(rawValue: _CLOCK_MONOTONIC_RAW) } #endif - #if SYSTEM_PACKAGE_DARWIN || os(Linux) || os(OpenBSD) || os(FreeBSD) || os(WASI) + #if SYSTEM_PACKAGE_DARWIN || os(Linux) || os(Android) || os(OpenBSD) || os(FreeBSD) || os(WASI) @_alwaysEmitIntoClient public static var monotonic: Clock { Clock(rawValue: _CLOCK_MONOTONIC) } #endif diff --git a/Sources/SystemExtras/Constants.swift b/Sources/SystemExtras/Constants.swift index 1a32cc06..1c48c0ed 100644 --- a/Sources/SystemExtras/Constants.swift +++ b/Sources/SystemExtras/Constants.swift @@ -6,6 +6,9 @@ import Glibc #elseif canImport(Musl) import CSystem import Musl +#elseif canImport(Android) +import CSystem +import Android #elseif os(Windows) import CSystem import ucrt @@ -34,7 +37,7 @@ internal var _AT_FDONLY: CInt { AT_FDONLY } internal var _AT_SYMLINK_NOFOLLOW_ANY: CInt { AT_SYMLINK_NOFOLLOW_ANY } #endif /* FIXME: Disabled until CSystem will include "linux/fcntl.h" -#if os(Linux) +#if os(Linux) || os(Android) @_alwaysEmitIntoClient internal var _AT_NO_AUTOMOUNT: CInt { AT_NO_AUTOMOUNT } #endif @@ -45,9 +48,14 @@ internal var _AT_NO_AUTOMOUNT: CInt { AT_NO_AUTOMOUNT } internal var _F_GETFL: CInt { F_GETFL } @_alwaysEmitIntoClient internal var _O_DSYNC: CInt { O_DSYNC } +#if os(Android) +@_alwaysEmitIntoClient +internal var _O_SYNC: CInt { __O_SYNC | O_DSYNC } +#else @_alwaysEmitIntoClient internal var _O_SYNC: CInt { O_SYNC } #endif +#endif #if os(Linux) @_alwaysEmitIntoClient internal var _O_RSYNC: CInt { O_RSYNC } @@ -56,7 +64,7 @@ internal var _O_RSYNC: CInt { O_RSYNC } #if !os(Windows) @_alwaysEmitIntoClient internal var _UTIME_NOW: CInt { - #if os(Linux) + #if os(Linux) || os(Android) // Hard-code constants because it's defined in glibc in a form that // ClangImporter cannot interpret as constants. // https://github.com/torvalds/linux/blob/92901222f83d988617aee37680cb29e1a743b5e4/include/linux/stat.h#L15 @@ -67,7 +75,7 @@ internal var _UTIME_NOW: CInt { } @_alwaysEmitIntoClient internal var _UTIME_OMIT: CInt { - #if os(Linux) + #if os(Linux) || os(Android) // Hard-code constants because it's defined in glibc in a form that // ClangImporter cannot interpret as constants. // https://github.com/torvalds/linux/blob/92901222f83d988617aee37680cb29e1a743b5e4/include/linux/stat.h#L16 @@ -116,7 +124,7 @@ internal var _S_IFLNK: CInterop.Mode { S_IFLNK } internal var _S_IFSOCK: CInterop.Mode { S_IFSOCK } #endif -#if os(Linux) +#if os(Linux) || os(Android) @_alwaysEmitIntoClient internal var _CLOCK_BOOTTIME: CInterop.ClockId { CLOCK_BOOTTIME } #endif @@ -124,7 +132,7 @@ internal var _CLOCK_BOOTTIME: CInterop.ClockId { CLOCK_BOOTTIME } @_alwaysEmitIntoClient internal var _CLOCK_MONOTONIC_RAW: CInterop.ClockId { CLOCK_MONOTONIC_RAW } #endif -#if SYSTEM_PACKAGE_DARWIN || os(Linux) || os(OpenBSD) || os(FreeBSD) || os(WASI) +#if SYSTEM_PACKAGE_DARWIN || os(Linux) || os(Android) || os(OpenBSD) || os(FreeBSD) || os(WASI) @_alwaysEmitIntoClient internal var _CLOCK_MONOTONIC: CInterop.ClockId { CLOCK_MONOTONIC } #endif diff --git a/Sources/SystemExtras/FileAtOperations.swift b/Sources/SystemExtras/FileAtOperations.swift index dc2ff8a2..6d54c42b 100644 --- a/Sources/SystemExtras/FileAtOperations.swift +++ b/Sources/SystemExtras/FileAtOperations.swift @@ -6,6 +6,9 @@ import Glibc #elseif canImport(Musl) import CSystem import Musl +#elseif canImport(Android) +import CSystem +import Android #elseif os(Windows) import ucrt import WinSDK @@ -44,7 +47,7 @@ extension FileDescriptor { #endif /* FIXME: Disabled until CSystem will include "linux/fcntl.h" - #if os(Linux) + #if os(Linux) || os(Android) /// Indicates the operation does't mount the basename component automatically /// /// If you specify this option and the file you pass to diff --git a/Sources/SystemExtras/FileOperations.swift b/Sources/SystemExtras/FileOperations.swift index a3b9dada..fc61b1d8 100644 --- a/Sources/SystemExtras/FileOperations.swift +++ b/Sources/SystemExtras/FileOperations.swift @@ -6,6 +6,9 @@ import Glibc #elseif canImport(Musl) import CSystem import Musl +#elseif canImport(Android) +import CSystem +import Android #elseif os(Windows) import ucrt import WinSDK @@ -50,7 +53,7 @@ extension FileDescriptor { @_alwaysEmitIntoClient public init(rawValue: CInt) { self.rawValue = rawValue } - #if os(Linux) + #if os(Linux) || os(Android) /// Access the specified data in the near future. /// /// The corresponding C constant is `POSIX_FADV_WILLNEED`. @@ -59,7 +62,7 @@ extension FileDescriptor { #endif } - #if os(Linux) + #if os(Linux) || os(Android) /// Announces an intention to access specific region of file data. /// /// - Parameters: @@ -576,6 +579,48 @@ extension FileDescriptor { return .success(DirectoryStream(rawValue: dirp)) #endif } + + public func sync() throws { + return try _sync().get() + } + + internal func _sync() -> Result { + #if os(Windows) + let handle = HANDLE(bitPattern: _get_osfhandle(self.rawValue)) + return nothingOrErrno(retryOnInterrupt: false) { + let ok = FlushFileBuffers(handle) + return ok ? 0 : -1 + } + #else + nothingOrErrno(retryOnInterrupt: false) { + #if SYSTEM_PACKAGE_DARWIN + system_fcntl(self.rawValue, F_FULLFSYNC) + #else + system_fsync(self.rawValue) + #endif + } + #endif + } + + public func datasync() throws { + return try _datasync().get() + } + + internal func _datasync() -> Result { + #if os(Windows) + return self._sync() + #else + nothingOrErrno(retryOnInterrupt: false) { + #if SYSTEM_PACKAGE_DARWIN + system_fcntl(self.rawValue, F_FULLFSYNC) + #elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(Cygwin) || os(PS4) + system_fdatasync(self.rawValue) + #else + system_fsync(self.rawValue) + #endif + } + #endif + } } #if os(Windows) diff --git a/Sources/SystemExtras/Syscalls.swift b/Sources/SystemExtras/Syscalls.swift index ddc37839..0f681824 100644 --- a/Sources/SystemExtras/Syscalls.swift +++ b/Sources/SystemExtras/Syscalls.swift @@ -6,6 +6,9 @@ import Glibc #elseif canImport(Musl) import CSystem import Musl +#elseif canImport(Android) +import CSystem +import Android #elseif os(Windows) import ucrt import WinSDK @@ -47,14 +50,26 @@ internal func system_fcntl(_ fd: Int32, _ cmd: Int32) -> CInt { return fcntl(fd, cmd) } +// fsync +internal func system_fsync(_ fd: Int32) -> CInt { + return fsync(fd) +} + +#endif + +#if os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(Cygwin) || os(PS4) +// fdatasync +internal func system_fdatasync(_ fd: Int32) -> CInt { + return fdatasync(fd) +} #endif -#if os(Linux) +#if os(Linux) || os(Android) // posix_fadvise internal func system_posix_fadvise( _ fd: Int32, _ offset: Int, _ length: Int, _ advice: CInt ) -> CInt { - return posix_fadvise(fd, offset, length, advice) + return posix_fadvise(fd, .init(offset), .init(length), advice) } #endif @@ -104,7 +119,7 @@ internal func system_symlinkat( extension CInterop { #if SYSTEM_PACKAGE_DARWIN public typealias DirP = UnsafeMutablePointer - #elseif os(Linux) + #elseif os(Linux) || os(Android) public typealias DirP = OpaquePointer #else #error("Unsupported Platform") diff --git a/Sources/SystemExtras/Vendor/Exports.swift b/Sources/SystemExtras/Vendor/Exports.swift index 1714316e..c130de74 100644 --- a/Sources/SystemExtras/Vendor/Exports.swift +++ b/Sources/SystemExtras/Vendor/Exports.swift @@ -23,11 +23,11 @@ import Glibc #elseif canImport(Musl) import CSystem import Musl -#elseif canImport(WASILibc) -import WASILibc #elseif canImport(Android) import CSystem import Android +#elseif canImport(WASILibc) +import WASILibc #else #error("Unsupported Platform") #endif diff --git a/Sources/WASI/Clock.swift b/Sources/WASI/Clock.swift index 36379a18..c84aee5c 100644 --- a/Sources/WASI/Clock.swift +++ b/Sources/WASI/Clock.swift @@ -92,7 +92,7 @@ public protocol MonotonicClock { /// A monotonic clock that uses the system's monotonic clock. public struct SystemMonotonicClock: MonotonicClock { private var underlying: SystemExtras.Clock { - #if os(Linux) + #if os(Linux) || os(Android) return .monotonic #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) return .rawUptime @@ -125,7 +125,7 @@ public protocol MonotonicClock { /// A wall clock that uses the system's wall clock. public struct SystemWallClock: WallClock { private var underlying: SystemExtras.Clock { - #if os(Linux) + #if os(Linux) || os(Android) return .boottime #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) return .rawMonotonic diff --git a/Sources/WASI/FileSystem.swift b/Sources/WASI/FileSystem.swift index 95ed2c47..c3741c2b 100644 --- a/Sources/WASI/FileSystem.swift +++ b/Sources/WASI/FileSystem.swift @@ -24,6 +24,8 @@ protocol WASIFile: WASIEntry { func fdStat() throws -> WASIAbi.FdStat func setFdStatFlags(_ flags: WASIAbi.Fdflags) throws func setFilestatSize(_ size: WASIAbi.FileSize) throws + func sync() throws + func datasync() throws func tell() throws -> WASIAbi.FileSize func seek(offset: WASIAbi.FileDelta, whence: WASIAbi.Whence) throws -> WASIAbi.FileSize diff --git a/Sources/WASI/Platform/Entry.swift b/Sources/WASI/Platform/Entry.swift index 420a7661..397e5448 100644 --- a/Sources/WASI/Platform/Entry.swift +++ b/Sources/WASI/Platform/Entry.swift @@ -23,7 +23,7 @@ extension FdWASIEntry { try WASIAbi.Errno.translatingPlatformErrno { try self.fd.adviseRead(offset: offset, length: length) } - #elseif os(Linux) + #elseif os(Linux) || os(Android) guard let offset = Int(exactly: offset), let length = Int(exactly: length) else { diff --git a/Sources/WASI/Platform/File.swift b/Sources/WASI/Platform/File.swift index a0d692fe..ce7b9335 100644 --- a/Sources/WASI/Platform/File.swift +++ b/Sources/WASI/Platform/File.swift @@ -24,6 +24,18 @@ extension FdWASIFile { ) } + func sync() throws { + try WASIAbi.Errno.translatingPlatformErrno { + try fd.sync() + } + } + + func datasync() throws { + try WASIAbi.Errno.translatingPlatformErrno { + try fd.datasync() + } + } + @inlinable func write(vectored buffer: Buffer) throws -> WASIAbi.Size where Buffer.Element == WASIAbi.IOVec { guard accessMode.contains(.write) else { diff --git a/Sources/WASI/Platform/PlatformTypes.swift b/Sources/WASI/Platform/PlatformTypes.swift index aeae09b4..6b0637ac 100644 --- a/Sources/WASI/Platform/PlatformTypes.swift +++ b/Sources/WASI/Platform/PlatformTypes.swift @@ -156,15 +156,23 @@ extension WASIAbi.Errno { do { return try body() } catch let errno as Errno { - guard let error = WASIAbi.Errno(platformErrno: errno) else { - throw WASIError(description: "Unknown underlying OS error: \(errno)") - } - throw error + throw try WASIAbi.Errno(platformErrno: errno) + } + } + + init(platformErrno: CInt) throws { + try self.init(platformErrno: SystemPackage.Errno(rawValue: platformErrno)) + } + + init(platformErrno: Errno) throws { + guard let error = WASIAbi.Errno(_platformErrno: platformErrno) else { + throw WASIError(description: "Unknown underlying OS error: \(platformErrno)") } + self = error } - init?(platformErrno: SystemPackage.Errno) { - switch platformErrno { + private init?(_platformErrno: SystemPackage.Errno) { + switch _platformErrno { case .permissionDenied: self = .EPERM case .notPermitted: self = .EPERM case .noSuchFileOrDirectory: self = .ENOENT diff --git a/Sources/WASI/Platform/SandboxPrimitives/Open.swift b/Sources/WASI/Platform/SandboxPrimitives/Open.swift index c6d4c293..a99f7bd0 100644 --- a/Sources/WASI/Platform/SandboxPrimitives/Open.swift +++ b/Sources/WASI/Platform/SandboxPrimitives/Open.swift @@ -1,6 +1,24 @@ import SystemExtras import SystemPackage +#if canImport(Darwin) + import Darwin +#elseif canImport(Glibc) + import CSystem + import Glibc +#elseif canImport(Musl) + import CSystem + import Musl +#elseif canImport(Android) + import CSystem + import Android +#elseif os(Windows) + import CSystem + import ucrt +#else + #error("Unsupported Platform") +#endif + struct PathResolution { private let mode: FileDescriptor.AccessMode private let options: FileDescriptor.OpenOptions @@ -10,7 +28,20 @@ struct PathResolution { private let path: FilePath private var openDirectories: [FileDescriptor] /// Reverse-ordered remaining path components + /// File name appears first, then parent directories. + /// e.g. `a/b/c` -> ["c", "b", "a"] + /// This ordering is just to avoid dropFirst() on Array. private var components: FilePath.ComponentView + private var resolvedSymlinks: Int = 0 + + private static var MAX_SYMLINKS: Int { + // Linux defines MAXSYMLINKS as 40, but on darwin platforms, it's 32. + // Take a single conservative value here to avoid platform-specific + // behavior as much as possible. + // * https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/sys/param.h#L207 + // * https://github.com/torvalds/linux/blob/850925a8133c73c4a2453c360b2c3beb3bab67c9/include/linux/namei.h#L13 + return 32 + } init( baseDirFd: FileDescriptor, @@ -33,39 +64,117 @@ struct PathResolution { // no more parent directory means too many `..` throw WASIAbi.Errno.EPERM } + try self.baseFd.close() self.baseFd = lastDirectory } mutating func regular(component: FilePath.Component) throws { - let options: FileDescriptor.OpenOptions + var options: FileDescriptor.OpenOptions = [] + #if !os(Windows) + // First, try without following symlinks as a fast path. + // If it's actually a symlink and options don't have O_NOFOLLOW, + // we'll try again with interpreting resolved symlink. + options.insert(.noFollow) + #endif let mode: FileDescriptor.AccessMode - if !self.components.isEmpty { - var intermediateOptions: FileDescriptor.OpenOptions = [] + if !self.components.isEmpty { #if !os(Windows) // When trying to open an intermediate directory, // we can assume it's directory. - intermediateOptions.insert(.directory) - // FIXME: Resolve symlink in safe way - intermediateOptions.insert(.noFollow) + options.insert(.directory) #endif - options = intermediateOptions mode = .readOnly } else { - options = self.options + options.formUnion(self.options) mode = self.mode } try WASIAbi.Errno.translatingPlatformErrno { - let newFd = try self.baseFd.open( - at: FilePath(root: nil, components: component), - mode, options: options, permissions: permissions - ) - self.openDirectories.append(self.baseFd) - self.baseFd = newFd + do { + let newFd = try self.baseFd.open( + at: FilePath(root: nil, components: component), + mode, options: options, permissions: permissions + ) + self.openDirectories.append(self.baseFd) + self.baseFd = newFd + return + } catch let openErrno as Errno { + #if os(Windows) + // Windows doesn't have O_NOFOLLOW, so we can't retry with following symlink. + throw openErrno + #else + if self.options.contains(.noFollow) { + // If "open" failed with O_NOFOLLOW, no need to retry. + throw openErrno + } + + // If "open" failed and it might be a symlink, try again with following symlink. + + // Check if it's a symlink by fstatat(2). + // + // NOTE: `errno` has enough information to check if the component is a symlink, + // but the value is platform-specific (e.g. ELOOP on POSIX standards, but EMLINK + // on BSD family), so we conservatively check it by fstatat(2). + let attrs = try self.baseFd.attributes( + at: FilePath(root: nil, components: component), options: [.noFollow] + ) + guard attrs.fileType.isSymlink else { + // openat(2) failed, fstatat(2) succeeded, and it said it's not a symlink. + // If it's not a symlink, the error is not due to symlink following + // but other reasons, so just throw the error. + // e.g. open with O_DIRECTORY on a regular file. + throw openErrno + } + + try self.symlink(component: component) + #endif + } } } + #if !os(Windows) + mutating func symlink(component: FilePath.Component) throws { + /// Thin wrapper around readlinkat(2) + func _readlinkat(_ fd: CInt, _ path: UnsafePointer) throws -> FilePath { + var buffer = [CChar](repeating: 0, count: Int(PATH_MAX)) + let length = try buffer.withUnsafeMutableBufferPointer { buffer in + try buffer.withMemoryRebound(to: Int8.self) { buffer in + guard let bufferBase = buffer.baseAddress else { + throw WASIAbi.Errno.EINVAL + } + return readlinkat(fd, path, bufferBase, buffer.count) + } + } + guard length >= 0 else { + throw try WASIAbi.Errno(platformErrno: errno) + } + return FilePath(String(cString: buffer)) + } + + guard resolvedSymlinks < Self.MAX_SYMLINKS else { + throw WASIAbi.Errno.ELOOP + } + + // If it's a symlink, readlink(2) and check it doesn't escape sandbox. + let linkPath = try component.withPlatformString { + return try _readlinkat(self.baseFd.rawValue, $0) + } + + guard !linkPath.isAbsolute else { + // Ban absolute symlink to avoid sandbox-escaping. + throw WASIAbi.Errno.EPERM + } + + // Increment the number of resolved symlinks to prevent infinite + // link loop. + resolvedSymlinks += 1 + + // Add resolved path to the worklist. + self.components.append(contentsOf: linkPath.components.reversed()) + } + #endif + mutating func resolve() throws -> FileDescriptor { if path.isAbsolute { // POSIX openat(2) interprets absolute path ignoring base directory fd diff --git a/Sources/WASI/WASI.swift b/Sources/WASI/WASI.swift index 57297871..631a3325 100644 --- a/Sources/WASI/WASI.swift +++ b/Sources/WASI/WASI.swift @@ -8,6 +8,8 @@ import WasmTypes import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(Android) + import Android #elseif os(Windows) import ucrt #else @@ -1516,7 +1518,10 @@ public class WASIBridgeToHost: WASI { } func fd_datasync(fd: WASIAbi.Fd) throws { - throw WASIAbi.Errno.ENOTSUP + guard case let .file(fileEntry) = fdTable[fd] else { + throw WASIAbi.Errno.EBADF + } + return try fileEntry.datasync() } func fd_fdstat_get(fileDescriptor: UInt32) throws -> WASIAbi.FdStat { @@ -1695,7 +1700,10 @@ public class WASIBridgeToHost: WASI { } func fd_sync(fd: WASIAbi.Fd) throws { - throw WASIAbi.Errno.ENOTSUP + guard case let .file(fileEntry) = fdTable[fd] else { + throw WASIAbi.Errno.EBADF + } + return try fileEntry.sync() } func fd_tell(fd: WASIAbi.Fd) throws -> WASIAbi.FileSize { diff --git a/Sources/WAT/BinaryInstructionEncoder.swift b/Sources/WAT/BinaryInstructionEncoder.swift new file mode 100644 index 00000000..c9bfe7c2 --- /dev/null +++ b/Sources/WAT/BinaryInstructionEncoder.swift @@ -0,0 +1,395 @@ +// swift-format-ignore-file +//// Automatically generated by Utilities/Sources/WasmGen.swift +//// DO NOT EDIT DIRECTLY + +import WasmParser +import WasmTypes + +/// An instruction encoder that is responsible for encoding opcodes and immediates +/// in Wasm binary format. +protocol BinaryInstructionEncoder: InstructionVisitor { + /// Encodes an instruction opcode. + mutating func encodeInstruction(_ opcode: [UInt8]) throws + + // MARK: - Immediates encoding + mutating func encodeImmediates(blockType: BlockType) throws + mutating func encodeImmediates(dataIndex: UInt32) throws + mutating func encodeImmediates(elemIndex: UInt32) throws + mutating func encodeImmediates(functionIndex: UInt32) throws + mutating func encodeImmediates(globalIndex: UInt32) throws + mutating func encodeImmediates(localIndex: UInt32) throws + mutating func encodeImmediates(memarg: MemArg) throws + mutating func encodeImmediates(memory: UInt32) throws + mutating func encodeImmediates(relativeDepth: UInt32) throws + mutating func encodeImmediates(table: UInt32) throws + mutating func encodeImmediates(targets: BrTable) throws + mutating func encodeImmediates(type: ReferenceType) throws + mutating func encodeImmediates(type: ValueType) throws + mutating func encodeImmediates(value: IEEE754.Float32) throws + mutating func encodeImmediates(value: IEEE754.Float64) throws + mutating func encodeImmediates(value: Int32) throws + mutating func encodeImmediates(value: Int64) throws + mutating func encodeImmediates(dstMem: UInt32, srcMem: UInt32) throws + mutating func encodeImmediates(dstTable: UInt32, srcTable: UInt32) throws + mutating func encodeImmediates(elemIndex: UInt32, table: UInt32) throws + mutating func encodeImmediates(typeIndex: UInt32, tableIndex: UInt32) throws +} + +// BinaryInstructionEncoder implements the InstructionVisitor protocol to call the corresponding encode method. +extension BinaryInstructionEncoder { + mutating func visitUnreachable() throws { try encodeInstruction([0x00]) } + mutating func visitNop() throws { try encodeInstruction([0x01]) } + mutating func visitBlock(blockType: BlockType) throws { + try encodeInstruction([0x02]) + try encodeImmediates(blockType: blockType) + } + mutating func visitLoop(blockType: BlockType) throws { + try encodeInstruction([0x03]) + try encodeImmediates(blockType: blockType) + } + mutating func visitIf(blockType: BlockType) throws { + try encodeInstruction([0x04]) + try encodeImmediates(blockType: blockType) + } + mutating func visitElse() throws { try encodeInstruction([0x05]) } + mutating func visitEnd() throws { try encodeInstruction([0x0B]) } + mutating func visitBr(relativeDepth: UInt32) throws { + try encodeInstruction([0x0C]) + try encodeImmediates(relativeDepth: relativeDepth) + } + mutating func visitBrIf(relativeDepth: UInt32) throws { + try encodeInstruction([0x0D]) + try encodeImmediates(relativeDepth: relativeDepth) + } + mutating func visitBrTable(targets: BrTable) throws { + try encodeInstruction([0x0E]) + try encodeImmediates(targets: targets) + } + mutating func visitReturn() throws { try encodeInstruction([0x0F]) } + mutating func visitCall(functionIndex: UInt32) throws { + try encodeInstruction([0x10]) + try encodeImmediates(functionIndex: functionIndex) + } + mutating func visitCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws { + try encodeInstruction([0x11]) + try encodeImmediates(typeIndex: typeIndex, tableIndex: tableIndex) + } + mutating func visitReturnCall(functionIndex: UInt32) throws { + try encodeInstruction([0x12]) + try encodeImmediates(functionIndex: functionIndex) + } + mutating func visitReturnCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws { + try encodeInstruction([0x13]) + try encodeImmediates(typeIndex: typeIndex, tableIndex: tableIndex) + } + mutating func visitDrop() throws { try encodeInstruction([0x1A]) } + mutating func visitSelect() throws { try encodeInstruction([0x1B]) } + mutating func visitTypedSelect(type: ValueType) throws { + try encodeInstruction([0x1C]) + try encodeImmediates(type: type) + } + mutating func visitLocalGet(localIndex: UInt32) throws { + try encodeInstruction([0x20]) + try encodeImmediates(localIndex: localIndex) + } + mutating func visitLocalSet(localIndex: UInt32) throws { + try encodeInstruction([0x21]) + try encodeImmediates(localIndex: localIndex) + } + mutating func visitLocalTee(localIndex: UInt32) throws { + try encodeInstruction([0x22]) + try encodeImmediates(localIndex: localIndex) + } + mutating func visitGlobalGet(globalIndex: UInt32) throws { + try encodeInstruction([0x23]) + try encodeImmediates(globalIndex: globalIndex) + } + mutating func visitGlobalSet(globalIndex: UInt32) throws { + try encodeInstruction([0x24]) + try encodeImmediates(globalIndex: globalIndex) + } + mutating func visitLoad(_ load: Instruction.Load, memarg: MemArg) throws { + let opcode: [UInt8] + switch load { + case .i32Load: opcode = [0x28] + case .i64Load: opcode = [0x29] + case .f32Load: opcode = [0x2A] + case .f64Load: opcode = [0x2B] + case .i32Load8S: opcode = [0x2C] + case .i32Load8U: opcode = [0x2D] + case .i32Load16S: opcode = [0x2E] + case .i32Load16U: opcode = [0x2F] + case .i64Load8S: opcode = [0x30] + case .i64Load8U: opcode = [0x31] + case .i64Load16S: opcode = [0x32] + case .i64Load16U: opcode = [0x33] + case .i64Load32S: opcode = [0x34] + case .i64Load32U: opcode = [0x35] + } + + try encodeInstruction(opcode) + try encodeImmediates(memarg: memarg) + } + mutating func visitStore(_ store: Instruction.Store, memarg: MemArg) throws { + let opcode: [UInt8] + switch store { + case .i32Store: opcode = [0x36] + case .i64Store: opcode = [0x37] + case .f32Store: opcode = [0x38] + case .f64Store: opcode = [0x39] + case .i32Store8: opcode = [0x3A] + case .i32Store16: opcode = [0x3B] + case .i64Store8: opcode = [0x3C] + case .i64Store16: opcode = [0x3D] + case .i64Store32: opcode = [0x3E] + } + + try encodeInstruction(opcode) + try encodeImmediates(memarg: memarg) + } + mutating func visitMemorySize(memory: UInt32) throws { + try encodeInstruction([0x3F]) + try encodeImmediates(memory: memory) + } + mutating func visitMemoryGrow(memory: UInt32) throws { + try encodeInstruction([0x40]) + try encodeImmediates(memory: memory) + } + mutating func visitI32Const(value: Int32) throws { + try encodeInstruction([0x41]) + try encodeImmediates(value: value) + } + mutating func visitI64Const(value: Int64) throws { + try encodeInstruction([0x42]) + try encodeImmediates(value: value) + } + mutating func visitF32Const(value: IEEE754.Float32) throws { + try encodeInstruction([0x43]) + try encodeImmediates(value: value) + } + mutating func visitF64Const(value: IEEE754.Float64) throws { + try encodeInstruction([0x44]) + try encodeImmediates(value: value) + } + mutating func visitRefNull(type: ReferenceType) throws { + try encodeInstruction([0xD0]) + try encodeImmediates(type: type) + } + mutating func visitRefIsNull() throws { try encodeInstruction([0xD1]) } + mutating func visitRefFunc(functionIndex: UInt32) throws { + try encodeInstruction([0xD2]) + try encodeImmediates(functionIndex: functionIndex) + } + mutating func visitI32Eqz() throws { try encodeInstruction([0x45]) } + mutating func visitCmp(_ cmp: Instruction.Cmp) throws { + let opcode: [UInt8] + switch cmp { + case .i32Eq: opcode = [0x46] + case .i32Ne: opcode = [0x47] + case .i32LtS: opcode = [0x48] + case .i32LtU: opcode = [0x49] + case .i32GtS: opcode = [0x4A] + case .i32GtU: opcode = [0x4B] + case .i32LeS: opcode = [0x4C] + case .i32LeU: opcode = [0x4D] + case .i32GeS: opcode = [0x4E] + case .i32GeU: opcode = [0x4F] + case .i64Eq: opcode = [0x51] + case .i64Ne: opcode = [0x52] + case .i64LtS: opcode = [0x53] + case .i64LtU: opcode = [0x54] + case .i64GtS: opcode = [0x55] + case .i64GtU: opcode = [0x56] + case .i64LeS: opcode = [0x57] + case .i64LeU: opcode = [0x58] + case .i64GeS: opcode = [0x59] + case .i64GeU: opcode = [0x5A] + case .f32Eq: opcode = [0x5B] + case .f32Ne: opcode = [0x5C] + case .f32Lt: opcode = [0x5D] + case .f32Gt: opcode = [0x5E] + case .f32Le: opcode = [0x5F] + case .f32Ge: opcode = [0x60] + case .f64Eq: opcode = [0x61] + case .f64Ne: opcode = [0x62] + case .f64Lt: opcode = [0x63] + case .f64Gt: opcode = [0x64] + case .f64Le: opcode = [0x65] + case .f64Ge: opcode = [0x66] + } + + try encodeInstruction(opcode) + } + mutating func visitI64Eqz() throws { try encodeInstruction([0x50]) } + mutating func visitUnary(_ unary: Instruction.Unary) throws { + let opcode: [UInt8] + switch unary { + case .i32Clz: opcode = [0x67] + case .i32Ctz: opcode = [0x68] + case .i32Popcnt: opcode = [0x69] + case .i64Clz: opcode = [0x79] + case .i64Ctz: opcode = [0x7A] + case .i64Popcnt: opcode = [0x7B] + case .f32Abs: opcode = [0x8B] + case .f32Neg: opcode = [0x8C] + case .f32Ceil: opcode = [0x8D] + case .f32Floor: opcode = [0x8E] + case .f32Trunc: opcode = [0x8F] + case .f32Nearest: opcode = [0x90] + case .f32Sqrt: opcode = [0x91] + case .f64Abs: opcode = [0x99] + case .f64Neg: opcode = [0x9A] + case .f64Ceil: opcode = [0x9B] + case .f64Floor: opcode = [0x9C] + case .f64Trunc: opcode = [0x9D] + case .f64Nearest: opcode = [0x9E] + case .f64Sqrt: opcode = [0x9F] + case .i32Extend8S: opcode = [0xC0] + case .i32Extend16S: opcode = [0xC1] + case .i64Extend8S: opcode = [0xC2] + case .i64Extend16S: opcode = [0xC3] + case .i64Extend32S: opcode = [0xC4] + } + + try encodeInstruction(opcode) + } + mutating func visitBinary(_ binary: Instruction.Binary) throws { + let opcode: [UInt8] + switch binary { + case .i32Add: opcode = [0x6A] + case .i32Sub: opcode = [0x6B] + case .i32Mul: opcode = [0x6C] + case .i32DivS: opcode = [0x6D] + case .i32DivU: opcode = [0x6E] + case .i32RemS: opcode = [0x6F] + case .i32RemU: opcode = [0x70] + case .i32And: opcode = [0x71] + case .i32Or: opcode = [0x72] + case .i32Xor: opcode = [0x73] + case .i32Shl: opcode = [0x74] + case .i32ShrS: opcode = [0x75] + case .i32ShrU: opcode = [0x76] + case .i32Rotl: opcode = [0x77] + case .i32Rotr: opcode = [0x78] + case .i64Add: opcode = [0x7C] + case .i64Sub: opcode = [0x7D] + case .i64Mul: opcode = [0x7E] + case .i64DivS: opcode = [0x7F] + case .i64DivU: opcode = [0x80] + case .i64RemS: opcode = [0x81] + case .i64RemU: opcode = [0x82] + case .i64And: opcode = [0x83] + case .i64Or: opcode = [0x84] + case .i64Xor: opcode = [0x85] + case .i64Shl: opcode = [0x86] + case .i64ShrS: opcode = [0x87] + case .i64ShrU: opcode = [0x88] + case .i64Rotl: opcode = [0x89] + case .i64Rotr: opcode = [0x8A] + case .f32Add: opcode = [0x92] + case .f32Sub: opcode = [0x93] + case .f32Mul: opcode = [0x94] + case .f32Div: opcode = [0x95] + case .f32Min: opcode = [0x96] + case .f32Max: opcode = [0x97] + case .f32Copysign: opcode = [0x98] + case .f64Add: opcode = [0xA0] + case .f64Sub: opcode = [0xA1] + case .f64Mul: opcode = [0xA2] + case .f64Div: opcode = [0xA3] + case .f64Min: opcode = [0xA4] + case .f64Max: opcode = [0xA5] + case .f64Copysign: opcode = [0xA6] + } + + try encodeInstruction(opcode) + } + mutating func visitConversion(_ conversion: Instruction.Conversion) throws { + let opcode: [UInt8] + switch conversion { + case .i32WrapI64: opcode = [0xA7] + case .i32TruncF32S: opcode = [0xA8] + case .i32TruncF32U: opcode = [0xA9] + case .i32TruncF64S: opcode = [0xAA] + case .i32TruncF64U: opcode = [0xAB] + case .i64ExtendI32S: opcode = [0xAC] + case .i64ExtendI32U: opcode = [0xAD] + case .i64TruncF32S: opcode = [0xAE] + case .i64TruncF32U: opcode = [0xAF] + case .i64TruncF64S: opcode = [0xB0] + case .i64TruncF64U: opcode = [0xB1] + case .f32ConvertI32S: opcode = [0xB2] + case .f32ConvertI32U: opcode = [0xB3] + case .f32ConvertI64S: opcode = [0xB4] + case .f32ConvertI64U: opcode = [0xB5] + case .f32DemoteF64: opcode = [0xB6] + case .f64ConvertI32S: opcode = [0xB7] + case .f64ConvertI32U: opcode = [0xB8] + case .f64ConvertI64S: opcode = [0xB9] + case .f64ConvertI64U: opcode = [0xBA] + case .f64PromoteF32: opcode = [0xBB] + case .i32ReinterpretF32: opcode = [0xBC] + case .i64ReinterpretF64: opcode = [0xBD] + case .f32ReinterpretI32: opcode = [0xBE] + case .f64ReinterpretI64: opcode = [0xBF] + case .i32TruncSatF32S: opcode = [0xFC, 0x00] + case .i32TruncSatF32U: opcode = [0xFC, 0x01] + case .i32TruncSatF64S: opcode = [0xFC, 0x02] + case .i32TruncSatF64U: opcode = [0xFC, 0x03] + case .i64TruncSatF32S: opcode = [0xFC, 0x04] + case .i64TruncSatF32U: opcode = [0xFC, 0x05] + case .i64TruncSatF64S: opcode = [0xFC, 0x06] + case .i64TruncSatF64U: opcode = [0xFC, 0x07] + } + + try encodeInstruction(opcode) + } + mutating func visitMemoryInit(dataIndex: UInt32) throws { + try encodeInstruction([0xFC, 0x08]) + try encodeImmediates(dataIndex: dataIndex) + } + mutating func visitDataDrop(dataIndex: UInt32) throws { + try encodeInstruction([0xFC, 0x09]) + try encodeImmediates(dataIndex: dataIndex) + } + mutating func visitMemoryCopy(dstMem: UInt32, srcMem: UInt32) throws { + try encodeInstruction([0xFC, 0x0A]) + try encodeImmediates(dstMem: dstMem, srcMem: srcMem) + } + mutating func visitMemoryFill(memory: UInt32) throws { + try encodeInstruction([0xFC, 0x0B]) + try encodeImmediates(memory: memory) + } + mutating func visitTableInit(elemIndex: UInt32, table: UInt32) throws { + try encodeInstruction([0xFC, 0x0C]) + try encodeImmediates(elemIndex: elemIndex, table: table) + } + mutating func visitElemDrop(elemIndex: UInt32) throws { + try encodeInstruction([0xFC, 0x0D]) + try encodeImmediates(elemIndex: elemIndex) + } + mutating func visitTableCopy(dstTable: UInt32, srcTable: UInt32) throws { + try encodeInstruction([0xFC, 0x0E]) + try encodeImmediates(dstTable: dstTable, srcTable: srcTable) + } + mutating func visitTableFill(table: UInt32) throws { + try encodeInstruction([0xFC, 0x11]) + try encodeImmediates(table: table) + } + mutating func visitTableGet(table: UInt32) throws { + try encodeInstruction([0x25]) + try encodeImmediates(table: table) + } + mutating func visitTableSet(table: UInt32) throws { + try encodeInstruction([0x26]) + try encodeImmediates(table: table) + } + mutating func visitTableGrow(table: UInt32) throws { + try encodeInstruction([0xFC, 0x0F]) + try encodeImmediates(table: table) + } + mutating func visitTableSize(table: UInt32) throws { + try encodeInstruction([0xFC, 0x10]) + try encodeImmediates(table: table) + } +} diff --git a/Sources/WAT/CMakeLists.txt b/Sources/WAT/CMakeLists.txt new file mode 100644 index 00000000..12bdbba5 --- /dev/null +++ b/Sources/WAT/CMakeLists.txt @@ -0,0 +1,16 @@ +add_wasmkit_library(WAT + BinaryInstructionEncoder.swift + Encoder.swift + Lexer.swift + Location.swift + NameMapping.swift + ParseTextInstruction.swift + Parser.swift + Parser/ExpressionParser.swift + Parser/WastParser.swift + Parser/WatParser.swift + WAT.swift +) + +target_link_wasmkit_libraries(WAT PUBLIC + WasmParser) diff --git a/Sources/WAT/Encoder.swift b/Sources/WAT/Encoder.swift index ec76b9a5..0f0829cc 100644 --- a/Sources/WAT/Encoder.swift +++ b/Sources/WAT/Encoder.swift @@ -119,6 +119,7 @@ extension ValueType: WasmEncodable { case .i64: encoder.output.append(0x7E) case .f32: encoder.output.append(0x7D) case .f64: encoder.output.append(0x7C) + case .v128: encoder.output.append(0x7B) case .ref(let refType): refType.encode(to: &encoder) } } @@ -441,7 +442,7 @@ extension WatParser.DataSegmentDecl { } } -struct ExpressionEncoder: InstructionEncoder { +struct ExpressionEncoder: BinaryInstructionEncoder { var encoder = Encoder() var hasDataSegmentInstruction: Bool = false @@ -463,24 +464,21 @@ struct ExpressionEncoder: InstructionEncoder { // MARK: Special instructions mutating func visitMemoryInit(dataIndex: UInt32) throws { - try encodeInstruction(0x08, 0xFC) + try encodeInstruction([0xFC, 0x08]) try encodeImmediates(dataIndex: dataIndex) encodeByte(0x00) // reserved value } mutating func visitTypedSelect(type: ValueType) throws { - try encodeInstruction(0x1C, nil) + try encodeInstruction([0x1C]) encodeByte(0x01) // number of result types try encodeImmediates(type: type) } // MARK: InstructionEncoder conformance - mutating func encodeInstruction(_ opcode: UInt8, _ prefix: UInt8?) throws { - if let prefix { - encoder.output.append(prefix) - } - encoder.output.append(opcode) + mutating func encodeInstruction(_ opcode: [UInt8]) throws { + encoder.output.append(contentsOf: opcode) } mutating func encodeImmediates(blockType: WasmParser.BlockType) throws { switch blockType { @@ -499,7 +497,7 @@ struct ExpressionEncoder: InstructionEncoder { mutating func encodeImmediates(globalIndex: UInt32) throws { encodeUnsigned(globalIndex) } mutating func encodeImmediates(localIndex: UInt32) throws { encodeUnsigned(localIndex) } mutating func encodeImmediates(memarg: WasmParser.MemArg) throws { - encodeUnsigned(UInt(memarg.align.trailingZeroBitCount)) + encodeUnsigned(UInt(memarg.align)) encodeUnsigned(memarg.offset) } mutating func encodeImmediates(memory: UInt32) throws { encodeUnsigned(memory) } @@ -535,7 +533,7 @@ struct ExpressionEncoder: InstructionEncoder { } } -func encode(module: inout Wat) throws -> [UInt8] { +func encode(module: inout Wat, options: EncodeOptions) throws -> [UInt8] { var encoder = Encoder() encoder.writeHeader() @@ -675,5 +673,25 @@ func encode(module: inout Wat) throws -> [UInt8] { } } + // (Optional) Name Section + if !module.functionsMap.isEmpty, options.nameSection { + encoder.section(id: 0) { encoder in + encoder.encode("name") + // Subsection 1: Function names + encoder.section(id: 1) { encoder in + let functionNames = module.functionsMap.enumerated().compactMap { i, decl -> (Int, String)? in + guard let name = decl.id else { return nil } + return (i, name.value) + } + encoder.encodeVector(functionNames) { entry, encoder in + let (index, name) = entry + encoder.writeUnsignedLEB128(UInt(index)) + // Drop initial "$" + encoder.encode(String(name.dropFirst())) + } + } + } + } + return encoder.output } diff --git a/Sources/WAT/InstructionEncoder.swift b/Sources/WAT/InstructionEncoder.swift deleted file mode 100644 index 05217cae..00000000 --- a/Sources/WAT/InstructionEncoder.swift +++ /dev/null @@ -1,411 +0,0 @@ -// swift-format-ignore-file -//// Automatically generated by Utilities/Sources/WasmGen.swift -//// DO NOT EDIT DIRECTLY - -import WasmParser -import WasmTypes - -/// An instruction encoder that is responsible for encoding opcodes and immediates. -protocol InstructionEncoder: InstructionVisitor { - /// Encodes an instruction opcode. - mutating func encodeInstruction(_ opcode: UInt8, _ prefix: UInt8?) throws - - // MARK: - Immediates encoding - mutating func encodeImmediates(blockType: BlockType) throws - mutating func encodeImmediates(dataIndex: UInt32) throws - mutating func encodeImmediates(elemIndex: UInt32) throws - mutating func encodeImmediates(functionIndex: UInt32) throws - mutating func encodeImmediates(globalIndex: UInt32) throws - mutating func encodeImmediates(localIndex: UInt32) throws - mutating func encodeImmediates(memarg: MemArg) throws - mutating func encodeImmediates(memory: UInt32) throws - mutating func encodeImmediates(relativeDepth: UInt32) throws - mutating func encodeImmediates(table: UInt32) throws - mutating func encodeImmediates(targets: BrTable) throws - mutating func encodeImmediates(type: ReferenceType) throws - mutating func encodeImmediates(type: ValueType) throws - mutating func encodeImmediates(value: IEEE754.Float32) throws - mutating func encodeImmediates(value: IEEE754.Float64) throws - mutating func encodeImmediates(value: Int32) throws - mutating func encodeImmediates(value: Int64) throws - mutating func encodeImmediates(dstMem: UInt32, srcMem: UInt32) throws - mutating func encodeImmediates(dstTable: UInt32, srcTable: UInt32) throws - mutating func encodeImmediates(elemIndex: UInt32, table: UInt32) throws - mutating func encodeImmediates(typeIndex: UInt32, tableIndex: UInt32) throws -} - -// InstructionEncoder implements the InstructionVisitor protocol to call the corresponding encode method. -extension InstructionEncoder { - mutating func visitUnreachable() throws { try encodeInstruction(0x00, nil) } - mutating func visitNop() throws { try encodeInstruction(0x01, nil) } - mutating func visitBlock(blockType: BlockType) throws { - try encodeInstruction(0x02, nil) - try encodeImmediates(blockType: blockType) - } - mutating func visitLoop(blockType: BlockType) throws { - try encodeInstruction(0x03, nil) - try encodeImmediates(blockType: blockType) - } - mutating func visitIf(blockType: BlockType) throws { - try encodeInstruction(0x04, nil) - try encodeImmediates(blockType: blockType) - } - mutating func visitElse() throws { try encodeInstruction(0x05, nil) } - mutating func visitEnd() throws { try encodeInstruction(0x0B, nil) } - mutating func visitBr(relativeDepth: UInt32) throws { - try encodeInstruction(0x0C, nil) - try encodeImmediates(relativeDepth: relativeDepth) - } - mutating func visitBrIf(relativeDepth: UInt32) throws { - try encodeInstruction(0x0D, nil) - try encodeImmediates(relativeDepth: relativeDepth) - } - mutating func visitBrTable(targets: BrTable) throws { - try encodeInstruction(0x0E, nil) - try encodeImmediates(targets: targets) - } - mutating func visitReturn() throws { try encodeInstruction(0x0F, nil) } - mutating func visitCall(functionIndex: UInt32) throws { - try encodeInstruction(0x10, nil) - try encodeImmediates(functionIndex: functionIndex) - } - mutating func visitCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws { - try encodeInstruction(0x11, nil) - try encodeImmediates(typeIndex: typeIndex, tableIndex: tableIndex) - } - mutating func visitDrop() throws { try encodeInstruction(0x1A, nil) } - mutating func visitSelect() throws { try encodeInstruction(0x1B, nil) } - mutating func visitTypedSelect(type: ValueType) throws { - try encodeInstruction(0x1C, nil) - try encodeImmediates(type: type) - } - mutating func visitLocalGet(localIndex: UInt32) throws { - try encodeInstruction(0x20, nil) - try encodeImmediates(localIndex: localIndex) - } - mutating func visitLocalSet(localIndex: UInt32) throws { - try encodeInstruction(0x21, nil) - try encodeImmediates(localIndex: localIndex) - } - mutating func visitLocalTee(localIndex: UInt32) throws { - try encodeInstruction(0x22, nil) - try encodeImmediates(localIndex: localIndex) - } - mutating func visitGlobalGet(globalIndex: UInt32) throws { - try encodeInstruction(0x23, nil) - try encodeImmediates(globalIndex: globalIndex) - } - mutating func visitGlobalSet(globalIndex: UInt32) throws { - try encodeInstruction(0x24, nil) - try encodeImmediates(globalIndex: globalIndex) - } - mutating func visitI32Load(memarg: MemArg) throws { - try encodeInstruction(0x28, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Load(memarg: MemArg) throws { - try encodeInstruction(0x29, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitF32Load(memarg: MemArg) throws { - try encodeInstruction(0x2A, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitF64Load(memarg: MemArg) throws { - try encodeInstruction(0x2B, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI32Load8S(memarg: MemArg) throws { - try encodeInstruction(0x2C, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI32Load8U(memarg: MemArg) throws { - try encodeInstruction(0x2D, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI32Load16S(memarg: MemArg) throws { - try encodeInstruction(0x2E, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI32Load16U(memarg: MemArg) throws { - try encodeInstruction(0x2F, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Load8S(memarg: MemArg) throws { - try encodeInstruction(0x30, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Load8U(memarg: MemArg) throws { - try encodeInstruction(0x31, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Load16S(memarg: MemArg) throws { - try encodeInstruction(0x32, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Load16U(memarg: MemArg) throws { - try encodeInstruction(0x33, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Load32S(memarg: MemArg) throws { - try encodeInstruction(0x34, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Load32U(memarg: MemArg) throws { - try encodeInstruction(0x35, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI32Store(memarg: MemArg) throws { - try encodeInstruction(0x36, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Store(memarg: MemArg) throws { - try encodeInstruction(0x37, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitF32Store(memarg: MemArg) throws { - try encodeInstruction(0x38, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitF64Store(memarg: MemArg) throws { - try encodeInstruction(0x39, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI32Store8(memarg: MemArg) throws { - try encodeInstruction(0x3A, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI32Store16(memarg: MemArg) throws { - try encodeInstruction(0x3B, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Store8(memarg: MemArg) throws { - try encodeInstruction(0x3C, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Store16(memarg: MemArg) throws { - try encodeInstruction(0x3D, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitI64Store32(memarg: MemArg) throws { - try encodeInstruction(0x3E, nil) - try encodeImmediates(memarg: memarg) - } - mutating func visitMemorySize(memory: UInt32) throws { - try encodeInstruction(0x3F, nil) - try encodeImmediates(memory: memory) - } - mutating func visitMemoryGrow(memory: UInt32) throws { - try encodeInstruction(0x40, nil) - try encodeImmediates(memory: memory) - } - mutating func visitI32Const(value: Int32) throws { - try encodeInstruction(0x41, nil) - try encodeImmediates(value: value) - } - mutating func visitI64Const(value: Int64) throws { - try encodeInstruction(0x42, nil) - try encodeImmediates(value: value) - } - mutating func visitF32Const(value: IEEE754.Float32) throws { - try encodeInstruction(0x43, nil) - try encodeImmediates(value: value) - } - mutating func visitF64Const(value: IEEE754.Float64) throws { - try encodeInstruction(0x44, nil) - try encodeImmediates(value: value) - } - mutating func visitRefNull(type: ReferenceType) throws { - try encodeInstruction(0xD0, nil) - try encodeImmediates(type: type) - } - mutating func visitRefIsNull() throws { try encodeInstruction(0xD1, nil) } - mutating func visitRefFunc(functionIndex: UInt32) throws { - try encodeInstruction(0xD2, nil) - try encodeImmediates(functionIndex: functionIndex) - } - mutating func visitI32Eqz() throws { try encodeInstruction(0x45, nil) } - mutating func visitI32Eq() throws { try encodeInstruction(0x46, nil) } - mutating func visitI32Ne() throws { try encodeInstruction(0x47, nil) } - mutating func visitI32LtS() throws { try encodeInstruction(0x48, nil) } - mutating func visitI32LtU() throws { try encodeInstruction(0x49, nil) } - mutating func visitI32GtS() throws { try encodeInstruction(0x4A, nil) } - mutating func visitI32GtU() throws { try encodeInstruction(0x4B, nil) } - mutating func visitI32LeS() throws { try encodeInstruction(0x4C, nil) } - mutating func visitI32LeU() throws { try encodeInstruction(0x4D, nil) } - mutating func visitI32GeS() throws { try encodeInstruction(0x4E, nil) } - mutating func visitI32GeU() throws { try encodeInstruction(0x4F, nil) } - mutating func visitI64Eqz() throws { try encodeInstruction(0x50, nil) } - mutating func visitI64Eq() throws { try encodeInstruction(0x51, nil) } - mutating func visitI64Ne() throws { try encodeInstruction(0x52, nil) } - mutating func visitI64LtS() throws { try encodeInstruction(0x53, nil) } - mutating func visitI64LtU() throws { try encodeInstruction(0x54, nil) } - mutating func visitI64GtS() throws { try encodeInstruction(0x55, nil) } - mutating func visitI64GtU() throws { try encodeInstruction(0x56, nil) } - mutating func visitI64LeS() throws { try encodeInstruction(0x57, nil) } - mutating func visitI64LeU() throws { try encodeInstruction(0x58, nil) } - mutating func visitI64GeS() throws { try encodeInstruction(0x59, nil) } - mutating func visitI64GeU() throws { try encodeInstruction(0x5A, nil) } - mutating func visitF32Eq() throws { try encodeInstruction(0x5B, nil) } - mutating func visitF32Ne() throws { try encodeInstruction(0x5C, nil) } - mutating func visitF32Lt() throws { try encodeInstruction(0x5D, nil) } - mutating func visitF32Gt() throws { try encodeInstruction(0x5E, nil) } - mutating func visitF32Le() throws { try encodeInstruction(0x5F, nil) } - mutating func visitF32Ge() throws { try encodeInstruction(0x60, nil) } - mutating func visitF64Eq() throws { try encodeInstruction(0x61, nil) } - mutating func visitF64Ne() throws { try encodeInstruction(0x62, nil) } - mutating func visitF64Lt() throws { try encodeInstruction(0x63, nil) } - mutating func visitF64Gt() throws { try encodeInstruction(0x64, nil) } - mutating func visitF64Le() throws { try encodeInstruction(0x65, nil) } - mutating func visitF64Ge() throws { try encodeInstruction(0x66, nil) } - mutating func visitI32Clz() throws { try encodeInstruction(0x67, nil) } - mutating func visitI32Ctz() throws { try encodeInstruction(0x68, nil) } - mutating func visitI32Popcnt() throws { try encodeInstruction(0x69, nil) } - mutating func visitI32Add() throws { try encodeInstruction(0x6A, nil) } - mutating func visitI32Sub() throws { try encodeInstruction(0x6B, nil) } - mutating func visitI32Mul() throws { try encodeInstruction(0x6C, nil) } - mutating func visitI32DivS() throws { try encodeInstruction(0x6D, nil) } - mutating func visitI32DivU() throws { try encodeInstruction(0x6E, nil) } - mutating func visitI32RemS() throws { try encodeInstruction(0x6F, nil) } - mutating func visitI32RemU() throws { try encodeInstruction(0x70, nil) } - mutating func visitI32And() throws { try encodeInstruction(0x71, nil) } - mutating func visitI32Or() throws { try encodeInstruction(0x72, nil) } - mutating func visitI32Xor() throws { try encodeInstruction(0x73, nil) } - mutating func visitI32Shl() throws { try encodeInstruction(0x74, nil) } - mutating func visitI32ShrS() throws { try encodeInstruction(0x75, nil) } - mutating func visitI32ShrU() throws { try encodeInstruction(0x76, nil) } - mutating func visitI32Rotl() throws { try encodeInstruction(0x77, nil) } - mutating func visitI32Rotr() throws { try encodeInstruction(0x78, nil) } - mutating func visitI64Clz() throws { try encodeInstruction(0x79, nil) } - mutating func visitI64Ctz() throws { try encodeInstruction(0x7A, nil) } - mutating func visitI64Popcnt() throws { try encodeInstruction(0x7B, nil) } - mutating func visitI64Add() throws { try encodeInstruction(0x7C, nil) } - mutating func visitI64Sub() throws { try encodeInstruction(0x7D, nil) } - mutating func visitI64Mul() throws { try encodeInstruction(0x7E, nil) } - mutating func visitI64DivS() throws { try encodeInstruction(0x7F, nil) } - mutating func visitI64DivU() throws { try encodeInstruction(0x80, nil) } - mutating func visitI64RemS() throws { try encodeInstruction(0x81, nil) } - mutating func visitI64RemU() throws { try encodeInstruction(0x82, nil) } - mutating func visitI64And() throws { try encodeInstruction(0x83, nil) } - mutating func visitI64Or() throws { try encodeInstruction(0x84, nil) } - mutating func visitI64Xor() throws { try encodeInstruction(0x85, nil) } - mutating func visitI64Shl() throws { try encodeInstruction(0x86, nil) } - mutating func visitI64ShrS() throws { try encodeInstruction(0x87, nil) } - mutating func visitI64ShrU() throws { try encodeInstruction(0x88, nil) } - mutating func visitI64Rotl() throws { try encodeInstruction(0x89, nil) } - mutating func visitI64Rotr() throws { try encodeInstruction(0x8A, nil) } - mutating func visitF32Abs() throws { try encodeInstruction(0x8B, nil) } - mutating func visitF32Neg() throws { try encodeInstruction(0x8C, nil) } - mutating func visitF32Ceil() throws { try encodeInstruction(0x8D, nil) } - mutating func visitF32Floor() throws { try encodeInstruction(0x8E, nil) } - mutating func visitF32Trunc() throws { try encodeInstruction(0x8F, nil) } - mutating func visitF32Nearest() throws { try encodeInstruction(0x90, nil) } - mutating func visitF32Sqrt() throws { try encodeInstruction(0x91, nil) } - mutating func visitF32Add() throws { try encodeInstruction(0x92, nil) } - mutating func visitF32Sub() throws { try encodeInstruction(0x93, nil) } - mutating func visitF32Mul() throws { try encodeInstruction(0x94, nil) } - mutating func visitF32Div() throws { try encodeInstruction(0x95, nil) } - mutating func visitF32Min() throws { try encodeInstruction(0x96, nil) } - mutating func visitF32Max() throws { try encodeInstruction(0x97, nil) } - mutating func visitF32Copysign() throws { try encodeInstruction(0x98, nil) } - mutating func visitF64Abs() throws { try encodeInstruction(0x99, nil) } - mutating func visitF64Neg() throws { try encodeInstruction(0x9A, nil) } - mutating func visitF64Ceil() throws { try encodeInstruction(0x9B, nil) } - mutating func visitF64Floor() throws { try encodeInstruction(0x9C, nil) } - mutating func visitF64Trunc() throws { try encodeInstruction(0x9D, nil) } - mutating func visitF64Nearest() throws { try encodeInstruction(0x9E, nil) } - mutating func visitF64Sqrt() throws { try encodeInstruction(0x9F, nil) } - mutating func visitF64Add() throws { try encodeInstruction(0xA0, nil) } - mutating func visitF64Sub() throws { try encodeInstruction(0xA1, nil) } - mutating func visitF64Mul() throws { try encodeInstruction(0xA2, nil) } - mutating func visitF64Div() throws { try encodeInstruction(0xA3, nil) } - mutating func visitF64Min() throws { try encodeInstruction(0xA4, nil) } - mutating func visitF64Max() throws { try encodeInstruction(0xA5, nil) } - mutating func visitF64Copysign() throws { try encodeInstruction(0xA6, nil) } - mutating func visitI32WrapI64() throws { try encodeInstruction(0xA7, nil) } - mutating func visitI32TruncF32S() throws { try encodeInstruction(0xA8, nil) } - mutating func visitI32TruncF32U() throws { try encodeInstruction(0xA9, nil) } - mutating func visitI32TruncF64S() throws { try encodeInstruction(0xAA, nil) } - mutating func visitI32TruncF64U() throws { try encodeInstruction(0xAB, nil) } - mutating func visitI64ExtendI32S() throws { try encodeInstruction(0xAC, nil) } - mutating func visitI64ExtendI32U() throws { try encodeInstruction(0xAD, nil) } - mutating func visitI64TruncF32S() throws { try encodeInstruction(0xAE, nil) } - mutating func visitI64TruncF32U() throws { try encodeInstruction(0xAF, nil) } - mutating func visitI64TruncF64S() throws { try encodeInstruction(0xB0, nil) } - mutating func visitI64TruncF64U() throws { try encodeInstruction(0xB1, nil) } - mutating func visitF32ConvertI32S() throws { try encodeInstruction(0xB2, nil) } - mutating func visitF32ConvertI32U() throws { try encodeInstruction(0xB3, nil) } - mutating func visitF32ConvertI64S() throws { try encodeInstruction(0xB4, nil) } - mutating func visitF32ConvertI64U() throws { try encodeInstruction(0xB5, nil) } - mutating func visitF32DemoteF64() throws { try encodeInstruction(0xB6, nil) } - mutating func visitF64ConvertI32S() throws { try encodeInstruction(0xB7, nil) } - mutating func visitF64ConvertI32U() throws { try encodeInstruction(0xB8, nil) } - mutating func visitF64ConvertI64S() throws { try encodeInstruction(0xB9, nil) } - mutating func visitF64ConvertI64U() throws { try encodeInstruction(0xBA, nil) } - mutating func visitF64PromoteF32() throws { try encodeInstruction(0xBB, nil) } - mutating func visitI32ReinterpretF32() throws { try encodeInstruction(0xBC, nil) } - mutating func visitI64ReinterpretF64() throws { try encodeInstruction(0xBD, nil) } - mutating func visitF32ReinterpretI32() throws { try encodeInstruction(0xBE, nil) } - mutating func visitF64ReinterpretI64() throws { try encodeInstruction(0xBF, nil) } - mutating func visitI32Extend8S() throws { try encodeInstruction(0xC0, nil) } - mutating func visitI32Extend16S() throws { try encodeInstruction(0xC1, nil) } - mutating func visitI64Extend8S() throws { try encodeInstruction(0xC2, nil) } - mutating func visitI64Extend16S() throws { try encodeInstruction(0xC3, nil) } - mutating func visitI64Extend32S() throws { try encodeInstruction(0xC4, nil) } - mutating func visitMemoryInit(dataIndex: UInt32) throws { - try encodeInstruction(0x08, 0xFC) - try encodeImmediates(dataIndex: dataIndex) - } - mutating func visitDataDrop(dataIndex: UInt32) throws { - try encodeInstruction(0x09, 0xFC) - try encodeImmediates(dataIndex: dataIndex) - } - mutating func visitMemoryCopy(dstMem: UInt32, srcMem: UInt32) throws { - try encodeInstruction(0x0A, 0xFC) - try encodeImmediates(dstMem: dstMem, srcMem: srcMem) - } - mutating func visitMemoryFill(memory: UInt32) throws { - try encodeInstruction(0x0B, 0xFC) - try encodeImmediates(memory: memory) - } - mutating func visitTableInit(elemIndex: UInt32, table: UInt32) throws { - try encodeInstruction(0x0C, 0xFC) - try encodeImmediates(elemIndex: elemIndex, table: table) - } - mutating func visitElemDrop(elemIndex: UInt32) throws { - try encodeInstruction(0x0D, 0xFC) - try encodeImmediates(elemIndex: elemIndex) - } - mutating func visitTableCopy(dstTable: UInt32, srcTable: UInt32) throws { - try encodeInstruction(0x0E, 0xFC) - try encodeImmediates(dstTable: dstTable, srcTable: srcTable) - } - mutating func visitTableFill(table: UInt32) throws { - try encodeInstruction(0x11, 0xFC) - try encodeImmediates(table: table) - } - mutating func visitTableGet(table: UInt32) throws { - try encodeInstruction(0x25, nil) - try encodeImmediates(table: table) - } - mutating func visitTableSet(table: UInt32) throws { - try encodeInstruction(0x26, nil) - try encodeImmediates(table: table) - } - mutating func visitTableGrow(table: UInt32) throws { - try encodeInstruction(0x0F, 0xFC) - try encodeImmediates(table: table) - } - mutating func visitTableSize(table: UInt32) throws { - try encodeInstruction(0x10, 0xFC) - try encodeImmediates(table: table) - } - mutating func visitI32TruncSatF32S() throws { try encodeInstruction(0x00, 0xFC) } - mutating func visitI32TruncSatF32U() throws { try encodeInstruction(0x01, 0xFC) } - mutating func visitI32TruncSatF64S() throws { try encodeInstruction(0x02, 0xFC) } - mutating func visitI32TruncSatF64U() throws { try encodeInstruction(0x03, 0xFC) } - mutating func visitI64TruncSatF32S() throws { try encodeInstruction(0x04, 0xFC) } - mutating func visitI64TruncSatF32U() throws { try encodeInstruction(0x05, 0xFC) } - mutating func visitI64TruncSatF64S() throws { try encodeInstruction(0x06, 0xFC) } - mutating func visitI64TruncSatF64U() throws { try encodeInstruction(0x07, 0xFC) } -} diff --git a/Sources/WAT/Lexer.swift b/Sources/WAT/Lexer.swift index e914b47a..15ebbf38 100644 --- a/Sources/WAT/Lexer.swift +++ b/Sources/WAT/Lexer.swift @@ -306,7 +306,10 @@ struct Lexer { private mutating func lexBlockComment() throws -> TokenKind { var level = 1 while true { - switch try cursor.next() { + guard let char = try cursor.next() else { + throw cursor.unexpectedEof() + } + switch char { case "(": if try cursor.peek() == ";" { // Nested comment block diff --git a/Sources/WAT/ParseInstruction.swift b/Sources/WAT/ParseInstruction.swift deleted file mode 100644 index 4cb90805..00000000 --- a/Sources/WAT/ParseInstruction.swift +++ /dev/null @@ -1,331 +0,0 @@ -// swift-format-ignore-file -//// Automatically generated by Utilities/Sources/WasmGen.swift -//// DO NOT EDIT DIRECTLY - -import WasmParser -import WasmTypes - -/// Parses a text instruction, consuming immediate tokens as necessary. -/// - Parameters: -/// - keyword: The keyword of the instruction. -/// - expressionParser: The expression parser. -/// - Returns: A closure that invokes the corresponding visitor method. Nil if the keyword is not recognized. -/// -/// Note: The returned closure does not consume any tokens. -func parseTextInstruction(keyword: String, expressionParser: inout ExpressionParser, wat: inout Wat) throws -> ((inout V) throws -> Void)? { - switch keyword { - case "unreachable": return { return try $0.visitUnreachable() } - case "nop": return { return try $0.visitNop() } - case "block": - let (blockType) = try expressionParser.visitBlock(wat: &wat) - return { return try $0.visitBlock(blockType: blockType) } - case "loop": - let (blockType) = try expressionParser.visitLoop(wat: &wat) - return { return try $0.visitLoop(blockType: blockType) } - case "if": - let (blockType) = try expressionParser.visitIf(wat: &wat) - return { return try $0.visitIf(blockType: blockType) } - case "else": return { return try $0.visitElse() } - case "end": return { return try $0.visitEnd() } - case "br": - let (relativeDepth) = try expressionParser.visitBr(wat: &wat) - return { return try $0.visitBr(relativeDepth: relativeDepth) } - case "br_if": - let (relativeDepth) = try expressionParser.visitBrIf(wat: &wat) - return { return try $0.visitBrIf(relativeDepth: relativeDepth) } - case "br_table": - let (targets) = try expressionParser.visitBrTable(wat: &wat) - return { return try $0.visitBrTable(targets: targets) } - case "return": return { return try $0.visitReturn() } - case "call": - let (functionIndex) = try expressionParser.visitCall(wat: &wat) - return { return try $0.visitCall(functionIndex: functionIndex) } - case "call_indirect": - let (typeIndex, tableIndex) = try expressionParser.visitCallIndirect(wat: &wat) - return { return try $0.visitCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex) } - case "drop": return { return try $0.visitDrop() } - case "select": return { return try $0.visitSelect() } - case "local.get": - let (localIndex) = try expressionParser.visitLocalGet(wat: &wat) - return { return try $0.visitLocalGet(localIndex: localIndex) } - case "local.set": - let (localIndex) = try expressionParser.visitLocalSet(wat: &wat) - return { return try $0.visitLocalSet(localIndex: localIndex) } - case "local.tee": - let (localIndex) = try expressionParser.visitLocalTee(wat: &wat) - return { return try $0.visitLocalTee(localIndex: localIndex) } - case "global.get": - let (globalIndex) = try expressionParser.visitGlobalGet(wat: &wat) - return { return try $0.visitGlobalGet(globalIndex: globalIndex) } - case "global.set": - let (globalIndex) = try expressionParser.visitGlobalSet(wat: &wat) - return { return try $0.visitGlobalSet(globalIndex: globalIndex) } - case "i32.load": - let (memarg) = try expressionParser.visitI32Load(wat: &wat) - return { return try $0.visitI32Load(memarg: memarg) } - case "i64.load": - let (memarg) = try expressionParser.visitI64Load(wat: &wat) - return { return try $0.visitI64Load(memarg: memarg) } - case "f32.load": - let (memarg) = try expressionParser.visitF32Load(wat: &wat) - return { return try $0.visitF32Load(memarg: memarg) } - case "f64.load": - let (memarg) = try expressionParser.visitF64Load(wat: &wat) - return { return try $0.visitF64Load(memarg: memarg) } - case "i32.load8_s": - let (memarg) = try expressionParser.visitI32Load8S(wat: &wat) - return { return try $0.visitI32Load8S(memarg: memarg) } - case "i32.load8_u": - let (memarg) = try expressionParser.visitI32Load8U(wat: &wat) - return { return try $0.visitI32Load8U(memarg: memarg) } - case "i32.load16_s": - let (memarg) = try expressionParser.visitI32Load16S(wat: &wat) - return { return try $0.visitI32Load16S(memarg: memarg) } - case "i32.load16_u": - let (memarg) = try expressionParser.visitI32Load16U(wat: &wat) - return { return try $0.visitI32Load16U(memarg: memarg) } - case "i64.load8_s": - let (memarg) = try expressionParser.visitI64Load8S(wat: &wat) - return { return try $0.visitI64Load8S(memarg: memarg) } - case "i64.load8_u": - let (memarg) = try expressionParser.visitI64Load8U(wat: &wat) - return { return try $0.visitI64Load8U(memarg: memarg) } - case "i64.load16_s": - let (memarg) = try expressionParser.visitI64Load16S(wat: &wat) - return { return try $0.visitI64Load16S(memarg: memarg) } - case "i64.load16_u": - let (memarg) = try expressionParser.visitI64Load16U(wat: &wat) - return { return try $0.visitI64Load16U(memarg: memarg) } - case "i64.load32_s": - let (memarg) = try expressionParser.visitI64Load32S(wat: &wat) - return { return try $0.visitI64Load32S(memarg: memarg) } - case "i64.load32_u": - let (memarg) = try expressionParser.visitI64Load32U(wat: &wat) - return { return try $0.visitI64Load32U(memarg: memarg) } - case "i32.store": - let (memarg) = try expressionParser.visitI32Store(wat: &wat) - return { return try $0.visitI32Store(memarg: memarg) } - case "i64.store": - let (memarg) = try expressionParser.visitI64Store(wat: &wat) - return { return try $0.visitI64Store(memarg: memarg) } - case "f32.store": - let (memarg) = try expressionParser.visitF32Store(wat: &wat) - return { return try $0.visitF32Store(memarg: memarg) } - case "f64.store": - let (memarg) = try expressionParser.visitF64Store(wat: &wat) - return { return try $0.visitF64Store(memarg: memarg) } - case "i32.store8": - let (memarg) = try expressionParser.visitI32Store8(wat: &wat) - return { return try $0.visitI32Store8(memarg: memarg) } - case "i32.store16": - let (memarg) = try expressionParser.visitI32Store16(wat: &wat) - return { return try $0.visitI32Store16(memarg: memarg) } - case "i64.store8": - let (memarg) = try expressionParser.visitI64Store8(wat: &wat) - return { return try $0.visitI64Store8(memarg: memarg) } - case "i64.store16": - let (memarg) = try expressionParser.visitI64Store16(wat: &wat) - return { return try $0.visitI64Store16(memarg: memarg) } - case "i64.store32": - let (memarg) = try expressionParser.visitI64Store32(wat: &wat) - return { return try $0.visitI64Store32(memarg: memarg) } - case "memory.size": - let (memory) = try expressionParser.visitMemorySize(wat: &wat) - return { return try $0.visitMemorySize(memory: memory) } - case "memory.grow": - let (memory) = try expressionParser.visitMemoryGrow(wat: &wat) - return { return try $0.visitMemoryGrow(memory: memory) } - case "i32.const": - let (value) = try expressionParser.visitI32Const(wat: &wat) - return { return try $0.visitI32Const(value: value) } - case "i64.const": - let (value) = try expressionParser.visitI64Const(wat: &wat) - return { return try $0.visitI64Const(value: value) } - case "f32.const": - let (value) = try expressionParser.visitF32Const(wat: &wat) - return { return try $0.visitF32Const(value: value) } - case "f64.const": - let (value) = try expressionParser.visitF64Const(wat: &wat) - return { return try $0.visitF64Const(value: value) } - case "ref.null": - let (type) = try expressionParser.visitRefNull(wat: &wat) - return { return try $0.visitRefNull(type: type) } - case "ref.is_null": return { return try $0.visitRefIsNull() } - case "ref.func": - let (functionIndex) = try expressionParser.visitRefFunc(wat: &wat) - return { return try $0.visitRefFunc(functionIndex: functionIndex) } - case "i32.eqz": return { return try $0.visitI32Eqz() } - case "i32.eq": return { return try $0.visitI32Eq() } - case "i32.ne": return { return try $0.visitI32Ne() } - case "i32.lt_s": return { return try $0.visitI32LtS() } - case "i32.lt_u": return { return try $0.visitI32LtU() } - case "i32.gt_s": return { return try $0.visitI32GtS() } - case "i32.gt_u": return { return try $0.visitI32GtU() } - case "i32.le_s": return { return try $0.visitI32LeS() } - case "i32.le_u": return { return try $0.visitI32LeU() } - case "i32.ge_s": return { return try $0.visitI32GeS() } - case "i32.ge_u": return { return try $0.visitI32GeU() } - case "i64.eqz": return { return try $0.visitI64Eqz() } - case "i64.eq": return { return try $0.visitI64Eq() } - case "i64.ne": return { return try $0.visitI64Ne() } - case "i64.lt_s": return { return try $0.visitI64LtS() } - case "i64.lt_u": return { return try $0.visitI64LtU() } - case "i64.gt_s": return { return try $0.visitI64GtS() } - case "i64.gt_u": return { return try $0.visitI64GtU() } - case "i64.le_s": return { return try $0.visitI64LeS() } - case "i64.le_u": return { return try $0.visitI64LeU() } - case "i64.ge_s": return { return try $0.visitI64GeS() } - case "i64.ge_u": return { return try $0.visitI64GeU() } - case "f32.eq": return { return try $0.visitF32Eq() } - case "f32.ne": return { return try $0.visitF32Ne() } - case "f32.lt": return { return try $0.visitF32Lt() } - case "f32.gt": return { return try $0.visitF32Gt() } - case "f32.le": return { return try $0.visitF32Le() } - case "f32.ge": return { return try $0.visitF32Ge() } - case "f64.eq": return { return try $0.visitF64Eq() } - case "f64.ne": return { return try $0.visitF64Ne() } - case "f64.lt": return { return try $0.visitF64Lt() } - case "f64.gt": return { return try $0.visitF64Gt() } - case "f64.le": return { return try $0.visitF64Le() } - case "f64.ge": return { return try $0.visitF64Ge() } - case "i32.clz": return { return try $0.visitI32Clz() } - case "i32.ctz": return { return try $0.visitI32Ctz() } - case "i32.popcnt": return { return try $0.visitI32Popcnt() } - case "i32.add": return { return try $0.visitI32Add() } - case "i32.sub": return { return try $0.visitI32Sub() } - case "i32.mul": return { return try $0.visitI32Mul() } - case "i32.div_s": return { return try $0.visitI32DivS() } - case "i32.div_u": return { return try $0.visitI32DivU() } - case "i32.rem_s": return { return try $0.visitI32RemS() } - case "i32.rem_u": return { return try $0.visitI32RemU() } - case "i32.and": return { return try $0.visitI32And() } - case "i32.or": return { return try $0.visitI32Or() } - case "i32.xor": return { return try $0.visitI32Xor() } - case "i32.shl": return { return try $0.visitI32Shl() } - case "i32.shr_s": return { return try $0.visitI32ShrS() } - case "i32.shr_u": return { return try $0.visitI32ShrU() } - case "i32.rotl": return { return try $0.visitI32Rotl() } - case "i32.rotr": return { return try $0.visitI32Rotr() } - case "i64.clz": return { return try $0.visitI64Clz() } - case "i64.ctz": return { return try $0.visitI64Ctz() } - case "i64.popcnt": return { return try $0.visitI64Popcnt() } - case "i64.add": return { return try $0.visitI64Add() } - case "i64.sub": return { return try $0.visitI64Sub() } - case "i64.mul": return { return try $0.visitI64Mul() } - case "i64.div_s": return { return try $0.visitI64DivS() } - case "i64.div_u": return { return try $0.visitI64DivU() } - case "i64.rem_s": return { return try $0.visitI64RemS() } - case "i64.rem_u": return { return try $0.visitI64RemU() } - case "i64.and": return { return try $0.visitI64And() } - case "i64.or": return { return try $0.visitI64Or() } - case "i64.xor": return { return try $0.visitI64Xor() } - case "i64.shl": return { return try $0.visitI64Shl() } - case "i64.shr_s": return { return try $0.visitI64ShrS() } - case "i64.shr_u": return { return try $0.visitI64ShrU() } - case "i64.rotl": return { return try $0.visitI64Rotl() } - case "i64.rotr": return { return try $0.visitI64Rotr() } - case "f32.abs": return { return try $0.visitF32Abs() } - case "f32.neg": return { return try $0.visitF32Neg() } - case "f32.ceil": return { return try $0.visitF32Ceil() } - case "f32.floor": return { return try $0.visitF32Floor() } - case "f32.trunc": return { return try $0.visitF32Trunc() } - case "f32.nearest": return { return try $0.visitF32Nearest() } - case "f32.sqrt": return { return try $0.visitF32Sqrt() } - case "f32.add": return { return try $0.visitF32Add() } - case "f32.sub": return { return try $0.visitF32Sub() } - case "f32.mul": return { return try $0.visitF32Mul() } - case "f32.div": return { return try $0.visitF32Div() } - case "f32.min": return { return try $0.visitF32Min() } - case "f32.max": return { return try $0.visitF32Max() } - case "f32.copysign": return { return try $0.visitF32Copysign() } - case "f64.abs": return { return try $0.visitF64Abs() } - case "f64.neg": return { return try $0.visitF64Neg() } - case "f64.ceil": return { return try $0.visitF64Ceil() } - case "f64.floor": return { return try $0.visitF64Floor() } - case "f64.trunc": return { return try $0.visitF64Trunc() } - case "f64.nearest": return { return try $0.visitF64Nearest() } - case "f64.sqrt": return { return try $0.visitF64Sqrt() } - case "f64.add": return { return try $0.visitF64Add() } - case "f64.sub": return { return try $0.visitF64Sub() } - case "f64.mul": return { return try $0.visitF64Mul() } - case "f64.div": return { return try $0.visitF64Div() } - case "f64.min": return { return try $0.visitF64Min() } - case "f64.max": return { return try $0.visitF64Max() } - case "f64.copysign": return { return try $0.visitF64Copysign() } - case "i32.wrap_i64": return { return try $0.visitI32WrapI64() } - case "i32.trunc_f32_s": return { return try $0.visitI32TruncF32S() } - case "i32.trunc_f32_u": return { return try $0.visitI32TruncF32U() } - case "i32.trunc_f64_s": return { return try $0.visitI32TruncF64S() } - case "i32.trunc_f64_u": return { return try $0.visitI32TruncF64U() } - case "i64.extend_i32_s": return { return try $0.visitI64ExtendI32S() } - case "i64.extend_i32_u": return { return try $0.visitI64ExtendI32U() } - case "i64.trunc_f32_s": return { return try $0.visitI64TruncF32S() } - case "i64.trunc_f32_u": return { return try $0.visitI64TruncF32U() } - case "i64.trunc_f64_s": return { return try $0.visitI64TruncF64S() } - case "i64.trunc_f64_u": return { return try $0.visitI64TruncF64U() } - case "f32.convert_i32_s": return { return try $0.visitF32ConvertI32S() } - case "f32.convert_i32_u": return { return try $0.visitF32ConvertI32U() } - case "f32.convert_i64_s": return { return try $0.visitF32ConvertI64S() } - case "f32.convert_i64_u": return { return try $0.visitF32ConvertI64U() } - case "f32.demote_f64": return { return try $0.visitF32DemoteF64() } - case "f64.convert_i32_s": return { return try $0.visitF64ConvertI32S() } - case "f64.convert_i32_u": return { return try $0.visitF64ConvertI32U() } - case "f64.convert_i64_s": return { return try $0.visitF64ConvertI64S() } - case "f64.convert_i64_u": return { return try $0.visitF64ConvertI64U() } - case "f64.promote_f32": return { return try $0.visitF64PromoteF32() } - case "i32.reinterpret_f32": return { return try $0.visitI32ReinterpretF32() } - case "i64.reinterpret_f64": return { return try $0.visitI64ReinterpretF64() } - case "f32.reinterpret_i32": return { return try $0.visitF32ReinterpretI32() } - case "f64.reinterpret_i64": return { return try $0.visitF64ReinterpretI64() } - case "i32.extend8_s": return { return try $0.visitI32Extend8S() } - case "i32.extend16_s": return { return try $0.visitI32Extend16S() } - case "i64.extend8_s": return { return try $0.visitI64Extend8S() } - case "i64.extend16_s": return { return try $0.visitI64Extend16S() } - case "i64.extend32_s": return { return try $0.visitI64Extend32S() } - case "memory.init": - let (dataIndex) = try expressionParser.visitMemoryInit(wat: &wat) - return { return try $0.visitMemoryInit(dataIndex: dataIndex) } - case "data.drop": - let (dataIndex) = try expressionParser.visitDataDrop(wat: &wat) - return { return try $0.visitDataDrop(dataIndex: dataIndex) } - case "memory.copy": - let (dstMem, srcMem) = try expressionParser.visitMemoryCopy(wat: &wat) - return { return try $0.visitMemoryCopy(dstMem: dstMem, srcMem: srcMem) } - case "memory.fill": - let (memory) = try expressionParser.visitMemoryFill(wat: &wat) - return { return try $0.visitMemoryFill(memory: memory) } - case "table.init": - let (elemIndex, table) = try expressionParser.visitTableInit(wat: &wat) - return { return try $0.visitTableInit(elemIndex: elemIndex, table: table) } - case "elem.drop": - let (elemIndex) = try expressionParser.visitElemDrop(wat: &wat) - return { return try $0.visitElemDrop(elemIndex: elemIndex) } - case "table.copy": - let (dstTable, srcTable) = try expressionParser.visitTableCopy(wat: &wat) - return { return try $0.visitTableCopy(dstTable: dstTable, srcTable: srcTable) } - case "table.fill": - let (table) = try expressionParser.visitTableFill(wat: &wat) - return { return try $0.visitTableFill(table: table) } - case "table.get": - let (table) = try expressionParser.visitTableGet(wat: &wat) - return { return try $0.visitTableGet(table: table) } - case "table.set": - let (table) = try expressionParser.visitTableSet(wat: &wat) - return { return try $0.visitTableSet(table: table) } - case "table.grow": - let (table) = try expressionParser.visitTableGrow(wat: &wat) - return { return try $0.visitTableGrow(table: table) } - case "table.size": - let (table) = try expressionParser.visitTableSize(wat: &wat) - return { return try $0.visitTableSize(table: table) } - case "i32.trunc_sat_f32_s": return { return try $0.visitI32TruncSatF32S() } - case "i32.trunc_sat_f32_u": return { return try $0.visitI32TruncSatF32U() } - case "i32.trunc_sat_f64_s": return { return try $0.visitI32TruncSatF64S() } - case "i32.trunc_sat_f64_u": return { return try $0.visitI32TruncSatF64U() } - case "i64.trunc_sat_f32_s": return { return try $0.visitI64TruncSatF32S() } - case "i64.trunc_sat_f32_u": return { return try $0.visitI64TruncSatF32U() } - case "i64.trunc_sat_f64_s": return { return try $0.visitI64TruncSatF64S() } - case "i64.trunc_sat_f64_u": return { return try $0.visitI64TruncSatF64U() } - default: return nil - } -} diff --git a/Sources/WAT/ParseTextInstruction.swift b/Sources/WAT/ParseTextInstruction.swift new file mode 100644 index 00000000..fb57a2e3 --- /dev/null +++ b/Sources/WAT/ParseTextInstruction.swift @@ -0,0 +1,337 @@ +// swift-format-ignore-file +//// Automatically generated by Utilities/Sources/WasmGen.swift +//// DO NOT EDIT DIRECTLY + +import WasmParser +import WasmTypes + +/// Parses a text instruction, consuming immediate tokens as necessary. +/// - Parameters: +/// - keyword: The keyword of the instruction. +/// - expressionParser: The expression parser. +/// - Returns: A closure that invokes the corresponding visitor method. Nil if the keyword is not recognized. +/// +/// Note: The returned closure does not consume any tokens. +func parseTextInstruction(keyword: String, expressionParser: inout ExpressionParser, wat: inout Wat) throws -> ((inout V) throws -> Void)? { + switch keyword { + case "unreachable": return { return try $0.visitUnreachable() } + case "nop": return { return try $0.visitNop() } + case "block": + let (blockType) = try expressionParser.visitBlock(wat: &wat) + return { return try $0.visitBlock(blockType: blockType) } + case "loop": + let (blockType) = try expressionParser.visitLoop(wat: &wat) + return { return try $0.visitLoop(blockType: blockType) } + case "if": + let (blockType) = try expressionParser.visitIf(wat: &wat) + return { return try $0.visitIf(blockType: blockType) } + case "else": return { return try $0.visitElse() } + case "end": return { return try $0.visitEnd() } + case "br": + let (relativeDepth) = try expressionParser.visitBr(wat: &wat) + return { return try $0.visitBr(relativeDepth: relativeDepth) } + case "br_if": + let (relativeDepth) = try expressionParser.visitBrIf(wat: &wat) + return { return try $0.visitBrIf(relativeDepth: relativeDepth) } + case "br_table": + let (targets) = try expressionParser.visitBrTable(wat: &wat) + return { return try $0.visitBrTable(targets: targets) } + case "return": return { return try $0.visitReturn() } + case "call": + let (functionIndex) = try expressionParser.visitCall(wat: &wat) + return { return try $0.visitCall(functionIndex: functionIndex) } + case "call_indirect": + let (typeIndex, tableIndex) = try expressionParser.visitCallIndirect(wat: &wat) + return { return try $0.visitCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex) } + case "return_call": + let (functionIndex) = try expressionParser.visitReturnCall(wat: &wat) + return { return try $0.visitReturnCall(functionIndex: functionIndex) } + case "return_call_indirect": + let (typeIndex, tableIndex) = try expressionParser.visitReturnCallIndirect(wat: &wat) + return { return try $0.visitReturnCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex) } + case "drop": return { return try $0.visitDrop() } + case "select": return { return try $0.visitSelect() } + case "local.get": + let (localIndex) = try expressionParser.visitLocalGet(wat: &wat) + return { return try $0.visitLocalGet(localIndex: localIndex) } + case "local.set": + let (localIndex) = try expressionParser.visitLocalSet(wat: &wat) + return { return try $0.visitLocalSet(localIndex: localIndex) } + case "local.tee": + let (localIndex) = try expressionParser.visitLocalTee(wat: &wat) + return { return try $0.visitLocalTee(localIndex: localIndex) } + case "global.get": + let (globalIndex) = try expressionParser.visitGlobalGet(wat: &wat) + return { return try $0.visitGlobalGet(globalIndex: globalIndex) } + case "global.set": + let (globalIndex) = try expressionParser.visitGlobalSet(wat: &wat) + return { return try $0.visitGlobalSet(globalIndex: globalIndex) } + case "i32.load": + let (memarg) = try expressionParser.visitLoad(.i32Load, wat: &wat) + return { return try $0.visitLoad(.i32Load, memarg: memarg) } + case "i64.load": + let (memarg) = try expressionParser.visitLoad(.i64Load, wat: &wat) + return { return try $0.visitLoad(.i64Load, memarg: memarg) } + case "f32.load": + let (memarg) = try expressionParser.visitLoad(.f32Load, wat: &wat) + return { return try $0.visitLoad(.f32Load, memarg: memarg) } + case "f64.load": + let (memarg) = try expressionParser.visitLoad(.f64Load, wat: &wat) + return { return try $0.visitLoad(.f64Load, memarg: memarg) } + case "i32.load8_s": + let (memarg) = try expressionParser.visitLoad(.i32Load8S, wat: &wat) + return { return try $0.visitLoad(.i32Load8S, memarg: memarg) } + case "i32.load8_u": + let (memarg) = try expressionParser.visitLoad(.i32Load8U, wat: &wat) + return { return try $0.visitLoad(.i32Load8U, memarg: memarg) } + case "i32.load16_s": + let (memarg) = try expressionParser.visitLoad(.i32Load16S, wat: &wat) + return { return try $0.visitLoad(.i32Load16S, memarg: memarg) } + case "i32.load16_u": + let (memarg) = try expressionParser.visitLoad(.i32Load16U, wat: &wat) + return { return try $0.visitLoad(.i32Load16U, memarg: memarg) } + case "i64.load8_s": + let (memarg) = try expressionParser.visitLoad(.i64Load8S, wat: &wat) + return { return try $0.visitLoad(.i64Load8S, memarg: memarg) } + case "i64.load8_u": + let (memarg) = try expressionParser.visitLoad(.i64Load8U, wat: &wat) + return { return try $0.visitLoad(.i64Load8U, memarg: memarg) } + case "i64.load16_s": + let (memarg) = try expressionParser.visitLoad(.i64Load16S, wat: &wat) + return { return try $0.visitLoad(.i64Load16S, memarg: memarg) } + case "i64.load16_u": + let (memarg) = try expressionParser.visitLoad(.i64Load16U, wat: &wat) + return { return try $0.visitLoad(.i64Load16U, memarg: memarg) } + case "i64.load32_s": + let (memarg) = try expressionParser.visitLoad(.i64Load32S, wat: &wat) + return { return try $0.visitLoad(.i64Load32S, memarg: memarg) } + case "i64.load32_u": + let (memarg) = try expressionParser.visitLoad(.i64Load32U, wat: &wat) + return { return try $0.visitLoad(.i64Load32U, memarg: memarg) } + case "i32.store": + let (memarg) = try expressionParser.visitStore(.i32Store, wat: &wat) + return { return try $0.visitStore(.i32Store, memarg: memarg) } + case "i64.store": + let (memarg) = try expressionParser.visitStore(.i64Store, wat: &wat) + return { return try $0.visitStore(.i64Store, memarg: memarg) } + case "f32.store": + let (memarg) = try expressionParser.visitStore(.f32Store, wat: &wat) + return { return try $0.visitStore(.f32Store, memarg: memarg) } + case "f64.store": + let (memarg) = try expressionParser.visitStore(.f64Store, wat: &wat) + return { return try $0.visitStore(.f64Store, memarg: memarg) } + case "i32.store8": + let (memarg) = try expressionParser.visitStore(.i32Store8, wat: &wat) + return { return try $0.visitStore(.i32Store8, memarg: memarg) } + case "i32.store16": + let (memarg) = try expressionParser.visitStore(.i32Store16, wat: &wat) + return { return try $0.visitStore(.i32Store16, memarg: memarg) } + case "i64.store8": + let (memarg) = try expressionParser.visitStore(.i64Store8, wat: &wat) + return { return try $0.visitStore(.i64Store8, memarg: memarg) } + case "i64.store16": + let (memarg) = try expressionParser.visitStore(.i64Store16, wat: &wat) + return { return try $0.visitStore(.i64Store16, memarg: memarg) } + case "i64.store32": + let (memarg) = try expressionParser.visitStore(.i64Store32, wat: &wat) + return { return try $0.visitStore(.i64Store32, memarg: memarg) } + case "memory.size": + let (memory) = try expressionParser.visitMemorySize(wat: &wat) + return { return try $0.visitMemorySize(memory: memory) } + case "memory.grow": + let (memory) = try expressionParser.visitMemoryGrow(wat: &wat) + return { return try $0.visitMemoryGrow(memory: memory) } + case "i32.const": + let (value) = try expressionParser.visitI32Const(wat: &wat) + return { return try $0.visitI32Const(value: value) } + case "i64.const": + let (value) = try expressionParser.visitI64Const(wat: &wat) + return { return try $0.visitI64Const(value: value) } + case "f32.const": + let (value) = try expressionParser.visitF32Const(wat: &wat) + return { return try $0.visitF32Const(value: value) } + case "f64.const": + let (value) = try expressionParser.visitF64Const(wat: &wat) + return { return try $0.visitF64Const(value: value) } + case "ref.null": + let (type) = try expressionParser.visitRefNull(wat: &wat) + return { return try $0.visitRefNull(type: type) } + case "ref.is_null": return { return try $0.visitRefIsNull() } + case "ref.func": + let (functionIndex) = try expressionParser.visitRefFunc(wat: &wat) + return { return try $0.visitRefFunc(functionIndex: functionIndex) } + case "i32.eqz": return { return try $0.visitI32Eqz() } + case "i32.eq": return { return try $0.visitCmp(.i32Eq) } + case "i32.ne": return { return try $0.visitCmp(.i32Ne) } + case "i32.lt_s": return { return try $0.visitCmp(.i32LtS) } + case "i32.lt_u": return { return try $0.visitCmp(.i32LtU) } + case "i32.gt_s": return { return try $0.visitCmp(.i32GtS) } + case "i32.gt_u": return { return try $0.visitCmp(.i32GtU) } + case "i32.le_s": return { return try $0.visitCmp(.i32LeS) } + case "i32.le_u": return { return try $0.visitCmp(.i32LeU) } + case "i32.ge_s": return { return try $0.visitCmp(.i32GeS) } + case "i32.ge_u": return { return try $0.visitCmp(.i32GeU) } + case "i64.eqz": return { return try $0.visitI64Eqz() } + case "i64.eq": return { return try $0.visitCmp(.i64Eq) } + case "i64.ne": return { return try $0.visitCmp(.i64Ne) } + case "i64.lt_s": return { return try $0.visitCmp(.i64LtS) } + case "i64.lt_u": return { return try $0.visitCmp(.i64LtU) } + case "i64.gt_s": return { return try $0.visitCmp(.i64GtS) } + case "i64.gt_u": return { return try $0.visitCmp(.i64GtU) } + case "i64.le_s": return { return try $0.visitCmp(.i64LeS) } + case "i64.le_u": return { return try $0.visitCmp(.i64LeU) } + case "i64.ge_s": return { return try $0.visitCmp(.i64GeS) } + case "i64.ge_u": return { return try $0.visitCmp(.i64GeU) } + case "f32.eq": return { return try $0.visitCmp(.f32Eq) } + case "f32.ne": return { return try $0.visitCmp(.f32Ne) } + case "f32.lt": return { return try $0.visitCmp(.f32Lt) } + case "f32.gt": return { return try $0.visitCmp(.f32Gt) } + case "f32.le": return { return try $0.visitCmp(.f32Le) } + case "f32.ge": return { return try $0.visitCmp(.f32Ge) } + case "f64.eq": return { return try $0.visitCmp(.f64Eq) } + case "f64.ne": return { return try $0.visitCmp(.f64Ne) } + case "f64.lt": return { return try $0.visitCmp(.f64Lt) } + case "f64.gt": return { return try $0.visitCmp(.f64Gt) } + case "f64.le": return { return try $0.visitCmp(.f64Le) } + case "f64.ge": return { return try $0.visitCmp(.f64Ge) } + case "i32.clz": return { return try $0.visitUnary(.i32Clz) } + case "i32.ctz": return { return try $0.visitUnary(.i32Ctz) } + case "i32.popcnt": return { return try $0.visitUnary(.i32Popcnt) } + case "i32.add": return { return try $0.visitBinary(.i32Add) } + case "i32.sub": return { return try $0.visitBinary(.i32Sub) } + case "i32.mul": return { return try $0.visitBinary(.i32Mul) } + case "i32.div_s": return { return try $0.visitBinary(.i32DivS) } + case "i32.div_u": return { return try $0.visitBinary(.i32DivU) } + case "i32.rem_s": return { return try $0.visitBinary(.i32RemS) } + case "i32.rem_u": return { return try $0.visitBinary(.i32RemU) } + case "i32.and": return { return try $0.visitBinary(.i32And) } + case "i32.or": return { return try $0.visitBinary(.i32Or) } + case "i32.xor": return { return try $0.visitBinary(.i32Xor) } + case "i32.shl": return { return try $0.visitBinary(.i32Shl) } + case "i32.shr_s": return { return try $0.visitBinary(.i32ShrS) } + case "i32.shr_u": return { return try $0.visitBinary(.i32ShrU) } + case "i32.rotl": return { return try $0.visitBinary(.i32Rotl) } + case "i32.rotr": return { return try $0.visitBinary(.i32Rotr) } + case "i64.clz": return { return try $0.visitUnary(.i64Clz) } + case "i64.ctz": return { return try $0.visitUnary(.i64Ctz) } + case "i64.popcnt": return { return try $0.visitUnary(.i64Popcnt) } + case "i64.add": return { return try $0.visitBinary(.i64Add) } + case "i64.sub": return { return try $0.visitBinary(.i64Sub) } + case "i64.mul": return { return try $0.visitBinary(.i64Mul) } + case "i64.div_s": return { return try $0.visitBinary(.i64DivS) } + case "i64.div_u": return { return try $0.visitBinary(.i64DivU) } + case "i64.rem_s": return { return try $0.visitBinary(.i64RemS) } + case "i64.rem_u": return { return try $0.visitBinary(.i64RemU) } + case "i64.and": return { return try $0.visitBinary(.i64And) } + case "i64.or": return { return try $0.visitBinary(.i64Or) } + case "i64.xor": return { return try $0.visitBinary(.i64Xor) } + case "i64.shl": return { return try $0.visitBinary(.i64Shl) } + case "i64.shr_s": return { return try $0.visitBinary(.i64ShrS) } + case "i64.shr_u": return { return try $0.visitBinary(.i64ShrU) } + case "i64.rotl": return { return try $0.visitBinary(.i64Rotl) } + case "i64.rotr": return { return try $0.visitBinary(.i64Rotr) } + case "f32.abs": return { return try $0.visitUnary(.f32Abs) } + case "f32.neg": return { return try $0.visitUnary(.f32Neg) } + case "f32.ceil": return { return try $0.visitUnary(.f32Ceil) } + case "f32.floor": return { return try $0.visitUnary(.f32Floor) } + case "f32.trunc": return { return try $0.visitUnary(.f32Trunc) } + case "f32.nearest": return { return try $0.visitUnary(.f32Nearest) } + case "f32.sqrt": return { return try $0.visitUnary(.f32Sqrt) } + case "f32.add": return { return try $0.visitBinary(.f32Add) } + case "f32.sub": return { return try $0.visitBinary(.f32Sub) } + case "f32.mul": return { return try $0.visitBinary(.f32Mul) } + case "f32.div": return { return try $0.visitBinary(.f32Div) } + case "f32.min": return { return try $0.visitBinary(.f32Min) } + case "f32.max": return { return try $0.visitBinary(.f32Max) } + case "f32.copysign": return { return try $0.visitBinary(.f32Copysign) } + case "f64.abs": return { return try $0.visitUnary(.f64Abs) } + case "f64.neg": return { return try $0.visitUnary(.f64Neg) } + case "f64.ceil": return { return try $0.visitUnary(.f64Ceil) } + case "f64.floor": return { return try $0.visitUnary(.f64Floor) } + case "f64.trunc": return { return try $0.visitUnary(.f64Trunc) } + case "f64.nearest": return { return try $0.visitUnary(.f64Nearest) } + case "f64.sqrt": return { return try $0.visitUnary(.f64Sqrt) } + case "f64.add": return { return try $0.visitBinary(.f64Add) } + case "f64.sub": return { return try $0.visitBinary(.f64Sub) } + case "f64.mul": return { return try $0.visitBinary(.f64Mul) } + case "f64.div": return { return try $0.visitBinary(.f64Div) } + case "f64.min": return { return try $0.visitBinary(.f64Min) } + case "f64.max": return { return try $0.visitBinary(.f64Max) } + case "f64.copysign": return { return try $0.visitBinary(.f64Copysign) } + case "i32.wrap_i64": return { return try $0.visitConversion(.i32WrapI64) } + case "i32.trunc_f32_s": return { return try $0.visitConversion(.i32TruncF32S) } + case "i32.trunc_f32_u": return { return try $0.visitConversion(.i32TruncF32U) } + case "i32.trunc_f64_s": return { return try $0.visitConversion(.i32TruncF64S) } + case "i32.trunc_f64_u": return { return try $0.visitConversion(.i32TruncF64U) } + case "i64.extend_i32_s": return { return try $0.visitConversion(.i64ExtendI32S) } + case "i64.extend_i32_u": return { return try $0.visitConversion(.i64ExtendI32U) } + case "i64.trunc_f32_s": return { return try $0.visitConversion(.i64TruncF32S) } + case "i64.trunc_f32_u": return { return try $0.visitConversion(.i64TruncF32U) } + case "i64.trunc_f64_s": return { return try $0.visitConversion(.i64TruncF64S) } + case "i64.trunc_f64_u": return { return try $0.visitConversion(.i64TruncF64U) } + case "f32.convert_i32_s": return { return try $0.visitConversion(.f32ConvertI32S) } + case "f32.convert_i32_u": return { return try $0.visitConversion(.f32ConvertI32U) } + case "f32.convert_i64_s": return { return try $0.visitConversion(.f32ConvertI64S) } + case "f32.convert_i64_u": return { return try $0.visitConversion(.f32ConvertI64U) } + case "f32.demote_f64": return { return try $0.visitConversion(.f32DemoteF64) } + case "f64.convert_i32_s": return { return try $0.visitConversion(.f64ConvertI32S) } + case "f64.convert_i32_u": return { return try $0.visitConversion(.f64ConvertI32U) } + case "f64.convert_i64_s": return { return try $0.visitConversion(.f64ConvertI64S) } + case "f64.convert_i64_u": return { return try $0.visitConversion(.f64ConvertI64U) } + case "f64.promote_f32": return { return try $0.visitConversion(.f64PromoteF32) } + case "i32.reinterpret_f32": return { return try $0.visitConversion(.i32ReinterpretF32) } + case "i64.reinterpret_f64": return { return try $0.visitConversion(.i64ReinterpretF64) } + case "f32.reinterpret_i32": return { return try $0.visitConversion(.f32ReinterpretI32) } + case "f64.reinterpret_i64": return { return try $0.visitConversion(.f64ReinterpretI64) } + case "i32.extend8_s": return { return try $0.visitUnary(.i32Extend8S) } + case "i32.extend16_s": return { return try $0.visitUnary(.i32Extend16S) } + case "i64.extend8_s": return { return try $0.visitUnary(.i64Extend8S) } + case "i64.extend16_s": return { return try $0.visitUnary(.i64Extend16S) } + case "i64.extend32_s": return { return try $0.visitUnary(.i64Extend32S) } + case "memory.init": + let (dataIndex) = try expressionParser.visitMemoryInit(wat: &wat) + return { return try $0.visitMemoryInit(dataIndex: dataIndex) } + case "data.drop": + let (dataIndex) = try expressionParser.visitDataDrop(wat: &wat) + return { return try $0.visitDataDrop(dataIndex: dataIndex) } + case "memory.copy": + let (dstMem, srcMem) = try expressionParser.visitMemoryCopy(wat: &wat) + return { return try $0.visitMemoryCopy(dstMem: dstMem, srcMem: srcMem) } + case "memory.fill": + let (memory) = try expressionParser.visitMemoryFill(wat: &wat) + return { return try $0.visitMemoryFill(memory: memory) } + case "table.init": + let (elemIndex, table) = try expressionParser.visitTableInit(wat: &wat) + return { return try $0.visitTableInit(elemIndex: elemIndex, table: table) } + case "elem.drop": + let (elemIndex) = try expressionParser.visitElemDrop(wat: &wat) + return { return try $0.visitElemDrop(elemIndex: elemIndex) } + case "table.copy": + let (dstTable, srcTable) = try expressionParser.visitTableCopy(wat: &wat) + return { return try $0.visitTableCopy(dstTable: dstTable, srcTable: srcTable) } + case "table.fill": + let (table) = try expressionParser.visitTableFill(wat: &wat) + return { return try $0.visitTableFill(table: table) } + case "table.get": + let (table) = try expressionParser.visitTableGet(wat: &wat) + return { return try $0.visitTableGet(table: table) } + case "table.set": + let (table) = try expressionParser.visitTableSet(wat: &wat) + return { return try $0.visitTableSet(table: table) } + case "table.grow": + let (table) = try expressionParser.visitTableGrow(wat: &wat) + return { return try $0.visitTableGrow(table: table) } + case "table.size": + let (table) = try expressionParser.visitTableSize(wat: &wat) + return { return try $0.visitTableSize(table: table) } + case "i32.trunc_sat_f32_s": return { return try $0.visitConversion(.i32TruncSatF32S) } + case "i32.trunc_sat_f32_u": return { return try $0.visitConversion(.i32TruncSatF32U) } + case "i32.trunc_sat_f64_s": return { return try $0.visitConversion(.i32TruncSatF64S) } + case "i32.trunc_sat_f64_u": return { return try $0.visitConversion(.i32TruncSatF64U) } + case "i64.trunc_sat_f32_s": return { return try $0.visitConversion(.i64TruncSatF32S) } + case "i64.trunc_sat_f32_u": return { return try $0.visitConversion(.i64TruncSatF32U) } + case "i64.trunc_sat_f64_s": return { return try $0.visitConversion(.i64TruncSatF64S) } + case "i64.trunc_sat_f64_u": return { return try $0.visitConversion(.i64TruncSatF64U) } + default: return nil + } +} diff --git a/Sources/WAT/Parser/ExpressionParser.swift b/Sources/WAT/Parser/ExpressionParser.swift index 7d7952d6..b832e1a4 100644 --- a/Sources/WAT/Parser/ExpressionParser.swift +++ b/Sources/WAT/Parser/ExpressionParser.swift @@ -387,11 +387,12 @@ struct ExpressionParser { if let maybeAlign = try parser.peekKeyword(), maybeAlign.starts(with: alignPrefix) { try parser.consume() var subParser = Parser(String(maybeAlign.dropFirst(alignPrefix.count))) - align = try subParser.expectUnsignedInt(UInt32.self) + let rawAlign = try subParser.expectUnsignedInt(UInt32.self) - if align == 0 || align & (align - 1) != 0 { + if rawAlign == 0 || rawAlign & (rawAlign - 1) != 0 { throw WatParserError("alignment must be a power of 2", location: subParser.lexer.location()) } + align = UInt32(rawAlign.trailingZeroBitCount) } return MemArg(offset: offset, align: align) } @@ -449,6 +450,12 @@ extension ExpressionParser { let (_, typeIndex) = try wat.types.resolve(use: typeUse) return (UInt32(typeIndex), tableIndex) } + mutating func visitReturnCall(wat: inout Wat) throws -> UInt32 { + return try visitCall(wat: &wat) + } + mutating func visitReturnCallIndirect(wat: inout Wat) throws -> (typeIndex: UInt32, tableIndex: UInt32) { + return try visitCallIndirect(wat: &wat) + } mutating func visitTypedSelect(wat: inout Wat) throws -> ValueType { fatalError("unreachable because Instruction.json does not define the name of typed select and it is handled in parseTextInstruction() manually") } @@ -467,74 +474,11 @@ extension ExpressionParser { mutating func visitGlobalSet(wat: inout Wat) throws -> UInt32 { return try globalIndex(wat: &wat) } - mutating func visitI32Load(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 4) - } - mutating func visitI64Load(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 8) - } - mutating func visitF32Load(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 4) - } - mutating func visitF64Load(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 8) - } - mutating func visitI32Load8S(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 1) - } - mutating func visitI32Load8U(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 1) - } - mutating func visitI32Load16S(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 2) - } - mutating func visitI32Load16U(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 2) - } - mutating func visitI64Load8S(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 1) - } - mutating func visitI64Load8U(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 1) - } - mutating func visitI64Load16S(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 2) - } - mutating func visitI64Load16U(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 2) - } - mutating func visitI64Load32S(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 4) - } - mutating func visitI64Load32U(wat: inout Wat) throws -> MemArg { - return try visitLoad(defaultAlign: 4) - } - mutating func visitI32Store(wat: inout Wat) throws -> MemArg { - return try visitStore(defaultAlign: 4) - } - mutating func visitI64Store(wat: inout Wat) throws -> MemArg { - return try visitStore(defaultAlign: 8) - } - mutating func visitF32Store(wat: inout Wat) throws -> MemArg { - return try visitStore(defaultAlign: 4) - } - mutating func visitF64Store(wat: inout Wat) throws -> MemArg { - return try visitStore(defaultAlign: 8) - } - mutating func visitI32Store8(wat: inout Wat) throws -> MemArg { - return try visitStore(defaultAlign: 1) - } - mutating func visitI32Store16(wat: inout Wat) throws -> MemArg { - return try visitStore(defaultAlign: 2) - } - mutating func visitI64Store8(wat: inout Wat) throws -> MemArg { - return try visitStore(defaultAlign: 1) - } - mutating func visitI64Store16(wat: inout Wat) throws -> MemArg { - return try visitStore(defaultAlign: 2) + mutating func visitLoad(_ load: Instruction.Load, wat: inout Wat) throws -> MemArg { + return try visitLoad(defaultAlign: UInt32(load.naturalAlignment)) } - mutating func visitI64Store32(wat: inout Wat) throws -> MemArg { - return try visitStore(defaultAlign: 4) + mutating func visitStore(_ store: Instruction.Store, wat: inout Wat) throws -> MemArg { + return try visitStore(defaultAlign: UInt32(store.naturalAlignment)) } mutating func visitMemorySize(wat: inout Wat) throws -> UInt32 { return try memoryIndex(wat: &wat) diff --git a/Sources/WAT/WAT.swift b/Sources/WAT/WAT.swift index 29fffcbe..707f7161 100644 --- a/Sources/WAT/WAT.swift +++ b/Sources/WAT/WAT.swift @@ -1,5 +1,19 @@ import WasmParser +/// Options for encoding a WebAssembly module into a binary format. +public struct EncodeOptions { + /// Whether to include the name section. + public var nameSection: Bool + + /// The default encoding options. + public static let `default` = EncodeOptions() + + /// Creates a new encoding options instance. + public init(nameSection: Bool = false) { + self.nameSection = nameSection + } +} + /// Transforms a WebAssembly text format (WAT) string into a WebAssembly binary format byte array. /// - Parameter input: The WAT string to transform /// - Returns: The WebAssembly binary format byte array @@ -17,9 +31,13 @@ import WasmParser /// ) /// """) /// ``` -public func wat2wasm(_ input: String, features: WasmFeatureSet = .default) throws -> [UInt8] { +public func wat2wasm( + _ input: String, + features: WasmFeatureSet = .default, + options: EncodeOptions = .default +) throws -> [UInt8] { var wat = try parseWAT(input, features: features) - return try encode(module: &wat) + return try encode(module: &wat, options: options) } /// A WAT module representation. @@ -65,8 +83,8 @@ public struct Wat { /// This method effectively consumes the module value, encoding it into a /// binary format byte array. If you need to encode the module multiple times, /// you should create a copy of the module value before encoding it. - public mutating func encode() throws -> [UInt8] { - try WAT.encode(module: &self) + public mutating func encode(options: EncodeOptions = .default) throws -> [UInt8] { + try WAT.encode(module: &self, options: options) } } diff --git a/Sources/WIT/AST.swift b/Sources/WIT/AST.swift index c6f96eba..497e4c49 100644 --- a/Sources/WIT/AST.swift +++ b/Sources/WIT/AST.swift @@ -68,6 +68,7 @@ public struct PackageNameSyntax: Equatable, Hashable, CustomStringConvertible { } public struct TopLevelUseSyntax: Equatable, Hashable, SyntaxNodeProtocol { + var attributes: [AttributeSyntax] var item: UsePathSyntax var asName: Identifier? } @@ -75,6 +76,7 @@ public struct TopLevelUseSyntax: Equatable, Hashable, SyntaxNodeProtocol { public struct WorldSyntax: Equatable, Hashable, SyntaxNodeProtocol { public typealias Parent = SourceFileSyntax public var documents: DocumentsSyntax + public var attributes: [AttributeSyntax] public var name: Identifier public var items: [WorldItemSyntax] } @@ -89,11 +91,13 @@ public enum WorldItemSyntax: Equatable, Hashable { public struct ImportSyntax: Equatable, Hashable { public var documents: DocumentsSyntax + public var attributes: [AttributeSyntax] public var kind: ExternKindSyntax } public struct ExportSyntax: Equatable, Hashable { public var documents: DocumentsSyntax + public var attributes: [AttributeSyntax] public var kind: ExternKindSyntax } @@ -105,6 +109,7 @@ public enum ExternKindSyntax: Equatable, Hashable { public struct InterfaceSyntax: Equatable, Hashable, CustomStringConvertible, SyntaxNodeProtocol { public var documents: DocumentsSyntax + public var attributes: [AttributeSyntax] public var name: Identifier public var items: [InterfaceItemSyntax] @@ -121,6 +126,7 @@ public enum InterfaceItemSyntax: Equatable, Hashable, SyntaxNodeProtocol { public struct TypeDefSyntax: Equatable, Hashable, SyntaxNodeProtocol { public var documents: DocumentsSyntax + public var attributes: [AttributeSyntax] public var name: Identifier public var body: TypeDefBodySyntax } @@ -240,6 +246,7 @@ public struct StreamSyntax: Equatable, Hashable { public struct NamedFunctionSyntax: Equatable, Hashable, SyntaxNodeProtocol { public var documents: DocumentsSyntax + public var attributes: [AttributeSyntax] public var name: Identifier public var function: FunctionSyntax } @@ -281,6 +288,7 @@ public struct FunctionSyntax: Equatable, Hashable { } public struct UseSyntax: Equatable, Hashable, SyntaxNodeProtocol { + public var attributes: [AttributeSyntax] public var from: UsePathSyntax public var names: [UseNameSyntax] } @@ -303,6 +311,7 @@ public struct UseNameSyntax: Equatable, Hashable { } public struct IncludeSyntax: Equatable, Hashable { + var attributes: [AttributeSyntax] var from: UsePathSyntax var names: [IncludeNameSyntax] } @@ -324,3 +333,25 @@ public struct Identifier: Equatable, Hashable, CustomStringConvertible { public struct DocumentsSyntax: Equatable, Hashable { var comments: [String] } + +public enum AttributeSyntax: Equatable, Hashable { + case since(SinceAttributeSyntax) + case unstable(UnstableAttributeSyntax) + case deprecated(DeprecatedAttributeSyntax) +} + +public struct SinceAttributeSyntax: Equatable, Hashable { + let version: Version + let feature: Identifier? + let textRange: TextRange +} + +public struct UnstableAttributeSyntax: Equatable, Hashable { + let textRange: TextRange + let feature: Identifier +} + +public struct DeprecatedAttributeSyntax: Equatable, Hashable { + let textRange: TextRange + let version: Version +} diff --git a/Sources/WIT/Lexer.swift b/Sources/WIT/Lexer.swift index 88d54a39..81c78cf4 100644 --- a/Sources/WIT/Lexer.swift +++ b/Sources/WIT/Lexer.swift @@ -44,6 +44,12 @@ struct Lexer { } var cursor: Cursor + let requireSemicolon: Bool + + init(cursor: Cursor, requireSemicolon: Bool = false) { + self.cursor = cursor + self.requireSemicolon = requireSemicolon + } mutating func advanceToEndOfBlockComment() -> Diagnostic? { var depth = 1 @@ -245,6 +251,24 @@ struct Lexer { return actual } + @discardableResult + mutating func expectIdentifier(_ expected: String) throws -> Lexer.Lexeme { + let lexme = try self.expect(.id) + let actualText = self.parseText(in: lexme.textRange) + guard actualText == expected else { + throw ParseError(description: "\(expected) expected but got \(actualText)") + } + return lexme + } + + mutating func expectSemicolon() throws { + if self.requireSemicolon { + try self.expect(.semicolon) + } else { + self.eat(.semicolon) + } + } + @discardableResult mutating func eat(_ expected: TokenKind) -> Bool { var other = self diff --git a/Sources/WIT/TextParser/ParseFunctionDecl.swift b/Sources/WIT/TextParser/ParseFunctionDecl.swift index dff1ed74..146f8ea5 100644 --- a/Sources/WIT/TextParser/ParseFunctionDecl.swift +++ b/Sources/WIT/TextParser/ParseFunctionDecl.swift @@ -1,5 +1,9 @@ extension ResourceFunctionSyntax { - static func parse(lexer: inout Lexer, documents: DocumentsSyntax) throws -> ResourceFunctionSyntax { + static func parse( + lexer: inout Lexer, + documents: DocumentsSyntax, + attributes: [AttributeSyntax] + ) throws -> ResourceFunctionSyntax { guard let token = lexer.peek() else { throw ParseError(description: "`constructor` or identifier expected but got nothing") } @@ -18,10 +22,12 @@ extension ResourceFunctionSyntax { let type = try TypeReprSyntax.parse(lexer: &lexer) return ParameterSyntax(name: name, type: type, textRange: start.. SyntaxNode { try lexer.expect(.interface) let name = try Identifier.parse(lexer: &lexer) let items = try parseItems(lexer: &lexer) - return .init(syntax: InterfaceSyntax(documents: documents, name: name, items: items)) + return .init( + syntax: InterfaceSyntax( + documents: documents, attributes: attributes, name: name, items: items + )) } static func parseItems(lexer: inout Lexer) throws -> [InterfaceItemSyntax] { @@ -16,29 +19,30 @@ extension InterfaceSyntax { if lexer.eat(.rightBrace) { break } - items.append(try InterfaceItemSyntax.parse(lexer: &lexer, documents: docs)) + let attributes = try AttributeSyntax.parseItems(lexer: &lexer) + items.append(try InterfaceItemSyntax.parse(lexer: &lexer, documents: docs, attributes: attributes)) } return items } } extension InterfaceItemSyntax { - static func parse(lexer: inout Lexer, documents: DocumentsSyntax) throws -> InterfaceItemSyntax { + static func parse(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> InterfaceItemSyntax { switch lexer.peek()?.kind { case .type: - return try .typeDef(.init(syntax: .parse(lexer: &lexer, documents: documents))) + return try .typeDef(.init(syntax: .parse(lexer: &lexer, documents: documents, attributes: attributes))) case .flags: - return try .typeDef(.init(syntax: .parseFlags(lexer: &lexer, documents: documents))) + return try .typeDef(.init(syntax: .parseFlags(lexer: &lexer, documents: documents, attributes: attributes))) case .enum: - return try .typeDef(.init(syntax: .parseEnum(lexer: &lexer, documents: documents))) + return try .typeDef(.init(syntax: .parseEnum(lexer: &lexer, documents: documents, attributes: attributes))) case .variant: - return try .typeDef(.init(syntax: .parseVariant(lexer: &lexer, documents: documents))) + return try .typeDef(.init(syntax: .parseVariant(lexer: &lexer, documents: documents, attributes: attributes))) case .resource: - return try .typeDef(TypeDefSyntax.parseResource(lexer: &lexer, documents: documents)) + return try .typeDef(TypeDefSyntax.parseResource(lexer: &lexer, documents: documents, attributes: attributes)) case .record: - return try .typeDef(.init(syntax: .parseRecord(lexer: &lexer, documents: documents))) + return try .typeDef(.init(syntax: .parseRecord(lexer: &lexer, documents: documents, attributes: attributes))) case .union: - return try .typeDef(.init(syntax: .parseUnion(lexer: &lexer, documents: documents))) + return try .typeDef(.init(syntax: .parseUnion(lexer: &lexer, documents: documents, attributes: attributes))) case .id, .explicitId: return try .function(NamedFunctionSyntax.parse(lexer: &lexer, documents: documents)) case .use: diff --git a/Sources/WIT/TextParser/ParseTop.swift b/Sources/WIT/TextParser/ParseTop.swift index a3674ba3..f4619d1a 100644 --- a/Sources/WIT/TextParser/ParseTop.swift +++ b/Sources/WIT/TextParser/ParseTop.swift @@ -3,6 +3,7 @@ extension SourceFileSyntax { var packageId: PackageNameSyntax? if lexer.peek()?.kind == .package { packageId = try PackageNameSyntax.parse(lexer: &lexer) + try lexer.expectSemicolon() } var items: [ASTItemSyntax] = [] @@ -136,12 +137,24 @@ extension ASTItemSyntax { static func parse( lexer: inout Lexer, documents: DocumentsSyntax ) throws -> ASTItemSyntax { + let attributes = try AttributeSyntax.parseItems(lexer: &lexer) switch lexer.peek()?.kind { case .interface: - return try .interface(InterfaceSyntax.parse(lexer: &lexer, documents: documents)) + return try .interface( + InterfaceSyntax.parse( + lexer: &lexer, documents: documents, attributes: attributes + )) case .world: - return try .world(WorldSyntax.parse(lexer: &lexer, documents: documents)) - case .use: return try .use(.init(syntax: .parse(lexer: &lexer, documents: documents))) + return try .world( + WorldSyntax.parse( + lexer: &lexer, documents: documents, attributes: attributes + )) + case .use: + return try .use( + .init( + syntax: .parse( + lexer: &lexer, documents: documents, attributes: attributes + ))) default: throw ParseError(description: "`world`, `interface` or `use` expected") } @@ -149,14 +162,18 @@ extension ASTItemSyntax { } extension TopLevelUseSyntax { - static func parse(lexer: inout Lexer, documents: DocumentsSyntax) throws -> TopLevelUseSyntax { + static func parse( + lexer: inout Lexer, + documents: DocumentsSyntax, attributes: [AttributeSyntax] + ) throws -> TopLevelUseSyntax { try lexer.expect(.use) let item = try UsePathSyntax.parse(lexer: &lexer) var asName: Identifier? if lexer.eat(.as) { asName = try .parse(lexer: &lexer) } - return TopLevelUseSyntax(item: item, asName: asName) + try lexer.expectSemicolon() + return TopLevelUseSyntax(attributes: attributes, item: item, asName: asName) } } @@ -179,7 +196,8 @@ extension UseSyntax { break } } - return .init(syntax: UseSyntax(from: from, names: names)) + try lexer.expectSemicolon() + return .init(syntax: UseSyntax(attributes: [], from: from, names: names)) } } @@ -222,3 +240,70 @@ extension DocumentsSyntax { return DocumentsSyntax(comments: comments) } } + +extension AttributeSyntax { + static func parseItems(lexer: inout Lexer) throws -> [AttributeSyntax] { + var items: [AttributeSyntax] = [] + while lexer.eat(.at) { + let id = try Identifier.parse(lexer: &lexer) + let item: AttributeSyntax + switch id.text { + case "since": item = .since(try .parse(lexer: &lexer, id: id)) + case "unstable": item = .unstable(try .parse(lexer: &lexer, id: id)) + case "deprecated": item = .deprecated(try .parse(lexer: &lexer, id: id)) + default: + throw ParseError(description: "Unexpected attribute: \(id.text)") + } + items.append(item) + } + return items + } +} + +extension SinceAttributeSyntax { + static func parse(lexer: inout Lexer, id: Identifier) throws -> SinceAttributeSyntax { + try lexer.expect(.leftParen) + try lexer.expectIdentifier("version") + try lexer.expect(.equals) + let version = try Version.parse(lexer: &lexer) + var feature: Identifier? + if lexer.eat(.comma) { + try lexer.expectIdentifier("feature") + try lexer.expect(.equals) + feature = try Identifier.parse(lexer: &lexer) + } + try lexer.expect(.rightParen) + return SinceAttributeSyntax( + version: version, feature: feature, + textRange: id.textRange.lowerBound.. UnstableAttributeSyntax { + try lexer.expect(.leftParen) + try lexer.expectIdentifier("feature") + try lexer.expect(.equals) + let feature = try Identifier.parse(lexer: &lexer) + try lexer.expect(.rightParen) + return UnstableAttributeSyntax( + textRange: id.textRange.lowerBound.. DeprecatedAttributeSyntax { + try lexer.expect(.leftParen) + try lexer.expectIdentifier("version") + try lexer.expect(.equals) + let version = try Version.parse(lexer: &lexer) + try lexer.expect(.rightParen) + return DeprecatedAttributeSyntax( + textRange: id.textRange.lowerBound.. TypeDefSyntax { + static func parse(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> TypeDefSyntax { try lexer.expect(.type) let name = try Identifier.parse(lexer: &lexer) try lexer.expect(.equals) let repr = try TypeReprSyntax.parse(lexer: &lexer) - return TypeDefSyntax(documents: documents, name: name, body: .alias(TypeAliasSyntax(typeRepr: repr))) + try lexer.expectSemicolon() + return TypeDefSyntax(documents: documents, attributes: attributes, name: name, body: .alias(TypeAliasSyntax(typeRepr: repr))) } - static func parseFlags(lexer: inout Lexer, documents: DocumentsSyntax) throws -> TypeDefSyntax { + static func parseFlags(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> TypeDefSyntax { try lexer.expect(.flags) let name = try Identifier.parse(lexer: &lexer) let body = try TypeDefBodySyntax.flags( @@ -147,24 +148,26 @@ extension TypeDefSyntax { return FlagSyntax(documents: docs, name: name) } )) - return TypeDefSyntax(documents: documents, name: name, body: body) + return TypeDefSyntax(documents: documents, attributes: attributes, name: name, body: body) } - static func parseResource(lexer: inout Lexer, documents: DocumentsSyntax) throws -> SyntaxNode { + static func parseResource(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> SyntaxNode { try lexer.expect(.resource) let name = try Identifier.parse(lexer: &lexer) var functions: [ResourceFunctionSyntax] = [] if lexer.eat(.leftBrace) { while !lexer.eat(.rightBrace) { let docs = try DocumentsSyntax.parse(lexer: &lexer) - functions.append(try ResourceFunctionSyntax.parse(lexer: &lexer, documents: docs)) + functions.append(try ResourceFunctionSyntax.parse(lexer: &lexer, documents: docs, attributes: [])) } + } else { + try lexer.expectSemicolon() } let body = TypeDefBodySyntax.resource(ResourceSyntax(functions: functions)) - return .init(syntax: TypeDefSyntax(documents: documents, name: name, body: body)) + return .init(syntax: TypeDefSyntax(documents: documents, attributes: attributes, name: name, body: body)) } - static func parseRecord(lexer: inout Lexer, documents: DocumentsSyntax) throws -> TypeDefSyntax { + static func parseRecord(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> TypeDefSyntax { try lexer.expect(.record) let name = try Identifier.parse(lexer: &lexer) let body = try TypeDefBodySyntax.record( @@ -180,10 +183,10 @@ extension TypeDefSyntax { return FieldSyntax(documents: docs, name: name, type: type, textRange: start.. TypeDefSyntax { + static func parseVariant(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> TypeDefSyntax { try lexer.expect(.variant) let name = try Identifier.parse(lexer: &lexer) let body = try TypeDefBodySyntax.variant( @@ -203,10 +206,10 @@ extension TypeDefSyntax { }, textRange: name.textRange )) - return TypeDefSyntax(documents: documents, name: name, body: body) + return TypeDefSyntax(documents: documents, attributes: attributes, name: name, body: body) } - static func parseUnion(lexer: inout Lexer, documents: DocumentsSyntax) throws -> TypeDefSyntax { + static func parseUnion(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> TypeDefSyntax { try lexer.expect(.union) let name = try Identifier.parse(lexer: &lexer) let body = try TypeDefBodySyntax.union( @@ -221,10 +224,10 @@ extension TypeDefSyntax { }, textRange: name.textRange )) - return TypeDefSyntax(documents: documents, name: name, body: body) + return TypeDefSyntax(documents: documents, attributes: attributes, name: name, body: body) } - static func parseEnum(lexer: inout Lexer, documents: DocumentsSyntax) throws -> TypeDefSyntax { + static func parseEnum(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> TypeDefSyntax { try lexer.expect(.enum) let name = try Identifier.parse(lexer: &lexer) let body = try TypeDefBodySyntax.enum( @@ -239,6 +242,6 @@ extension TypeDefSyntax { }, textRange: name.textRange )) - return TypeDefSyntax(documents: documents, name: name, body: body) + return TypeDefSyntax(documents: documents, attributes: attributes, name: name, body: body) } } diff --git a/Sources/WIT/TextParser/ParseWorld.swift b/Sources/WIT/TextParser/ParseWorld.swift index 11083bfd..d325d8e9 100644 --- a/Sources/WIT/TextParser/ParseWorld.swift +++ b/Sources/WIT/TextParser/ParseWorld.swift @@ -1,9 +1,14 @@ extension WorldSyntax { - static func parse(lexer: inout Lexer, documents: DocumentsSyntax) throws -> SyntaxNode { + static func parse( + lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax] + ) throws -> SyntaxNode { try lexer.expect(.world) let name = try Identifier.parse(lexer: &lexer) let items = try parseItems(lexer: &lexer) - return .init(syntax: WorldSyntax(documents: documents, name: name, items: items)) + return .init( + syntax: WorldSyntax( + documents: documents, attributes: attributes, name: name, items: items + )) } static func parseItems(lexer: inout Lexer) throws -> [WorldItemSyntax] { @@ -14,37 +19,38 @@ extension WorldSyntax { if lexer.eat(.rightBrace) { break } - items.append(try WorldItemSyntax.parse(lexer: &lexer, documents: docs)) + let attributes = try AttributeSyntax.parseItems(lexer: &lexer) + items.append(try WorldItemSyntax.parse(lexer: &lexer, documents: docs, attributes: attributes)) } return items } } extension WorldItemSyntax { - static func parse(lexer: inout Lexer, documents: DocumentsSyntax) throws -> WorldItemSyntax { + static func parse(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> WorldItemSyntax { switch lexer.peek()?.kind { case .import: - return try .import(.parse(lexer: &lexer, documents: documents)) + return try .import(.parse(lexer: &lexer, documents: documents, attributes: attributes)) case .export: - return try .export(.parse(lexer: &lexer, documents: documents)) + return try .export(.parse(lexer: &lexer, documents: documents, attributes: attributes)) case .use: return try .use(UseSyntax.parse(lexer: &lexer)) case .type: - return try .type(.init(syntax: .parse(lexer: &lexer, documents: documents))) + return try .type(.init(syntax: .parse(lexer: &lexer, documents: documents, attributes: attributes))) case .flags: - return try .type(.init(syntax: .parseFlags(lexer: &lexer, documents: documents))) + return try .type(.init(syntax: .parseFlags(lexer: &lexer, documents: documents, attributes: attributes))) case .enum: - return try .type(.init(syntax: .parseEnum(lexer: &lexer, documents: documents))) + return try .type(.init(syntax: .parseEnum(lexer: &lexer, documents: documents, attributes: attributes))) case .variant: - return try .type(.init(syntax: .parseVariant(lexer: &lexer, documents: documents))) + return try .type(.init(syntax: .parseVariant(lexer: &lexer, documents: documents, attributes: attributes))) case .resource: - return try .type(TypeDefSyntax.parseResource(lexer: &lexer, documents: documents)) + return try .type(TypeDefSyntax.parseResource(lexer: &lexer, documents: documents, attributes: attributes)) case .record: - return try .type(.init(syntax: .parseRecord(lexer: &lexer, documents: documents))) + return try .type(.init(syntax: .parseRecord(lexer: &lexer, documents: documents, attributes: attributes))) case .union: - return try .type(.init(syntax: .parseUnion(lexer: &lexer, documents: documents))) + return try .type(.init(syntax: .parseUnion(lexer: &lexer, documents: documents, attributes: attributes))) case .include: - return try .include(.parse(lexer: &lexer)) + return try .include(.parse(lexer: &lexer, attributes: attributes)) default: throw ParseError(description: "`type`, `resource` or `func` expected") } @@ -52,18 +58,22 @@ extension WorldItemSyntax { } extension ImportSyntax { - static func parse(lexer: inout Lexer, documents: DocumentsSyntax) throws -> ImportSyntax { + static func parse( + lexer: inout Lexer, + documents: DocumentsSyntax, + attributes: [AttributeSyntax] + ) throws -> ImportSyntax { try lexer.expect(.import) let kind = try ExternKindSyntax.parse(lexer: &lexer) - return ImportSyntax(documents: documents, kind: kind) + return ImportSyntax(documents: documents, attributes: attributes, kind: kind) } } extension ExportSyntax { - static func parse(lexer: inout Lexer, documents: DocumentsSyntax) throws -> ExportSyntax { + static func parse(lexer: inout Lexer, documents: DocumentsSyntax, attributes: [AttributeSyntax]) throws -> ExportSyntax { try lexer.expect(.export) let kind = try ExternKindSyntax.parse(lexer: &lexer) - return ExportSyntax(documents: documents, kind: kind) + return ExportSyntax(documents: documents, attributes: attributes, kind: kind) } } @@ -74,9 +84,11 @@ extension ExternKindSyntax { if clone.eat(.colon) { switch clone.peek()?.kind { case .func: - // import foo: func(...) + // import foo: func(...); lexer = clone - return try .function(id, .parse(lexer: &lexer)) + let result: ExternKindSyntax = try .function(id, .parse(lexer: &lexer)) + try lexer.expectSemicolon() + return result case .interface: // import foo: interface { ... } try clone.expect(.interface) @@ -85,13 +97,15 @@ extension ExternKindSyntax { default: break } } - // import foo:bar/baz - return try .path(.parse(lexer: &lexer)) + // import foo:bar/baz; + let result: ExternKindSyntax = try .path(.parse(lexer: &lexer)) + try lexer.expectSemicolon() + return result } } extension IncludeSyntax { - static func parse(lexer: inout Lexer) throws -> IncludeSyntax { + static func parse(lexer: inout Lexer, attributes: [AttributeSyntax]) throws -> IncludeSyntax { try lexer.expect(.include) let from = try UsePathSyntax.parse(lexer: &lexer) @@ -104,6 +118,7 @@ extension IncludeSyntax { return IncludeNameSyntax(name: name, asName: asName) } } - return IncludeSyntax(from: from, names: names) + try lexer.expectSemicolon() + return IncludeSyntax(attributes: attributes, from: from, names: names) } } diff --git a/Sources/WITExtractor/SwiftAPIDigester.swift b/Sources/WITExtractor/SwiftAPIDigester.swift index f9c126dc..5c6b2604 100644 --- a/Sources/WITExtractor/SwiftAPIDigester.swift +++ b/Sources/WITExtractor/SwiftAPIDigester.swift @@ -138,6 +138,9 @@ struct SwiftAPIDigester { throw SwiftAPIDigesterError.unexpectedEmptyOutput } process.waitUntilExit() + guard process.terminationStatus == 0 else { + throw SwiftAPIDigesterError.nonZeroExitCode(process.terminationStatus, arguments: args) + } return try Output.parse(output) #endif } @@ -145,4 +148,5 @@ struct SwiftAPIDigester { enum SwiftAPIDigesterError: Error { case unexpectedEmptyOutput + case nonZeroExitCode(Int32, arguments: [String]) } diff --git a/Sources/WITOverlayGenerator/GuestGenerators/GuestPrelude.swift b/Sources/WITOverlayGenerator/GuestGenerators/GuestPrelude.swift index cf206da6..627e7954 100644 --- a/Sources/WITOverlayGenerator/GuestGenerators/GuestPrelude.swift +++ b/Sources/WITOverlayGenerator/GuestGenerators/GuestPrelude.swift @@ -7,7 +7,11 @@ let guestPrelude = """ #if canImport(WASILibc) import WASILibc #endif + #if compiler(>=6.0) + private import _CabiShims + #else @_implementationOnly import _CabiShims + #endif fileprivate enum Prelude { class LeakBox { diff --git a/Sources/WasmKit/Engine.swift b/Sources/WasmKit/Engine.swift index 4cf3bd6d..c559fa47 100644 --- a/Sources/WasmKit/Engine.swift +++ b/Sources/WasmKit/Engine.swift @@ -1,5 +1,7 @@ import _CWasmKit.Platform +import struct WasmParser.WasmFeatureSet + /// A WebAssembly execution engine. /// /// An engine is responsible storing the configuration for the execution of @@ -85,14 +87,30 @@ public struct EngineConfiguration { /// "call stack exhausted" ``Trap`` errors thrown by the interpreter. public var stackSize: Int + /// The WebAssembly features that can be used by Wasm modules running on this engine. + public var features: WasmFeatureSet + /// Initializes a new instance of `EngineConfiguration`. + /// /// - Parameter threadingModel: The threading model to use for the virtual /// machine interpreter. If `nil`, the default threading model for the /// current platform will be used. - public init(threadingModel: ThreadingModel? = nil, compilationMode: CompilationMode? = nil, stackSize: Int? = nil) { + /// - Parameter compilationMode: The compilation mode to use for WebAssembly + /// modules. If `nil`, the default compilation mode (lazy) will be used. + /// - Parameter stackSize: The stack size in bytes for the virtual machine + /// interpreter. If `nil`, the default stack size (512KB) will be used. + /// - Parameter features: The WebAssembly features that can be used by Wasm + /// modules running on this engine. + public init( + threadingModel: ThreadingModel? = nil, + compilationMode: CompilationMode? = nil, + stackSize: Int? = nil, + features: WasmFeatureSet = .default + ) { self.threadingModel = threadingModel ?? .defaultForCurrentPlatform self.compilationMode = compilationMode ?? .lazy self.stackSize = stackSize ?? (1 << 19) + self.features = features } } diff --git a/Sources/WasmKit/Execution/ConstEvaluation.swift b/Sources/WasmKit/Execution/ConstEvaluation.swift index df995d25..3f155e6b 100644 --- a/Sources/WasmKit/Execution/ConstEvaluation.swift +++ b/Sources/WasmKit/Execution/ConstEvaluation.swift @@ -50,7 +50,7 @@ extension ConstExpression { private func _evaluate(context: C) throws -> Value { guard self.last == .end, self.count == 2 else { - throw InstantiationError.unsupported("Expect `end` at the end of offset expression") + throw ValidationError(.expectedEndAtOffsetExpression) } let constInst = self[0] switch constInst { @@ -68,7 +68,7 @@ extension ConstExpression { case .refFunc(let functionIndex): return try .ref(context.functionRef(functionIndex)) default: - throw InstantiationError.unsupported("illegal const expression instruction: \(constInst)") + throw ValidationError(.illegalConstExpressionInstruction(constInst)) } } } @@ -97,10 +97,10 @@ extension WasmParser.ElementSegment { case .ref(.function(let addr)): return .function(addr) default: - throw Trap._raw("Unexpected global value type for element initializer expression") + throw ValidationError(.unexpectedGlobalValueType) } default: - throw Trap._raw("Unexpected element initializer expression: \(expression)") + throw ValidationError(.unexpectedElementInitializer(expression: "\(expression)")) } } } diff --git a/Sources/WasmKit/Execution/DispatchInstruction.swift b/Sources/WasmKit/Execution/DispatchInstruction.swift index 4129725b..4d79e822 100644 --- a/Sources/WasmKit/Execution/DispatchInstruction.swift +++ b/Sources/WasmKit/Execution/DispatchInstruction.swift @@ -20,195 +20,198 @@ extension Execution { case 4: return try self.execute_compilingCall(sp: &sp, pc: &pc, md: &md, ms: &ms) case 5: return try self.execute_internalCall(sp: &sp, pc: &pc, md: &md, ms: &ms) case 6: return try self.execute_callIndirect(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 7: return try self.execute_unreachable(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 8: return self.execute_nop(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 9: return self.execute_br(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 10: return self.execute_brIf(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 11: return self.execute_brIfNot(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 12: return self.execute_brTable(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 13: return self.execute__return(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 14: return try self.execute_endOfExecution(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 15: return try self.execute_i32Load(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 16: return try self.execute_i64Load(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 17: return try self.execute_f32Load(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 18: return try self.execute_f64Load(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 19: return try self.execute_i32Load8S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 20: return try self.execute_i32Load8U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 21: return try self.execute_i32Load16S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 22: return try self.execute_i32Load16U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 23: return try self.execute_i64Load8S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 24: return try self.execute_i64Load8U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 25: return try self.execute_i64Load16S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 26: return try self.execute_i64Load16U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 27: return try self.execute_i64Load32S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 28: return try self.execute_i64Load32U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 29: return try self.execute_i32Store(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 30: return try self.execute_i64Store(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 31: return try self.execute_f32Store(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 32: return try self.execute_f64Store(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 33: return try self.execute_i32Store8(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 34: return try self.execute_i32Store16(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 35: return try self.execute_i64Store8(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 36: return try self.execute_i64Store16(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 37: return try self.execute_i64Store32(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 38: return self.execute_memorySize(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 39: return try self.execute_memoryGrow(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 40: return try self.execute_memoryInit(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 41: return self.execute_memoryDataDrop(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 42: return try self.execute_memoryCopy(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 43: return try self.execute_memoryFill(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 44: return self.execute_const32(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 45: return self.execute_const64(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 46: return self.execute_i32Add(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 47: return self.execute_i64Add(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 48: return self.execute_i32Sub(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 49: return self.execute_i64Sub(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 50: return self.execute_i32Mul(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 51: return self.execute_i64Mul(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 52: return self.execute_i32And(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 53: return self.execute_i64And(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 54: return self.execute_i32Or(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 55: return self.execute_i64Or(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 56: return self.execute_i32Xor(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 57: return self.execute_i64Xor(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 58: return self.execute_i32Shl(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 59: return self.execute_i64Shl(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 60: return self.execute_i32ShrS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 61: return self.execute_i64ShrS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 62: return self.execute_i32ShrU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 63: return self.execute_i64ShrU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 64: return self.execute_i32Rotl(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 65: return self.execute_i64Rotl(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 66: return self.execute_i32Rotr(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 67: return self.execute_i64Rotr(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 68: return try self.execute_i32DivS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 69: return try self.execute_i64DivS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 70: return try self.execute_i32DivU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 71: return try self.execute_i64DivU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 72: return try self.execute_i32RemS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 73: return try self.execute_i64RemS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 74: return try self.execute_i32RemU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 75: return try self.execute_i64RemU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 76: return self.execute_i32Eq(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 77: return self.execute_i64Eq(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 78: return self.execute_i32Ne(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 79: return self.execute_i64Ne(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 80: return self.execute_i32LtS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 81: return self.execute_i64LtS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 82: return self.execute_i32LtU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 83: return self.execute_i64LtU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 84: return self.execute_i32GtS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 85: return self.execute_i64GtS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 86: return self.execute_i32GtU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 87: return self.execute_i64GtU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 88: return self.execute_i32LeS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 89: return self.execute_i64LeS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 90: return self.execute_i32LeU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 91: return self.execute_i64LeU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 92: return self.execute_i32GeS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 93: return self.execute_i64GeS(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 94: return self.execute_i32GeU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 95: return self.execute_i64GeU(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 96: return self.execute_i32Clz(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 97: return self.execute_i64Clz(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 98: return self.execute_i32Ctz(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 99: return self.execute_i64Ctz(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 100: return self.execute_i32Popcnt(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 101: return self.execute_i64Popcnt(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 102: return self.execute_i32Eqz(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 103: return self.execute_i64Eqz(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 104: return self.execute_i32WrapI64(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 105: return self.execute_i64ExtendI32S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 106: return self.execute_i64ExtendI32U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 107: return self.execute_i32Extend8S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 108: return self.execute_i64Extend8S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 109: return self.execute_i32Extend16S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 110: return self.execute_i64Extend16S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 111: return self.execute_i64Extend32S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 112: return try self.execute_i32TruncF32S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 113: return try self.execute_i32TruncF32U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 114: return try self.execute_i32TruncSatF32S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 115: return try self.execute_i32TruncSatF32U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 116: return try self.execute_i32TruncF64S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 117: return try self.execute_i32TruncF64U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 118: return try self.execute_i32TruncSatF64S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 119: return try self.execute_i32TruncSatF64U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 120: return try self.execute_i64TruncF32S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 121: return try self.execute_i64TruncF32U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 122: return try self.execute_i64TruncSatF32S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 123: return try self.execute_i64TruncSatF32U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 124: return try self.execute_i64TruncF64S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 125: return try self.execute_i64TruncF64U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 126: return try self.execute_i64TruncSatF64S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 127: return try self.execute_i64TruncSatF64U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 128: return self.execute_f32ConvertI32S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 129: return self.execute_f32ConvertI32U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 130: return self.execute_f32ConvertI64S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 131: return self.execute_f32ConvertI64U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 132: return self.execute_f64ConvertI32S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 133: return self.execute_f64ConvertI32U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 134: return self.execute_f64ConvertI64S(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 135: return self.execute_f64ConvertI64U(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 136: return self.execute_f32ReinterpretI32(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 137: return self.execute_f64ReinterpretI64(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 138: return self.execute_i32ReinterpretF32(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 139: return self.execute_i64ReinterpretF64(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 140: return self.execute_f32Add(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 141: return self.execute_f64Add(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 142: return self.execute_f32Sub(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 143: return self.execute_f64Sub(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 144: return self.execute_f32Mul(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 145: return self.execute_f64Mul(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 146: return self.execute_f32Div(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 147: return self.execute_f64Div(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 148: return self.execute_f32Min(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 149: return self.execute_f64Min(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 150: return self.execute_f32Max(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 151: return self.execute_f64Max(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 152: return self.execute_f32CopySign(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 153: return self.execute_f64CopySign(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 154: return self.execute_f32Eq(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 155: return self.execute_f64Eq(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 156: return self.execute_f32Ne(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 157: return self.execute_f64Ne(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 158: return self.execute_f32Lt(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 159: return self.execute_f64Lt(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 160: return self.execute_f32Gt(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 161: return self.execute_f64Gt(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 162: return self.execute_f32Le(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 163: return self.execute_f64Le(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 164: return self.execute_f32Ge(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 165: return self.execute_f64Ge(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 166: return self.execute_f32Abs(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 167: return self.execute_f64Abs(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 168: return self.execute_f32Neg(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 169: return self.execute_f64Neg(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 170: return self.execute_f32Ceil(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 171: return self.execute_f64Ceil(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 172: return self.execute_f32Floor(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 173: return self.execute_f64Floor(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 174: return self.execute_f32Trunc(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 175: return self.execute_f64Trunc(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 176: return self.execute_f32Nearest(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 177: return self.execute_f64Nearest(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 178: return self.execute_f32Sqrt(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 179: return self.execute_f64Sqrt(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 180: return self.execute_f64PromoteF32(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 181: return self.execute_f32DemoteF64(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 182: return self.execute_select(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 183: return self.execute_refNull(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 184: return self.execute_refIsNull(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 185: return self.execute_refFunc(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 186: return try self.execute_tableGet(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 187: return try self.execute_tableSet(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 188: return self.execute_tableSize(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 189: return try self.execute_tableGrow(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 190: return try self.execute_tableFill(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 191: return try self.execute_tableCopy(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 192: return try self.execute_tableInit(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 193: return self.execute_tableElementDrop(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 194: return self.execute_onEnter(sp: &sp, pc: &pc, md: &md, ms: &ms) - case 195: return self.execute_onExit(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 7: return try self.execute_resizeFrameHeader(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 8: return try self.execute_returnCall(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 9: return try self.execute_returnCallIndirect(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 10: return try self.execute_unreachable(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 11: return self.execute_nop(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 12: return self.execute_br(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 13: return self.execute_brIf(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 14: return self.execute_brIfNot(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 15: return self.execute_brTable(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 16: return self.execute__return(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 17: return try self.execute_endOfExecution(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 18: return try self.execute_i32Load(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 19: return try self.execute_i64Load(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 20: return try self.execute_f32Load(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 21: return try self.execute_f64Load(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 22: return try self.execute_i32Load8S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 23: return try self.execute_i32Load8U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 24: return try self.execute_i32Load16S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 25: return try self.execute_i32Load16U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 26: return try self.execute_i64Load8S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 27: return try self.execute_i64Load8U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 28: return try self.execute_i64Load16S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 29: return try self.execute_i64Load16U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 30: return try self.execute_i64Load32S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 31: return try self.execute_i64Load32U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 32: return try self.execute_i32Store(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 33: return try self.execute_i64Store(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 34: return try self.execute_f32Store(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 35: return try self.execute_f64Store(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 36: return try self.execute_i32Store8(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 37: return try self.execute_i32Store16(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 38: return try self.execute_i64Store8(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 39: return try self.execute_i64Store16(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 40: return try self.execute_i64Store32(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 41: return self.execute_memorySize(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 42: return try self.execute_memoryGrow(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 43: return try self.execute_memoryInit(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 44: return self.execute_memoryDataDrop(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 45: return try self.execute_memoryCopy(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 46: return try self.execute_memoryFill(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 47: return self.execute_const32(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 48: return self.execute_const64(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 49: return self.execute_i32Add(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 50: return self.execute_i64Add(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 51: return self.execute_i32Sub(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 52: return self.execute_i64Sub(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 53: return self.execute_i32Mul(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 54: return self.execute_i64Mul(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 55: return self.execute_i32And(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 56: return self.execute_i64And(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 57: return self.execute_i32Or(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 58: return self.execute_i64Or(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 59: return self.execute_i32Xor(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 60: return self.execute_i64Xor(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 61: return self.execute_i32Shl(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 62: return self.execute_i64Shl(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 63: return self.execute_i32ShrS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 64: return self.execute_i64ShrS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 65: return self.execute_i32ShrU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 66: return self.execute_i64ShrU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 67: return self.execute_i32Rotl(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 68: return self.execute_i64Rotl(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 69: return self.execute_i32Rotr(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 70: return self.execute_i64Rotr(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 71: return try self.execute_i32DivS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 72: return try self.execute_i64DivS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 73: return try self.execute_i32DivU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 74: return try self.execute_i64DivU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 75: return try self.execute_i32RemS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 76: return try self.execute_i64RemS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 77: return try self.execute_i32RemU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 78: return try self.execute_i64RemU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 79: return self.execute_i32Eq(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 80: return self.execute_i64Eq(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 81: return self.execute_i32Ne(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 82: return self.execute_i64Ne(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 83: return self.execute_i32LtS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 84: return self.execute_i64LtS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 85: return self.execute_i32LtU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 86: return self.execute_i64LtU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 87: return self.execute_i32GtS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 88: return self.execute_i64GtS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 89: return self.execute_i32GtU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 90: return self.execute_i64GtU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 91: return self.execute_i32LeS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 92: return self.execute_i64LeS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 93: return self.execute_i32LeU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 94: return self.execute_i64LeU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 95: return self.execute_i32GeS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 96: return self.execute_i64GeS(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 97: return self.execute_i32GeU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 98: return self.execute_i64GeU(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 99: return self.execute_i32Clz(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 100: return self.execute_i64Clz(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 101: return self.execute_i32Ctz(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 102: return self.execute_i64Ctz(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 103: return self.execute_i32Popcnt(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 104: return self.execute_i64Popcnt(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 105: return self.execute_i32Eqz(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 106: return self.execute_i64Eqz(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 107: return self.execute_i32WrapI64(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 108: return self.execute_i64ExtendI32S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 109: return self.execute_i64ExtendI32U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 110: return self.execute_i32Extend8S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 111: return self.execute_i64Extend8S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 112: return self.execute_i32Extend16S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 113: return self.execute_i64Extend16S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 114: return self.execute_i64Extend32S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 115: return try self.execute_i32TruncF32S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 116: return try self.execute_i32TruncF32U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 117: return try self.execute_i32TruncSatF32S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 118: return try self.execute_i32TruncSatF32U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 119: return try self.execute_i32TruncF64S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 120: return try self.execute_i32TruncF64U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 121: return try self.execute_i32TruncSatF64S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 122: return try self.execute_i32TruncSatF64U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 123: return try self.execute_i64TruncF32S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 124: return try self.execute_i64TruncF32U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 125: return try self.execute_i64TruncSatF32S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 126: return try self.execute_i64TruncSatF32U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 127: return try self.execute_i64TruncF64S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 128: return try self.execute_i64TruncF64U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 129: return try self.execute_i64TruncSatF64S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 130: return try self.execute_i64TruncSatF64U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 131: return self.execute_f32ConvertI32S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 132: return self.execute_f32ConvertI32U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 133: return self.execute_f32ConvertI64S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 134: return self.execute_f32ConvertI64U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 135: return self.execute_f64ConvertI32S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 136: return self.execute_f64ConvertI32U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 137: return self.execute_f64ConvertI64S(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 138: return self.execute_f64ConvertI64U(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 139: return self.execute_f32ReinterpretI32(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 140: return self.execute_f64ReinterpretI64(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 141: return self.execute_i32ReinterpretF32(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 142: return self.execute_i64ReinterpretF64(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 143: return self.execute_f32Add(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 144: return self.execute_f64Add(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 145: return self.execute_f32Sub(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 146: return self.execute_f64Sub(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 147: return self.execute_f32Mul(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 148: return self.execute_f64Mul(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 149: return self.execute_f32Div(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 150: return self.execute_f64Div(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 151: return self.execute_f32Min(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 152: return self.execute_f64Min(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 153: return self.execute_f32Max(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 154: return self.execute_f64Max(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 155: return self.execute_f32CopySign(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 156: return self.execute_f64CopySign(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 157: return self.execute_f32Eq(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 158: return self.execute_f64Eq(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 159: return self.execute_f32Ne(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 160: return self.execute_f64Ne(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 161: return self.execute_f32Lt(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 162: return self.execute_f64Lt(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 163: return self.execute_f32Gt(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 164: return self.execute_f64Gt(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 165: return self.execute_f32Le(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 166: return self.execute_f64Le(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 167: return self.execute_f32Ge(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 168: return self.execute_f64Ge(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 169: return self.execute_f32Abs(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 170: return self.execute_f64Abs(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 171: return self.execute_f32Neg(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 172: return self.execute_f64Neg(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 173: return self.execute_f32Ceil(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 174: return self.execute_f64Ceil(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 175: return self.execute_f32Floor(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 176: return self.execute_f64Floor(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 177: return self.execute_f32Trunc(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 178: return self.execute_f64Trunc(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 179: return self.execute_f32Nearest(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 180: return self.execute_f64Nearest(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 181: return self.execute_f32Sqrt(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 182: return self.execute_f64Sqrt(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 183: return self.execute_f64PromoteF32(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 184: return self.execute_f32DemoteF64(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 185: return self.execute_select(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 186: return self.execute_refNull(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 187: return self.execute_refIsNull(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 188: return self.execute_refFunc(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 189: return try self.execute_tableGet(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 190: return try self.execute_tableSet(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 191: return self.execute_tableSize(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 192: return try self.execute_tableGrow(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 193: return try self.execute_tableFill(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 194: return try self.execute_tableCopy(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 195: return try self.execute_tableInit(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 196: return self.execute_tableElementDrop(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 197: return self.execute_onEnter(sp: &sp, pc: &pc, md: &md, ms: &ms) + case 198: return self.execute_onExit(sp: &sp, pc: &pc, md: &md, ms: &ms) default: preconditionFailure("Unknown instruction!?") } @@ -269,6 +272,28 @@ extension Execution { (pc.pointee, next) = try self.callIndirect(sp: &sp.pointee, pc: pc.pointee, md: &md.pointee, ms: &ms.pointee, immediate: immediate) return next } + @_silgen_name("wasmkit_execute_resizeFrameHeader") @inline(__always) + mutating func execute_resizeFrameHeader(sp: UnsafeMutablePointer, pc: UnsafeMutablePointer, md: UnsafeMutablePointer, ms: UnsafeMutablePointer) throws -> CodeSlot { + let immediate = Instruction.ResizeFrameHeaderOperand.load(from: &pc.pointee) + try self.resizeFrameHeader(sp: &sp.pointee, immediate: immediate) + let next = pc.pointee.pointee + pc.pointee = pc.pointee.advanced(by: 1) + return next + } + @_silgen_name("wasmkit_execute_returnCall") @inline(__always) + mutating func execute_returnCall(sp: UnsafeMutablePointer, pc: UnsafeMutablePointer, md: UnsafeMutablePointer, ms: UnsafeMutablePointer) throws -> CodeSlot { + let immediate = Instruction.ReturnCallOperand.load(from: &pc.pointee) + let next: CodeSlot + (pc.pointee, next) = try self.returnCall(sp: &sp.pointee, pc: pc.pointee, md: &md.pointee, ms: &ms.pointee, immediate: immediate) + return next + } + @_silgen_name("wasmkit_execute_returnCallIndirect") @inline(__always) + mutating func execute_returnCallIndirect(sp: UnsafeMutablePointer, pc: UnsafeMutablePointer, md: UnsafeMutablePointer, ms: UnsafeMutablePointer) throws -> CodeSlot { + let immediate = Instruction.ReturnCallIndirectOperand.load(from: &pc.pointee) + let next: CodeSlot + (pc.pointee, next) = try self.returnCallIndirect(sp: &sp.pointee, pc: pc.pointee, md: &md.pointee, ms: &ms.pointee, immediate: immediate) + return next + } @_silgen_name("wasmkit_execute_unreachable") @inline(__always) mutating func execute_unreachable(sp: UnsafeMutablePointer, pc: UnsafeMutablePointer, md: UnsafeMutablePointer, ms: UnsafeMutablePointer) throws -> CodeSlot { let next: CodeSlot diff --git a/Sources/WasmKit/Execution/Errors.swift b/Sources/WasmKit/Execution/Errors.swift index 29405ad9..884901b7 100644 --- a/Sources/WasmKit/Execution/Errors.swift +++ b/Sources/WasmKit/Execution/Errors.swift @@ -1,114 +1,207 @@ import WasmTypes -public enum Trap: Error { - // FIXME: for debugging purposes, to be eventually deleted - case _raw(String) +import struct WasmParser.Import - case unreachable +/// The backtrace of the trap. +struct Backtrace: CustomStringConvertible { + /// A symbol in the backtrace. + struct Symbol { + /// The function that the symbol represents. + let function: Function + + /// The name of the symbol. + let name: String? + } + + /// The symbols in the backtrace. + let symbols: [Symbol?] + + /// Textual description of the backtrace. + var description: String { + symbols.enumerated().map { (index, symbol) in + let name = symbol?.name ?? "unknown" + return " \(index): \(name)" + }.joined(separator: "\n") + } +} - // Stack - /// Stack overflow - case stackOverflow - /// The stack value type does not match the expected type - case stackValueTypesMismatch(expected: WasmTypes.ValueType, actual: WasmTypes.ValueType) +/// An error that occurs during execution of a WebAssembly module. +public struct Trap: Error, CustomStringConvertible { + /// The reason for the trap. + var reason: TrapReason + + /// The backtrace of the trap. + private(set) var backtrace: Backtrace? + + init(_ code: TrapReason, backtrace: Backtrace? = nil) { + self.reason = code + self.backtrace = backtrace + } + + init(_ message: TrapReason.Message, backtrace: Backtrace? = nil) { + self.init(.message(message), backtrace: backtrace) + } + + /// The description of the trap. + public var description: String { + var desc = "Trap: \(reason)" + if let backtrace = backtrace { + desc += "\n\(backtrace)" + } + return desc + } + + func withBacktrace(_ backtrace: Backtrace) -> Trap { + var trap = self + trap.backtrace = backtrace + return trap + } +} + +/// A reason for a trap that occurred during execution of a WebAssembly module. +enum TrapReason: Error, CustomStringConvertible { + struct Message { + let text: String + + init(_ text: String) { + self.text = text + } + } + /// A trap with a string message + case message(Message) + /// `unreachable` instruction executed + case unreachable /// Too deep call stack + /// + /// Note: When this trap occurs, consider extending ``EngineConfiguration/stackSize``. case callStackExhausted - - // Store /// Out of bounds table access - case outOfBoundsTableAccess(Int) - /// Reading a dropped reference - case readingDroppedReference(index: Int) - - // Invocation - /// Exported function not found - case exportedFunctionNotFound(Instance, name: String) - /// The table element is not initialized - case tableUninitialized(Int) - /// Undefined element in the table - case undefinedElement - /// Table size overflow - case tableSizeOverflow - /// Indirect call type mismatch - case callIndirectFunctionTypeMismatch(actual: FunctionType, expected: FunctionType) + case tableOutOfBounds(Int) /// Out of bounds memory access - case outOfBoundsMemoryAccess - /// Invalid function index - case invalidFunctionIndex(Int) + case memoryOutOfBounds + /// `call_indirect` instruction called an uninitialized table element. + case indirectCallToNull(Int) + /// Indirect call type mismatch + case typeMismatchCall(actual: FunctionType, expected: FunctionType) /// Integer divided by zero case integerDividedByZero /// Integer overflowed during arithmetic operation - case integerOverflowed + case integerOverflow /// Invalid conversion to integer case invalidConversionToInteger - /// Human-readable text representation of the trap that `.wast` text format expects in assertions - public var assertionText: String { + /// The description of the trap reason. + var description: String { switch self { - case .outOfBoundsMemoryAccess: + case .message(let message): + return message.text + case .unreachable: + return "unreachable" + case .callStackExhausted: + return "call stack exhausted" + case .memoryOutOfBounds: return "out of bounds memory access" case .integerDividedByZero: return "integer divide by zero" - case .integerOverflowed: + case .integerOverflow: return "integer overflow" case .invalidConversionToInteger: return "invalid conversion to integer" - case .undefinedElement: - return "undefined element" - case let .tableUninitialized(elementIndex): - return "uninitialized element \(elementIndex)" - case .callIndirectFunctionTypeMismatch: - return "indirect call type mismatch" - case .outOfBoundsTableAccess, .tableSizeOverflow: - return "out of bounds table access" - case .callStackExhausted: - return "call stack exhausted" - default: - return String(describing: self) + case let .indirectCallToNull(elementIndex): + return "indirect call to null element (uninitialized element \(elementIndex))" + case .typeMismatchCall(let actual, let expected): + return "indirect call type mismatch, expected \(expected), got \(actual)" + case .tableOutOfBounds(let index): + return "out of bounds table access at \(index) (undefined element)" } } } -public enum InstantiationError: Error { - case importsAndExternalValuesMismatch - case invalidTableExpression - case outOfBoundsTableAccess - case outOfBoundsMemoryAccess - case unsupported(String) - case exportIndexOutOfBounds(kind: String, index: Int, count: Int) +extension TrapReason.Message { + static func initialTableSizeExceedsLimit(numberOfElements: Int) -> Self { + Self("initial table size exceeds the resource limit: \(numberOfElements) elements") + } + static func initialMemorySizeExceedsLimit(byteSize: Int) -> Self { + Self("initial memory size exceeds the resource limit: \(byteSize) bytes") + } + static func parameterTypesMismatch(expected: [ValueType], got: [Value]) -> Self { + Self("parameter types don't match, expected \(expected), got \(got)") + } + static func resultTypesMismatch(expected: [ValueType], got: [Value]) -> Self { + Self("result types don't match, expected \(expected), got \(got)") + } + static var cannotAssignToImmutableGlobal: Self { + Self("cannot assign to an immutable global") + } + static func noGlobalExportWithName(globalName: String, instance: Instance) -> Self { + Self("no global export with name \(globalName) in a module instance \(instance)") + } + static func exportedFunctionNotFound(name: String, instance: Instance) -> Self { + Self("exported function \(name) not found in instance \(instance)") + } +} + +struct ImportError: Error { + struct Message { + let text: String - /// Human-readable text representation of the trap that `.wast` text format expects in assertions - public var assertionText: String { - switch self { - case .outOfBoundsTableAccess: - return "out of bounds table access" - case .outOfBoundsMemoryAccess: - return "out of bounds memory access" - case .unsupported(let message): - return "unsupported operation: \(message)" - default: - return String(describing: self) + init(_ text: String) { + self.text = text } } -} -public enum ImportError: Error { - case unknownImport(moduleName: String, externalName: String) - case incompatibleImportType - case importedEntityFromDifferentStore - case moduleInstanceAlreadyRegistered(String) + let message: Message - /// Human-readable text representation of the trap that `.wast` text format expects in assertions - public var assertionText: String { - switch self { - case .unknownImport: - return "unknown import" - case .incompatibleImportType: - return "incompatible import type" - case .importedEntityFromDifferentStore: - return "imported entity from different store" - case let .moduleInstanceAlreadyRegistered(name): - return "a module instance is already registered under a name `\(name)" + init(_ message: Message) { + self.message = message + } +} + +extension ImportError.Message { + static func missing(moduleName: String, externalName: String) -> Self { + Self("unknown import \(moduleName).\(externalName)") + } + static func incompatibleType(_ importEntry: Import, entity: InternalExternalValue) -> Self { + let expected: String + switch importEntry.descriptor { + case .function: + expected = "function" + case .global: + expected = "global" + case .memory: + expected = "memory" + case .table: + expected = "table" + } + let got: String + switch entity { + case .function: + got = "function" + case .global: + got = "global" + case .memory: + got = "memory" + case .table: + got = "table" } + return Self("incompatible import type for \(importEntry.module).\(importEntry.name), expected \(expected), got \(got)") + } + static func incompatibleFunctionType(_ importEntry: Import, actual: FunctionType, expected: FunctionType) -> Self { + Self("incompatible import type: function type for \(importEntry.module).\(importEntry.name), expected \(expected), got \(actual)") + } + static func incompatibleTableType(_ importEntry: Import, actual: TableType, expected: TableType) -> Self { + Self("incompatible import type: table type for \(importEntry.module).\(importEntry.name), expected \(expected), got \(actual)") + } + static func incompatibleMemoryType(_ importEntry: Import, actual: MemoryType, expected: MemoryType) -> Self { + Self("incompatible import type: memory type for \(importEntry.module).\(importEntry.name), expected \(expected), got \(actual)") + } + static func incompatibleGlobalType(_ importEntry: Import, actual: GlobalType, expected: GlobalType) -> Self { + Self("incompatible import type: global type for \(importEntry.module).\(importEntry.name), expected \(expected), got \(actual)") + } + static func importedEntityFromDifferentStore(_ importEntry: Import) -> Self { + Self("imported entity from different store: \(importEntry.module).\(importEntry.name)") + } + static func moduleInstanceAlreadyRegistered(_ name: String) -> Self { + Self("a module instance is already registered under a name `\(name)") } } diff --git a/Sources/WasmKit/Execution/Execution.swift b/Sources/WasmKit/Execution/Execution.swift index f262064b..7431d20f 100644 --- a/Sources/WasmKit/Execution/Execution.swift +++ b/Sources/WasmKit/Execution/Execution.swift @@ -12,7 +12,7 @@ struct Execution { /// The error trap thrown during execution. /// This property must not be assigned to be non-nil more than once. /// - Note: If the trap is set, it must be released manually. - private var trap: UnsafeRawPointer? = nil + private var trap: (error: UnsafeRawPointer, sp: Sp)? = nil /// Executes the given closure with a new execution state associated with /// the given ``Store`` instance. @@ -26,57 +26,98 @@ struct Execution { valueStack.deallocate() } var context = Execution(store: store, stackEnd: valueStack.advanced(by: limit)) - defer { - if let trap = context.trap { - // Manually release the error object because the trap is caught in C and - // held as a raw pointer. - wasmkit_swift_errorRelease(trap) - } - } return try body(&context, valueStack) } /// Gets the current instance from the stack pointer. @inline(__always) func currentInstance(sp: Sp) -> InternalInstance { - InternalInstance(bitPattern: UInt(sp[-3].i64)).unsafelyUnwrapped + sp.currentInstance.unsafelyUnwrapped + } + + /// An iterator for the call frames in the VM stack. + struct FrameIterator: IteratorProtocol { + struct Element { + let pc: Pc + let function: EntityHandle? + } + + /// The stack pointer currently traversed. + private var sp: Sp? + + init(sp: Sp) { + self.sp = sp + } + + mutating func next() -> Element? { + guard let sp = self.sp, let pc = sp.returnPC else { + // Reached the root frame, whose stack pointer is nil. + return nil + } + self.sp = sp.previousSP + return Element(pc: pc, function: sp.currentFunction) + } + } + + static func captureBacktrace(sp: Sp, store: Store) -> Backtrace { + var frames = FrameIterator(sp: sp) + var symbols: [Backtrace.Symbol?] = [] + while let frame = frames.next() { + guard let function = frame.function else { + symbols.append(nil) + continue + } + let symbolName = store.nameRegistry.symbolicate(.wasm(function)) + symbols.append( + Backtrace.Symbol( + function: Function(handle: .wasm(function), store: store), + name: symbolName + ) + ) + } + return Backtrace(symbols: symbols) + } + + private func initializeConstSlots( + sp: Sp, iseq: InstructionSequence, + numberOfNonParameterLocals: Int + ) { + // Initialize the locals with zeros (all types of value have the same representation) + sp.initialize(repeating: UntypedValue.default.storage, count: numberOfNonParameterLocals) + if let constants = iseq.constants.baseAddress { + let count = iseq.constants.count + sp.advanced(by: numberOfNonParameterLocals).withMemoryRebound(to: UntypedValue.self, capacity: count) { + $0.initialize(from: constants, count: count) + } + } } /// Pushes a new call frame to the VM stack. @inline(__always) - mutating func pushFrame( + func pushFrame( iseq: InstructionSequence, - instance: InternalInstance, + function: EntityHandle, numberOfNonParameterLocals: Int, sp: Sp, returnPC: Pc, spAddend: VReg ) throws -> Sp { let newSp = sp.advanced(by: Int(spAddend)) - guard newSp.advanced(by: iseq.maxStackHeight) < stackEnd else { - throw Trap.callStackExhausted - } - // Initialize the locals with zeros (all types of value have the same representation) - newSp.initialize(repeating: UntypedValue.default.storage, count: numberOfNonParameterLocals) - if let constants = iseq.constants.baseAddress { - let count = iseq.constants.count - newSp.advanced(by: numberOfNonParameterLocals).withMemoryRebound(to: UntypedValue.self, capacity: count) { - $0.initialize(from: constants, count: count) - } - } - newSp[-1] = UInt64(UInt(bitPattern: sp)) - newSp[-2] = UInt64(UInt(bitPattern: returnPC)) - newSp[-3] = UInt64(UInt(bitPattern: instance.bitPattern)) + try checkStackBoundary(newSp.advanced(by: iseq.maxStackHeight)) + initializeConstSlots(sp: newSp, iseq: iseq, numberOfNonParameterLocals: numberOfNonParameterLocals) + newSp.previousSP = sp + newSp.returnPC = returnPC + newSp.currentFunction = function return newSp } /// Pops the current frame from the VM stack. @inline(__always) - mutating func popFrame(sp: inout Sp, pc: inout Pc, md: inout Md, ms: inout Ms) { + func popFrame(sp: inout Sp, pc: inout Pc, md: inout Md, ms: inout Ms) { let oldSp = sp - sp = Sp(bitPattern: UInt(oldSp[-1])).unsafelyUnwrapped - pc = Pc(bitPattern: UInt(oldSp[-2])).unsafelyUnwrapped - let toInstance = InternalInstance(bitPattern: UInt(oldSp[-3])).unsafelyUnwrapped - let fromInstance = InternalInstance(bitPattern: UInt(sp[-3])) + sp = oldSp.previousSP.unsafelyUnwrapped + pc = oldSp.returnPC.unsafelyUnwrapped + let toInstance = oldSp.currentInstance.unsafelyUnwrapped + let fromInstance = sp.currentInstance CurrentMemory.mayUpdateCurrentInstance(instance: toInstance, from: fromInstance, md: &md, ms: &ms) } } @@ -187,6 +228,30 @@ extension Sp { get { return Float64(bitPattern: read(index)) } nonmutating set { write(index, .f64(newValue)) } } + + // MARK: - Special slots + + /// The current executing function. + fileprivate var currentFunction: EntityHandle? { + get { return EntityHandle(bitPattern: UInt(self[-3].i64)) } + nonmutating set { self[-3] = UInt64(UInt(bitPattern: newValue?.bitPattern ?? 0)) } + } + + /// The return program counter of the current frame. + fileprivate var returnPC: Pc? { + get { return Pc(bitPattern: UInt(self[-2])) } + nonmutating set { self[-2] = UInt64(UInt(bitPattern: newValue)) } + } + + /// The previous stack pointer of the current frame. + fileprivate var previousSP: Sp? { + get { return Sp(bitPattern: UInt(self[-1])) } + nonmutating set { self[-1] = UInt64(UInt(bitPattern: newValue)) } + } + + fileprivate var currentInstance: InternalInstance? { + currentFunction?.instance + } } extension Pc { @@ -206,7 +271,7 @@ extension Pc { /// Executes a WebAssembly function. /// /// - Parameters: -/// - runtime: The runtime instance. +/// - store: The store instance. /// - function: The function to be executed. /// - type: The function type. /// - arguments: The arguments to be passed to the function. @@ -220,12 +285,15 @@ func executeWasm( arguments: [Value], callerInstance: InternalInstance ) throws -> [Value] { - // NOTE: `runtime` variable must not outlive this function + // NOTE: `store` variable must not outlive this function let store = StoreRef(store) return try Execution.with(store: store) { (stack, sp) in // Advance the stack pointer to be able to reference negative indices // for saving slots. let sp = sp.advanced(by: FrameHeaderLayout.numberOfSavingSlots) + // Mark root stack pointer and current function as nil. + sp.previousSP = nil + sp.currentFunction = nil for (index, argument) in arguments.enumerated() { sp[VReg(index)] = UntypedValue(argument) } @@ -333,8 +401,17 @@ extension Execution { var pc = pc let handler = pc.read(wasmkit_tc_exec.self) wasmkit_tc_start(handler, sp, pc, md, ms, &self) - if let error = self.trap { - throw unsafeBitCast(error, to: Error.self) + if let (rawError, trappingSp) = self.trap { + let error = unsafeBitCast(rawError, to: Error.self) + // Manually release the error object because the trap is caught in C and + // held as a raw pointer. + wasmkit_swift_errorRelease(rawError) + + guard let trap = error as? Trap else { + throw error + } + // Attach backtrace if the thrown error is a trap + throw trap.withBacktrace(Self.captureBacktrace(sp: trappingSp, store: store.value)) } } @@ -410,11 +487,15 @@ extension Execution { defer { stats.dump() } #endif var opcode = pc.read(OpcodeID.self) - while true { - #if EngineStats - stats.track(inst) - #endif - opcode = try doExecute(opcode, sp: &sp, pc: &pc, md: &md, ms: &ms) + do { + while true { + #if EngineStats + stats.track(inst) + #endif + opcode = try doExecute(opcode, sp: &sp, pc: &pc, md: &md, ms: &ms) + } + } catch let trap as Trap { + throw trap.withBacktrace(Self.captureBacktrace(sp: sp, store: store.value)) } } @@ -424,53 +505,119 @@ extension Execution { /// It's used only when direct threading is enabled. /// - Parameter trap: The error trap thrown during execution. @_silgen_name("wasmkit_execution_state_set_error") - mutating func setError(_ trap: UnsafeRawPointer) { + mutating func setError(_ rawError: UnsafeRawPointer, sp: Sp) { precondition(self.trap == nil) - self.trap = trap + self.trap = (rawError, sp) + } + + @inline(__always) + func checkStackBoundary(_ sp: Sp) throws { + guard sp < stackEnd else { throw Trap(.callStackExhausted) } } /// Returns the new program counter and stack pointer. @inline(never) - mutating func invoke( + func invoke( function: InternalFunction, callerInstance: InternalInstance?, spAddend: VReg, sp: Sp, pc: Pc, md: inout Md, ms: inout Ms ) throws -> (Pc, Sp) { if function.isWasm { - let function = function.wasm - let iseq = try function.ensureCompiled(store: store) - - let newSp = try pushFrame( - iseq: iseq, - instance: function.instance, - numberOfNonParameterLocals: function.numberOfNonParameterLocals, - sp: sp, - returnPC: pc, - spAddend: spAddend - ) - Execution.CurrentMemory.mayUpdateCurrentInstance( - instance: function.instance, - from: callerInstance, md: &md, ms: &ms + return try invokeWasmFunction( + function: function.wasm, callerInstance: callerInstance, + spAddend: spAddend, sp: sp, pc: pc, md: &md, ms: &ms ) - return (iseq.baseAddress, newSp) } else { - let function = function.host - let resolvedType = store.value.engine.resolveType(function.type) - let layout = FrameHeaderLayout(type: resolvedType) - let parameters = resolvedType.parameters.enumerated().map { (i, type) in - sp[spAddend + layout.paramReg(i)].cast(to: type) - } - let instance = self.currentInstance(sp: sp) - let caller = Caller( - instanceHandle: instance, - store: store.value + try invokeHostFunction(function: function.host, sp: sp, spAddend: spAddend) + return (pc, sp) + } + } + + @inline(never) + func tailInvoke( + function: InternalFunction, + callerInstance: InternalInstance?, + sp: Sp, pc: Pc, md: inout Md, ms: inout Ms + ) throws -> (Pc, Sp) { + if function.isWasm { + return try tailInvokeWasmFunction( + function: function.wasm, callerInstance: callerInstance, + sp: sp, md: &md, ms: &ms ) - let results = try function.implementation(caller, Array(parameters)) - for (index, result) in results.enumerated() { - sp[spAddend + layout.returnReg(index)] = UntypedValue(result) - } + } else { + try invokeHostFunction(function: function.host, sp: sp, spAddend: 0) return (pc, sp) } } + + /// Executes the given wasm function while overwriting the current frame. + /// + /// Precondition: The frame header must be already resized to be compatible + /// with the callee's frame header layout. + @inline(__always) + private func tailInvokeWasmFunction( + function: EntityHandle, + callerInstance: InternalInstance?, + sp: Sp, md: inout Md, ms: inout Ms + ) throws -> (Pc, Sp) { + let iseq = try function.ensureCompiled(store: store) + try checkStackBoundary(sp.advanced(by: iseq.maxStackHeight)) + sp.currentFunction = function + + initializeConstSlots(sp: sp, iseq: iseq, numberOfNonParameterLocals: function.numberOfNonParameterLocals) + + Execution.CurrentMemory.mayUpdateCurrentInstance( + instance: function.instance, + from: callerInstance, md: &md, ms: &ms + ) + return (iseq.baseAddress, sp) + } + + /// Executes the given WebAssembly function. + @inline(__always) + private func invokeWasmFunction( + function: EntityHandle, + callerInstance: InternalInstance?, + spAddend: VReg, + sp: Sp, pc: Pc, md: inout Md, ms: inout Ms + ) throws -> (Pc, Sp) { + let iseq = try function.ensureCompiled(store: store) + + let newSp = try pushFrame( + iseq: iseq, + function: function, + numberOfNonParameterLocals: function.numberOfNonParameterLocals, + sp: sp, + returnPC: pc, + spAddend: spAddend + ) + Execution.CurrentMemory.mayUpdateCurrentInstance( + instance: function.instance, + from: callerInstance, md: &md, ms: &ms + ) + return (iseq.baseAddress, newSp) + } + + /// Executes the given host function. + /// + /// Note that this function does not modify neither the positions of the + /// stack pointer nor the program counter. + @inline(never) + private func invokeHostFunction(function: EntityHandle, sp: Sp, spAddend: VReg) throws { + let resolvedType = store.value.engine.resolveType(function.type) + let layout = FrameHeaderLayout(type: resolvedType) + let parameters = resolvedType.parameters.enumerated().map { (i, type) in + sp[spAddend + layout.paramReg(i)].cast(to: type) + } + let instance = self.currentInstance(sp: sp) + let caller = Caller( + instanceHandle: instance, + store: store.value + ) + let results = try function.implementation(caller, Array(parameters)) + for (index, result) in results.enumerated() { + sp[spAddend + layout.returnReg(index)] = UntypedValue(result) + } + } } diff --git a/Sources/WasmKit/Execution/Function.swift b/Sources/WasmKit/Execution/Function.swift index 4bcf02a5..e87f8b8d 100644 --- a/Sources/WasmKit/Execution/Function.swift +++ b/Sources/WasmKit/Execution/Function.swift @@ -156,7 +156,7 @@ struct InternalFunction: Equatable, Hashable { extension InternalFunction: ValidatableEntity { static func createOutOfBoundsError(index: Int, count: Int) -> any Error { - Trap.invalidFunctionIndex(index) + ValidationError(.indexOutOfBounds("function", index, max: count)) } } @@ -199,26 +199,26 @@ extension InternalFunction { private func check(functionType: FunctionType, parameters: [Value]) throws { guard check(expectedTypes: functionType.parameters, values: parameters) else { - throw Trap._raw("parameters types don't match, expected \(functionType.parameters), got \(parameters)") + throw Trap(.parameterTypesMismatch(expected: functionType.parameters, got: parameters)) } } private func check(functionType: FunctionType, results: [Value]) throws { guard check(expectedTypes: functionType.results, values: results) else { - throw Trap._raw("result types don't match, expected \(functionType.results), got \(results)") + throw Trap(.resultTypesMismatch(expected: functionType.results, got: results)) } } func assumeCompiled() -> ( InstructionSequence, locals: Int, - instance: InternalInstance + function: EntityHandle ) { let entity = self.wasm guard case let .compiled(iseq) = entity.code else { preconditionFailure() } - return (iseq, entity.numberOfNonParameterLocals, entity.instance) + return (iseq, entity.numberOfNonParameterLocals, entity) } } diff --git a/Sources/WasmKit/Execution/Instances.swift b/Sources/WasmKit/Execution/Instances.swift index 2f47c78e..27a2452a 100644 --- a/Sources/WasmKit/Execution/Instances.swift +++ b/Sources/WasmKit/Execution/Instances.swift @@ -82,7 +82,7 @@ struct InstanceEntity /* : ~Copyable */ { var exports: [String: InternalExternalValue] var functionRefs: Set var features: WasmFeatureSet - var hasDataCount: Bool + var dataCount: UInt32? static var empty: InstanceEntity { InstanceEntity( @@ -96,7 +96,7 @@ struct InstanceEntity /* : ~Copyable */ { exports: [:], functionRefs: [], features: [], - hasDataCount: false + dataCount: nil ) } @@ -283,7 +283,7 @@ struct TableEntity /* : ~Copyable */ { let numberOfElements = Int(tableType.limits.min) guard try resourceLimiter.limitTableGrowth(to: numberOfElements) else { - throw Trap._raw("Initial table size exceeds the resource limit: \(numberOfElements) elements") + throw Trap(.initialTableSizeExceedsLimit(numberOfElements: numberOfElements)) } elements = Array(repeating: emptyElement, count: numberOfElements) self.tableType = tableType @@ -318,9 +318,12 @@ struct TableEntity /* : ~Copyable */ { let (destinationEnd, destinationOverflow) = destination.addingReportingOverflow(count) let (sourceEnd, sourceOverflow) = source.addingReportingOverflow(count) - guard !destinationOverflow, !sourceOverflow else { throw Trap.tableSizeOverflow } - guard destinationEnd <= elements.count else { throw Trap.outOfBoundsTableAccess(destinationEnd) } - guard sourceEnd <= references.count else { throw Trap.outOfBoundsTableAccess(sourceEnd) } + guard !destinationOverflow, destinationEnd <= elements.count else { + throw Trap(.tableOutOfBounds(destinationEnd)) + } + guard !sourceOverflow, sourceEnd <= references.count else { + throw Trap(.tableOutOfBounds(sourceEnd)) + } elements.withUnsafeMutableBufferPointer { table in references.withUnsafeBufferPointer { segment in @@ -331,7 +334,7 @@ struct TableEntity /* : ~Copyable */ { mutating func fill(repeating value: Reference, from index: Int, count: Int) throws { let (end, overflow) = index.addingReportingOverflow(count) - guard !overflow, end <= elements.count else { throw Trap.tableSizeOverflow } + guard !overflow, end <= elements.count else { throw Trap(.tableOutOfBounds(end)) } elements.withUnsafeMutableBufferPointer { $0[index.. Error { - Trap._raw("Table index out of bounds: \(index) (max: \(count))") + ValidationError(.indexOutOfBounds("table", index, max: count)) } } @@ -457,7 +463,7 @@ struct MemoryEntity /* : ~Copyable */ { init(_ memoryType: MemoryType, resourceLimiter: any ResourceLimiter) throws { let byteSize = Int(memoryType.min) * Self.pageSize guard try resourceLimiter.limitMemoryGrowth(to: byteSize) else { - throw Trap._raw("Initial memory size exceeds the resource limit: \(byteSize) bytes") + throw Trap(.initialMemorySizeExceedsLimit(byteSize: byteSize)) } data = Array(repeating: 0, count: byteSize) let defaultMaxPageCount = Self.maxPageCount(isMemory64: memoryType.isMemory64) @@ -490,7 +496,7 @@ struct MemoryEntity /* : ~Copyable */ { guard !destinationOverflow, destinationEnd <= data.count, !sourceOverflow, sourceEnd <= data.count else { - throw Trap.outOfBoundsMemoryAccess + throw Trap(.memoryOutOfBounds) } data.withUnsafeMutableBufferPointer { guard let base = UnsafeMutableRawPointer($0.baseAddress) else { return } @@ -507,7 +513,7 @@ struct MemoryEntity /* : ~Copyable */ { guard !destinationOverflow, destinationEnd <= data.count, !sourceOverflow, sourceEnd <= segment.data.count else { - throw Trap.outOfBoundsMemoryAccess + throw Trap(.memoryOutOfBounds) } data.withUnsafeMutableBufferPointer { memory in segment.data.withUnsafeBufferPointer { segment in @@ -525,7 +531,7 @@ struct MemoryEntity /* : ~Copyable */ { mutating func write(offset: Int, bytes: ArraySlice) throws { let endOffset = offset + bytes.count guard endOffset <= data.count else { - throw Trap.outOfBoundsMemoryAccess + throw Trap(.memoryOutOfBounds) } data[offset.. Error { - Trap._raw("Memory index out of bounds: \(index) (max: \(count))") + ValidationError(.indexOutOfBounds("memory", index, max: count)) } } @@ -574,6 +580,9 @@ public struct Memory: Equatable { /// let instance = try module.instantiate(store: store, imports: imports) /// ``` public init(store: Store, type: MemoryType) throws { + // Validate the memory type because the type is not validated at instantiation time. + try ModuleValidator.checkMemoryType(type, features: store.engine.configuration.features) + self.init( handle: try store.allocator.allocate(memoryType: type, resourceLimiter: store.resourceLimiter), allocator: store.allocator @@ -624,7 +633,7 @@ struct GlobalEntity /* : ~Copyable */ { extension GlobalEntity: ValidatableEntity { static func createOutOfBoundsError(index: Int, count: Int) -> Error { - Trap._raw("Global index out of bounds: \(index) (max: \(count))") + ValidationError(.indexOutOfBounds("global", index, max: count)) } } @@ -649,7 +658,7 @@ public struct Global: Equatable { public func assign(_ value: Value) throws { try handle.withValue { global in guard global.globalType.mutability == .variable else { - throw Trap._raw("Cannot assign to an immutable global") + throw Trap(.cannotAssignToImmutableGlobal) } global.value = value } @@ -710,7 +719,7 @@ struct ElementSegmentEntity { extension ElementSegmentEntity: ValidatableEntity { static func createOutOfBoundsError(index: Int, count: Int) -> Error { - Trap._raw("Element index out of bounds: \(index) (max: \(count))") + ValidationError(.indexOutOfBounds("element", index, max: count)) } } @@ -727,12 +736,6 @@ struct DataSegmentEntity { } } -extension DataSegmentEntity: ValidatableEntity { - static func createOutOfBoundsError(index: Int, count: Int) -> Error { - Trap._raw("Data index out of bounds: \(index) (max: \(count))") - } -} - typealias InternalDataSegment = EntityHandle /// > Note: diff --git a/Sources/WasmKit/Execution/Instructions/Control.swift b/Sources/WasmKit/Execution/Instructions/Control.swift index a3566222..47dbbb49 100644 --- a/Sources/WasmKit/Execution/Instructions/Control.swift +++ b/Sources/WasmKit/Execution/Instructions/Control.swift @@ -2,7 +2,7 @@ /// extension Execution { func unreachable(sp: Sp, pc: Pc) throws -> (Pc, CodeSlot) { - throw Trap.unreachable + throw Trap(.unreachable) } mutating func nop(sp: Sp) { } @@ -80,7 +80,7 @@ extension Execution { let (iseq, locals, instance) = internalCallOperand.callee.assumeCompiled() sp = try pushFrame( iseq: iseq, - instance: instance, + function: instance, numberOfNonParameterLocals: locals, sp: sp, returnPC: pc, spAddend: internalCallOperand.spAddend @@ -112,25 +112,26 @@ extension Execution { @inline(never) private func prepareForIndirectCall( sp: Sp, tableIndex: TableIndex, expectedType: InternedFuncType, - callIndirectOperand: Instruction.CallIndirectOperand + address: VReg ) throws -> (InternalFunction, InternalInstance) { let callerInstance = currentInstance(sp: sp) let table = callerInstance.tables[Int(tableIndex)] - let value = sp[callIndirectOperand.index].asAddressOffset(table.limits.isMemory64) + let value = sp[address].asAddressOffset(table.limits.isMemory64) let elementIndex = Int(value) guard elementIndex < table.elements.count else { - throw Trap.undefinedElement + throw Trap(.tableOutOfBounds(elementIndex)) } guard case let .function(rawBitPattern?) = table.elements[elementIndex] else { - throw Trap.tableUninitialized(elementIndex) + throw Trap(.indirectCallToNull(elementIndex)) } let function = InternalFunction(bitPattern: rawBitPattern) guard function.type == expectedType else { - throw Trap.callIndirectFunctionTypeMismatch( - actual: store.value.engine.resolveType(function.type), - expected: store.value.engine.resolveType(expectedType) - ) + throw Trap( + .typeMismatchCall( + actual: store.value.engine.resolveType(function.type), + expected: store.value.engine.resolveType(expectedType) + )) } return (function, callerInstance) } @@ -140,7 +141,7 @@ extension Execution { var pc = pc let (function, callerInstance) = try prepareForIndirectCall( sp: sp, tableIndex: immediate.tableIndex, expectedType: immediate.type, - callIndirectOperand: immediate + address: immediate.index ) (pc, sp) = try invoke( function: function, @@ -151,6 +152,65 @@ extension Execution { return pc.next() } + mutating func returnCall(sp: inout Sp, pc: Pc, md: inout Md, ms: inout Ms, immediate: Instruction.ReturnCallOperand) throws -> (Pc, CodeSlot) { + var pc = pc + (pc, sp) = try tailInvoke( + function: immediate.callee, + callerInstance: currentInstance(sp: sp), + sp: sp, pc: pc, md: &md, ms: &ms + ) + return pc.next() + } + + mutating func returnCallIndirect(sp: inout Sp, pc: Pc, md: inout Md, ms: inout Ms, immediate: Instruction.ReturnCallIndirectOperand) throws -> (Pc, CodeSlot) { + var pc = pc + let (function, callerInstance) = try prepareForIndirectCall( + sp: sp, tableIndex: immediate.tableIndex, expectedType: immediate.type, + address: immediate.index + ) + (pc, sp) = try tailInvoke( + function: function, + callerInstance: callerInstance, + sp: sp, pc: pc, md: &md, ms: &ms + ) + return pc.next() + } + + mutating func resizeFrameHeader(sp: inout Sp, immediate: Instruction.ResizeFrameHeaderOperand) throws { + // The params/results space are resized by `delta` slots and the rest of the + // frame is copied to the new location. See the following diagram for the + // layout of the frame before and after the resize operation: + // + // + // |--------BEFORE-------| |--------AFTER--------| + // | Params | Results | | Params | Results | + // | ... | ... | | ... | ... | + // Old Header ->|---------------------|\ | ... | ... | -+ + // | Sp | \ | ... | ... | | delta + // |---------------------| \|---------------------|<- New Header -+ -+ + // | Pc | | Sp | | + // |---------------------| |---------------------| | + // | Current Func | C | Pc | | + // Old Sp ->|---------------------| O |---------------------| | + // | Locals | P | Current Func | | + // | ... | Y |---------------------|<- New Sp | + // |---------------------| | Locals | | sizeToCopy + // | Consts | | ... | | + // | ... | |---------------------| | + // |---------------------| | Consts | | + // | Value Stack | | ... | | + // | ... | |---------------------| | + // |---------------------|\ | Value Stack | | + // \ | ... | | + // \|---------------------| -+ + let newSp = sp.advanced(by: Int(immediate.delta)) + try checkStackBoundary(newSp) + let oldFrameHeader = sp.advanced(by: -FrameHeaderLayout.numberOfSavingSlots) + let newFrameHeader = newSp.advanced(by: -FrameHeaderLayout.numberOfSavingSlots) + newFrameHeader.update(from: oldFrameHeader, count: Int(immediate.sizeToCopy)) + sp = newSp + } + mutating func onEnter(sp: Sp, immediate: Instruction.OnEnterOperand) { let function = currentInstance(sp: sp).functions[Int(immediate)] self.store.value.engine.interceptor?.onEnterFunction( diff --git a/Sources/WasmKit/Execution/Instructions/Instruction.swift b/Sources/WasmKit/Execution/Instructions/Instruction.swift index 2f9a9697..65b4028c 100644 --- a/Sources/WasmKit/Execution/Instructions/Instruction.swift +++ b/Sources/WasmKit/Execution/Instructions/Instruction.swift @@ -25,6 +25,13 @@ enum Instruction: Equatable { case internalCall(Instruction.CallOperand) /// WebAssembly Core Instruction `call_indirect` case callIndirect(Instruction.CallIndirectOperand) + /// Resize the frame header by increasing param/result slots and copying `sizeToCopy` + /// slots placed after the header + case resizeFrameHeader(Instruction.ResizeFrameHeaderOperand) + /// WebAssembly Core Instruction `return_call` + case returnCall(Instruction.ReturnCallOperand) + /// WebAssembly Core Instruction `return_call_indirect` + case returnCallIndirect(Instruction.ReturnCallIndirectOperand) /// WebAssembly Core Instruction `unreachable` case unreachable /// WebAssembly Core Instruction `nop` @@ -467,6 +474,44 @@ extension Instruction { } } + struct ResizeFrameHeaderOperand: Equatable, InstructionImmediate { + var delta: VReg + var sizeToCopy: VReg + @inline(__always) static func load(from pc: inout Pc) -> Self { + let (delta, sizeToCopy, _, _, _, _) = pc.read((VReg, VReg, UInt8, UInt8, UInt8, UInt8).self) + return Self(delta: delta, sizeToCopy: sizeToCopy) + } + @inline(__always) static func emit(to emitSlot: ((Self) -> CodeSlot) -> Void) { + emitSlot { unsafeBitCast(($0.delta, $0.sizeToCopy, 0, 0, 0, 0) as (VReg, VReg, UInt8, UInt8, UInt8, UInt8), to: CodeSlot.self) } + } + } + + struct ReturnCallOperand: Equatable, InstructionImmediate { + var rawCallee: UInt64 + @inline(__always) static func load(from pc: inout Pc) -> Self { + let (rawCallee) = pc.read((UInt64).self) + return Self(rawCallee: rawCallee) + } + @inline(__always) static func emit(to emitSlot: ((Self) -> CodeSlot) -> Void) { + emitSlot { $0.rawCallee } + } + } + + struct ReturnCallIndirectOperand: Equatable, InstructionImmediate { + var tableIndex: UInt32 + var rawType: UInt32 + var index: VReg + @inline(__always) static func load(from pc: inout Pc) -> Self { + let (tableIndex, rawType) = pc.read((UInt32, UInt32).self) + let (index, _, _, _, _, _, _) = pc.read((VReg, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8).self) + return Self(tableIndex: tableIndex, rawType: rawType, index: index) + } + @inline(__always) static func emit(to emitSlot: ((Self) -> CodeSlot) -> Void) { + emitSlot { unsafeBitCast(($0.tableIndex, $0.rawType) as (UInt32, UInt32), to: CodeSlot.self) } + emitSlot { unsafeBitCast(($0.index, 0, 0, 0, 0, 0, 0) as (VReg, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8), to: CodeSlot.self) } + } + } + struct BrIfOperand: Equatable, InstructionImmediate { var condition: LVReg var offset: Int32 @@ -829,6 +874,9 @@ extension Instruction { case .compilingCall(let immediate): return immediate case .internalCall(let immediate): return immediate case .callIndirect(let immediate): return immediate + case .resizeFrameHeader(let immediate): return immediate + case .returnCall(let immediate): return immediate + case .returnCallIndirect(let immediate): return immediate case .br(let immediate): return immediate case .brIf(let immediate): return immediate case .brIfNot(let immediate): return immediate @@ -1031,195 +1079,198 @@ extension Instruction { case .compilingCall: return 4 case .internalCall: return 5 case .callIndirect: return 6 - case .unreachable: return 7 - case .nop: return 8 - case .br: return 9 - case .brIf: return 10 - case .brIfNot: return 11 - case .brTable: return 12 - case ._return: return 13 - case .endOfExecution: return 14 - case .i32Load: return 15 - case .i64Load: return 16 - case .f32Load: return 17 - case .f64Load: return 18 - case .i32Load8S: return 19 - case .i32Load8U: return 20 - case .i32Load16S: return 21 - case .i32Load16U: return 22 - case .i64Load8S: return 23 - case .i64Load8U: return 24 - case .i64Load16S: return 25 - case .i64Load16U: return 26 - case .i64Load32S: return 27 - case .i64Load32U: return 28 - case .i32Store: return 29 - case .i64Store: return 30 - case .f32Store: return 31 - case .f64Store: return 32 - case .i32Store8: return 33 - case .i32Store16: return 34 - case .i64Store8: return 35 - case .i64Store16: return 36 - case .i64Store32: return 37 - case .memorySize: return 38 - case .memoryGrow: return 39 - case .memoryInit: return 40 - case .memoryDataDrop: return 41 - case .memoryCopy: return 42 - case .memoryFill: return 43 - case .const32: return 44 - case .const64: return 45 - case .i32Add: return 46 - case .i64Add: return 47 - case .i32Sub: return 48 - case .i64Sub: return 49 - case .i32Mul: return 50 - case .i64Mul: return 51 - case .i32And: return 52 - case .i64And: return 53 - case .i32Or: return 54 - case .i64Or: return 55 - case .i32Xor: return 56 - case .i64Xor: return 57 - case .i32Shl: return 58 - case .i64Shl: return 59 - case .i32ShrS: return 60 - case .i64ShrS: return 61 - case .i32ShrU: return 62 - case .i64ShrU: return 63 - case .i32Rotl: return 64 - case .i64Rotl: return 65 - case .i32Rotr: return 66 - case .i64Rotr: return 67 - case .i32DivS: return 68 - case .i64DivS: return 69 - case .i32DivU: return 70 - case .i64DivU: return 71 - case .i32RemS: return 72 - case .i64RemS: return 73 - case .i32RemU: return 74 - case .i64RemU: return 75 - case .i32Eq: return 76 - case .i64Eq: return 77 - case .i32Ne: return 78 - case .i64Ne: return 79 - case .i32LtS: return 80 - case .i64LtS: return 81 - case .i32LtU: return 82 - case .i64LtU: return 83 - case .i32GtS: return 84 - case .i64GtS: return 85 - case .i32GtU: return 86 - case .i64GtU: return 87 - case .i32LeS: return 88 - case .i64LeS: return 89 - case .i32LeU: return 90 - case .i64LeU: return 91 - case .i32GeS: return 92 - case .i64GeS: return 93 - case .i32GeU: return 94 - case .i64GeU: return 95 - case .i32Clz: return 96 - case .i64Clz: return 97 - case .i32Ctz: return 98 - case .i64Ctz: return 99 - case .i32Popcnt: return 100 - case .i64Popcnt: return 101 - case .i32Eqz: return 102 - case .i64Eqz: return 103 - case .i32WrapI64: return 104 - case .i64ExtendI32S: return 105 - case .i64ExtendI32U: return 106 - case .i32Extend8S: return 107 - case .i64Extend8S: return 108 - case .i32Extend16S: return 109 - case .i64Extend16S: return 110 - case .i64Extend32S: return 111 - case .i32TruncF32S: return 112 - case .i32TruncF32U: return 113 - case .i32TruncSatF32S: return 114 - case .i32TruncSatF32U: return 115 - case .i32TruncF64S: return 116 - case .i32TruncF64U: return 117 - case .i32TruncSatF64S: return 118 - case .i32TruncSatF64U: return 119 - case .i64TruncF32S: return 120 - case .i64TruncF32U: return 121 - case .i64TruncSatF32S: return 122 - case .i64TruncSatF32U: return 123 - case .i64TruncF64S: return 124 - case .i64TruncF64U: return 125 - case .i64TruncSatF64S: return 126 - case .i64TruncSatF64U: return 127 - case .f32ConvertI32S: return 128 - case .f32ConvertI32U: return 129 - case .f32ConvertI64S: return 130 - case .f32ConvertI64U: return 131 - case .f64ConvertI32S: return 132 - case .f64ConvertI32U: return 133 - case .f64ConvertI64S: return 134 - case .f64ConvertI64U: return 135 - case .f32ReinterpretI32: return 136 - case .f64ReinterpretI64: return 137 - case .i32ReinterpretF32: return 138 - case .i64ReinterpretF64: return 139 - case .f32Add: return 140 - case .f64Add: return 141 - case .f32Sub: return 142 - case .f64Sub: return 143 - case .f32Mul: return 144 - case .f64Mul: return 145 - case .f32Div: return 146 - case .f64Div: return 147 - case .f32Min: return 148 - case .f64Min: return 149 - case .f32Max: return 150 - case .f64Max: return 151 - case .f32CopySign: return 152 - case .f64CopySign: return 153 - case .f32Eq: return 154 - case .f64Eq: return 155 - case .f32Ne: return 156 - case .f64Ne: return 157 - case .f32Lt: return 158 - case .f64Lt: return 159 - case .f32Gt: return 160 - case .f64Gt: return 161 - case .f32Le: return 162 - case .f64Le: return 163 - case .f32Ge: return 164 - case .f64Ge: return 165 - case .f32Abs: return 166 - case .f64Abs: return 167 - case .f32Neg: return 168 - case .f64Neg: return 169 - case .f32Ceil: return 170 - case .f64Ceil: return 171 - case .f32Floor: return 172 - case .f64Floor: return 173 - case .f32Trunc: return 174 - case .f64Trunc: return 175 - case .f32Nearest: return 176 - case .f64Nearest: return 177 - case .f32Sqrt: return 178 - case .f64Sqrt: return 179 - case .f64PromoteF32: return 180 - case .f32DemoteF64: return 181 - case .select: return 182 - case .refNull: return 183 - case .refIsNull: return 184 - case .refFunc: return 185 - case .tableGet: return 186 - case .tableSet: return 187 - case .tableSize: return 188 - case .tableGrow: return 189 - case .tableFill: return 190 - case .tableCopy: return 191 - case .tableInit: return 192 - case .tableElementDrop: return 193 - case .onEnter: return 194 - case .onExit: return 195 + case .resizeFrameHeader: return 7 + case .returnCall: return 8 + case .returnCallIndirect: return 9 + case .unreachable: return 10 + case .nop: return 11 + case .br: return 12 + case .brIf: return 13 + case .brIfNot: return 14 + case .brTable: return 15 + case ._return: return 16 + case .endOfExecution: return 17 + case .i32Load: return 18 + case .i64Load: return 19 + case .f32Load: return 20 + case .f64Load: return 21 + case .i32Load8S: return 22 + case .i32Load8U: return 23 + case .i32Load16S: return 24 + case .i32Load16U: return 25 + case .i64Load8S: return 26 + case .i64Load8U: return 27 + case .i64Load16S: return 28 + case .i64Load16U: return 29 + case .i64Load32S: return 30 + case .i64Load32U: return 31 + case .i32Store: return 32 + case .i64Store: return 33 + case .f32Store: return 34 + case .f64Store: return 35 + case .i32Store8: return 36 + case .i32Store16: return 37 + case .i64Store8: return 38 + case .i64Store16: return 39 + case .i64Store32: return 40 + case .memorySize: return 41 + case .memoryGrow: return 42 + case .memoryInit: return 43 + case .memoryDataDrop: return 44 + case .memoryCopy: return 45 + case .memoryFill: return 46 + case .const32: return 47 + case .const64: return 48 + case .i32Add: return 49 + case .i64Add: return 50 + case .i32Sub: return 51 + case .i64Sub: return 52 + case .i32Mul: return 53 + case .i64Mul: return 54 + case .i32And: return 55 + case .i64And: return 56 + case .i32Or: return 57 + case .i64Or: return 58 + case .i32Xor: return 59 + case .i64Xor: return 60 + case .i32Shl: return 61 + case .i64Shl: return 62 + case .i32ShrS: return 63 + case .i64ShrS: return 64 + case .i32ShrU: return 65 + case .i64ShrU: return 66 + case .i32Rotl: return 67 + case .i64Rotl: return 68 + case .i32Rotr: return 69 + case .i64Rotr: return 70 + case .i32DivS: return 71 + case .i64DivS: return 72 + case .i32DivU: return 73 + case .i64DivU: return 74 + case .i32RemS: return 75 + case .i64RemS: return 76 + case .i32RemU: return 77 + case .i64RemU: return 78 + case .i32Eq: return 79 + case .i64Eq: return 80 + case .i32Ne: return 81 + case .i64Ne: return 82 + case .i32LtS: return 83 + case .i64LtS: return 84 + case .i32LtU: return 85 + case .i64LtU: return 86 + case .i32GtS: return 87 + case .i64GtS: return 88 + case .i32GtU: return 89 + case .i64GtU: return 90 + case .i32LeS: return 91 + case .i64LeS: return 92 + case .i32LeU: return 93 + case .i64LeU: return 94 + case .i32GeS: return 95 + case .i64GeS: return 96 + case .i32GeU: return 97 + case .i64GeU: return 98 + case .i32Clz: return 99 + case .i64Clz: return 100 + case .i32Ctz: return 101 + case .i64Ctz: return 102 + case .i32Popcnt: return 103 + case .i64Popcnt: return 104 + case .i32Eqz: return 105 + case .i64Eqz: return 106 + case .i32WrapI64: return 107 + case .i64ExtendI32S: return 108 + case .i64ExtendI32U: return 109 + case .i32Extend8S: return 110 + case .i64Extend8S: return 111 + case .i32Extend16S: return 112 + case .i64Extend16S: return 113 + case .i64Extend32S: return 114 + case .i32TruncF32S: return 115 + case .i32TruncF32U: return 116 + case .i32TruncSatF32S: return 117 + case .i32TruncSatF32U: return 118 + case .i32TruncF64S: return 119 + case .i32TruncF64U: return 120 + case .i32TruncSatF64S: return 121 + case .i32TruncSatF64U: return 122 + case .i64TruncF32S: return 123 + case .i64TruncF32U: return 124 + case .i64TruncSatF32S: return 125 + case .i64TruncSatF32U: return 126 + case .i64TruncF64S: return 127 + case .i64TruncF64U: return 128 + case .i64TruncSatF64S: return 129 + case .i64TruncSatF64U: return 130 + case .f32ConvertI32S: return 131 + case .f32ConvertI32U: return 132 + case .f32ConvertI64S: return 133 + case .f32ConvertI64U: return 134 + case .f64ConvertI32S: return 135 + case .f64ConvertI32U: return 136 + case .f64ConvertI64S: return 137 + case .f64ConvertI64U: return 138 + case .f32ReinterpretI32: return 139 + case .f64ReinterpretI64: return 140 + case .i32ReinterpretF32: return 141 + case .i64ReinterpretF64: return 142 + case .f32Add: return 143 + case .f64Add: return 144 + case .f32Sub: return 145 + case .f64Sub: return 146 + case .f32Mul: return 147 + case .f64Mul: return 148 + case .f32Div: return 149 + case .f64Div: return 150 + case .f32Min: return 151 + case .f64Min: return 152 + case .f32Max: return 153 + case .f64Max: return 154 + case .f32CopySign: return 155 + case .f64CopySign: return 156 + case .f32Eq: return 157 + case .f64Eq: return 158 + case .f32Ne: return 159 + case .f64Ne: return 160 + case .f32Lt: return 161 + case .f64Lt: return 162 + case .f32Gt: return 163 + case .f64Gt: return 164 + case .f32Le: return 165 + case .f64Le: return 166 + case .f32Ge: return 167 + case .f64Ge: return 168 + case .f32Abs: return 169 + case .f64Abs: return 170 + case .f32Neg: return 171 + case .f64Neg: return 172 + case .f32Ceil: return 173 + case .f64Ceil: return 174 + case .f32Floor: return 175 + case .f64Floor: return 176 + case .f32Trunc: return 177 + case .f64Trunc: return 178 + case .f32Nearest: return 179 + case .f64Nearest: return 180 + case .f32Sqrt: return 181 + case .f64Sqrt: return 182 + case .f64PromoteF32: return 183 + case .f32DemoteF64: return 184 + case .select: return 185 + case .refNull: return 186 + case .refIsNull: return 187 + case .refFunc: return 188 + case .tableGet: return 189 + case .tableSet: return 190 + case .tableSize: return 191 + case .tableGrow: return 192 + case .tableFill: return 193 + case .tableCopy: return 194 + case .tableInit: return 195 + case .tableElementDrop: return 196 + case .onEnter: return 197 + case .onExit: return 198 } } } @@ -1238,195 +1289,198 @@ extension Instruction { case 4: return .compilingCall(Instruction.CallOperand.load(from: &pc)) case 5: return .internalCall(Instruction.CallOperand.load(from: &pc)) case 6: return .callIndirect(Instruction.CallIndirectOperand.load(from: &pc)) - case 7: return .unreachable - case 8: return .nop - case 9: return .br(Instruction.BrOperand.load(from: &pc)) - case 10: return .brIf(Instruction.BrIfOperand.load(from: &pc)) - case 11: return .brIfNot(Instruction.BrIfOperand.load(from: &pc)) - case 12: return .brTable(Instruction.BrTableOperand.load(from: &pc)) - case 13: return ._return - case 14: return .endOfExecution - case 15: return .i32Load(Instruction.LoadOperand.load(from: &pc)) - case 16: return .i64Load(Instruction.LoadOperand.load(from: &pc)) - case 17: return .f32Load(Instruction.LoadOperand.load(from: &pc)) - case 18: return .f64Load(Instruction.LoadOperand.load(from: &pc)) - case 19: return .i32Load8S(Instruction.LoadOperand.load(from: &pc)) - case 20: return .i32Load8U(Instruction.LoadOperand.load(from: &pc)) - case 21: return .i32Load16S(Instruction.LoadOperand.load(from: &pc)) - case 22: return .i32Load16U(Instruction.LoadOperand.load(from: &pc)) - case 23: return .i64Load8S(Instruction.LoadOperand.load(from: &pc)) - case 24: return .i64Load8U(Instruction.LoadOperand.load(from: &pc)) - case 25: return .i64Load16S(Instruction.LoadOperand.load(from: &pc)) - case 26: return .i64Load16U(Instruction.LoadOperand.load(from: &pc)) - case 27: return .i64Load32S(Instruction.LoadOperand.load(from: &pc)) - case 28: return .i64Load32U(Instruction.LoadOperand.load(from: &pc)) - case 29: return .i32Store(Instruction.StoreOperand.load(from: &pc)) - case 30: return .i64Store(Instruction.StoreOperand.load(from: &pc)) - case 31: return .f32Store(Instruction.StoreOperand.load(from: &pc)) - case 32: return .f64Store(Instruction.StoreOperand.load(from: &pc)) - case 33: return .i32Store8(Instruction.StoreOperand.load(from: &pc)) - case 34: return .i32Store16(Instruction.StoreOperand.load(from: &pc)) - case 35: return .i64Store8(Instruction.StoreOperand.load(from: &pc)) - case 36: return .i64Store16(Instruction.StoreOperand.load(from: &pc)) - case 37: return .i64Store32(Instruction.StoreOperand.load(from: &pc)) - case 38: return .memorySize(Instruction.MemorySizeOperand.load(from: &pc)) - case 39: return .memoryGrow(Instruction.MemoryGrowOperand.load(from: &pc)) - case 40: return .memoryInit(Instruction.MemoryInitOperand.load(from: &pc)) - case 41: return .memoryDataDrop(Instruction.MemoryDataDropOperand.load(from: &pc)) - case 42: return .memoryCopy(Instruction.MemoryCopyOperand.load(from: &pc)) - case 43: return .memoryFill(Instruction.MemoryFillOperand.load(from: &pc)) - case 44: return .const32(Instruction.Const32Operand.load(from: &pc)) - case 45: return .const64(Instruction.Const64Operand.load(from: &pc)) - case 46: return .i32Add(Instruction.BinaryOperand.load(from: &pc)) - case 47: return .i64Add(Instruction.BinaryOperand.load(from: &pc)) - case 48: return .i32Sub(Instruction.BinaryOperand.load(from: &pc)) - case 49: return .i64Sub(Instruction.BinaryOperand.load(from: &pc)) - case 50: return .i32Mul(Instruction.BinaryOperand.load(from: &pc)) - case 51: return .i64Mul(Instruction.BinaryOperand.load(from: &pc)) - case 52: return .i32And(Instruction.BinaryOperand.load(from: &pc)) - case 53: return .i64And(Instruction.BinaryOperand.load(from: &pc)) - case 54: return .i32Or(Instruction.BinaryOperand.load(from: &pc)) - case 55: return .i64Or(Instruction.BinaryOperand.load(from: &pc)) - case 56: return .i32Xor(Instruction.BinaryOperand.load(from: &pc)) - case 57: return .i64Xor(Instruction.BinaryOperand.load(from: &pc)) - case 58: return .i32Shl(Instruction.BinaryOperand.load(from: &pc)) - case 59: return .i64Shl(Instruction.BinaryOperand.load(from: &pc)) - case 60: return .i32ShrS(Instruction.BinaryOperand.load(from: &pc)) - case 61: return .i64ShrS(Instruction.BinaryOperand.load(from: &pc)) - case 62: return .i32ShrU(Instruction.BinaryOperand.load(from: &pc)) - case 63: return .i64ShrU(Instruction.BinaryOperand.load(from: &pc)) - case 64: return .i32Rotl(Instruction.BinaryOperand.load(from: &pc)) - case 65: return .i64Rotl(Instruction.BinaryOperand.load(from: &pc)) - case 66: return .i32Rotr(Instruction.BinaryOperand.load(from: &pc)) - case 67: return .i64Rotr(Instruction.BinaryOperand.load(from: &pc)) - case 68: return .i32DivS(Instruction.BinaryOperand.load(from: &pc)) - case 69: return .i64DivS(Instruction.BinaryOperand.load(from: &pc)) - case 70: return .i32DivU(Instruction.BinaryOperand.load(from: &pc)) - case 71: return .i64DivU(Instruction.BinaryOperand.load(from: &pc)) - case 72: return .i32RemS(Instruction.BinaryOperand.load(from: &pc)) - case 73: return .i64RemS(Instruction.BinaryOperand.load(from: &pc)) - case 74: return .i32RemU(Instruction.BinaryOperand.load(from: &pc)) - case 75: return .i64RemU(Instruction.BinaryOperand.load(from: &pc)) - case 76: return .i32Eq(Instruction.BinaryOperand.load(from: &pc)) - case 77: return .i64Eq(Instruction.BinaryOperand.load(from: &pc)) - case 78: return .i32Ne(Instruction.BinaryOperand.load(from: &pc)) - case 79: return .i64Ne(Instruction.BinaryOperand.load(from: &pc)) - case 80: return .i32LtS(Instruction.BinaryOperand.load(from: &pc)) - case 81: return .i64LtS(Instruction.BinaryOperand.load(from: &pc)) - case 82: return .i32LtU(Instruction.BinaryOperand.load(from: &pc)) - case 83: return .i64LtU(Instruction.BinaryOperand.load(from: &pc)) - case 84: return .i32GtS(Instruction.BinaryOperand.load(from: &pc)) - case 85: return .i64GtS(Instruction.BinaryOperand.load(from: &pc)) - case 86: return .i32GtU(Instruction.BinaryOperand.load(from: &pc)) - case 87: return .i64GtU(Instruction.BinaryOperand.load(from: &pc)) - case 88: return .i32LeS(Instruction.BinaryOperand.load(from: &pc)) - case 89: return .i64LeS(Instruction.BinaryOperand.load(from: &pc)) - case 90: return .i32LeU(Instruction.BinaryOperand.load(from: &pc)) - case 91: return .i64LeU(Instruction.BinaryOperand.load(from: &pc)) - case 92: return .i32GeS(Instruction.BinaryOperand.load(from: &pc)) - case 93: return .i64GeS(Instruction.BinaryOperand.load(from: &pc)) - case 94: return .i32GeU(Instruction.BinaryOperand.load(from: &pc)) - case 95: return .i64GeU(Instruction.BinaryOperand.load(from: &pc)) - case 96: return .i32Clz(Instruction.UnaryOperand.load(from: &pc)) - case 97: return .i64Clz(Instruction.UnaryOperand.load(from: &pc)) - case 98: return .i32Ctz(Instruction.UnaryOperand.load(from: &pc)) - case 99: return .i64Ctz(Instruction.UnaryOperand.load(from: &pc)) - case 100: return .i32Popcnt(Instruction.UnaryOperand.load(from: &pc)) - case 101: return .i64Popcnt(Instruction.UnaryOperand.load(from: &pc)) - case 102: return .i32Eqz(Instruction.UnaryOperand.load(from: &pc)) - case 103: return .i64Eqz(Instruction.UnaryOperand.load(from: &pc)) - case 104: return .i32WrapI64(Instruction.UnaryOperand.load(from: &pc)) - case 105: return .i64ExtendI32S(Instruction.UnaryOperand.load(from: &pc)) - case 106: return .i64ExtendI32U(Instruction.UnaryOperand.load(from: &pc)) - case 107: return .i32Extend8S(Instruction.UnaryOperand.load(from: &pc)) - case 108: return .i64Extend8S(Instruction.UnaryOperand.load(from: &pc)) - case 109: return .i32Extend16S(Instruction.UnaryOperand.load(from: &pc)) - case 110: return .i64Extend16S(Instruction.UnaryOperand.load(from: &pc)) - case 111: return .i64Extend32S(Instruction.UnaryOperand.load(from: &pc)) - case 112: return .i32TruncF32S(Instruction.UnaryOperand.load(from: &pc)) - case 113: return .i32TruncF32U(Instruction.UnaryOperand.load(from: &pc)) - case 114: return .i32TruncSatF32S(Instruction.UnaryOperand.load(from: &pc)) - case 115: return .i32TruncSatF32U(Instruction.UnaryOperand.load(from: &pc)) - case 116: return .i32TruncF64S(Instruction.UnaryOperand.load(from: &pc)) - case 117: return .i32TruncF64U(Instruction.UnaryOperand.load(from: &pc)) - case 118: return .i32TruncSatF64S(Instruction.UnaryOperand.load(from: &pc)) - case 119: return .i32TruncSatF64U(Instruction.UnaryOperand.load(from: &pc)) - case 120: return .i64TruncF32S(Instruction.UnaryOperand.load(from: &pc)) - case 121: return .i64TruncF32U(Instruction.UnaryOperand.load(from: &pc)) - case 122: return .i64TruncSatF32S(Instruction.UnaryOperand.load(from: &pc)) - case 123: return .i64TruncSatF32U(Instruction.UnaryOperand.load(from: &pc)) - case 124: return .i64TruncF64S(Instruction.UnaryOperand.load(from: &pc)) - case 125: return .i64TruncF64U(Instruction.UnaryOperand.load(from: &pc)) - case 126: return .i64TruncSatF64S(Instruction.UnaryOperand.load(from: &pc)) - case 127: return .i64TruncSatF64U(Instruction.UnaryOperand.load(from: &pc)) - case 128: return .f32ConvertI32S(Instruction.UnaryOperand.load(from: &pc)) - case 129: return .f32ConvertI32U(Instruction.UnaryOperand.load(from: &pc)) - case 130: return .f32ConvertI64S(Instruction.UnaryOperand.load(from: &pc)) - case 131: return .f32ConvertI64U(Instruction.UnaryOperand.load(from: &pc)) - case 132: return .f64ConvertI32S(Instruction.UnaryOperand.load(from: &pc)) - case 133: return .f64ConvertI32U(Instruction.UnaryOperand.load(from: &pc)) - case 134: return .f64ConvertI64S(Instruction.UnaryOperand.load(from: &pc)) - case 135: return .f64ConvertI64U(Instruction.UnaryOperand.load(from: &pc)) - case 136: return .f32ReinterpretI32(Instruction.UnaryOperand.load(from: &pc)) - case 137: return .f64ReinterpretI64(Instruction.UnaryOperand.load(from: &pc)) - case 138: return .i32ReinterpretF32(Instruction.UnaryOperand.load(from: &pc)) - case 139: return .i64ReinterpretF64(Instruction.UnaryOperand.load(from: &pc)) - case 140: return .f32Add(Instruction.BinaryOperand.load(from: &pc)) - case 141: return .f64Add(Instruction.BinaryOperand.load(from: &pc)) - case 142: return .f32Sub(Instruction.BinaryOperand.load(from: &pc)) - case 143: return .f64Sub(Instruction.BinaryOperand.load(from: &pc)) - case 144: return .f32Mul(Instruction.BinaryOperand.load(from: &pc)) - case 145: return .f64Mul(Instruction.BinaryOperand.load(from: &pc)) - case 146: return .f32Div(Instruction.BinaryOperand.load(from: &pc)) - case 147: return .f64Div(Instruction.BinaryOperand.load(from: &pc)) - case 148: return .f32Min(Instruction.BinaryOperand.load(from: &pc)) - case 149: return .f64Min(Instruction.BinaryOperand.load(from: &pc)) - case 150: return .f32Max(Instruction.BinaryOperand.load(from: &pc)) - case 151: return .f64Max(Instruction.BinaryOperand.load(from: &pc)) - case 152: return .f32CopySign(Instruction.BinaryOperand.load(from: &pc)) - case 153: return .f64CopySign(Instruction.BinaryOperand.load(from: &pc)) - case 154: return .f32Eq(Instruction.BinaryOperand.load(from: &pc)) - case 155: return .f64Eq(Instruction.BinaryOperand.load(from: &pc)) - case 156: return .f32Ne(Instruction.BinaryOperand.load(from: &pc)) - case 157: return .f64Ne(Instruction.BinaryOperand.load(from: &pc)) - case 158: return .f32Lt(Instruction.BinaryOperand.load(from: &pc)) - case 159: return .f64Lt(Instruction.BinaryOperand.load(from: &pc)) - case 160: return .f32Gt(Instruction.BinaryOperand.load(from: &pc)) - case 161: return .f64Gt(Instruction.BinaryOperand.load(from: &pc)) - case 162: return .f32Le(Instruction.BinaryOperand.load(from: &pc)) - case 163: return .f64Le(Instruction.BinaryOperand.load(from: &pc)) - case 164: return .f32Ge(Instruction.BinaryOperand.load(from: &pc)) - case 165: return .f64Ge(Instruction.BinaryOperand.load(from: &pc)) - case 166: return .f32Abs(Instruction.UnaryOperand.load(from: &pc)) - case 167: return .f64Abs(Instruction.UnaryOperand.load(from: &pc)) - case 168: return .f32Neg(Instruction.UnaryOperand.load(from: &pc)) - case 169: return .f64Neg(Instruction.UnaryOperand.load(from: &pc)) - case 170: return .f32Ceil(Instruction.UnaryOperand.load(from: &pc)) - case 171: return .f64Ceil(Instruction.UnaryOperand.load(from: &pc)) - case 172: return .f32Floor(Instruction.UnaryOperand.load(from: &pc)) - case 173: return .f64Floor(Instruction.UnaryOperand.load(from: &pc)) - case 174: return .f32Trunc(Instruction.UnaryOperand.load(from: &pc)) - case 175: return .f64Trunc(Instruction.UnaryOperand.load(from: &pc)) - case 176: return .f32Nearest(Instruction.UnaryOperand.load(from: &pc)) - case 177: return .f64Nearest(Instruction.UnaryOperand.load(from: &pc)) - case 178: return .f32Sqrt(Instruction.UnaryOperand.load(from: &pc)) - case 179: return .f64Sqrt(Instruction.UnaryOperand.load(from: &pc)) - case 180: return .f64PromoteF32(Instruction.UnaryOperand.load(from: &pc)) - case 181: return .f32DemoteF64(Instruction.UnaryOperand.load(from: &pc)) - case 182: return .select(Instruction.SelectOperand.load(from: &pc)) - case 183: return .refNull(Instruction.RefNullOperand.load(from: &pc)) - case 184: return .refIsNull(Instruction.RefIsNullOperand.load(from: &pc)) - case 185: return .refFunc(Instruction.RefFuncOperand.load(from: &pc)) - case 186: return .tableGet(Instruction.TableGetOperand.load(from: &pc)) - case 187: return .tableSet(Instruction.TableSetOperand.load(from: &pc)) - case 188: return .tableSize(Instruction.TableSizeOperand.load(from: &pc)) - case 189: return .tableGrow(Instruction.TableGrowOperand.load(from: &pc)) - case 190: return .tableFill(Instruction.TableFillOperand.load(from: &pc)) - case 191: return .tableCopy(Instruction.TableCopyOperand.load(from: &pc)) - case 192: return .tableInit(Instruction.TableInitOperand.load(from: &pc)) - case 193: return .tableElementDrop(Instruction.TableElementDropOperand.load(from: &pc)) - case 194: return .onEnter(Instruction.OnEnterOperand.load(from: &pc)) - case 195: return .onExit(Instruction.OnExitOperand.load(from: &pc)) + case 7: return .resizeFrameHeader(Instruction.ResizeFrameHeaderOperand.load(from: &pc)) + case 8: return .returnCall(Instruction.ReturnCallOperand.load(from: &pc)) + case 9: return .returnCallIndirect(Instruction.ReturnCallIndirectOperand.load(from: &pc)) + case 10: return .unreachable + case 11: return .nop + case 12: return .br(Instruction.BrOperand.load(from: &pc)) + case 13: return .brIf(Instruction.BrIfOperand.load(from: &pc)) + case 14: return .brIfNot(Instruction.BrIfOperand.load(from: &pc)) + case 15: return .brTable(Instruction.BrTableOperand.load(from: &pc)) + case 16: return ._return + case 17: return .endOfExecution + case 18: return .i32Load(Instruction.LoadOperand.load(from: &pc)) + case 19: return .i64Load(Instruction.LoadOperand.load(from: &pc)) + case 20: return .f32Load(Instruction.LoadOperand.load(from: &pc)) + case 21: return .f64Load(Instruction.LoadOperand.load(from: &pc)) + case 22: return .i32Load8S(Instruction.LoadOperand.load(from: &pc)) + case 23: return .i32Load8U(Instruction.LoadOperand.load(from: &pc)) + case 24: return .i32Load16S(Instruction.LoadOperand.load(from: &pc)) + case 25: return .i32Load16U(Instruction.LoadOperand.load(from: &pc)) + case 26: return .i64Load8S(Instruction.LoadOperand.load(from: &pc)) + case 27: return .i64Load8U(Instruction.LoadOperand.load(from: &pc)) + case 28: return .i64Load16S(Instruction.LoadOperand.load(from: &pc)) + case 29: return .i64Load16U(Instruction.LoadOperand.load(from: &pc)) + case 30: return .i64Load32S(Instruction.LoadOperand.load(from: &pc)) + case 31: return .i64Load32U(Instruction.LoadOperand.load(from: &pc)) + case 32: return .i32Store(Instruction.StoreOperand.load(from: &pc)) + case 33: return .i64Store(Instruction.StoreOperand.load(from: &pc)) + case 34: return .f32Store(Instruction.StoreOperand.load(from: &pc)) + case 35: return .f64Store(Instruction.StoreOperand.load(from: &pc)) + case 36: return .i32Store8(Instruction.StoreOperand.load(from: &pc)) + case 37: return .i32Store16(Instruction.StoreOperand.load(from: &pc)) + case 38: return .i64Store8(Instruction.StoreOperand.load(from: &pc)) + case 39: return .i64Store16(Instruction.StoreOperand.load(from: &pc)) + case 40: return .i64Store32(Instruction.StoreOperand.load(from: &pc)) + case 41: return .memorySize(Instruction.MemorySizeOperand.load(from: &pc)) + case 42: return .memoryGrow(Instruction.MemoryGrowOperand.load(from: &pc)) + case 43: return .memoryInit(Instruction.MemoryInitOperand.load(from: &pc)) + case 44: return .memoryDataDrop(Instruction.MemoryDataDropOperand.load(from: &pc)) + case 45: return .memoryCopy(Instruction.MemoryCopyOperand.load(from: &pc)) + case 46: return .memoryFill(Instruction.MemoryFillOperand.load(from: &pc)) + case 47: return .const32(Instruction.Const32Operand.load(from: &pc)) + case 48: return .const64(Instruction.Const64Operand.load(from: &pc)) + case 49: return .i32Add(Instruction.BinaryOperand.load(from: &pc)) + case 50: return .i64Add(Instruction.BinaryOperand.load(from: &pc)) + case 51: return .i32Sub(Instruction.BinaryOperand.load(from: &pc)) + case 52: return .i64Sub(Instruction.BinaryOperand.load(from: &pc)) + case 53: return .i32Mul(Instruction.BinaryOperand.load(from: &pc)) + case 54: return .i64Mul(Instruction.BinaryOperand.load(from: &pc)) + case 55: return .i32And(Instruction.BinaryOperand.load(from: &pc)) + case 56: return .i64And(Instruction.BinaryOperand.load(from: &pc)) + case 57: return .i32Or(Instruction.BinaryOperand.load(from: &pc)) + case 58: return .i64Or(Instruction.BinaryOperand.load(from: &pc)) + case 59: return .i32Xor(Instruction.BinaryOperand.load(from: &pc)) + case 60: return .i64Xor(Instruction.BinaryOperand.load(from: &pc)) + case 61: return .i32Shl(Instruction.BinaryOperand.load(from: &pc)) + case 62: return .i64Shl(Instruction.BinaryOperand.load(from: &pc)) + case 63: return .i32ShrS(Instruction.BinaryOperand.load(from: &pc)) + case 64: return .i64ShrS(Instruction.BinaryOperand.load(from: &pc)) + case 65: return .i32ShrU(Instruction.BinaryOperand.load(from: &pc)) + case 66: return .i64ShrU(Instruction.BinaryOperand.load(from: &pc)) + case 67: return .i32Rotl(Instruction.BinaryOperand.load(from: &pc)) + case 68: return .i64Rotl(Instruction.BinaryOperand.load(from: &pc)) + case 69: return .i32Rotr(Instruction.BinaryOperand.load(from: &pc)) + case 70: return .i64Rotr(Instruction.BinaryOperand.load(from: &pc)) + case 71: return .i32DivS(Instruction.BinaryOperand.load(from: &pc)) + case 72: return .i64DivS(Instruction.BinaryOperand.load(from: &pc)) + case 73: return .i32DivU(Instruction.BinaryOperand.load(from: &pc)) + case 74: return .i64DivU(Instruction.BinaryOperand.load(from: &pc)) + case 75: return .i32RemS(Instruction.BinaryOperand.load(from: &pc)) + case 76: return .i64RemS(Instruction.BinaryOperand.load(from: &pc)) + case 77: return .i32RemU(Instruction.BinaryOperand.load(from: &pc)) + case 78: return .i64RemU(Instruction.BinaryOperand.load(from: &pc)) + case 79: return .i32Eq(Instruction.BinaryOperand.load(from: &pc)) + case 80: return .i64Eq(Instruction.BinaryOperand.load(from: &pc)) + case 81: return .i32Ne(Instruction.BinaryOperand.load(from: &pc)) + case 82: return .i64Ne(Instruction.BinaryOperand.load(from: &pc)) + case 83: return .i32LtS(Instruction.BinaryOperand.load(from: &pc)) + case 84: return .i64LtS(Instruction.BinaryOperand.load(from: &pc)) + case 85: return .i32LtU(Instruction.BinaryOperand.load(from: &pc)) + case 86: return .i64LtU(Instruction.BinaryOperand.load(from: &pc)) + case 87: return .i32GtS(Instruction.BinaryOperand.load(from: &pc)) + case 88: return .i64GtS(Instruction.BinaryOperand.load(from: &pc)) + case 89: return .i32GtU(Instruction.BinaryOperand.load(from: &pc)) + case 90: return .i64GtU(Instruction.BinaryOperand.load(from: &pc)) + case 91: return .i32LeS(Instruction.BinaryOperand.load(from: &pc)) + case 92: return .i64LeS(Instruction.BinaryOperand.load(from: &pc)) + case 93: return .i32LeU(Instruction.BinaryOperand.load(from: &pc)) + case 94: return .i64LeU(Instruction.BinaryOperand.load(from: &pc)) + case 95: return .i32GeS(Instruction.BinaryOperand.load(from: &pc)) + case 96: return .i64GeS(Instruction.BinaryOperand.load(from: &pc)) + case 97: return .i32GeU(Instruction.BinaryOperand.load(from: &pc)) + case 98: return .i64GeU(Instruction.BinaryOperand.load(from: &pc)) + case 99: return .i32Clz(Instruction.UnaryOperand.load(from: &pc)) + case 100: return .i64Clz(Instruction.UnaryOperand.load(from: &pc)) + case 101: return .i32Ctz(Instruction.UnaryOperand.load(from: &pc)) + case 102: return .i64Ctz(Instruction.UnaryOperand.load(from: &pc)) + case 103: return .i32Popcnt(Instruction.UnaryOperand.load(from: &pc)) + case 104: return .i64Popcnt(Instruction.UnaryOperand.load(from: &pc)) + case 105: return .i32Eqz(Instruction.UnaryOperand.load(from: &pc)) + case 106: return .i64Eqz(Instruction.UnaryOperand.load(from: &pc)) + case 107: return .i32WrapI64(Instruction.UnaryOperand.load(from: &pc)) + case 108: return .i64ExtendI32S(Instruction.UnaryOperand.load(from: &pc)) + case 109: return .i64ExtendI32U(Instruction.UnaryOperand.load(from: &pc)) + case 110: return .i32Extend8S(Instruction.UnaryOperand.load(from: &pc)) + case 111: return .i64Extend8S(Instruction.UnaryOperand.load(from: &pc)) + case 112: return .i32Extend16S(Instruction.UnaryOperand.load(from: &pc)) + case 113: return .i64Extend16S(Instruction.UnaryOperand.load(from: &pc)) + case 114: return .i64Extend32S(Instruction.UnaryOperand.load(from: &pc)) + case 115: return .i32TruncF32S(Instruction.UnaryOperand.load(from: &pc)) + case 116: return .i32TruncF32U(Instruction.UnaryOperand.load(from: &pc)) + case 117: return .i32TruncSatF32S(Instruction.UnaryOperand.load(from: &pc)) + case 118: return .i32TruncSatF32U(Instruction.UnaryOperand.load(from: &pc)) + case 119: return .i32TruncF64S(Instruction.UnaryOperand.load(from: &pc)) + case 120: return .i32TruncF64U(Instruction.UnaryOperand.load(from: &pc)) + case 121: return .i32TruncSatF64S(Instruction.UnaryOperand.load(from: &pc)) + case 122: return .i32TruncSatF64U(Instruction.UnaryOperand.load(from: &pc)) + case 123: return .i64TruncF32S(Instruction.UnaryOperand.load(from: &pc)) + case 124: return .i64TruncF32U(Instruction.UnaryOperand.load(from: &pc)) + case 125: return .i64TruncSatF32S(Instruction.UnaryOperand.load(from: &pc)) + case 126: return .i64TruncSatF32U(Instruction.UnaryOperand.load(from: &pc)) + case 127: return .i64TruncF64S(Instruction.UnaryOperand.load(from: &pc)) + case 128: return .i64TruncF64U(Instruction.UnaryOperand.load(from: &pc)) + case 129: return .i64TruncSatF64S(Instruction.UnaryOperand.load(from: &pc)) + case 130: return .i64TruncSatF64U(Instruction.UnaryOperand.load(from: &pc)) + case 131: return .f32ConvertI32S(Instruction.UnaryOperand.load(from: &pc)) + case 132: return .f32ConvertI32U(Instruction.UnaryOperand.load(from: &pc)) + case 133: return .f32ConvertI64S(Instruction.UnaryOperand.load(from: &pc)) + case 134: return .f32ConvertI64U(Instruction.UnaryOperand.load(from: &pc)) + case 135: return .f64ConvertI32S(Instruction.UnaryOperand.load(from: &pc)) + case 136: return .f64ConvertI32U(Instruction.UnaryOperand.load(from: &pc)) + case 137: return .f64ConvertI64S(Instruction.UnaryOperand.load(from: &pc)) + case 138: return .f64ConvertI64U(Instruction.UnaryOperand.load(from: &pc)) + case 139: return .f32ReinterpretI32(Instruction.UnaryOperand.load(from: &pc)) + case 140: return .f64ReinterpretI64(Instruction.UnaryOperand.load(from: &pc)) + case 141: return .i32ReinterpretF32(Instruction.UnaryOperand.load(from: &pc)) + case 142: return .i64ReinterpretF64(Instruction.UnaryOperand.load(from: &pc)) + case 143: return .f32Add(Instruction.BinaryOperand.load(from: &pc)) + case 144: return .f64Add(Instruction.BinaryOperand.load(from: &pc)) + case 145: return .f32Sub(Instruction.BinaryOperand.load(from: &pc)) + case 146: return .f64Sub(Instruction.BinaryOperand.load(from: &pc)) + case 147: return .f32Mul(Instruction.BinaryOperand.load(from: &pc)) + case 148: return .f64Mul(Instruction.BinaryOperand.load(from: &pc)) + case 149: return .f32Div(Instruction.BinaryOperand.load(from: &pc)) + case 150: return .f64Div(Instruction.BinaryOperand.load(from: &pc)) + case 151: return .f32Min(Instruction.BinaryOperand.load(from: &pc)) + case 152: return .f64Min(Instruction.BinaryOperand.load(from: &pc)) + case 153: return .f32Max(Instruction.BinaryOperand.load(from: &pc)) + case 154: return .f64Max(Instruction.BinaryOperand.load(from: &pc)) + case 155: return .f32CopySign(Instruction.BinaryOperand.load(from: &pc)) + case 156: return .f64CopySign(Instruction.BinaryOperand.load(from: &pc)) + case 157: return .f32Eq(Instruction.BinaryOperand.load(from: &pc)) + case 158: return .f64Eq(Instruction.BinaryOperand.load(from: &pc)) + case 159: return .f32Ne(Instruction.BinaryOperand.load(from: &pc)) + case 160: return .f64Ne(Instruction.BinaryOperand.load(from: &pc)) + case 161: return .f32Lt(Instruction.BinaryOperand.load(from: &pc)) + case 162: return .f64Lt(Instruction.BinaryOperand.load(from: &pc)) + case 163: return .f32Gt(Instruction.BinaryOperand.load(from: &pc)) + case 164: return .f64Gt(Instruction.BinaryOperand.load(from: &pc)) + case 165: return .f32Le(Instruction.BinaryOperand.load(from: &pc)) + case 166: return .f64Le(Instruction.BinaryOperand.load(from: &pc)) + case 167: return .f32Ge(Instruction.BinaryOperand.load(from: &pc)) + case 168: return .f64Ge(Instruction.BinaryOperand.load(from: &pc)) + case 169: return .f32Abs(Instruction.UnaryOperand.load(from: &pc)) + case 170: return .f64Abs(Instruction.UnaryOperand.load(from: &pc)) + case 171: return .f32Neg(Instruction.UnaryOperand.load(from: &pc)) + case 172: return .f64Neg(Instruction.UnaryOperand.load(from: &pc)) + case 173: return .f32Ceil(Instruction.UnaryOperand.load(from: &pc)) + case 174: return .f64Ceil(Instruction.UnaryOperand.load(from: &pc)) + case 175: return .f32Floor(Instruction.UnaryOperand.load(from: &pc)) + case 176: return .f64Floor(Instruction.UnaryOperand.load(from: &pc)) + case 177: return .f32Trunc(Instruction.UnaryOperand.load(from: &pc)) + case 178: return .f64Trunc(Instruction.UnaryOperand.load(from: &pc)) + case 179: return .f32Nearest(Instruction.UnaryOperand.load(from: &pc)) + case 180: return .f64Nearest(Instruction.UnaryOperand.load(from: &pc)) + case 181: return .f32Sqrt(Instruction.UnaryOperand.load(from: &pc)) + case 182: return .f64Sqrt(Instruction.UnaryOperand.load(from: &pc)) + case 183: return .f64PromoteF32(Instruction.UnaryOperand.load(from: &pc)) + case 184: return .f32DemoteF64(Instruction.UnaryOperand.load(from: &pc)) + case 185: return .select(Instruction.SelectOperand.load(from: &pc)) + case 186: return .refNull(Instruction.RefNullOperand.load(from: &pc)) + case 187: return .refIsNull(Instruction.RefIsNullOperand.load(from: &pc)) + case 188: return .refFunc(Instruction.RefFuncOperand.load(from: &pc)) + case 189: return .tableGet(Instruction.TableGetOperand.load(from: &pc)) + case 190: return .tableSet(Instruction.TableSetOperand.load(from: &pc)) + case 191: return .tableSize(Instruction.TableSizeOperand.load(from: &pc)) + case 192: return .tableGrow(Instruction.TableGrowOperand.load(from: &pc)) + case 193: return .tableFill(Instruction.TableFillOperand.load(from: &pc)) + case 194: return .tableCopy(Instruction.TableCopyOperand.load(from: &pc)) + case 195: return .tableInit(Instruction.TableInitOperand.load(from: &pc)) + case 196: return .tableElementDrop(Instruction.TableElementDropOperand.load(from: &pc)) + case 197: return .onEnter(Instruction.OnEnterOperand.load(from: &pc)) + case 198: return .onExit(Instruction.OnExitOperand.load(from: &pc)) default: fatalError("Unknown instruction opcode: \(opcode)") } } @@ -1448,195 +1502,198 @@ extension Instruction { case 4: return "compilingCall" case 5: return "internalCall" case 6: return "callIndirect" - case 7: return "unreachable" - case 8: return "nop" - case 9: return "br" - case 10: return "brIf" - case 11: return "brIfNot" - case 12: return "brTable" - case 13: return "_return" - case 14: return "endOfExecution" - case 15: return "i32Load" - case 16: return "i64Load" - case 17: return "f32Load" - case 18: return "f64Load" - case 19: return "i32Load8S" - case 20: return "i32Load8U" - case 21: return "i32Load16S" - case 22: return "i32Load16U" - case 23: return "i64Load8S" - case 24: return "i64Load8U" - case 25: return "i64Load16S" - case 26: return "i64Load16U" - case 27: return "i64Load32S" - case 28: return "i64Load32U" - case 29: return "i32Store" - case 30: return "i64Store" - case 31: return "f32Store" - case 32: return "f64Store" - case 33: return "i32Store8" - case 34: return "i32Store16" - case 35: return "i64Store8" - case 36: return "i64Store16" - case 37: return "i64Store32" - case 38: return "memorySize" - case 39: return "memoryGrow" - case 40: return "memoryInit" - case 41: return "memoryDataDrop" - case 42: return "memoryCopy" - case 43: return "memoryFill" - case 44: return "const32" - case 45: return "const64" - case 46: return "i32Add" - case 47: return "i64Add" - case 48: return "i32Sub" - case 49: return "i64Sub" - case 50: return "i32Mul" - case 51: return "i64Mul" - case 52: return "i32And" - case 53: return "i64And" - case 54: return "i32Or" - case 55: return "i64Or" - case 56: return "i32Xor" - case 57: return "i64Xor" - case 58: return "i32Shl" - case 59: return "i64Shl" - case 60: return "i32ShrS" - case 61: return "i64ShrS" - case 62: return "i32ShrU" - case 63: return "i64ShrU" - case 64: return "i32Rotl" - case 65: return "i64Rotl" - case 66: return "i32Rotr" - case 67: return "i64Rotr" - case 68: return "i32DivS" - case 69: return "i64DivS" - case 70: return "i32DivU" - case 71: return "i64DivU" - case 72: return "i32RemS" - case 73: return "i64RemS" - case 74: return "i32RemU" - case 75: return "i64RemU" - case 76: return "i32Eq" - case 77: return "i64Eq" - case 78: return "i32Ne" - case 79: return "i64Ne" - case 80: return "i32LtS" - case 81: return "i64LtS" - case 82: return "i32LtU" - case 83: return "i64LtU" - case 84: return "i32GtS" - case 85: return "i64GtS" - case 86: return "i32GtU" - case 87: return "i64GtU" - case 88: return "i32LeS" - case 89: return "i64LeS" - case 90: return "i32LeU" - case 91: return "i64LeU" - case 92: return "i32GeS" - case 93: return "i64GeS" - case 94: return "i32GeU" - case 95: return "i64GeU" - case 96: return "i32Clz" - case 97: return "i64Clz" - case 98: return "i32Ctz" - case 99: return "i64Ctz" - case 100: return "i32Popcnt" - case 101: return "i64Popcnt" - case 102: return "i32Eqz" - case 103: return "i64Eqz" - case 104: return "i32WrapI64" - case 105: return "i64ExtendI32S" - case 106: return "i64ExtendI32U" - case 107: return "i32Extend8S" - case 108: return "i64Extend8S" - case 109: return "i32Extend16S" - case 110: return "i64Extend16S" - case 111: return "i64Extend32S" - case 112: return "i32TruncF32S" - case 113: return "i32TruncF32U" - case 114: return "i32TruncSatF32S" - case 115: return "i32TruncSatF32U" - case 116: return "i32TruncF64S" - case 117: return "i32TruncF64U" - case 118: return "i32TruncSatF64S" - case 119: return "i32TruncSatF64U" - case 120: return "i64TruncF32S" - case 121: return "i64TruncF32U" - case 122: return "i64TruncSatF32S" - case 123: return "i64TruncSatF32U" - case 124: return "i64TruncF64S" - case 125: return "i64TruncF64U" - case 126: return "i64TruncSatF64S" - case 127: return "i64TruncSatF64U" - case 128: return "f32ConvertI32S" - case 129: return "f32ConvertI32U" - case 130: return "f32ConvertI64S" - case 131: return "f32ConvertI64U" - case 132: return "f64ConvertI32S" - case 133: return "f64ConvertI32U" - case 134: return "f64ConvertI64S" - case 135: return "f64ConvertI64U" - case 136: return "f32ReinterpretI32" - case 137: return "f64ReinterpretI64" - case 138: return "i32ReinterpretF32" - case 139: return "i64ReinterpretF64" - case 140: return "f32Add" - case 141: return "f64Add" - case 142: return "f32Sub" - case 143: return "f64Sub" - case 144: return "f32Mul" - case 145: return "f64Mul" - case 146: return "f32Div" - case 147: return "f64Div" - case 148: return "f32Min" - case 149: return "f64Min" - case 150: return "f32Max" - case 151: return "f64Max" - case 152: return "f32CopySign" - case 153: return "f64CopySign" - case 154: return "f32Eq" - case 155: return "f64Eq" - case 156: return "f32Ne" - case 157: return "f64Ne" - case 158: return "f32Lt" - case 159: return "f64Lt" - case 160: return "f32Gt" - case 161: return "f64Gt" - case 162: return "f32Le" - case 163: return "f64Le" - case 164: return "f32Ge" - case 165: return "f64Ge" - case 166: return "f32Abs" - case 167: return "f64Abs" - case 168: return "f32Neg" - case 169: return "f64Neg" - case 170: return "f32Ceil" - case 171: return "f64Ceil" - case 172: return "f32Floor" - case 173: return "f64Floor" - case 174: return "f32Trunc" - case 175: return "f64Trunc" - case 176: return "f32Nearest" - case 177: return "f64Nearest" - case 178: return "f32Sqrt" - case 179: return "f64Sqrt" - case 180: return "f64PromoteF32" - case 181: return "f32DemoteF64" - case 182: return "select" - case 183: return "refNull" - case 184: return "refIsNull" - case 185: return "refFunc" - case 186: return "tableGet" - case 187: return "tableSet" - case 188: return "tableSize" - case 189: return "tableGrow" - case 190: return "tableFill" - case 191: return "tableCopy" - case 192: return "tableInit" - case 193: return "tableElementDrop" - case 194: return "onEnter" - case 195: return "onExit" + case 7: return "resizeFrameHeader" + case 8: return "returnCall" + case 9: return "returnCallIndirect" + case 10: return "unreachable" + case 11: return "nop" + case 12: return "br" + case 13: return "brIf" + case 14: return "brIfNot" + case 15: return "brTable" + case 16: return "_return" + case 17: return "endOfExecution" + case 18: return "i32Load" + case 19: return "i64Load" + case 20: return "f32Load" + case 21: return "f64Load" + case 22: return "i32Load8S" + case 23: return "i32Load8U" + case 24: return "i32Load16S" + case 25: return "i32Load16U" + case 26: return "i64Load8S" + case 27: return "i64Load8U" + case 28: return "i64Load16S" + case 29: return "i64Load16U" + case 30: return "i64Load32S" + case 31: return "i64Load32U" + case 32: return "i32Store" + case 33: return "i64Store" + case 34: return "f32Store" + case 35: return "f64Store" + case 36: return "i32Store8" + case 37: return "i32Store16" + case 38: return "i64Store8" + case 39: return "i64Store16" + case 40: return "i64Store32" + case 41: return "memorySize" + case 42: return "memoryGrow" + case 43: return "memoryInit" + case 44: return "memoryDataDrop" + case 45: return "memoryCopy" + case 46: return "memoryFill" + case 47: return "const32" + case 48: return "const64" + case 49: return "i32Add" + case 50: return "i64Add" + case 51: return "i32Sub" + case 52: return "i64Sub" + case 53: return "i32Mul" + case 54: return "i64Mul" + case 55: return "i32And" + case 56: return "i64And" + case 57: return "i32Or" + case 58: return "i64Or" + case 59: return "i32Xor" + case 60: return "i64Xor" + case 61: return "i32Shl" + case 62: return "i64Shl" + case 63: return "i32ShrS" + case 64: return "i64ShrS" + case 65: return "i32ShrU" + case 66: return "i64ShrU" + case 67: return "i32Rotl" + case 68: return "i64Rotl" + case 69: return "i32Rotr" + case 70: return "i64Rotr" + case 71: return "i32DivS" + case 72: return "i64DivS" + case 73: return "i32DivU" + case 74: return "i64DivU" + case 75: return "i32RemS" + case 76: return "i64RemS" + case 77: return "i32RemU" + case 78: return "i64RemU" + case 79: return "i32Eq" + case 80: return "i64Eq" + case 81: return "i32Ne" + case 82: return "i64Ne" + case 83: return "i32LtS" + case 84: return "i64LtS" + case 85: return "i32LtU" + case 86: return "i64LtU" + case 87: return "i32GtS" + case 88: return "i64GtS" + case 89: return "i32GtU" + case 90: return "i64GtU" + case 91: return "i32LeS" + case 92: return "i64LeS" + case 93: return "i32LeU" + case 94: return "i64LeU" + case 95: return "i32GeS" + case 96: return "i64GeS" + case 97: return "i32GeU" + case 98: return "i64GeU" + case 99: return "i32Clz" + case 100: return "i64Clz" + case 101: return "i32Ctz" + case 102: return "i64Ctz" + case 103: return "i32Popcnt" + case 104: return "i64Popcnt" + case 105: return "i32Eqz" + case 106: return "i64Eqz" + case 107: return "i32WrapI64" + case 108: return "i64ExtendI32S" + case 109: return "i64ExtendI32U" + case 110: return "i32Extend8S" + case 111: return "i64Extend8S" + case 112: return "i32Extend16S" + case 113: return "i64Extend16S" + case 114: return "i64Extend32S" + case 115: return "i32TruncF32S" + case 116: return "i32TruncF32U" + case 117: return "i32TruncSatF32S" + case 118: return "i32TruncSatF32U" + case 119: return "i32TruncF64S" + case 120: return "i32TruncF64U" + case 121: return "i32TruncSatF64S" + case 122: return "i32TruncSatF64U" + case 123: return "i64TruncF32S" + case 124: return "i64TruncF32U" + case 125: return "i64TruncSatF32S" + case 126: return "i64TruncSatF32U" + case 127: return "i64TruncF64S" + case 128: return "i64TruncF64U" + case 129: return "i64TruncSatF64S" + case 130: return "i64TruncSatF64U" + case 131: return "f32ConvertI32S" + case 132: return "f32ConvertI32U" + case 133: return "f32ConvertI64S" + case 134: return "f32ConvertI64U" + case 135: return "f64ConvertI32S" + case 136: return "f64ConvertI32U" + case 137: return "f64ConvertI64S" + case 138: return "f64ConvertI64U" + case 139: return "f32ReinterpretI32" + case 140: return "f64ReinterpretI64" + case 141: return "i32ReinterpretF32" + case 142: return "i64ReinterpretF64" + case 143: return "f32Add" + case 144: return "f64Add" + case 145: return "f32Sub" + case 146: return "f64Sub" + case 147: return "f32Mul" + case 148: return "f64Mul" + case 149: return "f32Div" + case 150: return "f64Div" + case 151: return "f32Min" + case 152: return "f64Min" + case 153: return "f32Max" + case 154: return "f64Max" + case 155: return "f32CopySign" + case 156: return "f64CopySign" + case 157: return "f32Eq" + case 158: return "f64Eq" + case 159: return "f32Ne" + case 160: return "f64Ne" + case 161: return "f32Lt" + case 162: return "f64Lt" + case 163: return "f32Gt" + case 164: return "f64Gt" + case 165: return "f32Le" + case 166: return "f64Le" + case 167: return "f32Ge" + case 168: return "f64Ge" + case 169: return "f32Abs" + case 170: return "f64Abs" + case 171: return "f32Neg" + case 172: return "f64Neg" + case 173: return "f32Ceil" + case 174: return "f64Ceil" + case 175: return "f32Floor" + case 176: return "f64Floor" + case 177: return "f32Trunc" + case 178: return "f64Trunc" + case 179: return "f32Nearest" + case 180: return "f64Nearest" + case 181: return "f32Sqrt" + case 182: return "f64Sqrt" + case 183: return "f64PromoteF32" + case 184: return "f32DemoteF64" + case 185: return "select" + case 186: return "refNull" + case 187: return "refIsNull" + case 188: return "refFunc" + case 189: return "tableGet" + case 190: return "tableSet" + case 191: return "tableSize" + case 192: return "tableGrow" + case 193: return "tableFill" + case 194: return "tableCopy" + case 195: return "tableInit" + case 196: return "tableElementDrop" + case 197: return "onEnter" + case 198: return "onExit" default: fatalError("Unknown instruction index: \(opcode)") } } diff --git a/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift b/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift index 2cb2314a..2888bee8 100644 --- a/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift +++ b/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift @@ -157,6 +157,27 @@ extension Instruction.CallIndirectOperand { } } +extension Instruction.ReturnCallOperand { + init(callee: InternalFunction) { + self.init(rawCallee: UInt64(UInt(bitPattern: callee.bitPattern))) + } + + var callee: InternalFunction { + InternalFunction(bitPattern: Int(bitPattern: UInt(rawCallee))) + } +} + +extension Instruction.ReturnCallIndirectOperand { + + init(tableIndex: UInt32, type: InternedFuncType, index: VReg) { + self.init(tableIndex: tableIndex, rawType: type.id, index: index) + } + + var type: InternedFuncType { + InternedFuncType(id: rawType) + } +} + extension Instruction { typealias BrOperand = Int32 typealias OnEnterOperand = FunctionIndex @@ -314,6 +335,8 @@ struct InstructionPrintingContext { target.write("call_indirect \(reg(op.index)), \(op.tableIndex), (func_ty id:\(op.type.id)), sp: +\(op.spAddend)") case .compilingCall(let op): target.write("compiling_call \(callee(op.callee)), sp: +\(op.spAddend)") + case .returnCall(let op): + target.write("return_call \(callee(op.callee))") case .i32Load(let op): load("i32.load", op) case .i64Load(let op): load("i64.load", op) case .f32Load(let op): load("f32.load", op) diff --git a/Sources/WasmKit/Execution/Instructions/Memory.swift b/Sources/WasmKit/Execution/Instructions/Memory.swift index fc86e5e3..29f59fee 100644 --- a/Sources/WasmKit/Execution/Instructions/Memory.swift +++ b/Sources/WasmKit/Execution/Instructions/Memory.swift @@ -2,7 +2,7 @@ /// extension Execution { @inline(never) func throwOutOfBoundsMemoryAccess() throws -> Never { - throw Trap.outOfBoundsMemoryAccess + throw Trap(.memoryOutOfBounds) } mutating func memoryLoad( sp: Sp, md: Md, ms: Ms, loadOperand: Instruction.LoadOperand, loadAs _: T.Type = T.self, castToValue: (T) -> UntypedValue @@ -93,7 +93,7 @@ extension Execution { !destinationIndex.addingReportingOverflow(copyCounter).overflow && memoryInstance.data.count >= destinationIndex + copyCounter else { - throw Trap.outOfBoundsMemoryAccess + throw Trap(.memoryOutOfBounds) } memoryInstance.data.replaceSubrange( diff --git a/Sources/WasmKit/Execution/Instructions/Table.swift b/Sources/WasmKit/Execution/Instructions/Table.swift index bd23c803..b1ab63de 100644 --- a/Sources/WasmKit/Execution/Instructions/Table.swift +++ b/Sources/WasmKit/Execution/Instructions/Table.swift @@ -106,7 +106,7 @@ extension Execution { let elementIndex = sp[register].asAddressOffset(table.limits.isMemory64) guard elementIndex < table.elements.count else { - throw Trap.outOfBoundsTableAccess(Int(elementIndex)) + throw Trap(.tableOutOfBounds(Int(elementIndex))) } return ElementIndex(elementIndex) diff --git a/Sources/WasmKit/Execution/NameRegistry.swift b/Sources/WasmKit/Execution/NameRegistry.swift index 5847300c..671e9373 100644 --- a/Sources/WasmKit/Execution/NameRegistry.swift +++ b/Sources/WasmKit/Execution/NameRegistry.swift @@ -19,6 +19,13 @@ struct NameRegistry { registry.register(instance: instance, nameMap: nameMap) } } + + for (name, entry) in instance.exports { + // Use exported name if the function doesn't have name in name section. + guard case .function(let function) = entry else { continue } + guard registry.functionNames[function] == nil else { continue } + registry.functionNames[function] = name + } } } @@ -48,7 +55,7 @@ struct NameRegistry { } // Fallback if function.isWasm { - return "function[\(function.wasm.index)]" + return "wasm function[\(function.wasm.index)]" } else { return "unknown host function" } diff --git a/Sources/WasmKit/Execution/Profiler.swift b/Sources/WasmKit/Execution/Profiler.swift index 46a6cbd0..8f2f4144 100644 --- a/Sources/WasmKit/Execution/Profiler.swift +++ b/Sources/WasmKit/Execution/Profiler.swift @@ -46,7 +46,7 @@ public class GuestTimeProfiler: EngineInterceptor { private static func getTimestamp() -> UInt64 { let clock: SystemExtras.Clock - #if os(Linux) + #if os(Linux) || os(Android) clock = .boottime #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) clock = .rawMonotonic diff --git a/Sources/WasmKit/Execution/Runtime.swift b/Sources/WasmKit/Execution/Runtime.swift index c0c14ccb..dcdeda47 100644 --- a/Sources/WasmKit/Execution/Runtime.swift +++ b/Sources/WasmKit/Execution/Runtime.swift @@ -55,7 +55,7 @@ public final class Runtime { /// Legacy compatibility method to register a module instance with a name. public func register(_ instance: Instance, as name: String) throws { guard availableExports[name] == nil else { - throw ImportError.moduleInstanceAlreadyRegistered(name) + throw ImportError(.moduleInstanceAlreadyRegistered(name)) } availableExports[name] = Dictionary(uniqueKeysWithValues: instance.exports.map { ($0, $1) }) @@ -64,7 +64,7 @@ public final class Runtime { /// Legacy compatibility method to register a host module with a name. public func register(_ hostModule: HostModule, as name: String) throws { guard availableExports[name] == nil else { - throw ImportError.moduleInstanceAlreadyRegistered(name) + throw ImportError(.moduleInstanceAlreadyRegistered(name)) } registerUniqueHostModule(hostModule, as: name, engine: engine) @@ -101,36 +101,9 @@ public final class Runtime { for i in module.imports { guard let moduleExports = availableExports[i.module], let external = moduleExports[i.name] else { - throw ImportError.unknownImport(moduleName: i.module, externalName: i.name) - } - - switch (i.descriptor, external) { - case let (.function(typeIndex), .function(externalFunc)): - let type = externalFunc.handle.type - guard runtime.internType(module.types[Int(typeIndex)]) == type else { - throw ImportError.incompatibleImportType - } - result.define(i, external) - - case let (.table(tableType), .table(table)): - if let max = table.handle.limits.max, max < tableType.limits.min { - throw ImportError.incompatibleImportType - } - result.define(i, external) - - case let (.memory(memoryType), .memory(memory)): - if let max = memory.handle.limit.max, max < memoryType.min { - throw ImportError.incompatibleImportType - } - result.define(i, external) - - case let (.global(globalType), .global(global)) - where globalType == global.handle.globalType: - result.define(i, external) - - default: - throw ImportError.incompatibleImportType + throw ImportError(.missing(moduleName: i.module, externalName: i.name)) } + result.define(i, external) } return result @@ -157,7 +130,7 @@ public final class Runtime { @available(*, deprecated, message: "Use `Instance.export` and `Global.value` instead") public func getGlobal(_ instance: Instance, globalName: String) throws -> Value { guard case let .global(global) = instance.export(globalName) else { - throw Trap._raw("no global export with name \(globalName) in a module instance \(instance)") + throw Trap(.noGlobalExportWithName(globalName: globalName, instance: instance)) } return global.value } @@ -165,7 +138,7 @@ public final class Runtime { /// Invokes a function in a given module instance. public func invoke(_ instance: Instance, function: String, with arguments: [Value] = []) throws -> [Value] { guard case let .function(function)? = instance.export(function) else { - throw Trap.exportedFunctionNotFound(instance, name: function) + throw Trap(.exportedFunctionNotFound(name: function, instance: instance)) } return try function.invoke(arguments) } diff --git a/Sources/WasmKit/Execution/StoreAllocator.swift b/Sources/WasmKit/Execution/StoreAllocator.swift index 065bd815..812ed5b3 100644 --- a/Sources/WasmKit/Execution/StoreAllocator.swift +++ b/Sources/WasmKit/Execution/StoreAllocator.swift @@ -265,43 +265,45 @@ extension StoreAllocator { // local to to the module are added. for importEntry in module.imports { guard let (external, allocator) = imports.lookup(module: importEntry.module, name: importEntry.name) else { - throw ImportError.unknownImport(moduleName: importEntry.module, externalName: importEntry.name) + throw ImportError(.missing(moduleName: importEntry.module, externalName: importEntry.name)) } guard allocator === self else { - throw ImportError.importedEntityFromDifferentStore + throw ImportError(.importedEntityFromDifferentStore(importEntry)) } switch (importEntry.descriptor, external) { case let (.function(typeIndex), .function(externalFunc)): let type = externalFunc.type guard typeIndex < module.types.count else { - throw ValidationError("Function type index out of bounds") + throw ValidationError(.indexOutOfBounds("type", typeIndex, max: module.types.count)) } - guard engine.internType(module.types[Int(typeIndex)]) == type else { - throw ImportError.incompatibleImportType + let expected = module.types[Int(typeIndex)] + guard engine.internType(expected) == type else { + let actual = engine.resolveType(type) + throw ImportError(.incompatibleFunctionType(importEntry, actual: actual, expected: expected)) } importedFunctions.append(externalFunc) case let (.table(tableType), .table(table)): if let max = table.limits.max, max < tableType.limits.min { - throw ImportError.incompatibleImportType + throw ImportError(.incompatibleTableType(importEntry, actual: tableType, expected: table.tableType)) } importedTables.append(table) case let (.memory(memoryType), .memory(memory)): if let max = memory.limit.max, max < memoryType.min { - throw ImportError.incompatibleImportType + throw ImportError(.incompatibleMemoryType(importEntry, actual: memoryType, expected: memory.limit)) } importedMemories.append(memory) case let (.global(globalType), .global(global)): guard globalType == global.globalType else { - throw ImportError.incompatibleImportType + throw ImportError(.incompatibleGlobalType(importEntry, actual: global.globalType, expected: globalType)) } importedGlobals.append(global) default: - throw ImportError.incompatibleImportType + throw ImportError(.incompatibleType(importEntry, entity: external)) } } @@ -313,9 +315,10 @@ extension StoreAllocator { for (index, importedEntity) in imports.enumerated() { buffer.initializeElement(at: index, to: importedEntity) } - for (index, internalEntity) in internals.enumerated() { + for (internalIndex, internalEntity) in internals.enumerated() { + let index = imports.count + internalIndex let allocated = try allocateHandle(internalEntity, index) - buffer.initializeElement(at: imports.count + index, to: allocated) + buffer.initializeElement(at: index, to: allocated) } } } @@ -370,7 +373,7 @@ extension StoreAllocator { let globals = try allocateEntities( imports: importedGlobals, internals: module.globals, - allocateHandle: { global, i in + allocateHandle: { global, _ in let initialValue = try global.initializer.evaluate( context: constEvalContext, expectedType: global.type.valueType ) @@ -412,30 +415,25 @@ extension StoreAllocator { } func createExportValue(_ export: WasmParser.Export) throws -> InternalExternalValue { - func createErrorFactory(_ kind: String) -> (_ index: Int, _ count: Int) -> any Error { - return { index, count in - InstantiationError.exportIndexOutOfBounds(kind: kind, index: index, count: count) - } - } switch export.descriptor { case let .function(index): - let handle = try functions[validating: Int(index), createErrorFactory("function")] + let handle = try functions[validating: Int(index)] return .function(handle) case let .table(index): - let handle = try tables[validating: Int(index), createErrorFactory("table")] + let handle = try tables[validating: Int(index)] return .table(handle) case let .memory(index): - let handle = try memories[validating: Int(index), createErrorFactory("memory")] + let handle = try memories[validating: Int(index)] return .memory(handle) case let .global(index): - let handle = try globals[validating: Int(index), createErrorFactory("global")] + let handle = try globals[validating: Int(index)] return .global(handle) } } let exports: [String: InternalExternalValue] = try module.exports.reduce(into: [:]) { result, export in guard result[export.name] == nil else { - throw ValidationError("Duplicate export name: \(export.name)") + throw ValidationError(.duplicateExportName(name: export.name)) } result[export.name] = try createExportValue(export) } @@ -452,7 +450,7 @@ extension StoreAllocator { exports: exports, functionRefs: functionRefs, features: module.features, - hasDataCount: module.hasDataCount + dataCount: module.dataCount ) instancePointer.initialize(to: instanceEntity) instanceInitialized = true diff --git a/Sources/WasmKit/Execution/UntypedValue.swift b/Sources/WasmKit/Execution/UntypedValue.swift index edcd3c63..57e80114 100644 --- a/Sources/WasmKit/Execution/UntypedValue.swift +++ b/Sources/WasmKit/Execution/UntypedValue.swift @@ -142,6 +142,8 @@ struct UntypedValue: Equatable, Hashable { case .i64: return .i64(i64) case .f32: return .f32(rawF32) case .f64: return .f64(rawF64) + case .v128: + fatalError("v128 value type is not supported yet.") case .ref(let referenceType): return .ref(asReference(referenceType)) } diff --git a/Sources/WasmKit/Execution/Value.swift b/Sources/WasmKit/Execution/Value.swift index 3e7b6777..aec5637d 100644 --- a/Sources/WasmKit/Execution/Value.swift +++ b/Sources/WasmKit/Execution/Value.swift @@ -146,27 +146,27 @@ extension RawUnsignedInteger { } func divS(_ other: Self) throws -> Self { - if _slowPath(other == 0) { throw Trap.integerDividedByZero } + if _slowPath(other == 0) { throw Trap(.integerDividedByZero) } let (signed, overflow) = signed.dividedReportingOverflow(by: other.signed) - guard !overflow else { throw Trap.integerOverflowed } + guard !overflow else { throw Trap(.integerOverflow) } return signed.unsigned } func divU(_ other: Self) throws -> Self { - if _slowPath(other == 0) { throw Trap.integerDividedByZero } + if _slowPath(other == 0) { throw Trap(.integerDividedByZero) } let (unsigned, overflow) = dividedReportingOverflow(by: other) - guard !overflow else { throw Trap.integerOverflowed } + guard !overflow else { throw Trap(.integerOverflow) } return unsigned } func remS(_ other: Self) throws -> Self { - if _slowPath(other == 0) { throw Trap.integerDividedByZero } + if _slowPath(other == 0) { throw Trap(.integerDividedByZero) } let (signed, overflow) = signed.remainderReportingOverflow(dividingBy: other.signed) guard !overflow else { return 0 } return signed.unsigned } func remU(_ other: Self) throws -> Self { - if _slowPath(other == 0) { throw Trap.integerDividedByZero } + if _slowPath(other == 0) { throw Trap(.integerDividedByZero) } let (unsigned, overflow) = remainderReportingOverflow(dividingBy: other) - guard !overflow else { throw Trap.integerOverflowed } + guard !overflow else { throw Trap(.integerOverflow) } return unsigned } } @@ -263,9 +263,9 @@ extension FloatingPoint { rounding: (Self) -> T, max: Self, min: Self ) throws -> T { - guard !self.isNaN else { throw Trap.invalidConversionToInteger } + guard !self.isNaN else { throw Trap(.invalidConversionToInteger) } if self <= min || self >= max { - throw Trap.integerOverflowed + throw Trap(.integerOverflow) } return rounding(self) } diff --git a/Sources/WasmKit/Module.swift b/Sources/WasmKit/Module.swift index 2e55a101..55603fc1 100644 --- a/Sources/WasmKit/Module.swift +++ b/Sources/WasmKit/Module.swift @@ -61,9 +61,8 @@ public struct Module { let importedFunctionTypes: [TypeIndex] let memoryTypes: [MemoryType] let tableTypes: [TableType] - let allocator: ISeqAllocator let features: WasmFeatureSet - let hasDataCount: Bool + let dataCount: UInt32? init( types: [FunctionType], @@ -77,9 +76,8 @@ public struct Module { memories: [MemoryType], tables: [TableType], customSections: [CustomSection], - allocator: ISeqAllocator, features: WasmFeatureSet, - hasDataCount: Bool + dataCount: UInt32? ) { self.functions = functions self.elements = elements @@ -89,9 +87,8 @@ public struct Module { self.exports = exports self.globals = globals self.customSections = customSections - self.allocator = allocator self.features = features - self.hasDataCount = hasDataCount + self.dataCount = dataCount var importedFunctionTypes: [TypeIndex] = [] var globalTypes: [GlobalType] = [] @@ -168,58 +165,49 @@ public struct Module { // Step 12-13. // Steps 14-15. - do { - for element in elements { - guard case let .active(tableIndex, offset) = element.mode else { continue } - let table = try instance.tables[validating: Int(tableIndex)] - let offsetValue = try offset.evaluate( - context: constEvalContext, - expectedType: .addressType(isMemory64: table.limits.isMemory64) - ) - try table.withValue { table in - guard let offset = offsetValue.maybeAddressOffset(table.limits.isMemory64) else { - throw InstantiationError.unsupported( - "Expect \(ValueType.addressType(isMemory64: table.limits.isMemory64)) offset of active element segment but got \(offsetValue)" - ) - } - guard table.tableType.elementType == element.type else { - throw ValidationError( - "Element segment type \(element.type) does not match table element type \(table.tableType.elementType)" + for element in elements { + guard case let .active(tableIndex, offset) = element.mode else { continue } + let table = try instance.tables[validating: Int(tableIndex)] + let offsetValue = try offset.evaluate( + context: constEvalContext, + expectedType: .addressType(isMemory64: table.limits.isMemory64) + ) + try table.withValue { table in + guard let offset = offsetValue.maybeAddressOffset(table.limits.isMemory64) else { + throw ValidationError( + .unexpectedOffsetInitializer(expected: .addressType(isMemory64: table.limits.isMemory64), got: offsetValue) + ) + } + guard table.tableType.elementType == element.type else { + throw ValidationError( + .elementSegmentTypeMismatch( + elementType: element.type, + tableElementType: table.tableType.elementType ) - } - let references = try element.evaluateInits(context: constEvalContext) - try table.initialize( - references, from: 0, to: Int(offset), count: references.count ) } + let references = try element.evaluateInits(context: constEvalContext) + try table.initialize( + references, from: 0, to: Int(offset), count: references.count + ) } - } catch Trap.undefinedElement, Trap.tableSizeOverflow, Trap.outOfBoundsTableAccess { - throw InstantiationError.outOfBoundsTableAccess - } catch { - throw error } // Step 16. - do { - for case let .active(data) in data { - let memory = try instance.memories[validating: Int(data.index)] - let offsetValue = try data.offset.evaluate( - context: constEvalContext, - expectedType: .addressType(isMemory64: memory.limit.isMemory64) - ) - try memory.withValue { memory in - guard let offset = offsetValue.maybeAddressOffset(memory.limit.isMemory64) else { - throw InstantiationError.unsupported( - "Expect \(ValueType.addressType(isMemory64: memory.limit.isMemory64)) offset of active data segment but got \(offsetValue)" - ) - } - try memory.write(offset: Int(offset), bytes: data.initializer) + for case let .active(data) in data { + let memory = try instance.memories[validating: Int(data.index)] + let offsetValue = try data.offset.evaluate( + context: constEvalContext, + expectedType: .addressType(isMemory64: memory.limit.isMemory64) + ) + try memory.withValue { memory in + guard let offset = offsetValue.maybeAddressOffset(memory.limit.isMemory64) else { + throw ValidationError( + .unexpectedOffsetInitializer(expected: .addressType(isMemory64: memory.limit.isMemory64), got: offsetValue) + ) } + try memory.write(offset: Int(offset), bytes: data.initializer) } - } catch Trap.outOfBoundsMemoryAccess { - throw InstantiationError.outOfBoundsMemoryAccess - } catch { - throw error } // Step 17. @@ -239,13 +227,8 @@ public struct Module { } /// Materialize lazily-computed elements in this module - public mutating func materializeAll() throws { - let allocator = ISeqAllocator() - let funcTypeInterner = Interner() - for function in functions { - _ = try function.compile(module: self, funcTypeInterner: funcTypeInterner, allocator: allocator) - } - } + @available(*, deprecated, message: "Module materialization is no longer supported. Instantiate the module explicitly instead.") + public mutating func materializeAll() throws {} } extension Module { @@ -287,8 +270,4 @@ typealias LabelIndex = UInt32 struct GuestFunction { let type: FunctionType let code: Code - - func compile(module: Module, funcTypeInterner: Interner, allocator: ISeqAllocator) throws -> InstructionSequence { - throw TranslationError("Compile without instantiation is no longer supported") - } } diff --git a/Sources/WasmKit/ModuleParser.swift b/Sources/WasmKit/ModuleParser.swift index bfe76346..46fafe26 100644 --- a/Sources/WasmKit/ModuleParser.swift +++ b/Sources/WasmKit/ModuleParser.swift @@ -31,37 +31,9 @@ public func parseWasm(bytes: [UInt8], features: WasmFeatureSet = .default) throw return module } -private struct OrderTracking { - enum Order: UInt8 { - case initial = 0 - case type - case _import - case function - case table - case memory - case tag - case global - case export - case start - case element - case dataCount - case code - case data - } - - private var last: Order = .initial - mutating func track(order: Order) throws { - guard last.rawValue < order.rawValue else { - throw WasmParserError.sectionOutOfOrder - } - last = order - } -} - /// > Note: /// func parseModule(stream: Stream, features: WasmFeatureSet = .default) throws -> Module { - var orderTracking = OrderTracking() var types: [FunctionType] = [] var typeIndices: [TypeIndex] = [] var codes: [Code] = [] @@ -86,59 +58,48 @@ func parseModule(stream: Stream, features: WasmFeatureSet = case .customSection(let customSection): customSections.append(customSection) case .typeSection(let typeSection): - try orderTracking.track(order: .type) types = typeSection case .importSection(let importSection): - try orderTracking.track(order: ._import) imports = importSection case .functionSection(let types): - try orderTracking.track(order: .function) typeIndices = types case .tableSection(let tableSection): - try orderTracking.track(order: .table) tables = tableSection.map(\.type) case .memorySection(let memorySection): - try orderTracking.track(order: .memory) memories = memorySection.map(\.type) case .globalSection(let globalSection): - try orderTracking.track(order: .global) globals = globalSection case .exportSection(let exportSection): - try orderTracking.track(order: .export) exports = exportSection case .startSection(let functionIndex): - try orderTracking.track(order: .start) start = functionIndex case .elementSection(let elementSection): - try orderTracking.track(order: .element) elements = elementSection case .codeSection(let codeSection): - try orderTracking.track(order: .code) codes = codeSection case .dataSection(let dataSection): - try orderTracking.track(order: .data) data = dataSection case .dataCount(let count): - try orderTracking.track(order: .dataCount) dataCount = count } } guard typeIndices.count == codes.count else { - throw WasmParserError.inconsistentFunctionAndCodeLength( - functionCount: typeIndices.count, - codeCount: codes.count - ) + throw ValidationError( + .inconsistentFunctionAndCodeLength( + functionCount: typeIndices.count, + codeCount: codes.count + )) } if let dataCount = dataCount, dataCount != UInt32(data.count) { - throw WasmParserError.inconsistentDataCountAndDataSectionLength( - dataCount: dataCount, - dataSection: data.count - ) + throw ValidationError( + .inconsistentDataCountAndDataSectionLength( + dataCount: dataCount, + dataSection: data.count + )) } - let allocator = ISeqAllocator() let functions = try codes.enumerated().map { index, code in // SAFETY: The number of typeIndices is guaranteed to be the same as the number of codes let funcTypeIndex = typeIndices[index] @@ -161,8 +122,7 @@ func parseModule(stream: Stream, features: WasmFeatureSet = memories: memories, tables: tables, customSections: customSections, - allocator: allocator, features: features, - hasDataCount: dataCount != nil + dataCount: dataCount ) } diff --git a/Sources/WasmKit/Translator.swift b/Sources/WasmKit/Translator.swift index 8cc45e25..55db6715 100644 --- a/Sources/WasmKit/Translator.swift +++ b/Sources/WasmKit/Translator.swift @@ -45,8 +45,8 @@ protocol TranslatorContext { func resolveCallee(_ index: FunctionIndex) -> InternalFunction? func isSameInstance(_ instance: InternalInstance) -> Bool func resolveGlobal(_ index: GlobalIndex) -> InternalGlobal? - func validateDataSegment(_ index: DataIndex) throws func validateFunctionIndex(_ index: FunctionIndex) throws + var dataCount: UInt32? { get } } extension TranslatorContext { @@ -64,7 +64,7 @@ extension TranslatorContext { extension InternalInstance: TranslatorContext { func resolveType(_ index: TypeIndex) throws -> FunctionType { guard Int(index) < self.types.count else { - throw TranslationError("Type index \(index) is out of range") + throw ValidationError(.indexOutOfBounds("type", index, max: UInt32(self.types.count))) } return self.types[Int(index)] } @@ -72,34 +72,19 @@ extension InternalInstance: TranslatorContext { try FunctionType(blockType: blockType, typeSection: self.types) } func functionType(_ index: FunctionIndex, interner: Interner) throws -> FunctionType { - guard Int(index) < self.functions.count else { - throw TranslationError("Function index \(index) is out of range") - } - return interner.resolve(self.functions[Int(index)].type) + return try interner.resolve(self.functions[validating: Int(index)].type) } func globalType(_ index: GlobalIndex) throws -> ValueType { - guard Int(index) < self.globals.count else { - throw TranslationError("Global index \(index) is out of range") - } - return self.globals[Int(index)].globalType.valueType + return try self.globals[validating: Int(index)].globalType.valueType } func isMemory64(memoryIndex index: MemoryIndex) throws -> Bool { - guard Int(index) < self.memories.count else { - throw TranslationError("Memory index \(index) is out of range") - } - return self.memories[Int(index)].limit.isMemory64 + return try self.memories[validating: Int(index)].limit.isMemory64 } func isMemory64(tableIndex index: TableIndex) throws -> Bool { - guard Int(index) < self.tables.count else { - throw TranslationError("Table index \(index) is out of range") - } - return self.tables[Int(index)].limits.isMemory64 + return try self.tables[validating: Int(index)].limits.isMemory64 } func tableType(_ index: TableIndex) throws -> TableType { - guard Int(index) < self.tables.count else { - throw TranslationError("Table index \(index) is out of range") - } - return self.tables[Int(index)].tableType + return try self.tables[validating: Int(index)].tableType } func elementType(_ index: ElementIndex) throws -> ReferenceType { try self.elementSegments[validating: Int(index)].type @@ -114,15 +99,15 @@ extension InternalInstance: TranslatorContext { func isSameInstance(_ instance: InternalInstance) -> Bool { return instance == self } - func validateDataSegment(_ index: DataIndex) throws { - _ = try self.dataSegments[validating: Int(index)] - } func validateFunctionIndex(_ index: FunctionIndex) throws { let function = try self.functions[validating: Int(index)] guard self.functionRefs.contains(function) else { - throw ValidationError("Function index \(index) is not declared but referenced as a function reference") + throw ValidationError(.functionIndexNotDeclared(index: index)) } } + var dataCount: UInt32? { + self.withValue { $0.dataCount } + } } private struct MetaProgramCounter { @@ -172,10 +157,11 @@ private struct MetaProgramCounter { /// ``` /// | Offset | Description | /// |------------------------------------|----------------------| -/// | SP-3 ~ SP-(max(params, results)+3) | Frame header | -/// | SP-3 | * Saved Instance | -/// | SP-2 | * Saved PC | -/// | SP-1 | * Saved SP | +/// | SP-(max(params, results)+3) | Param/result slots |------+ +/// | ... | ... | | +/// | SP-3 | Saved Instance | Frame header +/// | SP-2 | Saved PC | | +/// | SP-1 | Saved SP |------+ /// | SP+0 | Local variable 0 | /// | SP+1 | Local variable 1 | /// | ... | ... | @@ -190,6 +176,34 @@ private struct MetaProgramCounter { /// | SP+len(locals)+C+heighest(stack)-1 | Value stack N | /// ``` /// where `C` is the number of constant slots. +/// +/// ## Example +/// +/// Consider the following Wasm function: +/// +/// ```wat +/// (func (param i32 i32) (result i32) +/// (local i32) +/// (local i64) +/// (local.set 2 (i32.add (local.get 0) (i32.const 42))) +/// (return (local.get 2)) +/// ) +/// ``` +/// +/// Then the stack frame layout looks like: +/// +/// ``` +/// | Offset | Description | +/// |------------------------------------|----------------------| +/// | -5 | Param 0 / Result 0 |------+ +/// | -4 | Param 1 | | +/// | -3 | Saved Instance | Frame header +/// | -2 | Saved PC | | +/// | -1 | Saved SP |------+ +/// | 0 | Local 0 (i32) | +/// | 1 | Local 1 (i64) | +/// | 2 | Const 0 (i32:42) | +/// ``` struct FrameHeaderLayout { let type: FunctionType @@ -231,7 +245,9 @@ struct StackLayout { self.frameHeader = FrameHeaderLayout(type: type) self.numberOfLocals = numberOfLocals // The number of constant slots is determined by the code size - self.constantSlotSize = max(codeSize / 20, 4) + // This is a heuristic value to balance the fast access to constants + // and the size of stack frame. Cap the slot size to avoid size explosion. + self.constantSlotSize = min(max(codeSize / 20, 4), 128) let (maxSlots, overflow) = self.constantSlotSize.addingReportingOverflow(numberOfLocals) guard !overflow, maxSlots < VReg.max else { throw TranslationError("The number of constant slots overflows") @@ -354,14 +370,14 @@ struct InstructionTranslator: InstructionVisitor { private mutating func setReachability(_ value: Bool) throws { guard !self.frames.isEmpty else { - throw ValidationError("Control stack is empty. Instruction cannot be appeared after \"end\" of function") + throw ValidationError(.controlStackEmpty) } self.frames[self.frames.count - 1].reachable = value } func currentFrame() throws -> ControlFrame { guard let frame = self.frames.last else { - throw ValidationError("Control stack is empty. Instruction cannot be appeared after \"end\" of function") + throw ValidationError(.controlStackEmpty) } return frame } @@ -369,7 +385,7 @@ struct InstructionTranslator: InstructionVisitor { func branchTarget(relativeDepth: UInt32) throws -> ControlFrame { let index = frames.count - 1 - Int(relativeDepth) guard frames.indices.contains(index) else { - throw ValidationError("Relative depth \(relativeDepth) is out of range") + throw ValidationError(.relativeDepthOutOfRange(relativeDepth: relativeDepth)) } return frames[index] } @@ -913,25 +929,19 @@ struct InstructionTranslator: InstructionVisitor { /// /// - Parameter typeHint: A type expected to be popped. Only used for diagnostic purpose. /// - Returns: `true` if check succeed. `false` if the pop operation is going to be performed in unreachable code path. - private func checkBeforePop(typeHint: ValueType?, controlFrame: ControlStack.ControlFrame) throws -> Bool { - if _slowPath(valueStack.height <= controlFrame.stackHeight) { + private func checkBeforePop(typeHint: ValueType?, depth: Int = 0, controlFrame: ControlStack.ControlFrame) throws -> Bool { + if _slowPath(valueStack.height - depth <= controlFrame.stackHeight) { if controlFrame.reachable { - let message: String - if let typeHint { - message = "Expected a \(typeHint) value on stack but it's empty" - } else { - message = "Expected a value on stack but it's empty" - } - throw TranslationError(message) + throw ValidationError(.expectedTypeOnStackButEmpty(expected: typeHint)) } // Too many pop on unreachable path is ignored return false } return true } - private func checkBeforePop(typeHint: ValueType?) throws -> Bool { + private func checkBeforePop(typeHint: ValueType?, depth: Int = 0) throws -> Bool { let controlFrame = try controlStack.currentFrame() - return try self.checkBeforePop(typeHint: typeHint, controlFrame: controlFrame) + return try self.checkBeforePop(typeHint: typeHint, depth: depth, controlFrame: controlFrame) } private mutating func ensureOnVReg(_ source: ValueSource) -> VReg { // TODO: Copy to stack if source is on preg @@ -1007,12 +1017,12 @@ struct InstructionTranslator: InstructionVisitor { private func checkStackTop(_ valueTypes: [ValueType]) throws { for (stackDepth, type) in valueTypes.reversed().enumerated() { - guard try checkBeforePop(typeHint: type) else { return } + guard try checkBeforePop(typeHint: type, depth: stackDepth) else { return } let actual = valueStack.peekType(depth: stackDepth) switch actual { case .some(let actualType): guard actualType == type else { - throw ValidationError("Expected \(type) on the stack top but got \(actualType)") + throw ValidationError(.expectedTypeOnStack(expected: type, actual: actualType)) } case .unknown: break } @@ -1020,8 +1030,15 @@ struct InstructionTranslator: InstructionVisitor { } private mutating func visitReturnLike() throws { + try copyValuesIntoResultSlots(self.type.results, frameHeader: stackLayout.frameHeader) + } + + /// Pop values from the stack and copy them to the return slots. + /// + /// - Parameter valueTypes: The types of the values to copy. + private mutating func copyValuesIntoResultSlots(_ valueTypes: [ValueType], frameHeader: FrameHeaderLayout) throws { var copies: [(source: VReg, dest: VReg)] = [] - for (index, resultType) in self.type.results.enumerated().reversed() { + for (index, resultType) in valueTypes.enumerated().reversed() { guard let operand = try popOperand(resultType) else { continue } var source = ensureOnVReg(operand) if case .local(let localIndex) = operand, stackLayout.isParameter(localIndex) { @@ -1031,7 +1048,7 @@ struct InstructionTranslator: InstructionVisitor { emitCopyStack(from: localReg(localIndex), to: copyTo) source = copyTo } - let dest = returnReg(index) + let dest = frameHeader.returnReg(index) copies.append((source, dest)) } for (source, dest) in copies { @@ -1075,7 +1092,7 @@ struct InstructionTranslator: InstructionVisitor { private mutating func finalize() throws -> InstructionSequence { if controlStack.numberOfFrames > 1 { - throw TranslationError("Expect \(controlStack.numberOfFrames - 1) more `end` instructions") + throw ValidationError(.expectedMoreEndInstructions(count: controlStack.numberOfFrames - 1)) } // Check dangling labels try iseqBuilder.assertDanglingLabels() @@ -1187,7 +1204,7 @@ struct InstructionTranslator: InstructionVisitor { mutating func visitElse() throws -> Output { var frame = try controlStack.currentFrame() guard case let .if(elseLabel, endLabel, _) = frame.kind else { - throw TranslationError("Expected `if` control frame on top of the stack for `else` but got \(frame)") + throw ValidationError(.expectedIfControlFrame) } preserveOnStack(depth: valueStack.height - frame.stackHeight) try controlStack.resetReachability() @@ -1201,7 +1218,7 @@ struct InstructionTranslator: InstructionVisitor { _ = try valueStack.pop(result) } guard valueStack.height == frame.stackHeight else { - throw ValidationError("values remaining on stack at end of block") + throw ValidationError(.valuesRemainingAtEndOfBlock) } _ = controlStack.popFrame() frame.kind = .if(elseLabel: elseLabel, endLabel: endLabel, isElse: true) @@ -1217,40 +1234,23 @@ struct InstructionTranslator: InstructionVisitor { mutating func visitEnd() throws -> Output { let toBePopped = try controlStack.currentFrame() - // Reset the last emission to avoid relinking the result of the last instruction inside the block. - // Relinking results across the block boundary is invalid because the producer instruction is not - // statically known. Think about the following case: - // ``` - // local.get 0 - // if - // i32.const 2 - // else - // i32.const 3 - // end - // local.set 0 - // ``` - // iseqBuilder.resetLastEmission() if case .block(root: true) = toBePopped.kind { try translateReturn() - // TODO: Merge logic with regular block frame guard valueStack.height == toBePopped.stackHeight else { - throw ValidationError("values remaining on stack at end of block") + throw ValidationError(.valuesRemainingAtEndOfBlock) } try iseqBuilder.pinLabelHere(toBePopped.continuation) return } if case .if(_, _, isElse: false) = toBePopped.kind { - // `if` inst without `else` must have the same parameter and result types let blockType = toBePopped.blockType guard blockType.parameters == blockType.results else { - throw TranslationError("Expected the same parameter and result types for `if` block but got \(blockType)") + throw ValidationError(.parameterResultTypeMismatch(blockType: blockType)) } } - // NOTE: `valueStack.height - poppedFrame.stackHeight` is usually the same as `poppedFrame.copyCount` - // but it's not always the case when this block is already unreachable. preserveOnStack(depth: Int(valueStack.height - toBePopped.stackHeight)) switch toBePopped.kind { case .block: @@ -1264,7 +1264,7 @@ struct InstructionTranslator: InstructionVisitor { _ = try valueStack.pop(result) } guard valueStack.height == toBePopped.stackHeight else { - throw ValidationError("values remaining on stack at end of block") + throw ValidationError(.valuesRemainingAtEndOfBlock) } for result in toBePopped.blockType.results { _ = valueStack.push(result) @@ -1281,7 +1281,7 @@ struct InstructionTranslator: InstructionVisitor { if _fastPath(currentFrame.reachable) { let count = currentHeight - Int(destination.copyCount) - destination.stackHeight guard count >= 0 else { - throw TranslationError("Stack height underflow: available \(currentHeight), required \(destination.stackHeight + Int(destination.copyCount))") + throw ValidationError(.stackHeightUnderflow(available: currentHeight, required: destination.stackHeight + Int(destination.copyCount))) } popCount = UInt32(count) } else { @@ -1437,7 +1437,7 @@ struct InstructionTranslator: InstructionVisitor { // Check copyTypes consistency guard frame.copyTypes.count == defaultFrame.copyTypes.count else { - throw ValidationError("Expected the same copy types for all branches in `br_table` but got \(frame.copyTypes) and \(defaultFrame.copyTypes)") + throw ValidationError(.expectedSameCopyTypes(frameCopyTypes: frame.copyTypes, defaultFrameCopyTypes: defaultFrame.copyTypes)) } try checkStackTop(frame.copyTypes) @@ -1518,6 +1518,73 @@ struct InstructionTranslator: InstructionVisitor { emit(.callIndirect(operand)) } + /// Emit instructions to prepare the frame header for a return call to replace the + /// current frame header with the callee's frame header layout. + /// + /// The frame header should have the callee's frame header layout and parameter + /// slots are filled with arguments on the caller's stack. + /// + /// - Parameters: + /// - calleeType: The type of the callee function. + /// - stackTopHeightToCopy: The height of the stack top needed to be available at the + /// return-call-like instruction point. + private mutating func prepareFrameHeaderForReturnCall(calleeType: FunctionType, stackTopHeightToCopy: Int) throws { + let calleeFrameHeader = FrameHeaderLayout(type: calleeType) + if calleeType == self.type { + // Fast path: If the callee and the caller have the same signature, we can + // skip reconstructing the frame header and we can just copy the parameters. + } else { + // Ensure all parameters are on stack to avoid conflicting with the next resize. + preserveOnStack(depth: calleeType.parameters.count) + // Resize the current frame header while moving stack slots after the header + // to the resized positions + let newHeaderSize = FrameHeaderLayout.size(of: calleeType) + let delta = newHeaderSize - FrameHeaderLayout.size(of: type) + let sizeToCopy = VReg(FrameHeaderLayout.numberOfSavingSlots) + valueStack.stackRegBase + VReg(stackTopHeightToCopy) + emit(.resizeFrameHeader(Instruction.ResizeFrameHeaderOperand(delta: delta, sizeToCopy: sizeToCopy))) + } + try copyValuesIntoResultSlots(calleeType.parameters, frameHeader: calleeFrameHeader) + } + + mutating func visitReturnCall(functionIndex: UInt32) throws { + let calleeType = try self.module.functionType(functionIndex, interner: funcTypeInterner) + try validator.validateReturnCallLike(calleeType: calleeType, callerType: type) + + guard let callee = self.module.resolveCallee(functionIndex) else { + // Skip actual code emission if validation-only mode + return + } + try prepareFrameHeaderForReturnCall(calleeType: calleeType, stackTopHeightToCopy: valueStack.height) + emit(.returnCall(Instruction.ReturnCallOperand(callee: callee))) + try markUnreachable() + } + + mutating func visitReturnCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws { + let stackTopHeightToCopy = valueStack.height + let addressType = try module.addressType(tableIndex: tableIndex) + // Preserve function index slot on stack + let address = try popOnStackOperand(addressType) // function address + guard let address = address else { return } + + let calleeType = try self.module.resolveType(typeIndex) + let internType = funcTypeInterner.intern(calleeType) + + try prepareFrameHeaderForReturnCall( + calleeType: calleeType, + // Keep the stack space including the function index slot to be + // accessible at the `return_call_indirect` instruction point. + stackTopHeightToCopy: stackTopHeightToCopy + ) + + let operand = Instruction.ReturnCallIndirectOperand( + tableIndex: tableIndex, + type: internType, + index: address + ) + emit(.returnCallIndirect(operand)) + try markUnreachable() + } + mutating func visitDrop() throws -> Output { _ = try popAnyOperand() iseqBuilder.resetLastEmission() @@ -1528,10 +1595,10 @@ struct InstructionTranslator: InstructionVisitor { let (value2Type, value2) = try popAnyOperand() switch (value1Type, value2Type) { case (.some(.ref(_)), _), (_, .some(.ref(_))): - throw TranslationError("Cannot `select` on reference types") + throw ValidationError(.cannotSelectOnReferenceTypes) case let (.some(type1), .some(type2)): guard type1 == type2 else { - throw TranslationError("Type mismatch on `select`. Expected \(value1Type) and \(value2Type) to be same") + throw ValidationError(.typeMismatchOnSelect(expected: type1, actual: type2)) } case (.unknown, _), (_, .unknown): break @@ -1737,29 +1804,43 @@ struct InstructionTranslator: InstructionVisitor { emit(instruction(storeOperand)) } } - mutating func visitI32Load(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i32, 2, Instruction.i32Load) } - mutating func visitI64Load(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i64, 3, Instruction.i64Load) } - mutating func visitF32Load(memarg: MemArg) throws -> Output { try visitLoad(memarg, .f32, 2, Instruction.f32Load) } - mutating func visitF64Load(memarg: MemArg) throws -> Output { try visitLoad(memarg, .f64, 3, Instruction.f64Load) } - mutating func visitI32Load8S(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i32, 0, Instruction.i32Load8S) } - mutating func visitI32Load8U(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i32, 0, Instruction.i32Load8U) } - mutating func visitI32Load16S(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i32, 1, Instruction.i32Load16S) } - mutating func visitI32Load16U(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i32, 1, Instruction.i32Load16U) } - mutating func visitI64Load8S(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i64, 0, Instruction.i64Load8S) } - mutating func visitI64Load8U(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i64, 0, Instruction.i64Load8U) } - mutating func visitI64Load16S(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i64, 1, Instruction.i64Load16S) } - mutating func visitI64Load16U(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i64, 1, Instruction.i64Load16U) } - mutating func visitI64Load32S(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i64, 2, Instruction.i64Load32S) } - mutating func visitI64Load32U(memarg: MemArg) throws -> Output { try visitLoad(memarg, .i64, 2, Instruction.i64Load32U) } - mutating func visitI32Store(memarg: MemArg) throws -> Output { try visitStore(memarg, .i32, 2, Instruction.i32Store) } - mutating func visitI64Store(memarg: MemArg) throws -> Output { try visitStore(memarg, .i64, 3, Instruction.i64Store) } - mutating func visitF32Store(memarg: MemArg) throws -> Output { try visitStore(memarg, .f32, 2, Instruction.f32Store) } - mutating func visitF64Store(memarg: MemArg) throws -> Output { try visitStore(memarg, .f64, 3, Instruction.f64Store) } - mutating func visitI32Store8(memarg: MemArg) throws -> Output { try visitStore(memarg, .i32, 0, Instruction.i32Store8) } - mutating func visitI32Store16(memarg: MemArg) throws -> Output { try visitStore(memarg, .i32, 1, Instruction.i32Store16) } - mutating func visitI64Store8(memarg: MemArg) throws -> Output { try visitStore(memarg, .i64, 0, Instruction.i64Store8) } - mutating func visitI64Store16(memarg: MemArg) throws -> Output { try visitStore(memarg, .i64, 1, Instruction.i64Store16) } - mutating func visitI64Store32(memarg: MemArg) throws -> Output { try visitStore(memarg, .i64, 2, Instruction.i64Store32) } + + mutating func visitLoad(_ load: WasmParser.Instruction.Load, memarg: MemArg) throws { + let instruction: (Instruction.LoadOperand) -> Instruction + switch load { + case .i32Load: instruction = Instruction.i32Load + case .i64Load: instruction = Instruction.i64Load + case .f32Load: instruction = Instruction.f32Load + case .f64Load: instruction = Instruction.f64Load + case .i32Load8S: instruction = Instruction.i32Load8S + case .i32Load8U: instruction = Instruction.i32Load8U + case .i32Load16S: instruction = Instruction.i32Load16S + case .i32Load16U: instruction = Instruction.i32Load16U + case .i64Load8S: instruction = Instruction.i64Load8S + case .i64Load8U: instruction = Instruction.i64Load8U + case .i64Load16S: instruction = Instruction.i64Load16S + case .i64Load16U: instruction = Instruction.i64Load16U + case .i64Load32S: instruction = Instruction.i64Load32S + case .i64Load32U: instruction = Instruction.i64Load32U + } + try visitLoad(memarg, load.type, load.naturalAlignment, instruction) + } + + mutating func visitStore(_ store: WasmParser.Instruction.Store, memarg: MemArg) throws { + let instruction: (Instruction.StoreOperand) -> Instruction + switch store { + case .i32Store: instruction = Instruction.i32Store + case .i64Store: instruction = Instruction.i64Store + case .f32Store: instruction = Instruction.f32Store + case .f64Store: instruction = Instruction.f64Store + case .i32Store8: instruction = Instruction.i32Store8 + case .i32Store16: instruction = Instruction.i32Store16 + case .i64Store8: instruction = Instruction.i64Store8 + case .i64Store16: instruction = Instruction.i64Store16 + case .i64Store32: instruction = Instruction.i64Store32 + } + try visitStore(memarg, store.type, store.naturalAlignment, instruction) + } mutating func visitMemorySize(memory: UInt32) throws -> Output { let sizeType: ValueType = try module.isMemory64(memoryIndex: memory) ? .i64 : .i32 pushEmit(sizeType, { .memorySize(Instruction.MemorySizeOperand(memoryIndex: memory, result: LVReg($0))) }) @@ -1845,139 +1926,178 @@ struct InstructionTranslator: InstructionVisitor { .i32Eqz(Instruction.UnaryOperand(result: LVReg(result), input: LVReg(value))) } } - mutating func visitI32Eq() throws -> Output { try visitCmp(.i32, Instruction.i32Eq) } - mutating func visitI32Ne() throws -> Output { try visitCmp(.i32, Instruction.i32Ne) } - mutating func visitI32LtS() throws -> Output { try visitCmp(.i32, Instruction.i32LtS) } - mutating func visitI32LtU() throws -> Output { try visitCmp(.i32, Instruction.i32LtU) } - mutating func visitI32GtS() throws -> Output { try visitCmp(.i32, Instruction.i32GtS) } - mutating func visitI32GtU() throws -> Output { try visitCmp(.i32, Instruction.i32GtU) } - mutating func visitI32LeS() throws -> Output { try visitCmp(.i32, Instruction.i32LeS) } - mutating func visitI32LeU() throws -> Output { try visitCmp(.i32, Instruction.i32LeU) } - mutating func visitI32GeS() throws -> Output { try visitCmp(.i32, Instruction.i32GeS) } - mutating func visitI32GeU() throws -> Output { try visitCmp(.i32, Instruction.i32GeU) } + mutating func visitCmp(_ cmp: WasmParser.Instruction.Cmp) throws { + let operand: ValueType + let instruction: (Instruction.BinaryOperand) -> Instruction + switch cmp { + case .i32Eq: (operand, instruction) = (.i32, Instruction.i32Eq) + case .i32Ne: (operand, instruction) = (.i32, Instruction.i32Ne) + case .i32LtS: (operand, instruction) = (.i32, Instruction.i32LtS) + case .i32LtU: (operand, instruction) = (.i32, Instruction.i32LtU) + case .i32GtS: (operand, instruction) = (.i32, Instruction.i32GtS) + case .i32GtU: (operand, instruction) = (.i32, Instruction.i32GtU) + case .i32LeS: (operand, instruction) = (.i32, Instruction.i32LeS) + case .i32LeU: (operand, instruction) = (.i32, Instruction.i32LeU) + case .i32GeS: (operand, instruction) = (.i32, Instruction.i32GeS) + case .i32GeU: (operand, instruction) = (.i32, Instruction.i32GeU) + case .i64Eq: (operand, instruction) = (.i64, Instruction.i64Eq) + case .i64Ne: (operand, instruction) = (.i64, Instruction.i64Ne) + case .i64LtS: (operand, instruction) = (.i64, Instruction.i64LtS) + case .i64LtU: (operand, instruction) = (.i64, Instruction.i64LtU) + case .i64GtS: (operand, instruction) = (.i64, Instruction.i64GtS) + case .i64GtU: (operand, instruction) = (.i64, Instruction.i64GtU) + case .i64LeS: (operand, instruction) = (.i64, Instruction.i64LeS) + case .i64LeU: (operand, instruction) = (.i64, Instruction.i64LeU) + case .i64GeS: (operand, instruction) = (.i64, Instruction.i64GeS) + case .i64GeU: (operand, instruction) = (.i64, Instruction.i64GeU) + case .f32Eq: (operand, instruction) = (.f32, Instruction.f32Eq) + case .f32Ne: (operand, instruction) = (.f32, Instruction.f32Ne) + case .f32Lt: (operand, instruction) = (.f32, Instruction.f32Lt) + case .f32Gt: (operand, instruction) = (.f32, Instruction.f32Gt) + case .f32Le: (operand, instruction) = (.f32, Instruction.f32Le) + case .f32Ge: (operand, instruction) = (.f32, Instruction.f32Ge) + case .f64Eq: (operand, instruction) = (.f64, Instruction.f64Eq) + case .f64Ne: (operand, instruction) = (.f64, Instruction.f64Ne) + case .f64Lt: (operand, instruction) = (.f64, Instruction.f64Lt) + case .f64Gt: (operand, instruction) = (.f64, Instruction.f64Gt) + case .f64Le: (operand, instruction) = (.f64, Instruction.f64Le) + case .f64Ge: (operand, instruction) = (.f64, Instruction.f64Ge) + } + try visitCmp(operand, instruction) + } + public mutating func visitBinary(_ binary: WasmParser.Instruction.Binary) throws { + let operand: ValueType + let result: ValueType + let instruction: (Instruction.BinaryOperand) -> Instruction + switch binary { + case .i32Add: (operand, result, instruction) = (.i32, .i32, Instruction.i32Add) + case .i32Sub: (operand, result, instruction) = (.i32, .i32, Instruction.i32Sub) + case .i32Mul: (operand, result, instruction) = (.i32, .i32, Instruction.i32Mul) + case .i32DivS: (operand, result, instruction) = (.i32, .i32, Instruction.i32DivS) + case .i32DivU: (operand, result, instruction) = (.i32, .i32, Instruction.i32DivU) + case .i32RemS: (operand, result, instruction) = (.i32, .i32, Instruction.i32RemS) + case .i32RemU: (operand, result, instruction) = (.i32, .i32, Instruction.i32RemU) + case .i32And: (operand, result, instruction) = (.i32, .i32, Instruction.i32And) + case .i32Or: (operand, result, instruction) = (.i32, .i32, Instruction.i32Or) + case .i32Xor: (operand, result, instruction) = (.i32, .i32, Instruction.i32Xor) + case .i32Shl: (operand, result, instruction) = (.i32, .i32, Instruction.i32Shl) + case .i32ShrS: (operand, result, instruction) = (.i32, .i32, Instruction.i32ShrS) + case .i32ShrU: (operand, result, instruction) = (.i32, .i32, Instruction.i32ShrU) + case .i32Rotl: (operand, result, instruction) = (.i32, .i32, Instruction.i32Rotl) + case .i32Rotr: (operand, result, instruction) = (.i32, .i32, Instruction.i32Rotr) + case .i64Add: (operand, result, instruction) = (.i64, .i64, Instruction.i64Add) + case .i64Sub: (operand, result, instruction) = (.i64, .i64, Instruction.i64Sub) + case .i64Mul: (operand, result, instruction) = (.i64, .i64, Instruction.i64Mul) + case .i64DivS: (operand, result, instruction) = (.i64, .i64, Instruction.i64DivS) + case .i64DivU: (operand, result, instruction) = (.i64, .i64, Instruction.i64DivU) + case .i64RemS: (operand, result, instruction) = (.i64, .i64, Instruction.i64RemS) + case .i64RemU: (operand, result, instruction) = (.i64, .i64, Instruction.i64RemU) + case .i64And: (operand, result, instruction) = (.i64, .i64, Instruction.i64And) + case .i64Or: (operand, result, instruction) = (.i64, .i64, Instruction.i64Or) + case .i64Xor: (operand, result, instruction) = (.i64, .i64, Instruction.i64Xor) + case .i64Shl: (operand, result, instruction) = (.i64, .i64, Instruction.i64Shl) + case .i64ShrS: (operand, result, instruction) = (.i64, .i64, Instruction.i64ShrS) + case .i64ShrU: (operand, result, instruction) = (.i64, .i64, Instruction.i64ShrU) + case .i64Rotl: (operand, result, instruction) = (.i64, .i64, Instruction.i64Rotl) + case .i64Rotr: (operand, result, instruction) = (.i64, .i64, Instruction.i64Rotr) + case .f32Add: (operand, result, instruction) = (.f32, .f32, Instruction.f32Add) + case .f32Sub: (operand, result, instruction) = (.f32, .f32, Instruction.f32Sub) + case .f32Mul: (operand, result, instruction) = (.f32, .f32, Instruction.f32Mul) + case .f32Div: (operand, result, instruction) = (.f32, .f32, Instruction.f32Div) + case .f32Min: (operand, result, instruction) = (.f32, .f32, Instruction.f32Min) + case .f32Max: (operand, result, instruction) = (.f32, .f32, Instruction.f32Max) + case .f32Copysign: (operand, result, instruction) = (.f32, .f32, Instruction.f32CopySign) + case .f64Add: (operand, result, instruction) = (.f64, .f64, Instruction.f64Add) + case .f64Sub: (operand, result, instruction) = (.f64, .f64, Instruction.f64Sub) + case .f64Mul: (operand, result, instruction) = (.f64, .f64, Instruction.f64Mul) + case .f64Div: (operand, result, instruction) = (.f64, .f64, Instruction.f64Div) + case .f64Min: (operand, result, instruction) = (.f64, .f64, Instruction.f64Min) + case .f64Max: (operand, result, instruction) = (.f64, .f64, Instruction.f64Max) + case .f64Copysign: (operand, result, instruction) = (.f64, .f64, Instruction.f64CopySign) + } + try visitBinary(operand, result, instruction) + } mutating func visitI64Eqz() throws -> Output { try popPushEmit(.i64, .i32) { value, result, stack in .i64Eqz(Instruction.UnaryOperand(result: LVReg(result), input: LVReg(value))) } } - mutating func visitI64Eq() throws -> Output { try visitCmp(.i64, Instruction.i64Eq) } - mutating func visitI64Ne() throws -> Output { try visitCmp(.i64, Instruction.i64Ne) } - mutating func visitI64LtS() throws -> Output { try visitCmp(.i64, Instruction.i64LtS) } - mutating func visitI64LtU() throws -> Output { try visitCmp(.i64, Instruction.i64LtU) } - mutating func visitI64GtS() throws -> Output { try visitCmp(.i64, Instruction.i64GtS) } - mutating func visitI64GtU() throws -> Output { try visitCmp(.i64, Instruction.i64GtU) } - mutating func visitI64LeS() throws -> Output { try visitCmp(.i64, Instruction.i64LeS) } - mutating func visitI64LeU() throws -> Output { try visitCmp(.i64, Instruction.i64LeU) } - mutating func visitI64GeS() throws -> Output { try visitCmp(.i64, Instruction.i64GeS) } - mutating func visitI64GeU() throws -> Output { try visitCmp(.i64, Instruction.i64GeU) } - mutating func visitF32Eq() throws -> Output { try visitCmp(.f32, Instruction.f32Eq) } - mutating func visitF32Ne() throws -> Output { try visitCmp(.f32, Instruction.f32Ne) } - mutating func visitF32Lt() throws -> Output { try visitCmp(.f32, Instruction.f32Lt) } - mutating func visitF32Gt() throws -> Output { try visitCmp(.f32, Instruction.f32Gt) } - mutating func visitF32Le() throws -> Output { try visitCmp(.f32, Instruction.f32Le) } - mutating func visitF32Ge() throws -> Output { try visitCmp(.f32, Instruction.f32Ge) } - mutating func visitF64Eq() throws -> Output { try visitCmp(.f64, Instruction.f64Eq) } - mutating func visitF64Ne() throws -> Output { try visitCmp(.f64, Instruction.f64Ne) } - mutating func visitF64Lt() throws -> Output { try visitCmp(.f64, Instruction.f64Lt) } - mutating func visitF64Gt() throws -> Output { try visitCmp(.f64, Instruction.f64Gt) } - mutating func visitF64Le() throws -> Output { try visitCmp(.f64, Instruction.f64Le) } - mutating func visitF64Ge() throws -> Output { try visitCmp(.f64, Instruction.f64Ge) } - mutating func visitI32Clz() throws -> Output { try visitUnary(.i32, Instruction.i32Clz) } - mutating func visitI32Ctz() throws -> Output { try visitUnary(.i32, Instruction.i32Ctz) } - mutating func visitI32Popcnt() throws -> Output { try visitUnary(.i32, Instruction.i32Popcnt) } - mutating func visitI32Add() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32Add) } - mutating func visitI32Sub() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32Sub) } - mutating func visitI32Mul() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32Mul) } - mutating func visitI32DivS() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32DivS) } - mutating func visitI32DivU() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32DivU) } - mutating func visitI32RemS() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32RemS) } - mutating func visitI32RemU() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32RemU) } - mutating func visitI32And() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32And) } - mutating func visitI32Or() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32Or) } - mutating func visitI32Xor() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32Xor) } - mutating func visitI32Shl() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32Shl) } - mutating func visitI32ShrS() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32ShrS) } - mutating func visitI32ShrU() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32ShrU) } - mutating func visitI32Rotl() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32Rotl) } - mutating func visitI32Rotr() throws -> Output { try visitBinary(.i32, .i32, Instruction.i32Rotr) } - mutating func visitI64Clz() throws -> Output { try visitUnary(.i64, Instruction.i64Clz) } - mutating func visitI64Ctz() throws -> Output { try visitUnary(.i64, Instruction.i64Ctz) } - mutating func visitI64Popcnt() throws -> Output { try visitUnary(.i64, Instruction.i64Popcnt) } - mutating func visitI64Add() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64Add) } - mutating func visitI64Sub() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64Sub) } - mutating func visitI64Mul() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64Mul) } - mutating func visitI64DivS() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64DivS) } - mutating func visitI64DivU() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64DivU) } - mutating func visitI64RemS() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64RemS) } - mutating func visitI64RemU() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64RemU) } - mutating func visitI64And() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64And) } - mutating func visitI64Or() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64Or) } - mutating func visitI64Xor() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64Xor) } - mutating func visitI64Shl() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64Shl) } - mutating func visitI64ShrS() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64ShrS) } - mutating func visitI64ShrU() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64ShrU) } - mutating func visitI64Rotl() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64Rotl) } - mutating func visitI64Rotr() throws -> Output { try visitBinary(.i64, .i64, Instruction.i64Rotr) } - mutating func visitF32Abs() throws -> Output { try visitUnary(.f32, Instruction.f32Abs) } - mutating func visitF32Neg() throws -> Output { try visitUnary(.f32, Instruction.f32Neg) } - mutating func visitF32Ceil() throws -> Output { try visitUnary(.f32, Instruction.f32Ceil) } - mutating func visitF32Floor() throws -> Output { try visitUnary(.f32, Instruction.f32Floor) } - mutating func visitF32Trunc() throws -> Output { try visitUnary(.f32, Instruction.f32Trunc) } - mutating func visitF32Nearest() throws -> Output { try visitUnary(.f32, Instruction.f32Nearest) } - mutating func visitF32Sqrt() throws -> Output { try visitUnary(.f32, Instruction.f32Sqrt) } - mutating func visitF32Add() throws -> Output { try visitBinary(.f32, .f32, Instruction.f32Add) } - mutating func visitF32Sub() throws -> Output { try visitBinary(.f32, .f32, Instruction.f32Sub) } - mutating func visitF32Mul() throws -> Output { try visitBinary(.f32, .f32, Instruction.f32Mul) } - mutating func visitF32Div() throws -> Output { try visitBinary(.f32, .f32, Instruction.f32Div) } - mutating func visitF32Min() throws -> Output { try visitBinary(.f32, .f32, Instruction.f32Min) } - mutating func visitF32Max() throws -> Output { try visitBinary(.f32, .f32, Instruction.f32Max) } - mutating func visitF32Copysign() throws -> Output { try visitBinary(.f32, .f32, Instruction.f32CopySign) } - mutating func visitF64Abs() throws -> Output { try visitUnary(.f64, Instruction.f64Abs) } - mutating func visitF64Neg() throws -> Output { try visitUnary(.f64, Instruction.f64Neg) } - mutating func visitF64Ceil() throws -> Output { try visitUnary(.f64, Instruction.f64Ceil) } - mutating func visitF64Floor() throws -> Output { try visitUnary(.f64, Instruction.f64Floor) } - mutating func visitF64Trunc() throws -> Output { try visitUnary(.f64, Instruction.f64Trunc) } - mutating func visitF64Nearest() throws -> Output { try visitUnary(.f64, Instruction.f64Nearest) } - mutating func visitF64Sqrt() throws -> Output { try visitUnary(.f64, Instruction.f64Sqrt) } - mutating func visitF64Add() throws -> Output { try visitBinary(.f64, .f64, Instruction.f64Add) } - mutating func visitF64Sub() throws -> Output { try visitBinary(.f64, .f64, Instruction.f64Sub) } - mutating func visitF64Mul() throws -> Output { try visitBinary(.f64, .f64, Instruction.f64Mul) } - mutating func visitF64Div() throws -> Output { try visitBinary(.f64, .f64, Instruction.f64Div) } - mutating func visitF64Min() throws -> Output { try visitBinary(.f64, .f64, Instruction.f64Min) } - mutating func visitF64Max() throws -> Output { try visitBinary(.f64, .f64, Instruction.f64Max) } - mutating func visitF64Copysign() throws -> Output { try visitBinary(.f64, .f64, Instruction.f64CopySign) } - mutating func visitI32WrapI64() throws -> Output { try visitConversion(.i64, .i32, Instruction.i32WrapI64) } - mutating func visitI32TruncF32S() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32TruncF32S) } - mutating func visitI32TruncF32U() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32TruncF32U) } - mutating func visitI32TruncF64S() throws -> Output { try visitConversion(.f64, .i32, Instruction.i32TruncF64S) } - mutating func visitI32TruncF64U() throws -> Output { try visitConversion(.f64, .i32, Instruction.i32TruncF64U) } - mutating func visitI64ExtendI32S() throws -> Output { try visitConversion(.i32, .i64, Instruction.i64ExtendI32S) } - mutating func visitI64ExtendI32U() throws -> Output { try visitConversion(.i32, .i64, Instruction.i64ExtendI32U) } - mutating func visitI64TruncF32S() throws -> Output { try visitConversion(.f32, .i64, Instruction.i64TruncF32S) } - mutating func visitI64TruncF32U() throws -> Output { try visitConversion(.f32, .i64, Instruction.i64TruncF32U) } - mutating func visitI64TruncF64S() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64TruncF64S) } - mutating func visitI64TruncF64U() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64TruncF64U) } - mutating func visitF32ConvertI32S() throws -> Output { try visitConversion(.i32, .f32, Instruction.f32ConvertI32S) } - mutating func visitF32ConvertI32U() throws -> Output { try visitConversion(.i32, .f32, Instruction.f32ConvertI32U) } - mutating func visitF32ConvertI64S() throws -> Output { try visitConversion(.i64, .f32, Instruction.f32ConvertI64S) } - mutating func visitF32ConvertI64U() throws -> Output { try visitConversion(.i64, .f32, Instruction.f32ConvertI64U) } - mutating func visitF32DemoteF64() throws -> Output { try visitConversion(.f64, .f32, Instruction.f32DemoteF64) } - mutating func visitF64ConvertI32S() throws -> Output { try visitConversion(.i32, .f64, Instruction.f64ConvertI32S) } - mutating func visitF64ConvertI32U() throws -> Output { try visitConversion(.i32, .f64, Instruction.f64ConvertI32U) } - mutating func visitF64ConvertI64S() throws -> Output { try visitConversion(.i64, .f64, Instruction.f64ConvertI64S) } - mutating func visitF64ConvertI64U() throws -> Output { try visitConversion(.i64, .f64, Instruction.f64ConvertI64U) } - mutating func visitF64PromoteF32() throws -> Output { try visitConversion(.f32, .f64, Instruction.f64PromoteF32) } - mutating func visitI32ReinterpretF32() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32ReinterpretF32) } - mutating func visitI64ReinterpretF64() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64ReinterpretF64) } - mutating func visitF32ReinterpretI32() throws -> Output { try visitConversion(.i32, .f32, Instruction.f32ReinterpretI32) } - mutating func visitF64ReinterpretI64() throws -> Output { try visitConversion(.i64, .f64, Instruction.f64ReinterpretI64) } - mutating func visitI32Extend8S() throws -> Output { try visitUnary(.i32, Instruction.i32Extend8S) } - mutating func visitI32Extend16S() throws -> Output { try visitUnary(.i32, Instruction.i32Extend16S) } - mutating func visitI64Extend8S() throws -> Output { try visitUnary(.i64, Instruction.i64Extend8S) } - mutating func visitI64Extend16S() throws -> Output { try visitUnary(.i64, Instruction.i64Extend16S) } - mutating func visitI64Extend32S() throws -> Output { try visitUnary(.i64, Instruction.i64Extend32S) } + mutating func visitUnary(_ unary: WasmParser.Instruction.Unary) throws { + let operand: ValueType + let instruction: (Instruction.UnaryOperand) -> Instruction + switch unary { + case .i32Clz: (operand, instruction) = (.i32, Instruction.i32Clz) + case .i32Ctz: (operand, instruction) = (.i32, Instruction.i32Ctz) + case .i32Popcnt: (operand, instruction) = (.i32, Instruction.i32Popcnt) + case .i64Clz: (operand, instruction) = (.i64, Instruction.i64Clz) + case .i64Ctz: (operand, instruction) = (.i64, Instruction.i64Ctz) + case .i64Popcnt: (operand, instruction) = (.i64, Instruction.i64Popcnt) + case .f32Abs: (operand, instruction) = (.f32, Instruction.f32Abs) + case .f32Neg: (operand, instruction) = (.f32, Instruction.f32Neg) + case .f32Ceil: (operand, instruction) = (.f32, Instruction.f32Ceil) + case .f32Floor: (operand, instruction) = (.f32, Instruction.f32Floor) + case .f32Trunc: (operand, instruction) = (.f32, Instruction.f32Trunc) + case .f32Nearest: (operand, instruction) = (.f32, Instruction.f32Nearest) + case .f32Sqrt: (operand, instruction) = (.f32, Instruction.f32Sqrt) + case .f64Abs: (operand, instruction) = (.f64, Instruction.f64Abs) + case .f64Neg: (operand, instruction) = (.f64, Instruction.f64Neg) + case .f64Ceil: (operand, instruction) = (.f64, Instruction.f64Ceil) + case .f64Floor: (operand, instruction) = (.f64, Instruction.f64Floor) + case .f64Trunc: (operand, instruction) = (.f64, Instruction.f64Trunc) + case .f64Nearest: (operand, instruction) = (.f64, Instruction.f64Nearest) + case .f64Sqrt: (operand, instruction) = (.f64, Instruction.f64Sqrt) + case .i32Extend8S: (operand, instruction) = (.i32, Instruction.i32Extend8S) + case .i32Extend16S: (operand, instruction) = (.i32, Instruction.i32Extend16S) + case .i64Extend8S: (operand, instruction) = (.i64, Instruction.i64Extend8S) + case .i64Extend16S: (operand, instruction) = (.i64, Instruction.i64Extend16S) + case .i64Extend32S: (operand, instruction) = (.i64, Instruction.i64Extend32S) + } + try visitUnary(operand, instruction) + } + mutating func visitConversion(_ conversion: WasmParser.Instruction.Conversion) throws { + let from: ValueType + let to: ValueType + let instruction: (Instruction.UnaryOperand) -> Instruction + switch conversion { + case .i32WrapI64: (from, to, instruction) = (.i64, .i32, Instruction.i32WrapI64) + case .i32TruncF32S: (from, to, instruction) = (.f32, .i32, Instruction.i32TruncF32S) + case .i32TruncF32U: (from, to, instruction) = (.f32, .i32, Instruction.i32TruncF32U) + case .i32TruncF64S: (from, to, instruction) = (.f64, .i32, Instruction.i32TruncF64S) + case .i32TruncF64U: (from, to, instruction) = (.f64, .i32, Instruction.i32TruncF64U) + case .i64ExtendI32S: (from, to, instruction) = (.i32, .i64, Instruction.i64ExtendI32S) + case .i64ExtendI32U: (from, to, instruction) = (.i32, .i64, Instruction.i64ExtendI32U) + case .i64TruncF32S: (from, to, instruction) = (.f32, .i64, Instruction.i64TruncF32S) + case .i64TruncF32U: (from, to, instruction) = (.f32, .i64, Instruction.i64TruncF32U) + case .i64TruncF64S: (from, to, instruction) = (.f64, .i64, Instruction.i64TruncF64S) + case .i64TruncF64U: (from, to, instruction) = (.f64, .i64, Instruction.i64TruncF64U) + case .f32ConvertI32S: (from, to, instruction) = (.i32, .f32, Instruction.f32ConvertI32S) + case .f32ConvertI32U: (from, to, instruction) = (.i32, .f32, Instruction.f32ConvertI32U) + case .f32ConvertI64S: (from, to, instruction) = (.i64, .f32, Instruction.f32ConvertI64S) + case .f32ConvertI64U: (from, to, instruction) = (.i64, .f32, Instruction.f32ConvertI64U) + case .f32DemoteF64: (from, to, instruction) = (.f64, .f32, Instruction.f32DemoteF64) + case .f64ConvertI32S: (from, to, instruction) = (.i32, .f64, Instruction.f64ConvertI32S) + case .f64ConvertI32U: (from, to, instruction) = (.i32, .f64, Instruction.f64ConvertI32U) + case .f64ConvertI64S: (from, to, instruction) = (.i64, .f64, Instruction.f64ConvertI64S) + case .f64ConvertI64U: (from, to, instruction) = (.i64, .f64, Instruction.f64ConvertI64U) + case .f64PromoteF32: (from, to, instruction) = (.f32, .f64, Instruction.f64PromoteF32) + case .i32ReinterpretF32: (from, to, instruction) = (.f32, .i32, Instruction.i32ReinterpretF32) + case .i64ReinterpretF64: (from, to, instruction) = (.f64, .i64, Instruction.i64ReinterpretF64) + case .f32ReinterpretI32: (from, to, instruction) = (.i32, .f32, Instruction.f32ReinterpretI32) + case .f64ReinterpretI64: (from, to, instruction) = (.i64, .f64, Instruction.f64ReinterpretI64) + case .i32TruncSatF32S: (from, to, instruction) = (.f32, .i32, Instruction.i32TruncSatF32S) + case .i32TruncSatF32U: (from, to, instruction) = (.f32, .i32, Instruction.i32TruncSatF32U) + case .i32TruncSatF64S: (from, to, instruction) = (.f64, .i32, Instruction.i32TruncSatF64S) + case .i32TruncSatF64U: (from, to, instruction) = (.f64, .i32, Instruction.i32TruncSatF64U) + case .i64TruncSatF32S: (from, to, instruction) = (.f32, .i64, Instruction.i64TruncSatF32S) + case .i64TruncSatF32U: (from, to, instruction) = (.f32, .i64, Instruction.i64TruncSatF32U) + case .i64TruncSatF64S: (from, to, instruction) = (.f64, .i64, Instruction.i64TruncSatF64S) + case .i64TruncSatF64U: (from, to, instruction) = (.f64, .i64, Instruction.i64TruncSatF64U) + } + try visitConversion(from, to, instruction) + } + mutating func visitMemoryInit(dataIndex: UInt32) throws -> Output { - try self.module.validateDataSegment(dataIndex) + try self.validator.validateDataSegment(dataIndex) let addressType = try module.addressType(memoryIndex: 0) try pop3Emit((.i32, .i32, addressType)) { values, stack in let (size, sourceOffset, destOffset) = values @@ -1992,7 +2112,7 @@ struct InstructionTranslator: InstructionVisitor { } } mutating func visitDataDrop(dataIndex: UInt32) throws -> Output { - try self.module.validateDataSegment(dataIndex) + try self.validator.validateDataSegment(dataIndex) emit(.memoryDataDrop(Instruction.MemoryDataDropOperand(segmentIndex: dataIndex))) } mutating func visitMemoryCopy(dstMem: UInt32, srcMem: UInt32) throws -> Output { @@ -2140,14 +2260,6 @@ struct InstructionTranslator: InstructionVisitor { return .tableSize(Instruction.TableSizeOperand(tableIndex: table, result: LVReg(result))) } } - mutating func visitI32TruncSatF32S() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32TruncSatF32S) } - mutating func visitI32TruncSatF32U() throws -> Output { try visitConversion(.f32, .i32, Instruction.i32TruncSatF32U) } - mutating func visitI32TruncSatF64S() throws -> Output { try visitConversion(.f64, .i32, Instruction.i32TruncSatF64S) } - mutating func visitI32TruncSatF64U() throws -> Output { try visitConversion(.f64, .i32, Instruction.i32TruncSatF64U) } - mutating func visitI64TruncSatF32S() throws -> Output { try visitConversion(.f32, .i64, Instruction.i64TruncSatF32S) } - mutating func visitI64TruncSatF32U() throws -> Output { try visitConversion(.f32, .i64, Instruction.i64TruncSatF32U) } - mutating func visitI64TruncSatF64S() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64TruncSatF64S) } - mutating func visitI64TruncSatF64U() throws -> Output { try visitConversion(.f64, .i64, Instruction.i64TruncSatF64U) } } struct TranslationError: Error, CustomStringConvertible { @@ -2168,7 +2280,7 @@ extension FunctionType { case let .funcType(typeIndex): let typeIndex = Int(typeIndex) guard typeIndex < typeSection.count else { - throw WasmParserError.invalidTypeSectionReference + throw ValidationError(.indexOutOfBounds("type", typeIndex, max: typeSection.count)) } let funcType = typeSection[typeIndex] self.init( diff --git a/Sources/WasmKit/Validator.swift b/Sources/WasmKit/Validator.swift index d6a0cc18..a313901f 100644 --- a/Sources/WasmKit/Validator.swift +++ b/Sources/WasmKit/Validator.swift @@ -1,35 +1,208 @@ import WasmParser +/// Represents an error that occurs during validation struct ValidationError: Error, CustomStringConvertible { - let message: String + /// Represents a validation error message. + struct Message { + let text: String + + init(_ text: String) { + self.text = text + } + } + + /// The error message. + let message: Message + + /// The offset in the input WebAssembly module binary where the error occurred. + /// NOTE: This field is set when the error is temporarily caught by the ``InstructionTranslator``. var offset: Int? + /// The error description. var description: String { if let offset = offset { - return "\(message) at offset 0x\(String(offset, radix: 16))" + return "\(message.text) at offset 0x\(String(offset, radix: 16))" } else { - return message + return message.text } } - init(_ message: String) { + init(_ message: Message) { self.message = message } } +extension ValidationError.Message { + static func invalidMemArgAlignment(memarg: MemArg, naturalAlignment: Int) -> Self { + Self("alignment 2**\(memarg.align) is out of limit \(naturalAlignment)") + } + + static var globalSetConstant: Self { + Self("cannot set a constant global") + } + + static var multipleMemoriesNotPermitted: Self { + Self("multiple memories are not permitted") + } + + static func startFunctionInvalidParameters() -> Self { + Self("start function must have no parameters and no results") + } + + static var memory64FeatureRequired: Self { + Self("memory64 feature is required for 64-bit memories") + } + + static func sizeMinimumExceeded(max: UInt64) -> Self { + Self("size minimum must not be greater than \(max)") + } + + static func sizeMaximumExceeded(max: UInt64) -> Self { + Self("size maximum must not be greater than \(max)") + } + + static var referenceTypesFeatureRequiredForSharedMemories: Self { + Self("reference-types feature is required for shared memories") + } + + static var referenceTypesFeatureRequiredForNonFuncrefTables: Self { + Self("reference-types feature is required for non-funcref tables") + } + + static var dataCountSectionRequired: Self { + Self("data count section is required but not found") + } + + static func indexOutOfBounds(_ entity: StaticString, _ index: Index, max: Max) -> Self { + Self("\(entity) index out of bounds: \(index) (max: \(max))") + } + + static func tableElementTypeMismatch(tableType: String, elementType: String) -> Self { + Self("table element type mismatch: \(tableType) != \(elementType)") + } + + static func expectTypeButGot(expected: String, got: String) -> Self { + Self("expect \(expected) but got \(got)") + } + + static var sizeMinimumMustNotExceedMaximum: Self { + Self("size minimum must not be greater than maximum") + } + + static func functionIndexNotDeclared(index: FunctionIndex) -> Self { + Self("function index \(index) is not declared but referenced as a function reference") + } + + static func duplicateExportName(name: String) -> Self { + Self("duplicate export name: \(name)") + } + + static func elementSegmentTypeMismatch( + elementType: ReferenceType, + tableElementType: ReferenceType + ) -> Self { + Self("element segment type \(elementType) does not match table element type \(tableElementType)") + } + + static var controlStackEmpty: Self { + Self("control stack is empty. Instruction cannot be appeared after \"end\" of function") + } + + static func relativeDepthOutOfRange(relativeDepth: UInt32) -> Self { + Self("relative depth \(relativeDepth) is out of range") + } + + static var expectedIfControlFrame: Self { + Self("expected `if` control frame on top of the stack for `else`") + } + + static var valuesRemainingAtEndOfBlock: Self { + Self("values remaining on stack at end of block") + } + + static func parameterResultTypeMismatch(blockType: FunctionType) -> Self { + Self("expected the same parameter and result types for `if` block but got \(blockType)") + } + + static func stackHeightUnderflow(available: Int, required: Int) -> Self { + Self("stack height underflow: available \(available), required \(required)") + } + + static func expectedTypeOnStack(expected: ValueType, actual: ValueType) -> Self { + Self("expected \(expected) on the stack top but got \(actual)") + } + + static func expectedTypeOnStackButEmpty(expected: ValueType?) -> Self { + let typeHint = expected.map(String.init(describing:)) ?? "a value" + return Self("expected \(typeHint) on the stack top but it's empty") + } + + static func expectedMoreEndInstructions(count: Int) -> Self { + Self("expect \(count) more `end` instructions") + } + + static func expectedSameCopyTypes( + frameCopyTypes: [ValueType], + defaultFrameCopyTypes: [ValueType] + ) -> Self { + Self("expected the same copy types for all branches in `br_table` but got \(frameCopyTypes) and \(defaultFrameCopyTypes)") + } + + static var cannotSelectOnReferenceTypes: Self { + Self("cannot `select` on reference types") + } + + static func typeMismatchOnSelect(expected: ValueType, actual: ValueType) -> Self { + Self("type mismatch on `select`. Expected \(expected) and \(actual) to be same") + } + + static var unexpectedGlobalValueType: Self { + Self("unexpected global value type for element initializer expression") + } + + static func unexpectedElementInitializer(expression: String) -> Self { + Self("unexpected element initializer expression: \(expression)") + } + + static func unexpectedOffsetInitializer(expected: ValueType, got: Value) -> Self { + Self("expect \(expected) offset but got \(got)") + } + + static var expectedEndAtOffsetExpression: Self { + Self("expect `end` at the end of offset expression") + } + + static func illegalConstExpressionInstruction(_ constInst: WasmParser.Instruction) -> Self { + Self("illegal const expression instruction: \(constInst)") + } + + static func inconsistentFunctionAndCodeLength(functionCount: Int, codeCount: Int) -> Self { + Self("Inconsistent function and code length: \(functionCount) vs \(codeCount)") + } + + static func inconsistentDataCountAndDataSectionLength(dataCount: UInt32, dataSection: Int) -> Self { + Self("Inconsistent data count and data section length: \(dataCount) vs \(dataSection)") + } + + static func typeMismatchOnReturnCall(expected: [ValueType], actual: [ValueType]) -> Self { + Self("return signatures have inconsistent types: expected \(expected) but got \(actual)") + } +} + +/// Validates instructions within a given context. struct InstructionValidator { let context: Context func validateMemArg(_ memarg: MemArg, naturalAlignment: Int) throws { if memarg.align > naturalAlignment { - throw ValidationError("Alignment 2**\(memarg.align) is out of limit \(naturalAlignment)") + throw ValidationError(.invalidMemArgAlignment(memarg: memarg, naturalAlignment: naturalAlignment)) } } func validateGlobalSet(_ type: GlobalType) throws { switch type.mutability { case .constant: - throw ValidationError("Cannot set a constant global") + throw ValidationError(.globalSetConstant) case .variable: break } @@ -39,7 +212,7 @@ struct InstructionValidator { let tableType = try context.tableType(table) let elementType = try context.elementType(elemIndex) guard tableType.elementType == elementType else { - throw ValidationError("Table element type mismatch in table.init: \(tableType.elementType) != \(elementType)") + throw ValidationError(.tableElementTypeMismatch(tableType: "\(tableType.elementType)", elementType: "\(elementType)")) } } @@ -47,15 +220,31 @@ struct InstructionValidator { let tableType1 = try context.tableType(source) let tableType2 = try context.tableType(dest) guard tableType1.elementType == tableType2.elementType else { - throw ValidationError("Table element type mismatch in table.copy: \(tableType1.elementType) != \(tableType2.elementType)") + throw ValidationError(.tableElementTypeMismatch(tableType: "\(tableType1.elementType)", elementType: "\(tableType2.elementType)")) } } func validateRefFunc(functionIndex: UInt32) throws { try context.validateFunctionIndex(functionIndex) } + + func validateDataSegment(_ dataIndex: DataIndex) throws { + guard let dataCount = context.dataCount else { + throw ValidationError(.dataCountSectionRequired) + } + guard dataIndex < dataCount else { + throw ValidationError(.indexOutOfBounds("data", dataIndex, max: dataCount)) + } + } + + func validateReturnCallLike(calleeType: FunctionType, callerType: FunctionType) throws { + guard calleeType.results == callerType.results else { + throw ValidationError(.typeMismatchOnReturnCall(expected: callerType.results, actual: calleeType.results)) + } + } } +/// Validates a WebAssembly module. struct ModuleValidator { let module: Module init(module: Module) { @@ -64,7 +253,7 @@ struct ModuleValidator { func validate() throws { if module.memoryTypes.count > 1 { - throw ValidationError("Multiple memories are not permitted") + throw ValidationError(.multipleMemoriesNotPermitted) } for memoryType in module.memoryTypes { try Self.checkMemoryType(memoryType, features: module.features) @@ -79,7 +268,7 @@ struct ModuleValidator { if let startFunction = module.start { let type = try module.resolveFunctionType(startFunction) guard type.parameters.isEmpty, type.results.isEmpty else { - throw ValidationError("Start function must have no parameters and no results") + throw ValidationError(.startFunctionInvalidParameters()) } } } @@ -89,70 +278,72 @@ struct ModuleValidator { if type.isMemory64 { guard features.contains(.memory64) else { - throw ValidationError("memory64 feature is required for 64-bit memories") + throw ValidationError(.memory64FeatureRequired) } } let hardMax = MemoryEntity.maxPageCount(isMemory64: type.isMemory64) if type.min > hardMax { - throw ValidationError("size minimum must not be greater than \(hardMax)") + throw ValidationError(.sizeMinimumExceeded(max: hardMax)) } if let max = type.max, max > hardMax { - throw ValidationError("size maximum must not be greater than \(hardMax)") + throw ValidationError(.sizeMaximumExceeded(max: hardMax)) } if type.shared { guard features.contains(.threads) else { - throw ValidationError("reference-types feature is required for shared memories") + throw ValidationError(.referenceTypesFeatureRequiredForSharedMemories) } } } static func checkTableType(_ type: TableType, features: WasmFeatureSet) throws { if type.elementType != .funcRef, !features.contains(.referenceTypes) { - throw ValidationError("reference-types feature is required for non-funcref tables") + throw ValidationError(.referenceTypesFeatureRequiredForNonFuncrefTables) } try checkLimit(type.limits) if type.limits.isMemory64 { guard features.contains(.memory64) else { - throw ValidationError("memory64 feature is required for 64-bit tables") + throw ValidationError(.memory64FeatureRequired) } } let hardMax = TableEntity.maxSize(isMemory64: type.limits.isMemory64) if type.limits.min > hardMax { - throw ValidationError("size minimum must not be greater than \(hardMax)") + throw ValidationError(.sizeMinimumExceeded(max: hardMax)) } if let max = type.limits.max, max > hardMax { - throw ValidationError("size maximum must not be greater than \(hardMax)") + throw ValidationError(.sizeMaximumExceeded(max: hardMax)) } } private static func checkLimit(_ limit: Limits) throws { guard let max = limit.max else { return } if limit.min > max { - throw ValidationError("size minimum must not be greater than maximum") + throw ValidationError(.sizeMinimumMustNotExceedMaximum) } } } extension WasmTypes.Reference { + /// Checks if the reference type matches the expected type. func checkType(_ type: WasmTypes.ReferenceType) throws { switch (self, type) { case (.function, .funcRef): return case (.extern, .externRef): return default: - throw ValidationError("Expect \(type) but got \(self)") + throw ValidationError(.expectTypeButGot(expected: "\(type)", got: "\(self)")) } } } extension Value { + /// Checks if the value type matches the expected type. func checkType(_ type: WasmTypes.ValueType) throws { switch (self, type) { case (.i32, .i32): return @@ -162,7 +353,7 @@ extension Value { case (.ref(let ref), .ref(let refType)): try ref.checkType(refType) default: - throw ValidationError("Expect \(type) but got \(self)") + throw ValidationError(.expectTypeButGot(expected: "\(type)", got: "\(self)")) } } } diff --git a/Sources/WasmKitWASI/WASIBridgeToHost+WasmKit.swift b/Sources/WasmKitWASI/WASIBridgeToHost+WasmKit.swift index 67de2d90..f5470276 100644 --- a/Sources/WasmKitWASI/WASIBridgeToHost+WasmKit.swift +++ b/Sources/WasmKitWASI/WASIBridgeToHost+WasmKit.swift @@ -4,6 +4,12 @@ import WasmKit public typealias WASIBridgeToHost = WASI.WASIBridgeToHost extension WASIBridgeToHost { + + /// Register the WASI implementation to the given `imports`. + /// + /// - Parameters: + /// - imports: The imports scope to register the WASI implementation. + /// - store: The store to create the host functions. public func link(to imports: inout Imports, store: Store) { for (moduleName, module) in wasiHostModules { for (name, function) in module.functions { @@ -35,6 +41,13 @@ extension WASIBridgeToHost { } } + /// Start a WASI application as a `command` instance. + /// + /// See + /// for more information about the WASI Preview 1 Application ABI. + /// + /// - Parameter instance: The WASI application instance. + /// - Returns: The exit code returned by the WASI application. public func start(_ instance: Instance) throws -> UInt32 { do { guard let start = instance.exports[function: "_start"] else { @@ -47,6 +60,19 @@ extension WASIBridgeToHost { return 0 } + /// Start a WASI application as a `reactor` instance. + /// + /// See + /// for more information about the WASI Preview 1 Application ABI. + /// + /// - Parameter instance: The WASI application instance. + public func initialize(_ instance: Instance) throws { + if let initialize = instance.exports[function: "_initialize"] { + // Call the optional `_initialize` function. + _ = try initialize() + } + } + @available(*, deprecated, message: "Use `Engine`-based API instead") public func start(_ instance: Instance, runtime: Runtime) throws -> UInt32 { return try start(instance) diff --git a/Sources/WasmParser/BinaryInstructionDecoder.swift b/Sources/WasmParser/BinaryInstructionDecoder.swift new file mode 100644 index 00000000..a89443c2 --- /dev/null +++ b/Sources/WasmParser/BinaryInstructionDecoder.swift @@ -0,0 +1,571 @@ +// swift-format-ignore-file +//// Automatically generated by Utilities/Sources/WasmGen.swift +//// DO NOT EDIT DIRECTLY + +import WasmTypes + +@usableFromInline +protocol BinaryInstructionDecoder { + /// Claim the next byte to be decoded + @inlinable func claimNextByte() throws -> UInt8 + /// Visit unknown instruction + @inlinable func visitUnknown(_ opcode: [UInt8]) throws + /// Decode `block` immediates + @inlinable mutating func visitBlock() throws -> BlockType + /// Decode `loop` immediates + @inlinable mutating func visitLoop() throws -> BlockType + /// Decode `if` immediates + @inlinable mutating func visitIf() throws -> BlockType + /// Decode `br` immediates + @inlinable mutating func visitBr() throws -> UInt32 + /// Decode `br_if` immediates + @inlinable mutating func visitBrIf() throws -> UInt32 + /// Decode `br_table` immediates + @inlinable mutating func visitBrTable() throws -> BrTable + /// Decode `call` immediates + @inlinable mutating func visitCall() throws -> UInt32 + /// Decode `call_indirect` immediates + @inlinable mutating func visitCallIndirect() throws -> (typeIndex: UInt32, tableIndex: UInt32) + /// Decode `return_call` immediates + @inlinable mutating func visitReturnCall() throws -> UInt32 + /// Decode `return_call_indirect` immediates + @inlinable mutating func visitReturnCallIndirect() throws -> (typeIndex: UInt32, tableIndex: UInt32) + /// Decode `typedSelect` immediates + @inlinable mutating func visitTypedSelect() throws -> ValueType + /// Decode `local.get` immediates + @inlinable mutating func visitLocalGet() throws -> UInt32 + /// Decode `local.set` immediates + @inlinable mutating func visitLocalSet() throws -> UInt32 + /// Decode `local.tee` immediates + @inlinable mutating func visitLocalTee() throws -> UInt32 + /// Decode `global.get` immediates + @inlinable mutating func visitGlobalGet() throws -> UInt32 + /// Decode `global.set` immediates + @inlinable mutating func visitGlobalSet() throws -> UInt32 + /// Decode `load` category immediates + @inlinable mutating func visitLoad(_: Instruction.Load) throws -> MemArg + /// Decode `store` category immediates + @inlinable mutating func visitStore(_: Instruction.Store) throws -> MemArg + /// Decode `memory.size` immediates + @inlinable mutating func visitMemorySize() throws -> UInt32 + /// Decode `memory.grow` immediates + @inlinable mutating func visitMemoryGrow() throws -> UInt32 + /// Decode `i32.const` immediates + @inlinable mutating func visitI32Const() throws -> Int32 + /// Decode `i64.const` immediates + @inlinable mutating func visitI64Const() throws -> Int64 + /// Decode `f32.const` immediates + @inlinable mutating func visitF32Const() throws -> IEEE754.Float32 + /// Decode `f64.const` immediates + @inlinable mutating func visitF64Const() throws -> IEEE754.Float64 + /// Decode `ref.null` immediates + @inlinable mutating func visitRefNull() throws -> ReferenceType + /// Decode `ref.func` immediates + @inlinable mutating func visitRefFunc() throws -> UInt32 + /// Decode `memory.init` immediates + @inlinable mutating func visitMemoryInit() throws -> UInt32 + /// Decode `data.drop` immediates + @inlinable mutating func visitDataDrop() throws -> UInt32 + /// Decode `memory.copy` immediates + @inlinable mutating func visitMemoryCopy() throws -> (dstMem: UInt32, srcMem: UInt32) + /// Decode `memory.fill` immediates + @inlinable mutating func visitMemoryFill() throws -> UInt32 + /// Decode `table.init` immediates + @inlinable mutating func visitTableInit() throws -> (elemIndex: UInt32, table: UInt32) + /// Decode `elem.drop` immediates + @inlinable mutating func visitElemDrop() throws -> UInt32 + /// Decode `table.copy` immediates + @inlinable mutating func visitTableCopy() throws -> (dstTable: UInt32, srcTable: UInt32) + /// Decode `table.fill` immediates + @inlinable mutating func visitTableFill() throws -> UInt32 + /// Decode `table.get` immediates + @inlinable mutating func visitTableGet() throws -> UInt32 + /// Decode `table.set` immediates + @inlinable mutating func visitTableSet() throws -> UInt32 + /// Decode `table.grow` immediates + @inlinable mutating func visitTableGrow() throws -> UInt32 + /// Decode `table.size` immediates + @inlinable mutating func visitTableSize() throws -> UInt32 +} +@inlinable +func parseBinaryInstruction(visitor: inout V, decoder: inout D) throws -> Bool { + let opcode0 = try decoder.claimNextByte() + switch opcode0 { + case 0x00: + try visitor.visitUnreachable() + case 0x01: + try visitor.visitNop() + case 0x02: + let (blockType) = try decoder.visitBlock() + try visitor.visitBlock(blockType: blockType) + case 0x03: + let (blockType) = try decoder.visitLoop() + try visitor.visitLoop(blockType: blockType) + case 0x04: + let (blockType) = try decoder.visitIf() + try visitor.visitIf(blockType: blockType) + case 0x05: + try visitor.visitElse() + case 0x0B: + try visitor.visitEnd() + return true + case 0x0C: + let (relativeDepth) = try decoder.visitBr() + try visitor.visitBr(relativeDepth: relativeDepth) + case 0x0D: + let (relativeDepth) = try decoder.visitBrIf() + try visitor.visitBrIf(relativeDepth: relativeDepth) + case 0x0E: + let (targets) = try decoder.visitBrTable() + try visitor.visitBrTable(targets: targets) + case 0x0F: + try visitor.visitReturn() + case 0x10: + let (functionIndex) = try decoder.visitCall() + try visitor.visitCall(functionIndex: functionIndex) + case 0x11: + let (typeIndex, tableIndex) = try decoder.visitCallIndirect() + try visitor.visitCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex) + case 0x12: + let (functionIndex) = try decoder.visitReturnCall() + try visitor.visitReturnCall(functionIndex: functionIndex) + case 0x13: + let (typeIndex, tableIndex) = try decoder.visitReturnCallIndirect() + try visitor.visitReturnCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex) + case 0x1A: + try visitor.visitDrop() + case 0x1B: + try visitor.visitSelect() + case 0x1C: + let (type) = try decoder.visitTypedSelect() + try visitor.visitTypedSelect(type: type) + case 0x20: + let (localIndex) = try decoder.visitLocalGet() + try visitor.visitLocalGet(localIndex: localIndex) + case 0x21: + let (localIndex) = try decoder.visitLocalSet() + try visitor.visitLocalSet(localIndex: localIndex) + case 0x22: + let (localIndex) = try decoder.visitLocalTee() + try visitor.visitLocalTee(localIndex: localIndex) + case 0x23: + let (globalIndex) = try decoder.visitGlobalGet() + try visitor.visitGlobalGet(globalIndex: globalIndex) + case 0x24: + let (globalIndex) = try decoder.visitGlobalSet() + try visitor.visitGlobalSet(globalIndex: globalIndex) + case 0x25: + let (table) = try decoder.visitTableGet() + try visitor.visitTableGet(table: table) + case 0x26: + let (table) = try decoder.visitTableSet() + try visitor.visitTableSet(table: table) + case 0x28: + let (memarg) = try decoder.visitLoad(.i32Load) + try visitor.visitLoad(.i32Load, memarg: memarg) + case 0x29: + let (memarg) = try decoder.visitLoad(.i64Load) + try visitor.visitLoad(.i64Load, memarg: memarg) + case 0x2A: + let (memarg) = try decoder.visitLoad(.f32Load) + try visitor.visitLoad(.f32Load, memarg: memarg) + case 0x2B: + let (memarg) = try decoder.visitLoad(.f64Load) + try visitor.visitLoad(.f64Load, memarg: memarg) + case 0x2C: + let (memarg) = try decoder.visitLoad(.i32Load8S) + try visitor.visitLoad(.i32Load8S, memarg: memarg) + case 0x2D: + let (memarg) = try decoder.visitLoad(.i32Load8U) + try visitor.visitLoad(.i32Load8U, memarg: memarg) + case 0x2E: + let (memarg) = try decoder.visitLoad(.i32Load16S) + try visitor.visitLoad(.i32Load16S, memarg: memarg) + case 0x2F: + let (memarg) = try decoder.visitLoad(.i32Load16U) + try visitor.visitLoad(.i32Load16U, memarg: memarg) + case 0x30: + let (memarg) = try decoder.visitLoad(.i64Load8S) + try visitor.visitLoad(.i64Load8S, memarg: memarg) + case 0x31: + let (memarg) = try decoder.visitLoad(.i64Load8U) + try visitor.visitLoad(.i64Load8U, memarg: memarg) + case 0x32: + let (memarg) = try decoder.visitLoad(.i64Load16S) + try visitor.visitLoad(.i64Load16S, memarg: memarg) + case 0x33: + let (memarg) = try decoder.visitLoad(.i64Load16U) + try visitor.visitLoad(.i64Load16U, memarg: memarg) + case 0x34: + let (memarg) = try decoder.visitLoad(.i64Load32S) + try visitor.visitLoad(.i64Load32S, memarg: memarg) + case 0x35: + let (memarg) = try decoder.visitLoad(.i64Load32U) + try visitor.visitLoad(.i64Load32U, memarg: memarg) + case 0x36: + let (memarg) = try decoder.visitStore(.i32Store) + try visitor.visitStore(.i32Store, memarg: memarg) + case 0x37: + let (memarg) = try decoder.visitStore(.i64Store) + try visitor.visitStore(.i64Store, memarg: memarg) + case 0x38: + let (memarg) = try decoder.visitStore(.f32Store) + try visitor.visitStore(.f32Store, memarg: memarg) + case 0x39: + let (memarg) = try decoder.visitStore(.f64Store) + try visitor.visitStore(.f64Store, memarg: memarg) + case 0x3A: + let (memarg) = try decoder.visitStore(.i32Store8) + try visitor.visitStore(.i32Store8, memarg: memarg) + case 0x3B: + let (memarg) = try decoder.visitStore(.i32Store16) + try visitor.visitStore(.i32Store16, memarg: memarg) + case 0x3C: + let (memarg) = try decoder.visitStore(.i64Store8) + try visitor.visitStore(.i64Store8, memarg: memarg) + case 0x3D: + let (memarg) = try decoder.visitStore(.i64Store16) + try visitor.visitStore(.i64Store16, memarg: memarg) + case 0x3E: + let (memarg) = try decoder.visitStore(.i64Store32) + try visitor.visitStore(.i64Store32, memarg: memarg) + case 0x3F: + let (memory) = try decoder.visitMemorySize() + try visitor.visitMemorySize(memory: memory) + case 0x40: + let (memory) = try decoder.visitMemoryGrow() + try visitor.visitMemoryGrow(memory: memory) + case 0x41: + let (value) = try decoder.visitI32Const() + try visitor.visitI32Const(value: value) + case 0x42: + let (value) = try decoder.visitI64Const() + try visitor.visitI64Const(value: value) + case 0x43: + let (value) = try decoder.visitF32Const() + try visitor.visitF32Const(value: value) + case 0x44: + let (value) = try decoder.visitF64Const() + try visitor.visitF64Const(value: value) + case 0x45: + try visitor.visitI32Eqz() + case 0x46: + try visitor.visitCmp(.i32Eq) + case 0x47: + try visitor.visitCmp(.i32Ne) + case 0x48: + try visitor.visitCmp(.i32LtS) + case 0x49: + try visitor.visitCmp(.i32LtU) + case 0x4A: + try visitor.visitCmp(.i32GtS) + case 0x4B: + try visitor.visitCmp(.i32GtU) + case 0x4C: + try visitor.visitCmp(.i32LeS) + case 0x4D: + try visitor.visitCmp(.i32LeU) + case 0x4E: + try visitor.visitCmp(.i32GeS) + case 0x4F: + try visitor.visitCmp(.i32GeU) + case 0x50: + try visitor.visitI64Eqz() + case 0x51: + try visitor.visitCmp(.i64Eq) + case 0x52: + try visitor.visitCmp(.i64Ne) + case 0x53: + try visitor.visitCmp(.i64LtS) + case 0x54: + try visitor.visitCmp(.i64LtU) + case 0x55: + try visitor.visitCmp(.i64GtS) + case 0x56: + try visitor.visitCmp(.i64GtU) + case 0x57: + try visitor.visitCmp(.i64LeS) + case 0x58: + try visitor.visitCmp(.i64LeU) + case 0x59: + try visitor.visitCmp(.i64GeS) + case 0x5A: + try visitor.visitCmp(.i64GeU) + case 0x5B: + try visitor.visitCmp(.f32Eq) + case 0x5C: + try visitor.visitCmp(.f32Ne) + case 0x5D: + try visitor.visitCmp(.f32Lt) + case 0x5E: + try visitor.visitCmp(.f32Gt) + case 0x5F: + try visitor.visitCmp(.f32Le) + case 0x60: + try visitor.visitCmp(.f32Ge) + case 0x61: + try visitor.visitCmp(.f64Eq) + case 0x62: + try visitor.visitCmp(.f64Ne) + case 0x63: + try visitor.visitCmp(.f64Lt) + case 0x64: + try visitor.visitCmp(.f64Gt) + case 0x65: + try visitor.visitCmp(.f64Le) + case 0x66: + try visitor.visitCmp(.f64Ge) + case 0x67: + try visitor.visitUnary(.i32Clz) + case 0x68: + try visitor.visitUnary(.i32Ctz) + case 0x69: + try visitor.visitUnary(.i32Popcnt) + case 0x6A: + try visitor.visitBinary(.i32Add) + case 0x6B: + try visitor.visitBinary(.i32Sub) + case 0x6C: + try visitor.visitBinary(.i32Mul) + case 0x6D: + try visitor.visitBinary(.i32DivS) + case 0x6E: + try visitor.visitBinary(.i32DivU) + case 0x6F: + try visitor.visitBinary(.i32RemS) + case 0x70: + try visitor.visitBinary(.i32RemU) + case 0x71: + try visitor.visitBinary(.i32And) + case 0x72: + try visitor.visitBinary(.i32Or) + case 0x73: + try visitor.visitBinary(.i32Xor) + case 0x74: + try visitor.visitBinary(.i32Shl) + case 0x75: + try visitor.visitBinary(.i32ShrS) + case 0x76: + try visitor.visitBinary(.i32ShrU) + case 0x77: + try visitor.visitBinary(.i32Rotl) + case 0x78: + try visitor.visitBinary(.i32Rotr) + case 0x79: + try visitor.visitUnary(.i64Clz) + case 0x7A: + try visitor.visitUnary(.i64Ctz) + case 0x7B: + try visitor.visitUnary(.i64Popcnt) + case 0x7C: + try visitor.visitBinary(.i64Add) + case 0x7D: + try visitor.visitBinary(.i64Sub) + case 0x7E: + try visitor.visitBinary(.i64Mul) + case 0x7F: + try visitor.visitBinary(.i64DivS) + case 0x80: + try visitor.visitBinary(.i64DivU) + case 0x81: + try visitor.visitBinary(.i64RemS) + case 0x82: + try visitor.visitBinary(.i64RemU) + case 0x83: + try visitor.visitBinary(.i64And) + case 0x84: + try visitor.visitBinary(.i64Or) + case 0x85: + try visitor.visitBinary(.i64Xor) + case 0x86: + try visitor.visitBinary(.i64Shl) + case 0x87: + try visitor.visitBinary(.i64ShrS) + case 0x88: + try visitor.visitBinary(.i64ShrU) + case 0x89: + try visitor.visitBinary(.i64Rotl) + case 0x8A: + try visitor.visitBinary(.i64Rotr) + case 0x8B: + try visitor.visitUnary(.f32Abs) + case 0x8C: + try visitor.visitUnary(.f32Neg) + case 0x8D: + try visitor.visitUnary(.f32Ceil) + case 0x8E: + try visitor.visitUnary(.f32Floor) + case 0x8F: + try visitor.visitUnary(.f32Trunc) + case 0x90: + try visitor.visitUnary(.f32Nearest) + case 0x91: + try visitor.visitUnary(.f32Sqrt) + case 0x92: + try visitor.visitBinary(.f32Add) + case 0x93: + try visitor.visitBinary(.f32Sub) + case 0x94: + try visitor.visitBinary(.f32Mul) + case 0x95: + try visitor.visitBinary(.f32Div) + case 0x96: + try visitor.visitBinary(.f32Min) + case 0x97: + try visitor.visitBinary(.f32Max) + case 0x98: + try visitor.visitBinary(.f32Copysign) + case 0x99: + try visitor.visitUnary(.f64Abs) + case 0x9A: + try visitor.visitUnary(.f64Neg) + case 0x9B: + try visitor.visitUnary(.f64Ceil) + case 0x9C: + try visitor.visitUnary(.f64Floor) + case 0x9D: + try visitor.visitUnary(.f64Trunc) + case 0x9E: + try visitor.visitUnary(.f64Nearest) + case 0x9F: + try visitor.visitUnary(.f64Sqrt) + case 0xA0: + try visitor.visitBinary(.f64Add) + case 0xA1: + try visitor.visitBinary(.f64Sub) + case 0xA2: + try visitor.visitBinary(.f64Mul) + case 0xA3: + try visitor.visitBinary(.f64Div) + case 0xA4: + try visitor.visitBinary(.f64Min) + case 0xA5: + try visitor.visitBinary(.f64Max) + case 0xA6: + try visitor.visitBinary(.f64Copysign) + case 0xA7: + try visitor.visitConversion(.i32WrapI64) + case 0xA8: + try visitor.visitConversion(.i32TruncF32S) + case 0xA9: + try visitor.visitConversion(.i32TruncF32U) + case 0xAA: + try visitor.visitConversion(.i32TruncF64S) + case 0xAB: + try visitor.visitConversion(.i32TruncF64U) + case 0xAC: + try visitor.visitConversion(.i64ExtendI32S) + case 0xAD: + try visitor.visitConversion(.i64ExtendI32U) + case 0xAE: + try visitor.visitConversion(.i64TruncF32S) + case 0xAF: + try visitor.visitConversion(.i64TruncF32U) + case 0xB0: + try visitor.visitConversion(.i64TruncF64S) + case 0xB1: + try visitor.visitConversion(.i64TruncF64U) + case 0xB2: + try visitor.visitConversion(.f32ConvertI32S) + case 0xB3: + try visitor.visitConversion(.f32ConvertI32U) + case 0xB4: + try visitor.visitConversion(.f32ConvertI64S) + case 0xB5: + try visitor.visitConversion(.f32ConvertI64U) + case 0xB6: + try visitor.visitConversion(.f32DemoteF64) + case 0xB7: + try visitor.visitConversion(.f64ConvertI32S) + case 0xB8: + try visitor.visitConversion(.f64ConvertI32U) + case 0xB9: + try visitor.visitConversion(.f64ConvertI64S) + case 0xBA: + try visitor.visitConversion(.f64ConvertI64U) + case 0xBB: + try visitor.visitConversion(.f64PromoteF32) + case 0xBC: + try visitor.visitConversion(.i32ReinterpretF32) + case 0xBD: + try visitor.visitConversion(.i64ReinterpretF64) + case 0xBE: + try visitor.visitConversion(.f32ReinterpretI32) + case 0xBF: + try visitor.visitConversion(.f64ReinterpretI64) + case 0xC0: + try visitor.visitUnary(.i32Extend8S) + case 0xC1: + try visitor.visitUnary(.i32Extend16S) + case 0xC2: + try visitor.visitUnary(.i64Extend8S) + case 0xC3: + try visitor.visitUnary(.i64Extend16S) + case 0xC4: + try visitor.visitUnary(.i64Extend32S) + case 0xD0: + let (type) = try decoder.visitRefNull() + try visitor.visitRefNull(type: type) + case 0xD1: + try visitor.visitRefIsNull() + case 0xD2: + let (functionIndex) = try decoder.visitRefFunc() + try visitor.visitRefFunc(functionIndex: functionIndex) + case 0xFC: + + let opcode1 = try decoder.claimNextByte() + switch opcode1 { + case 0x00: + try visitor.visitConversion(.i32TruncSatF32S) + case 0x01: + try visitor.visitConversion(.i32TruncSatF32U) + case 0x02: + try visitor.visitConversion(.i32TruncSatF64S) + case 0x03: + try visitor.visitConversion(.i32TruncSatF64U) + case 0x04: + try visitor.visitConversion(.i64TruncSatF32S) + case 0x05: + try visitor.visitConversion(.i64TruncSatF32U) + case 0x06: + try visitor.visitConversion(.i64TruncSatF64S) + case 0x07: + try visitor.visitConversion(.i64TruncSatF64U) + case 0x08: + let (dataIndex) = try decoder.visitMemoryInit() + try visitor.visitMemoryInit(dataIndex: dataIndex) + case 0x09: + let (dataIndex) = try decoder.visitDataDrop() + try visitor.visitDataDrop(dataIndex: dataIndex) + case 0x0A: + let (dstMem, srcMem) = try decoder.visitMemoryCopy() + try visitor.visitMemoryCopy(dstMem: dstMem, srcMem: srcMem) + case 0x0B: + let (memory) = try decoder.visitMemoryFill() + try visitor.visitMemoryFill(memory: memory) + case 0x0C: + let (elemIndex, table) = try decoder.visitTableInit() + try visitor.visitTableInit(elemIndex: elemIndex, table: table) + case 0x0D: + let (elemIndex) = try decoder.visitElemDrop() + try visitor.visitElemDrop(elemIndex: elemIndex) + case 0x0E: + let (dstTable, srcTable) = try decoder.visitTableCopy() + try visitor.visitTableCopy(dstTable: dstTable, srcTable: srcTable) + case 0x0F: + let (table) = try decoder.visitTableGrow() + try visitor.visitTableGrow(table: table) + case 0x10: + let (table) = try decoder.visitTableSize() + try visitor.visitTableSize(table: table) + case 0x11: + let (table) = try decoder.visitTableFill() + try visitor.visitTableFill(table: table) + default: + try decoder.visitUnknown([opcode0, opcode1]) + } + default: + try decoder.visitUnknown([opcode0]) + } + return false +} diff --git a/Sources/WasmParser/CMakeLists.txt b/Sources/WasmParser/CMakeLists.txt index 3428378c..b5b05612 100644 --- a/Sources/WasmParser/CMakeLists.txt +++ b/Sources/WasmParser/CMakeLists.txt @@ -2,7 +2,7 @@ add_wasmkit_library(WasmParser Stream/ByteStream.swift Stream/FileHandleStream.swift Stream/Stream.swift - InstructionCode.swift + BinaryInstructionDecoder.swift InstructionVisitor.swift LEB.swift ParsingLimits.swift diff --git a/Sources/WasmParser/Docs.docc/Docs.md b/Sources/WasmParser/Docs.docc/Docs.md index 3019648b..99bc3aea 100644 --- a/Sources/WasmParser/Docs.docc/Docs.md +++ b/Sources/WasmParser/Docs.docc/Docs.md @@ -54,8 +54,6 @@ while let payload = try parser.parseNext() { - ``InstructionVisitor`` - ``AnyInstructionVisitor`` -- ``InstructionTracingVisitor`` - ### Core Module Elements diff --git a/Sources/WasmParser/InstructionCode.swift b/Sources/WasmParser/InstructionCode.swift deleted file mode 100644 index 67ab4e62..00000000 --- a/Sources/WasmParser/InstructionCode.swift +++ /dev/null @@ -1,209 +0,0 @@ -@usableFromInline -enum InstructionCode: UInt8 { - case unreachable = 0x00 - case nop = 0x01 - case block = 0x02 - case loop = 0x03 - case `if` = 0x04 - case `else` = 0x05 - - case end = 0x0B - case br - case br_if - case br_table - case `return` - case call - case call_indirect - - case drop = 0x1A - case select = 0x1B - case typed_select = 0x1C - - case local_get = 0x20 - case local_set = 0x21 - case local_tee = 0x22 - - case global_get = 0x23 - case global_set = 0x24 - - case table_get = 0x25 - case table_set = 0x26 - - case i32_load = 0x28 - case i64_load = 0x29 - case f32_load = 0x2A - case f64_load = 0x2B - - case i32_load8_s = 0x2C - case i32_load8_u - case i32_load16_s - case i32_load16_u - case i64_load8_s - case i64_load8_u - case i64_load16_s - case i64_load16_u - case i64_load32_s - case i64_load32_u - - case i32_store = 0x36 - case i64_store = 0x37 - case f32_store = 0x38 - case f64_store = 0x39 - case i32_store8 = 0x3A - case i32_store16 = 0x3B - case i64_store8 = 0x3C - case i64_store16 = 0x3D - case i64_store32 = 0x3E - - case memory_size = 0x3F - case memory_grow = 0x40 - - case i32_const - case i64_const - case f32_const - case f64_const - - case i32_eqz - case i32_eq - case i32_ne - case i32_lt_s - case i32_lt_u - case i32_gt_s - case i32_gt_u - case i32_le_s - case i32_le_u - case i32_ge_s - case i32_ge_u - - case i64_eqz - case i64_eq - case i64_ne - case i64_lt_s - case i64_lt_u - case i64_gt_s - case i64_gt_u - case i64_le_s - case i64_le_u - case i64_ge_s - case i64_ge_u - - case f32_eq - case f32_ne - case f32_lt - case f32_gt - case f32_le - case f32_ge - - case f64_eq - case f64_ne - case f64_lt - case f64_gt - case f64_le - case f64_ge - - case i32_clz - case i32_ctz - case i32_popcnt - case i32_add - case i32_sub - case i32_mul - case i32_div_s - case i32_div_u - case i32_rem_s - case i32_rem_u - case i32_and - case i32_or - case i32_xor - case i32_shl - case i32_shr_s - case i32_shr_u - case i32_rotl - case i32_rotr - - case i64_clz - case i64_ctz - case i64_popcnt - case i64_add - case i64_sub - case i64_mul - case i64_div_s - case i64_div_u - case i64_rem_s - case i64_rem_u - case i64_and - case i64_or - case i64_xor - case i64_shl - case i64_shr_s - case i64_shr_u - case i64_rotl - case i64_rotr - - case f32_abs - case f32_neg - case f32_ceil - case f32_floor - case f32_trunc - case f32_nearest - case f32_sqrt - case f32_add - case f32_sub - case f32_mul - case f32_div - case f32_min - case f32_max - case f32_copysign - - case f64_abs - case f64_neg - case f64_ceil - case f64_floor - case f64_trunc - case f64_nearest - case f64_sqrt - case f64_add - case f64_sub - case f64_mul - case f64_div - case f64_min - case f64_max - case f64_copysign - - case i32_wrap_i64 = 0xA7 - case i32_trunc_f32_s - case i32_trunc_f32_u - case i32_trunc_f64_s - case i32_trunc_f64_u - case i64_extend_i32_s - case i64_extend_i32_u - case i64_trunc_f32_s - case i64_trunc_f32_u - case i64_trunc_f64_s - case i64_trunc_f64_u - case f32_convert_i32_s - case f32_convert_i32_u - case f32_convert_i64_s - case f32_convert_i64_u - case f32_demote_f64 - case f64_convert_i32_s - case f64_convert_i32_u - case f64_convert_i64_s - case f64_convert_i64_u - case f64_promote_f32 - case i32_reinterpret_f32 = 0xBC - case i64_reinterpret_f64 = 0xBD - case f32_reinterpret_i32 = 0xBE - case f64_reinterpret_i64 = 0xBF - - case i32_extend8_s = 0xC0 - case i32_extend16_s = 0xC1 - case i64_extend8_s = 0xC2 - case i64_extend16_s = 0xC3 - case i64_extend32_s = 0xC4 - - case ref_null = 0xD0 - case ref_is_null = 0xD1 - case ref_func = 0xD2 - - case wasm2InstructionPrefix = 0xFC -} diff --git a/Sources/WasmParser/InstructionVisitor.swift b/Sources/WasmParser/InstructionVisitor.swift index 904a2704..230ba132 100644 --- a/Sources/WasmParser/InstructionVisitor.swift +++ b/Sources/WasmParser/InstructionVisitor.swift @@ -5,6 +5,175 @@ import WasmTypes public enum Instruction: Equatable { + public enum Load: Equatable { + case i32Load + case i64Load + case f32Load + case f64Load + case i32Load8S + case i32Load8U + case i32Load16S + case i32Load16U + case i64Load8S + case i64Load8U + case i64Load16S + case i64Load16U + case i64Load32S + case i64Load32U + } + public enum Store: Equatable { + case i32Store + case i64Store + case f32Store + case f64Store + case i32Store8 + case i32Store16 + case i64Store8 + case i64Store16 + case i64Store32 + } + public enum Cmp: Equatable { + case i32Eq + case i32Ne + case i32LtS + case i32LtU + case i32GtS + case i32GtU + case i32LeS + case i32LeU + case i32GeS + case i32GeU + case i64Eq + case i64Ne + case i64LtS + case i64LtU + case i64GtS + case i64GtU + case i64LeS + case i64LeU + case i64GeS + case i64GeU + case f32Eq + case f32Ne + case f32Lt + case f32Gt + case f32Le + case f32Ge + case f64Eq + case f64Ne + case f64Lt + case f64Gt + case f64Le + case f64Ge + } + public enum Unary: Equatable { + case i32Clz + case i32Ctz + case i32Popcnt + case i64Clz + case i64Ctz + case i64Popcnt + case f32Abs + case f32Neg + case f32Ceil + case f32Floor + case f32Trunc + case f32Nearest + case f32Sqrt + case f64Abs + case f64Neg + case f64Ceil + case f64Floor + case f64Trunc + case f64Nearest + case f64Sqrt + case i32Extend8S + case i32Extend16S + case i64Extend8S + case i64Extend16S + case i64Extend32S + } + public enum Binary: Equatable { + case i32Add + case i32Sub + case i32Mul + case i32DivS + case i32DivU + case i32RemS + case i32RemU + case i32And + case i32Or + case i32Xor + case i32Shl + case i32ShrS + case i32ShrU + case i32Rotl + case i32Rotr + case i64Add + case i64Sub + case i64Mul + case i64DivS + case i64DivU + case i64RemS + case i64RemU + case i64And + case i64Or + case i64Xor + case i64Shl + case i64ShrS + case i64ShrU + case i64Rotl + case i64Rotr + case f32Add + case f32Sub + case f32Mul + case f32Div + case f32Min + case f32Max + case f32Copysign + case f64Add + case f64Sub + case f64Mul + case f64Div + case f64Min + case f64Max + case f64Copysign + } + public enum Conversion: Equatable { + case i32WrapI64 + case i32TruncF32S + case i32TruncF32U + case i32TruncF64S + case i32TruncF64U + case i64ExtendI32S + case i64ExtendI32U + case i64TruncF32S + case i64TruncF32U + case i64TruncF64S + case i64TruncF64U + case f32ConvertI32S + case f32ConvertI32U + case f32ConvertI64S + case f32ConvertI64U + case f32DemoteF64 + case f64ConvertI32S + case f64ConvertI32U + case f64ConvertI64S + case f64ConvertI64U + case f64PromoteF32 + case i32ReinterpretF32 + case i64ReinterpretF64 + case f32ReinterpretI32 + case f64ReinterpretI64 + case i32TruncSatF32S + case i32TruncSatF32U + case i32TruncSatF64S + case i32TruncSatF64U + case i64TruncSatF32S + case i64TruncSatF32U + case i64TruncSatF64S + case i64TruncSatF64U + } case `unreachable` case `nop` case `block`(blockType: BlockType) @@ -18,6 +187,8 @@ public enum Instruction: Equatable { case `return` case `call`(functionIndex: UInt32) case `callIndirect`(typeIndex: UInt32, tableIndex: UInt32) + case `returnCall`(functionIndex: UInt32) + case `returnCallIndirect`(typeIndex: UInt32, tableIndex: UInt32) case `drop` case `select` case `typedSelect`(type: ValueType) @@ -26,29 +197,8 @@ public enum Instruction: Equatable { case `localTee`(localIndex: UInt32) case `globalGet`(globalIndex: UInt32) case `globalSet`(globalIndex: UInt32) - case `i32Load`(memarg: MemArg) - case `i64Load`(memarg: MemArg) - case `f32Load`(memarg: MemArg) - case `f64Load`(memarg: MemArg) - case `i32Load8S`(memarg: MemArg) - case `i32Load8U`(memarg: MemArg) - case `i32Load16S`(memarg: MemArg) - case `i32Load16U`(memarg: MemArg) - case `i64Load8S`(memarg: MemArg) - case `i64Load8U`(memarg: MemArg) - case `i64Load16S`(memarg: MemArg) - case `i64Load16U`(memarg: MemArg) - case `i64Load32S`(memarg: MemArg) - case `i64Load32U`(memarg: MemArg) - case `i32Store`(memarg: MemArg) - case `i64Store`(memarg: MemArg) - case `f32Store`(memarg: MemArg) - case `f64Store`(memarg: MemArg) - case `i32Store8`(memarg: MemArg) - case `i32Store16`(memarg: MemArg) - case `i64Store8`(memarg: MemArg) - case `i64Store16`(memarg: MemArg) - case `i64Store32`(memarg: MemArg) + case `load`(Instruction.Load, memarg: MemArg) + case `store`(Instruction.Store, memarg: MemArg) case `memorySize`(memory: UInt32) case `memoryGrow`(memory: UInt32) case `i32Const`(value: Int32) @@ -59,133 +209,11 @@ public enum Instruction: Equatable { case `refIsNull` case `refFunc`(functionIndex: UInt32) case `i32Eqz` - case `i32Eq` - case `i32Ne` - case `i32LtS` - case `i32LtU` - case `i32GtS` - case `i32GtU` - case `i32LeS` - case `i32LeU` - case `i32GeS` - case `i32GeU` + case `cmp`(Instruction.Cmp) case `i64Eqz` - case `i64Eq` - case `i64Ne` - case `i64LtS` - case `i64LtU` - case `i64GtS` - case `i64GtU` - case `i64LeS` - case `i64LeU` - case `i64GeS` - case `i64GeU` - case `f32Eq` - case `f32Ne` - case `f32Lt` - case `f32Gt` - case `f32Le` - case `f32Ge` - case `f64Eq` - case `f64Ne` - case `f64Lt` - case `f64Gt` - case `f64Le` - case `f64Ge` - case `i32Clz` - case `i32Ctz` - case `i32Popcnt` - case `i32Add` - case `i32Sub` - case `i32Mul` - case `i32DivS` - case `i32DivU` - case `i32RemS` - case `i32RemU` - case `i32And` - case `i32Or` - case `i32Xor` - case `i32Shl` - case `i32ShrS` - case `i32ShrU` - case `i32Rotl` - case `i32Rotr` - case `i64Clz` - case `i64Ctz` - case `i64Popcnt` - case `i64Add` - case `i64Sub` - case `i64Mul` - case `i64DivS` - case `i64DivU` - case `i64RemS` - case `i64RemU` - case `i64And` - case `i64Or` - case `i64Xor` - case `i64Shl` - case `i64ShrS` - case `i64ShrU` - case `i64Rotl` - case `i64Rotr` - case `f32Abs` - case `f32Neg` - case `f32Ceil` - case `f32Floor` - case `f32Trunc` - case `f32Nearest` - case `f32Sqrt` - case `f32Add` - case `f32Sub` - case `f32Mul` - case `f32Div` - case `f32Min` - case `f32Max` - case `f32Copysign` - case `f64Abs` - case `f64Neg` - case `f64Ceil` - case `f64Floor` - case `f64Trunc` - case `f64Nearest` - case `f64Sqrt` - case `f64Add` - case `f64Sub` - case `f64Mul` - case `f64Div` - case `f64Min` - case `f64Max` - case `f64Copysign` - case `i32WrapI64` - case `i32TruncF32S` - case `i32TruncF32U` - case `i32TruncF64S` - case `i32TruncF64U` - case `i64ExtendI32S` - case `i64ExtendI32U` - case `i64TruncF32S` - case `i64TruncF32U` - case `i64TruncF64S` - case `i64TruncF64U` - case `f32ConvertI32S` - case `f32ConvertI32U` - case `f32ConvertI64S` - case `f32ConvertI64U` - case `f32DemoteF64` - case `f64ConvertI32S` - case `f64ConvertI32U` - case `f64ConvertI64S` - case `f64ConvertI64U` - case `f64PromoteF32` - case `i32ReinterpretF32` - case `i64ReinterpretF64` - case `f32ReinterpretI32` - case `f64ReinterpretI64` - case `i32Extend8S` - case `i32Extend16S` - case `i64Extend8S` - case `i64Extend16S` - case `i64Extend32S` + case `unary`(Instruction.Unary) + case `binary`(Instruction.Binary) + case `conversion`(Instruction.Conversion) case `memoryInit`(dataIndex: UInt32) case `dataDrop`(dataIndex: UInt32) case `memoryCopy`(dstMem: UInt32, srcMem: UInt32) @@ -198,14 +226,6 @@ public enum Instruction: Equatable { case `tableSet`(table: UInt32) case `tableGrow`(table: UInt32) case `tableSize`(table: UInt32) - case `i32TruncSatF32S` - case `i32TruncSatF32U` - case `i32TruncSatF64S` - case `i32TruncSatF64U` - case `i64TruncSatF32S` - case `i64TruncSatF32U` - case `i64TruncSatF64S` - case `i64TruncSatF64U` } /// A visitor that visits all instructions by a single visit method. @@ -228,6 +248,8 @@ extension AnyInstructionVisitor { public mutating func visitReturn() throws { return try self.visit(.return) } public mutating func visitCall(functionIndex: UInt32) throws { return try self.visit(.call(functionIndex: functionIndex)) } public mutating func visitCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws { return try self.visit(.callIndirect(typeIndex: typeIndex, tableIndex: tableIndex)) } + public mutating func visitReturnCall(functionIndex: UInt32) throws { return try self.visit(.returnCall(functionIndex: functionIndex)) } + public mutating func visitReturnCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws { return try self.visit(.returnCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex)) } public mutating func visitDrop() throws { return try self.visit(.drop) } public mutating func visitSelect() throws { return try self.visit(.select) } public mutating func visitTypedSelect(type: ValueType) throws { return try self.visit(.typedSelect(type: type)) } @@ -236,29 +258,8 @@ extension AnyInstructionVisitor { public mutating func visitLocalTee(localIndex: UInt32) throws { return try self.visit(.localTee(localIndex: localIndex)) } public mutating func visitGlobalGet(globalIndex: UInt32) throws { return try self.visit(.globalGet(globalIndex: globalIndex)) } public mutating func visitGlobalSet(globalIndex: UInt32) throws { return try self.visit(.globalSet(globalIndex: globalIndex)) } - public mutating func visitI32Load(memarg: MemArg) throws { return try self.visit(.i32Load(memarg: memarg)) } - public mutating func visitI64Load(memarg: MemArg) throws { return try self.visit(.i64Load(memarg: memarg)) } - public mutating func visitF32Load(memarg: MemArg) throws { return try self.visit(.f32Load(memarg: memarg)) } - public mutating func visitF64Load(memarg: MemArg) throws { return try self.visit(.f64Load(memarg: memarg)) } - public mutating func visitI32Load8S(memarg: MemArg) throws { return try self.visit(.i32Load8S(memarg: memarg)) } - public mutating func visitI32Load8U(memarg: MemArg) throws { return try self.visit(.i32Load8U(memarg: memarg)) } - public mutating func visitI32Load16S(memarg: MemArg) throws { return try self.visit(.i32Load16S(memarg: memarg)) } - public mutating func visitI32Load16U(memarg: MemArg) throws { return try self.visit(.i32Load16U(memarg: memarg)) } - public mutating func visitI64Load8S(memarg: MemArg) throws { return try self.visit(.i64Load8S(memarg: memarg)) } - public mutating func visitI64Load8U(memarg: MemArg) throws { return try self.visit(.i64Load8U(memarg: memarg)) } - public mutating func visitI64Load16S(memarg: MemArg) throws { return try self.visit(.i64Load16S(memarg: memarg)) } - public mutating func visitI64Load16U(memarg: MemArg) throws { return try self.visit(.i64Load16U(memarg: memarg)) } - public mutating func visitI64Load32S(memarg: MemArg) throws { return try self.visit(.i64Load32S(memarg: memarg)) } - public mutating func visitI64Load32U(memarg: MemArg) throws { return try self.visit(.i64Load32U(memarg: memarg)) } - public mutating func visitI32Store(memarg: MemArg) throws { return try self.visit(.i32Store(memarg: memarg)) } - public mutating func visitI64Store(memarg: MemArg) throws { return try self.visit(.i64Store(memarg: memarg)) } - public mutating func visitF32Store(memarg: MemArg) throws { return try self.visit(.f32Store(memarg: memarg)) } - public mutating func visitF64Store(memarg: MemArg) throws { return try self.visit(.f64Store(memarg: memarg)) } - public mutating func visitI32Store8(memarg: MemArg) throws { return try self.visit(.i32Store8(memarg: memarg)) } - public mutating func visitI32Store16(memarg: MemArg) throws { return try self.visit(.i32Store16(memarg: memarg)) } - public mutating func visitI64Store8(memarg: MemArg) throws { return try self.visit(.i64Store8(memarg: memarg)) } - public mutating func visitI64Store16(memarg: MemArg) throws { return try self.visit(.i64Store16(memarg: memarg)) } - public mutating func visitI64Store32(memarg: MemArg) throws { return try self.visit(.i64Store32(memarg: memarg)) } + public mutating func visitLoad(_ load: Instruction.Load, memarg: MemArg) throws { return try self.visit(.load(load, memarg: memarg)) } + public mutating func visitStore(_ store: Instruction.Store, memarg: MemArg) throws { return try self.visit(.store(store, memarg: memarg)) } public mutating func visitMemorySize(memory: UInt32) throws { return try self.visit(.memorySize(memory: memory)) } public mutating func visitMemoryGrow(memory: UInt32) throws { return try self.visit(.memoryGrow(memory: memory)) } public mutating func visitI32Const(value: Int32) throws { return try self.visit(.i32Const(value: value)) } @@ -269,133 +270,11 @@ extension AnyInstructionVisitor { public mutating func visitRefIsNull() throws { return try self.visit(.refIsNull) } public mutating func visitRefFunc(functionIndex: UInt32) throws { return try self.visit(.refFunc(functionIndex: functionIndex)) } public mutating func visitI32Eqz() throws { return try self.visit(.i32Eqz) } - public mutating func visitI32Eq() throws { return try self.visit(.i32Eq) } - public mutating func visitI32Ne() throws { return try self.visit(.i32Ne) } - public mutating func visitI32LtS() throws { return try self.visit(.i32LtS) } - public mutating func visitI32LtU() throws { return try self.visit(.i32LtU) } - public mutating func visitI32GtS() throws { return try self.visit(.i32GtS) } - public mutating func visitI32GtU() throws { return try self.visit(.i32GtU) } - public mutating func visitI32LeS() throws { return try self.visit(.i32LeS) } - public mutating func visitI32LeU() throws { return try self.visit(.i32LeU) } - public mutating func visitI32GeS() throws { return try self.visit(.i32GeS) } - public mutating func visitI32GeU() throws { return try self.visit(.i32GeU) } + public mutating func visitCmp(_ cmp: Instruction.Cmp) throws { return try self.visit(.cmp(cmp)) } public mutating func visitI64Eqz() throws { return try self.visit(.i64Eqz) } - public mutating func visitI64Eq() throws { return try self.visit(.i64Eq) } - public mutating func visitI64Ne() throws { return try self.visit(.i64Ne) } - public mutating func visitI64LtS() throws { return try self.visit(.i64LtS) } - public mutating func visitI64LtU() throws { return try self.visit(.i64LtU) } - public mutating func visitI64GtS() throws { return try self.visit(.i64GtS) } - public mutating func visitI64GtU() throws { return try self.visit(.i64GtU) } - public mutating func visitI64LeS() throws { return try self.visit(.i64LeS) } - public mutating func visitI64LeU() throws { return try self.visit(.i64LeU) } - public mutating func visitI64GeS() throws { return try self.visit(.i64GeS) } - public mutating func visitI64GeU() throws { return try self.visit(.i64GeU) } - public mutating func visitF32Eq() throws { return try self.visit(.f32Eq) } - public mutating func visitF32Ne() throws { return try self.visit(.f32Ne) } - public mutating func visitF32Lt() throws { return try self.visit(.f32Lt) } - public mutating func visitF32Gt() throws { return try self.visit(.f32Gt) } - public mutating func visitF32Le() throws { return try self.visit(.f32Le) } - public mutating func visitF32Ge() throws { return try self.visit(.f32Ge) } - public mutating func visitF64Eq() throws { return try self.visit(.f64Eq) } - public mutating func visitF64Ne() throws { return try self.visit(.f64Ne) } - public mutating func visitF64Lt() throws { return try self.visit(.f64Lt) } - public mutating func visitF64Gt() throws { return try self.visit(.f64Gt) } - public mutating func visitF64Le() throws { return try self.visit(.f64Le) } - public mutating func visitF64Ge() throws { return try self.visit(.f64Ge) } - public mutating func visitI32Clz() throws { return try self.visit(.i32Clz) } - public mutating func visitI32Ctz() throws { return try self.visit(.i32Ctz) } - public mutating func visitI32Popcnt() throws { return try self.visit(.i32Popcnt) } - public mutating func visitI32Add() throws { return try self.visit(.i32Add) } - public mutating func visitI32Sub() throws { return try self.visit(.i32Sub) } - public mutating func visitI32Mul() throws { return try self.visit(.i32Mul) } - public mutating func visitI32DivS() throws { return try self.visit(.i32DivS) } - public mutating func visitI32DivU() throws { return try self.visit(.i32DivU) } - public mutating func visitI32RemS() throws { return try self.visit(.i32RemS) } - public mutating func visitI32RemU() throws { return try self.visit(.i32RemU) } - public mutating func visitI32And() throws { return try self.visit(.i32And) } - public mutating func visitI32Or() throws { return try self.visit(.i32Or) } - public mutating func visitI32Xor() throws { return try self.visit(.i32Xor) } - public mutating func visitI32Shl() throws { return try self.visit(.i32Shl) } - public mutating func visitI32ShrS() throws { return try self.visit(.i32ShrS) } - public mutating func visitI32ShrU() throws { return try self.visit(.i32ShrU) } - public mutating func visitI32Rotl() throws { return try self.visit(.i32Rotl) } - public mutating func visitI32Rotr() throws { return try self.visit(.i32Rotr) } - public mutating func visitI64Clz() throws { return try self.visit(.i64Clz) } - public mutating func visitI64Ctz() throws { return try self.visit(.i64Ctz) } - public mutating func visitI64Popcnt() throws { return try self.visit(.i64Popcnt) } - public mutating func visitI64Add() throws { return try self.visit(.i64Add) } - public mutating func visitI64Sub() throws { return try self.visit(.i64Sub) } - public mutating func visitI64Mul() throws { return try self.visit(.i64Mul) } - public mutating func visitI64DivS() throws { return try self.visit(.i64DivS) } - public mutating func visitI64DivU() throws { return try self.visit(.i64DivU) } - public mutating func visitI64RemS() throws { return try self.visit(.i64RemS) } - public mutating func visitI64RemU() throws { return try self.visit(.i64RemU) } - public mutating func visitI64And() throws { return try self.visit(.i64And) } - public mutating func visitI64Or() throws { return try self.visit(.i64Or) } - public mutating func visitI64Xor() throws { return try self.visit(.i64Xor) } - public mutating func visitI64Shl() throws { return try self.visit(.i64Shl) } - public mutating func visitI64ShrS() throws { return try self.visit(.i64ShrS) } - public mutating func visitI64ShrU() throws { return try self.visit(.i64ShrU) } - public mutating func visitI64Rotl() throws { return try self.visit(.i64Rotl) } - public mutating func visitI64Rotr() throws { return try self.visit(.i64Rotr) } - public mutating func visitF32Abs() throws { return try self.visit(.f32Abs) } - public mutating func visitF32Neg() throws { return try self.visit(.f32Neg) } - public mutating func visitF32Ceil() throws { return try self.visit(.f32Ceil) } - public mutating func visitF32Floor() throws { return try self.visit(.f32Floor) } - public mutating func visitF32Trunc() throws { return try self.visit(.f32Trunc) } - public mutating func visitF32Nearest() throws { return try self.visit(.f32Nearest) } - public mutating func visitF32Sqrt() throws { return try self.visit(.f32Sqrt) } - public mutating func visitF32Add() throws { return try self.visit(.f32Add) } - public mutating func visitF32Sub() throws { return try self.visit(.f32Sub) } - public mutating func visitF32Mul() throws { return try self.visit(.f32Mul) } - public mutating func visitF32Div() throws { return try self.visit(.f32Div) } - public mutating func visitF32Min() throws { return try self.visit(.f32Min) } - public mutating func visitF32Max() throws { return try self.visit(.f32Max) } - public mutating func visitF32Copysign() throws { return try self.visit(.f32Copysign) } - public mutating func visitF64Abs() throws { return try self.visit(.f64Abs) } - public mutating func visitF64Neg() throws { return try self.visit(.f64Neg) } - public mutating func visitF64Ceil() throws { return try self.visit(.f64Ceil) } - public mutating func visitF64Floor() throws { return try self.visit(.f64Floor) } - public mutating func visitF64Trunc() throws { return try self.visit(.f64Trunc) } - public mutating func visitF64Nearest() throws { return try self.visit(.f64Nearest) } - public mutating func visitF64Sqrt() throws { return try self.visit(.f64Sqrt) } - public mutating func visitF64Add() throws { return try self.visit(.f64Add) } - public mutating func visitF64Sub() throws { return try self.visit(.f64Sub) } - public mutating func visitF64Mul() throws { return try self.visit(.f64Mul) } - public mutating func visitF64Div() throws { return try self.visit(.f64Div) } - public mutating func visitF64Min() throws { return try self.visit(.f64Min) } - public mutating func visitF64Max() throws { return try self.visit(.f64Max) } - public mutating func visitF64Copysign() throws { return try self.visit(.f64Copysign) } - public mutating func visitI32WrapI64() throws { return try self.visit(.i32WrapI64) } - public mutating func visitI32TruncF32S() throws { return try self.visit(.i32TruncF32S) } - public mutating func visitI32TruncF32U() throws { return try self.visit(.i32TruncF32U) } - public mutating func visitI32TruncF64S() throws { return try self.visit(.i32TruncF64S) } - public mutating func visitI32TruncF64U() throws { return try self.visit(.i32TruncF64U) } - public mutating func visitI64ExtendI32S() throws { return try self.visit(.i64ExtendI32S) } - public mutating func visitI64ExtendI32U() throws { return try self.visit(.i64ExtendI32U) } - public mutating func visitI64TruncF32S() throws { return try self.visit(.i64TruncF32S) } - public mutating func visitI64TruncF32U() throws { return try self.visit(.i64TruncF32U) } - public mutating func visitI64TruncF64S() throws { return try self.visit(.i64TruncF64S) } - public mutating func visitI64TruncF64U() throws { return try self.visit(.i64TruncF64U) } - public mutating func visitF32ConvertI32S() throws { return try self.visit(.f32ConvertI32S) } - public mutating func visitF32ConvertI32U() throws { return try self.visit(.f32ConvertI32U) } - public mutating func visitF32ConvertI64S() throws { return try self.visit(.f32ConvertI64S) } - public mutating func visitF32ConvertI64U() throws { return try self.visit(.f32ConvertI64U) } - public mutating func visitF32DemoteF64() throws { return try self.visit(.f32DemoteF64) } - public mutating func visitF64ConvertI32S() throws { return try self.visit(.f64ConvertI32S) } - public mutating func visitF64ConvertI32U() throws { return try self.visit(.f64ConvertI32U) } - public mutating func visitF64ConvertI64S() throws { return try self.visit(.f64ConvertI64S) } - public mutating func visitF64ConvertI64U() throws { return try self.visit(.f64ConvertI64U) } - public mutating func visitF64PromoteF32() throws { return try self.visit(.f64PromoteF32) } - public mutating func visitI32ReinterpretF32() throws { return try self.visit(.i32ReinterpretF32) } - public mutating func visitI64ReinterpretF64() throws { return try self.visit(.i64ReinterpretF64) } - public mutating func visitF32ReinterpretI32() throws { return try self.visit(.f32ReinterpretI32) } - public mutating func visitF64ReinterpretI64() throws { return try self.visit(.f64ReinterpretI64) } - public mutating func visitI32Extend8S() throws { return try self.visit(.i32Extend8S) } - public mutating func visitI32Extend16S() throws { return try self.visit(.i32Extend16S) } - public mutating func visitI64Extend8S() throws { return try self.visit(.i64Extend8S) } - public mutating func visitI64Extend16S() throws { return try self.visit(.i64Extend16S) } - public mutating func visitI64Extend32S() throws { return try self.visit(.i64Extend32S) } + public mutating func visitUnary(_ unary: Instruction.Unary) throws { return try self.visit(.unary(unary)) } + public mutating func visitBinary(_ binary: Instruction.Binary) throws { return try self.visit(.binary(binary)) } + public mutating func visitConversion(_ conversion: Instruction.Conversion) throws { return try self.visit(.conversion(conversion)) } public mutating func visitMemoryInit(dataIndex: UInt32) throws { return try self.visit(.memoryInit(dataIndex: dataIndex)) } public mutating func visitDataDrop(dataIndex: UInt32) throws { return try self.visit(.dataDrop(dataIndex: dataIndex)) } public mutating func visitMemoryCopy(dstMem: UInt32, srcMem: UInt32) throws { return try self.visit(.memoryCopy(dstMem: dstMem, srcMem: srcMem)) } @@ -408,836 +287,6 @@ extension AnyInstructionVisitor { public mutating func visitTableSet(table: UInt32) throws { return try self.visit(.tableSet(table: table)) } public mutating func visitTableGrow(table: UInt32) throws { return try self.visit(.tableGrow(table: table)) } public mutating func visitTableSize(table: UInt32) throws { return try self.visit(.tableSize(table: table)) } - public mutating func visitI32TruncSatF32S() throws { return try self.visit(.i32TruncSatF32S) } - public mutating func visitI32TruncSatF32U() throws { return try self.visit(.i32TruncSatF32U) } - public mutating func visitI32TruncSatF64S() throws { return try self.visit(.i32TruncSatF64S) } - public mutating func visitI32TruncSatF64U() throws { return try self.visit(.i32TruncSatF64U) } - public mutating func visitI64TruncSatF32S() throws { return try self.visit(.i64TruncSatF32S) } - public mutating func visitI64TruncSatF32U() throws { return try self.visit(.i64TruncSatF32U) } - public mutating func visitI64TruncSatF64S() throws { return try self.visit(.i64TruncSatF64S) } - public mutating func visitI64TruncSatF64U() throws { return try self.visit(.i64TruncSatF64U) } -} - -/// A visitor that traces the instructions visited. -public struct InstructionTracingVisitor: InstructionVisitor { - /// A closure that is invoked with the visited instruction. - public let trace: (Instruction) -> Void - /// The visitor to forward the instructions to. - public var visitor: V - - /// Creates a new tracing visitor. - /// - /// - Parameters: - /// - trace: A closure that is invoked with the visited instruction. - /// - visitor: The visitor to forward the instructions to. - public init(trace: @escaping (Instruction) -> Void, visitor: V) { - self.trace = trace - self.visitor = visitor - } - public mutating func visitUnreachable() throws { - trace(.unreachable) - return try visitor.visitUnreachable() - } - public mutating func visitNop() throws { - trace(.nop) - return try visitor.visitNop() - } - public mutating func visitBlock(blockType: BlockType) throws { - trace(.block(blockType: blockType)) - return try visitor.visitBlock(blockType: blockType) - } - public mutating func visitLoop(blockType: BlockType) throws { - trace(.loop(blockType: blockType)) - return try visitor.visitLoop(blockType: blockType) - } - public mutating func visitIf(blockType: BlockType) throws { - trace(.if(blockType: blockType)) - return try visitor.visitIf(blockType: blockType) - } - public mutating func visitElse() throws { - trace(.else) - return try visitor.visitElse() - } - public mutating func visitEnd() throws { - trace(.end) - return try visitor.visitEnd() - } - public mutating func visitBr(relativeDepth: UInt32) throws { - trace(.br(relativeDepth: relativeDepth)) - return try visitor.visitBr(relativeDepth: relativeDepth) - } - public mutating func visitBrIf(relativeDepth: UInt32) throws { - trace(.brIf(relativeDepth: relativeDepth)) - return try visitor.visitBrIf(relativeDepth: relativeDepth) - } - public mutating func visitBrTable(targets: BrTable) throws { - trace(.brTable(targets: targets)) - return try visitor.visitBrTable(targets: targets) - } - public mutating func visitReturn() throws { - trace(.return) - return try visitor.visitReturn() - } - public mutating func visitCall(functionIndex: UInt32) throws { - trace(.call(functionIndex: functionIndex)) - return try visitor.visitCall(functionIndex: functionIndex) - } - public mutating func visitCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws { - trace(.callIndirect(typeIndex: typeIndex, tableIndex: tableIndex)) - return try visitor.visitCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex) - } - public mutating func visitDrop() throws { - trace(.drop) - return try visitor.visitDrop() - } - public mutating func visitSelect() throws { - trace(.select) - return try visitor.visitSelect() - } - public mutating func visitTypedSelect(type: ValueType) throws { - trace(.typedSelect(type: type)) - return try visitor.visitTypedSelect(type: type) - } - public mutating func visitLocalGet(localIndex: UInt32) throws { - trace(.localGet(localIndex: localIndex)) - return try visitor.visitLocalGet(localIndex: localIndex) - } - public mutating func visitLocalSet(localIndex: UInt32) throws { - trace(.localSet(localIndex: localIndex)) - return try visitor.visitLocalSet(localIndex: localIndex) - } - public mutating func visitLocalTee(localIndex: UInt32) throws { - trace(.localTee(localIndex: localIndex)) - return try visitor.visitLocalTee(localIndex: localIndex) - } - public mutating func visitGlobalGet(globalIndex: UInt32) throws { - trace(.globalGet(globalIndex: globalIndex)) - return try visitor.visitGlobalGet(globalIndex: globalIndex) - } - public mutating func visitGlobalSet(globalIndex: UInt32) throws { - trace(.globalSet(globalIndex: globalIndex)) - return try visitor.visitGlobalSet(globalIndex: globalIndex) - } - public mutating func visitI32Load(memarg: MemArg) throws { - trace(.i32Load(memarg: memarg)) - return try visitor.visitI32Load(memarg: memarg) - } - public mutating func visitI64Load(memarg: MemArg) throws { - trace(.i64Load(memarg: memarg)) - return try visitor.visitI64Load(memarg: memarg) - } - public mutating func visitF32Load(memarg: MemArg) throws { - trace(.f32Load(memarg: memarg)) - return try visitor.visitF32Load(memarg: memarg) - } - public mutating func visitF64Load(memarg: MemArg) throws { - trace(.f64Load(memarg: memarg)) - return try visitor.visitF64Load(memarg: memarg) - } - public mutating func visitI32Load8S(memarg: MemArg) throws { - trace(.i32Load8S(memarg: memarg)) - return try visitor.visitI32Load8S(memarg: memarg) - } - public mutating func visitI32Load8U(memarg: MemArg) throws { - trace(.i32Load8U(memarg: memarg)) - return try visitor.visitI32Load8U(memarg: memarg) - } - public mutating func visitI32Load16S(memarg: MemArg) throws { - trace(.i32Load16S(memarg: memarg)) - return try visitor.visitI32Load16S(memarg: memarg) - } - public mutating func visitI32Load16U(memarg: MemArg) throws { - trace(.i32Load16U(memarg: memarg)) - return try visitor.visitI32Load16U(memarg: memarg) - } - public mutating func visitI64Load8S(memarg: MemArg) throws { - trace(.i64Load8S(memarg: memarg)) - return try visitor.visitI64Load8S(memarg: memarg) - } - public mutating func visitI64Load8U(memarg: MemArg) throws { - trace(.i64Load8U(memarg: memarg)) - return try visitor.visitI64Load8U(memarg: memarg) - } - public mutating func visitI64Load16S(memarg: MemArg) throws { - trace(.i64Load16S(memarg: memarg)) - return try visitor.visitI64Load16S(memarg: memarg) - } - public mutating func visitI64Load16U(memarg: MemArg) throws { - trace(.i64Load16U(memarg: memarg)) - return try visitor.visitI64Load16U(memarg: memarg) - } - public mutating func visitI64Load32S(memarg: MemArg) throws { - trace(.i64Load32S(memarg: memarg)) - return try visitor.visitI64Load32S(memarg: memarg) - } - public mutating func visitI64Load32U(memarg: MemArg) throws { - trace(.i64Load32U(memarg: memarg)) - return try visitor.visitI64Load32U(memarg: memarg) - } - public mutating func visitI32Store(memarg: MemArg) throws { - trace(.i32Store(memarg: memarg)) - return try visitor.visitI32Store(memarg: memarg) - } - public mutating func visitI64Store(memarg: MemArg) throws { - trace(.i64Store(memarg: memarg)) - return try visitor.visitI64Store(memarg: memarg) - } - public mutating func visitF32Store(memarg: MemArg) throws { - trace(.f32Store(memarg: memarg)) - return try visitor.visitF32Store(memarg: memarg) - } - public mutating func visitF64Store(memarg: MemArg) throws { - trace(.f64Store(memarg: memarg)) - return try visitor.visitF64Store(memarg: memarg) - } - public mutating func visitI32Store8(memarg: MemArg) throws { - trace(.i32Store8(memarg: memarg)) - return try visitor.visitI32Store8(memarg: memarg) - } - public mutating func visitI32Store16(memarg: MemArg) throws { - trace(.i32Store16(memarg: memarg)) - return try visitor.visitI32Store16(memarg: memarg) - } - public mutating func visitI64Store8(memarg: MemArg) throws { - trace(.i64Store8(memarg: memarg)) - return try visitor.visitI64Store8(memarg: memarg) - } - public mutating func visitI64Store16(memarg: MemArg) throws { - trace(.i64Store16(memarg: memarg)) - return try visitor.visitI64Store16(memarg: memarg) - } - public mutating func visitI64Store32(memarg: MemArg) throws { - trace(.i64Store32(memarg: memarg)) - return try visitor.visitI64Store32(memarg: memarg) - } - public mutating func visitMemorySize(memory: UInt32) throws { - trace(.memorySize(memory: memory)) - return try visitor.visitMemorySize(memory: memory) - } - public mutating func visitMemoryGrow(memory: UInt32) throws { - trace(.memoryGrow(memory: memory)) - return try visitor.visitMemoryGrow(memory: memory) - } - public mutating func visitI32Const(value: Int32) throws { - trace(.i32Const(value: value)) - return try visitor.visitI32Const(value: value) - } - public mutating func visitI64Const(value: Int64) throws { - trace(.i64Const(value: value)) - return try visitor.visitI64Const(value: value) - } - public mutating func visitF32Const(value: IEEE754.Float32) throws { - trace(.f32Const(value: value)) - return try visitor.visitF32Const(value: value) - } - public mutating func visitF64Const(value: IEEE754.Float64) throws { - trace(.f64Const(value: value)) - return try visitor.visitF64Const(value: value) - } - public mutating func visitRefNull(type: ReferenceType) throws { - trace(.refNull(type: type)) - return try visitor.visitRefNull(type: type) - } - public mutating func visitRefIsNull() throws { - trace(.refIsNull) - return try visitor.visitRefIsNull() - } - public mutating func visitRefFunc(functionIndex: UInt32) throws { - trace(.refFunc(functionIndex: functionIndex)) - return try visitor.visitRefFunc(functionIndex: functionIndex) - } - public mutating func visitI32Eqz() throws { - trace(.i32Eqz) - return try visitor.visitI32Eqz() - } - public mutating func visitI32Eq() throws { - trace(.i32Eq) - return try visitor.visitI32Eq() - } - public mutating func visitI32Ne() throws { - trace(.i32Ne) - return try visitor.visitI32Ne() - } - public mutating func visitI32LtS() throws { - trace(.i32LtS) - return try visitor.visitI32LtS() - } - public mutating func visitI32LtU() throws { - trace(.i32LtU) - return try visitor.visitI32LtU() - } - public mutating func visitI32GtS() throws { - trace(.i32GtS) - return try visitor.visitI32GtS() - } - public mutating func visitI32GtU() throws { - trace(.i32GtU) - return try visitor.visitI32GtU() - } - public mutating func visitI32LeS() throws { - trace(.i32LeS) - return try visitor.visitI32LeS() - } - public mutating func visitI32LeU() throws { - trace(.i32LeU) - return try visitor.visitI32LeU() - } - public mutating func visitI32GeS() throws { - trace(.i32GeS) - return try visitor.visitI32GeS() - } - public mutating func visitI32GeU() throws { - trace(.i32GeU) - return try visitor.visitI32GeU() - } - public mutating func visitI64Eqz() throws { - trace(.i64Eqz) - return try visitor.visitI64Eqz() - } - public mutating func visitI64Eq() throws { - trace(.i64Eq) - return try visitor.visitI64Eq() - } - public mutating func visitI64Ne() throws { - trace(.i64Ne) - return try visitor.visitI64Ne() - } - public mutating func visitI64LtS() throws { - trace(.i64LtS) - return try visitor.visitI64LtS() - } - public mutating func visitI64LtU() throws { - trace(.i64LtU) - return try visitor.visitI64LtU() - } - public mutating func visitI64GtS() throws { - trace(.i64GtS) - return try visitor.visitI64GtS() - } - public mutating func visitI64GtU() throws { - trace(.i64GtU) - return try visitor.visitI64GtU() - } - public mutating func visitI64LeS() throws { - trace(.i64LeS) - return try visitor.visitI64LeS() - } - public mutating func visitI64LeU() throws { - trace(.i64LeU) - return try visitor.visitI64LeU() - } - public mutating func visitI64GeS() throws { - trace(.i64GeS) - return try visitor.visitI64GeS() - } - public mutating func visitI64GeU() throws { - trace(.i64GeU) - return try visitor.visitI64GeU() - } - public mutating func visitF32Eq() throws { - trace(.f32Eq) - return try visitor.visitF32Eq() - } - public mutating func visitF32Ne() throws { - trace(.f32Ne) - return try visitor.visitF32Ne() - } - public mutating func visitF32Lt() throws { - trace(.f32Lt) - return try visitor.visitF32Lt() - } - public mutating func visitF32Gt() throws { - trace(.f32Gt) - return try visitor.visitF32Gt() - } - public mutating func visitF32Le() throws { - trace(.f32Le) - return try visitor.visitF32Le() - } - public mutating func visitF32Ge() throws { - trace(.f32Ge) - return try visitor.visitF32Ge() - } - public mutating func visitF64Eq() throws { - trace(.f64Eq) - return try visitor.visitF64Eq() - } - public mutating func visitF64Ne() throws { - trace(.f64Ne) - return try visitor.visitF64Ne() - } - public mutating func visitF64Lt() throws { - trace(.f64Lt) - return try visitor.visitF64Lt() - } - public mutating func visitF64Gt() throws { - trace(.f64Gt) - return try visitor.visitF64Gt() - } - public mutating func visitF64Le() throws { - trace(.f64Le) - return try visitor.visitF64Le() - } - public mutating func visitF64Ge() throws { - trace(.f64Ge) - return try visitor.visitF64Ge() - } - public mutating func visitI32Clz() throws { - trace(.i32Clz) - return try visitor.visitI32Clz() - } - public mutating func visitI32Ctz() throws { - trace(.i32Ctz) - return try visitor.visitI32Ctz() - } - public mutating func visitI32Popcnt() throws { - trace(.i32Popcnt) - return try visitor.visitI32Popcnt() - } - public mutating func visitI32Add() throws { - trace(.i32Add) - return try visitor.visitI32Add() - } - public mutating func visitI32Sub() throws { - trace(.i32Sub) - return try visitor.visitI32Sub() - } - public mutating func visitI32Mul() throws { - trace(.i32Mul) - return try visitor.visitI32Mul() - } - public mutating func visitI32DivS() throws { - trace(.i32DivS) - return try visitor.visitI32DivS() - } - public mutating func visitI32DivU() throws { - trace(.i32DivU) - return try visitor.visitI32DivU() - } - public mutating func visitI32RemS() throws { - trace(.i32RemS) - return try visitor.visitI32RemS() - } - public mutating func visitI32RemU() throws { - trace(.i32RemU) - return try visitor.visitI32RemU() - } - public mutating func visitI32And() throws { - trace(.i32And) - return try visitor.visitI32And() - } - public mutating func visitI32Or() throws { - trace(.i32Or) - return try visitor.visitI32Or() - } - public mutating func visitI32Xor() throws { - trace(.i32Xor) - return try visitor.visitI32Xor() - } - public mutating func visitI32Shl() throws { - trace(.i32Shl) - return try visitor.visitI32Shl() - } - public mutating func visitI32ShrS() throws { - trace(.i32ShrS) - return try visitor.visitI32ShrS() - } - public mutating func visitI32ShrU() throws { - trace(.i32ShrU) - return try visitor.visitI32ShrU() - } - public mutating func visitI32Rotl() throws { - trace(.i32Rotl) - return try visitor.visitI32Rotl() - } - public mutating func visitI32Rotr() throws { - trace(.i32Rotr) - return try visitor.visitI32Rotr() - } - public mutating func visitI64Clz() throws { - trace(.i64Clz) - return try visitor.visitI64Clz() - } - public mutating func visitI64Ctz() throws { - trace(.i64Ctz) - return try visitor.visitI64Ctz() - } - public mutating func visitI64Popcnt() throws { - trace(.i64Popcnt) - return try visitor.visitI64Popcnt() - } - public mutating func visitI64Add() throws { - trace(.i64Add) - return try visitor.visitI64Add() - } - public mutating func visitI64Sub() throws { - trace(.i64Sub) - return try visitor.visitI64Sub() - } - public mutating func visitI64Mul() throws { - trace(.i64Mul) - return try visitor.visitI64Mul() - } - public mutating func visitI64DivS() throws { - trace(.i64DivS) - return try visitor.visitI64DivS() - } - public mutating func visitI64DivU() throws { - trace(.i64DivU) - return try visitor.visitI64DivU() - } - public mutating func visitI64RemS() throws { - trace(.i64RemS) - return try visitor.visitI64RemS() - } - public mutating func visitI64RemU() throws { - trace(.i64RemU) - return try visitor.visitI64RemU() - } - public mutating func visitI64And() throws { - trace(.i64And) - return try visitor.visitI64And() - } - public mutating func visitI64Or() throws { - trace(.i64Or) - return try visitor.visitI64Or() - } - public mutating func visitI64Xor() throws { - trace(.i64Xor) - return try visitor.visitI64Xor() - } - public mutating func visitI64Shl() throws { - trace(.i64Shl) - return try visitor.visitI64Shl() - } - public mutating func visitI64ShrS() throws { - trace(.i64ShrS) - return try visitor.visitI64ShrS() - } - public mutating func visitI64ShrU() throws { - trace(.i64ShrU) - return try visitor.visitI64ShrU() - } - public mutating func visitI64Rotl() throws { - trace(.i64Rotl) - return try visitor.visitI64Rotl() - } - public mutating func visitI64Rotr() throws { - trace(.i64Rotr) - return try visitor.visitI64Rotr() - } - public mutating func visitF32Abs() throws { - trace(.f32Abs) - return try visitor.visitF32Abs() - } - public mutating func visitF32Neg() throws { - trace(.f32Neg) - return try visitor.visitF32Neg() - } - public mutating func visitF32Ceil() throws { - trace(.f32Ceil) - return try visitor.visitF32Ceil() - } - public mutating func visitF32Floor() throws { - trace(.f32Floor) - return try visitor.visitF32Floor() - } - public mutating func visitF32Trunc() throws { - trace(.f32Trunc) - return try visitor.visitF32Trunc() - } - public mutating func visitF32Nearest() throws { - trace(.f32Nearest) - return try visitor.visitF32Nearest() - } - public mutating func visitF32Sqrt() throws { - trace(.f32Sqrt) - return try visitor.visitF32Sqrt() - } - public mutating func visitF32Add() throws { - trace(.f32Add) - return try visitor.visitF32Add() - } - public mutating func visitF32Sub() throws { - trace(.f32Sub) - return try visitor.visitF32Sub() - } - public mutating func visitF32Mul() throws { - trace(.f32Mul) - return try visitor.visitF32Mul() - } - public mutating func visitF32Div() throws { - trace(.f32Div) - return try visitor.visitF32Div() - } - public mutating func visitF32Min() throws { - trace(.f32Min) - return try visitor.visitF32Min() - } - public mutating func visitF32Max() throws { - trace(.f32Max) - return try visitor.visitF32Max() - } - public mutating func visitF32Copysign() throws { - trace(.f32Copysign) - return try visitor.visitF32Copysign() - } - public mutating func visitF64Abs() throws { - trace(.f64Abs) - return try visitor.visitF64Abs() - } - public mutating func visitF64Neg() throws { - trace(.f64Neg) - return try visitor.visitF64Neg() - } - public mutating func visitF64Ceil() throws { - trace(.f64Ceil) - return try visitor.visitF64Ceil() - } - public mutating func visitF64Floor() throws { - trace(.f64Floor) - return try visitor.visitF64Floor() - } - public mutating func visitF64Trunc() throws { - trace(.f64Trunc) - return try visitor.visitF64Trunc() - } - public mutating func visitF64Nearest() throws { - trace(.f64Nearest) - return try visitor.visitF64Nearest() - } - public mutating func visitF64Sqrt() throws { - trace(.f64Sqrt) - return try visitor.visitF64Sqrt() - } - public mutating func visitF64Add() throws { - trace(.f64Add) - return try visitor.visitF64Add() - } - public mutating func visitF64Sub() throws { - trace(.f64Sub) - return try visitor.visitF64Sub() - } - public mutating func visitF64Mul() throws { - trace(.f64Mul) - return try visitor.visitF64Mul() - } - public mutating func visitF64Div() throws { - trace(.f64Div) - return try visitor.visitF64Div() - } - public mutating func visitF64Min() throws { - trace(.f64Min) - return try visitor.visitF64Min() - } - public mutating func visitF64Max() throws { - trace(.f64Max) - return try visitor.visitF64Max() - } - public mutating func visitF64Copysign() throws { - trace(.f64Copysign) - return try visitor.visitF64Copysign() - } - public mutating func visitI32WrapI64() throws { - trace(.i32WrapI64) - return try visitor.visitI32WrapI64() - } - public mutating func visitI32TruncF32S() throws { - trace(.i32TruncF32S) - return try visitor.visitI32TruncF32S() - } - public mutating func visitI32TruncF32U() throws { - trace(.i32TruncF32U) - return try visitor.visitI32TruncF32U() - } - public mutating func visitI32TruncF64S() throws { - trace(.i32TruncF64S) - return try visitor.visitI32TruncF64S() - } - public mutating func visitI32TruncF64U() throws { - trace(.i32TruncF64U) - return try visitor.visitI32TruncF64U() - } - public mutating func visitI64ExtendI32S() throws { - trace(.i64ExtendI32S) - return try visitor.visitI64ExtendI32S() - } - public mutating func visitI64ExtendI32U() throws { - trace(.i64ExtendI32U) - return try visitor.visitI64ExtendI32U() - } - public mutating func visitI64TruncF32S() throws { - trace(.i64TruncF32S) - return try visitor.visitI64TruncF32S() - } - public mutating func visitI64TruncF32U() throws { - trace(.i64TruncF32U) - return try visitor.visitI64TruncF32U() - } - public mutating func visitI64TruncF64S() throws { - trace(.i64TruncF64S) - return try visitor.visitI64TruncF64S() - } - public mutating func visitI64TruncF64U() throws { - trace(.i64TruncF64U) - return try visitor.visitI64TruncF64U() - } - public mutating func visitF32ConvertI32S() throws { - trace(.f32ConvertI32S) - return try visitor.visitF32ConvertI32S() - } - public mutating func visitF32ConvertI32U() throws { - trace(.f32ConvertI32U) - return try visitor.visitF32ConvertI32U() - } - public mutating func visitF32ConvertI64S() throws { - trace(.f32ConvertI64S) - return try visitor.visitF32ConvertI64S() - } - public mutating func visitF32ConvertI64U() throws { - trace(.f32ConvertI64U) - return try visitor.visitF32ConvertI64U() - } - public mutating func visitF32DemoteF64() throws { - trace(.f32DemoteF64) - return try visitor.visitF32DemoteF64() - } - public mutating func visitF64ConvertI32S() throws { - trace(.f64ConvertI32S) - return try visitor.visitF64ConvertI32S() - } - public mutating func visitF64ConvertI32U() throws { - trace(.f64ConvertI32U) - return try visitor.visitF64ConvertI32U() - } - public mutating func visitF64ConvertI64S() throws { - trace(.f64ConvertI64S) - return try visitor.visitF64ConvertI64S() - } - public mutating func visitF64ConvertI64U() throws { - trace(.f64ConvertI64U) - return try visitor.visitF64ConvertI64U() - } - public mutating func visitF64PromoteF32() throws { - trace(.f64PromoteF32) - return try visitor.visitF64PromoteF32() - } - public mutating func visitI32ReinterpretF32() throws { - trace(.i32ReinterpretF32) - return try visitor.visitI32ReinterpretF32() - } - public mutating func visitI64ReinterpretF64() throws { - trace(.i64ReinterpretF64) - return try visitor.visitI64ReinterpretF64() - } - public mutating func visitF32ReinterpretI32() throws { - trace(.f32ReinterpretI32) - return try visitor.visitF32ReinterpretI32() - } - public mutating func visitF64ReinterpretI64() throws { - trace(.f64ReinterpretI64) - return try visitor.visitF64ReinterpretI64() - } - public mutating func visitI32Extend8S() throws { - trace(.i32Extend8S) - return try visitor.visitI32Extend8S() - } - public mutating func visitI32Extend16S() throws { - trace(.i32Extend16S) - return try visitor.visitI32Extend16S() - } - public mutating func visitI64Extend8S() throws { - trace(.i64Extend8S) - return try visitor.visitI64Extend8S() - } - public mutating func visitI64Extend16S() throws { - trace(.i64Extend16S) - return try visitor.visitI64Extend16S() - } - public mutating func visitI64Extend32S() throws { - trace(.i64Extend32S) - return try visitor.visitI64Extend32S() - } - public mutating func visitMemoryInit(dataIndex: UInt32) throws { - trace(.memoryInit(dataIndex: dataIndex)) - return try visitor.visitMemoryInit(dataIndex: dataIndex) - } - public mutating func visitDataDrop(dataIndex: UInt32) throws { - trace(.dataDrop(dataIndex: dataIndex)) - return try visitor.visitDataDrop(dataIndex: dataIndex) - } - public mutating func visitMemoryCopy(dstMem: UInt32, srcMem: UInt32) throws { - trace(.memoryCopy(dstMem: dstMem, srcMem: srcMem)) - return try visitor.visitMemoryCopy(dstMem: dstMem, srcMem: srcMem) - } - public mutating func visitMemoryFill(memory: UInt32) throws { - trace(.memoryFill(memory: memory)) - return try visitor.visitMemoryFill(memory: memory) - } - public mutating func visitTableInit(elemIndex: UInt32, table: UInt32) throws { - trace(.tableInit(elemIndex: elemIndex, table: table)) - return try visitor.visitTableInit(elemIndex: elemIndex, table: table) - } - public mutating func visitElemDrop(elemIndex: UInt32) throws { - trace(.elemDrop(elemIndex: elemIndex)) - return try visitor.visitElemDrop(elemIndex: elemIndex) - } - public mutating func visitTableCopy(dstTable: UInt32, srcTable: UInt32) throws { - trace(.tableCopy(dstTable: dstTable, srcTable: srcTable)) - return try visitor.visitTableCopy(dstTable: dstTable, srcTable: srcTable) - } - public mutating func visitTableFill(table: UInt32) throws { - trace(.tableFill(table: table)) - return try visitor.visitTableFill(table: table) - } - public mutating func visitTableGet(table: UInt32) throws { - trace(.tableGet(table: table)) - return try visitor.visitTableGet(table: table) - } - public mutating func visitTableSet(table: UInt32) throws { - trace(.tableSet(table: table)) - return try visitor.visitTableSet(table: table) - } - public mutating func visitTableGrow(table: UInt32) throws { - trace(.tableGrow(table: table)) - return try visitor.visitTableGrow(table: table) - } - public mutating func visitTableSize(table: UInt32) throws { - trace(.tableSize(table: table)) - return try visitor.visitTableSize(table: table) - } - public mutating func visitI32TruncSatF32S() throws { - trace(.i32TruncSatF32S) - return try visitor.visitI32TruncSatF32S() - } - public mutating func visitI32TruncSatF32U() throws { - trace(.i32TruncSatF32U) - return try visitor.visitI32TruncSatF32U() - } - public mutating func visitI32TruncSatF64S() throws { - trace(.i32TruncSatF64S) - return try visitor.visitI32TruncSatF64S() - } - public mutating func visitI32TruncSatF64U() throws { - trace(.i32TruncSatF64U) - return try visitor.visitI32TruncSatF64U() - } - public mutating func visitI64TruncSatF32S() throws { - trace(.i64TruncSatF32S) - return try visitor.visitI64TruncSatF32S() - } - public mutating func visitI64TruncSatF32U() throws { - trace(.i64TruncSatF32U) - return try visitor.visitI64TruncSatF32U() - } - public mutating func visitI64TruncSatF64S() throws { - trace(.i64TruncSatF64S) - return try visitor.visitI64TruncSatF64S() - } - public mutating func visitI64TruncSatF64U() throws { - trace(.i64TruncSatF64U) - return try visitor.visitI64TruncSatF64U() - } } /// A visitor for WebAssembly instructions. @@ -1271,6 +320,10 @@ public protocol InstructionVisitor { mutating func visitCall(functionIndex: UInt32) throws /// Visiting `call_indirect` instruction. mutating func visitCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws + /// Visiting `return_call` instruction. + mutating func visitReturnCall(functionIndex: UInt32) throws + /// Visiting `return_call_indirect` instruction. + mutating func visitReturnCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws /// Visiting `drop` instruction. mutating func visitDrop() throws /// Visiting `select` instruction. @@ -1287,52 +340,10 @@ public protocol InstructionVisitor { mutating func visitGlobalGet(globalIndex: UInt32) throws /// Visiting `global.set` instruction. mutating func visitGlobalSet(globalIndex: UInt32) throws - /// Visiting `i32.load` instruction. - mutating func visitI32Load(memarg: MemArg) throws - /// Visiting `i64.load` instruction. - mutating func visitI64Load(memarg: MemArg) throws - /// Visiting `f32.load` instruction. - mutating func visitF32Load(memarg: MemArg) throws - /// Visiting `f64.load` instruction. - mutating func visitF64Load(memarg: MemArg) throws - /// Visiting `i32.load8_s` instruction. - mutating func visitI32Load8S(memarg: MemArg) throws - /// Visiting `i32.load8_u` instruction. - mutating func visitI32Load8U(memarg: MemArg) throws - /// Visiting `i32.load16_s` instruction. - mutating func visitI32Load16S(memarg: MemArg) throws - /// Visiting `i32.load16_u` instruction. - mutating func visitI32Load16U(memarg: MemArg) throws - /// Visiting `i64.load8_s` instruction. - mutating func visitI64Load8S(memarg: MemArg) throws - /// Visiting `i64.load8_u` instruction. - mutating func visitI64Load8U(memarg: MemArg) throws - /// Visiting `i64.load16_s` instruction. - mutating func visitI64Load16S(memarg: MemArg) throws - /// Visiting `i64.load16_u` instruction. - mutating func visitI64Load16U(memarg: MemArg) throws - /// Visiting `i64.load32_s` instruction. - mutating func visitI64Load32S(memarg: MemArg) throws - /// Visiting `i64.load32_u` instruction. - mutating func visitI64Load32U(memarg: MemArg) throws - /// Visiting `i32.store` instruction. - mutating func visitI32Store(memarg: MemArg) throws - /// Visiting `i64.store` instruction. - mutating func visitI64Store(memarg: MemArg) throws - /// Visiting `f32.store` instruction. - mutating func visitF32Store(memarg: MemArg) throws - /// Visiting `f64.store` instruction. - mutating func visitF64Store(memarg: MemArg) throws - /// Visiting `i32.store8` instruction. - mutating func visitI32Store8(memarg: MemArg) throws - /// Visiting `i32.store16` instruction. - mutating func visitI32Store16(memarg: MemArg) throws - /// Visiting `i64.store8` instruction. - mutating func visitI64Store8(memarg: MemArg) throws - /// Visiting `i64.store16` instruction. - mutating func visitI64Store16(memarg: MemArg) throws - /// Visiting `i64.store32` instruction. - mutating func visitI64Store32(memarg: MemArg) throws + /// Visiting `load` category instruction. + mutating func visitLoad(_: Instruction.Load, memarg: MemArg) throws + /// Visiting `store` category instruction. + mutating func visitStore(_: Instruction.Store, memarg: MemArg) throws /// Visiting `memory.size` instruction. mutating func visitMemorySize(memory: UInt32) throws /// Visiting `memory.grow` instruction. @@ -1353,260 +364,16 @@ public protocol InstructionVisitor { mutating func visitRefFunc(functionIndex: UInt32) throws /// Visiting `i32.eqz` instruction. mutating func visitI32Eqz() throws - /// Visiting `i32.eq` instruction. - mutating func visitI32Eq() throws - /// Visiting `i32.ne` instruction. - mutating func visitI32Ne() throws - /// Visiting `i32.lt_s` instruction. - mutating func visitI32LtS() throws - /// Visiting `i32.lt_u` instruction. - mutating func visitI32LtU() throws - /// Visiting `i32.gt_s` instruction. - mutating func visitI32GtS() throws - /// Visiting `i32.gt_u` instruction. - mutating func visitI32GtU() throws - /// Visiting `i32.le_s` instruction. - mutating func visitI32LeS() throws - /// Visiting `i32.le_u` instruction. - mutating func visitI32LeU() throws - /// Visiting `i32.ge_s` instruction. - mutating func visitI32GeS() throws - /// Visiting `i32.ge_u` instruction. - mutating func visitI32GeU() throws + /// Visiting `cmp` category instruction. + mutating func visitCmp(_: Instruction.Cmp) throws /// Visiting `i64.eqz` instruction. mutating func visitI64Eqz() throws - /// Visiting `i64.eq` instruction. - mutating func visitI64Eq() throws - /// Visiting `i64.ne` instruction. - mutating func visitI64Ne() throws - /// Visiting `i64.lt_s` instruction. - mutating func visitI64LtS() throws - /// Visiting `i64.lt_u` instruction. - mutating func visitI64LtU() throws - /// Visiting `i64.gt_s` instruction. - mutating func visitI64GtS() throws - /// Visiting `i64.gt_u` instruction. - mutating func visitI64GtU() throws - /// Visiting `i64.le_s` instruction. - mutating func visitI64LeS() throws - /// Visiting `i64.le_u` instruction. - mutating func visitI64LeU() throws - /// Visiting `i64.ge_s` instruction. - mutating func visitI64GeS() throws - /// Visiting `i64.ge_u` instruction. - mutating func visitI64GeU() throws - /// Visiting `f32.eq` instruction. - mutating func visitF32Eq() throws - /// Visiting `f32.ne` instruction. - mutating func visitF32Ne() throws - /// Visiting `f32.lt` instruction. - mutating func visitF32Lt() throws - /// Visiting `f32.gt` instruction. - mutating func visitF32Gt() throws - /// Visiting `f32.le` instruction. - mutating func visitF32Le() throws - /// Visiting `f32.ge` instruction. - mutating func visitF32Ge() throws - /// Visiting `f64.eq` instruction. - mutating func visitF64Eq() throws - /// Visiting `f64.ne` instruction. - mutating func visitF64Ne() throws - /// Visiting `f64.lt` instruction. - mutating func visitF64Lt() throws - /// Visiting `f64.gt` instruction. - mutating func visitF64Gt() throws - /// Visiting `f64.le` instruction. - mutating func visitF64Le() throws - /// Visiting `f64.ge` instruction. - mutating func visitF64Ge() throws - /// Visiting `i32.clz` instruction. - mutating func visitI32Clz() throws - /// Visiting `i32.ctz` instruction. - mutating func visitI32Ctz() throws - /// Visiting `i32.popcnt` instruction. - mutating func visitI32Popcnt() throws - /// Visiting `i32.add` instruction. - mutating func visitI32Add() throws - /// Visiting `i32.sub` instruction. - mutating func visitI32Sub() throws - /// Visiting `i32.mul` instruction. - mutating func visitI32Mul() throws - /// Visiting `i32.div_s` instruction. - mutating func visitI32DivS() throws - /// Visiting `i32.div_u` instruction. - mutating func visitI32DivU() throws - /// Visiting `i32.rem_s` instruction. - mutating func visitI32RemS() throws - /// Visiting `i32.rem_u` instruction. - mutating func visitI32RemU() throws - /// Visiting `i32.and` instruction. - mutating func visitI32And() throws - /// Visiting `i32.or` instruction. - mutating func visitI32Or() throws - /// Visiting `i32.xor` instruction. - mutating func visitI32Xor() throws - /// Visiting `i32.shl` instruction. - mutating func visitI32Shl() throws - /// Visiting `i32.shr_s` instruction. - mutating func visitI32ShrS() throws - /// Visiting `i32.shr_u` instruction. - mutating func visitI32ShrU() throws - /// Visiting `i32.rotl` instruction. - mutating func visitI32Rotl() throws - /// Visiting `i32.rotr` instruction. - mutating func visitI32Rotr() throws - /// Visiting `i64.clz` instruction. - mutating func visitI64Clz() throws - /// Visiting `i64.ctz` instruction. - mutating func visitI64Ctz() throws - /// Visiting `i64.popcnt` instruction. - mutating func visitI64Popcnt() throws - /// Visiting `i64.add` instruction. - mutating func visitI64Add() throws - /// Visiting `i64.sub` instruction. - mutating func visitI64Sub() throws - /// Visiting `i64.mul` instruction. - mutating func visitI64Mul() throws - /// Visiting `i64.div_s` instruction. - mutating func visitI64DivS() throws - /// Visiting `i64.div_u` instruction. - mutating func visitI64DivU() throws - /// Visiting `i64.rem_s` instruction. - mutating func visitI64RemS() throws - /// Visiting `i64.rem_u` instruction. - mutating func visitI64RemU() throws - /// Visiting `i64.and` instruction. - mutating func visitI64And() throws - /// Visiting `i64.or` instruction. - mutating func visitI64Or() throws - /// Visiting `i64.xor` instruction. - mutating func visitI64Xor() throws - /// Visiting `i64.shl` instruction. - mutating func visitI64Shl() throws - /// Visiting `i64.shr_s` instruction. - mutating func visitI64ShrS() throws - /// Visiting `i64.shr_u` instruction. - mutating func visitI64ShrU() throws - /// Visiting `i64.rotl` instruction. - mutating func visitI64Rotl() throws - /// Visiting `i64.rotr` instruction. - mutating func visitI64Rotr() throws - /// Visiting `f32.abs` instruction. - mutating func visitF32Abs() throws - /// Visiting `f32.neg` instruction. - mutating func visitF32Neg() throws - /// Visiting `f32.ceil` instruction. - mutating func visitF32Ceil() throws - /// Visiting `f32.floor` instruction. - mutating func visitF32Floor() throws - /// Visiting `f32.trunc` instruction. - mutating func visitF32Trunc() throws - /// Visiting `f32.nearest` instruction. - mutating func visitF32Nearest() throws - /// Visiting `f32.sqrt` instruction. - mutating func visitF32Sqrt() throws - /// Visiting `f32.add` instruction. - mutating func visitF32Add() throws - /// Visiting `f32.sub` instruction. - mutating func visitF32Sub() throws - /// Visiting `f32.mul` instruction. - mutating func visitF32Mul() throws - /// Visiting `f32.div` instruction. - mutating func visitF32Div() throws - /// Visiting `f32.min` instruction. - mutating func visitF32Min() throws - /// Visiting `f32.max` instruction. - mutating func visitF32Max() throws - /// Visiting `f32.copysign` instruction. - mutating func visitF32Copysign() throws - /// Visiting `f64.abs` instruction. - mutating func visitF64Abs() throws - /// Visiting `f64.neg` instruction. - mutating func visitF64Neg() throws - /// Visiting `f64.ceil` instruction. - mutating func visitF64Ceil() throws - /// Visiting `f64.floor` instruction. - mutating func visitF64Floor() throws - /// Visiting `f64.trunc` instruction. - mutating func visitF64Trunc() throws - /// Visiting `f64.nearest` instruction. - mutating func visitF64Nearest() throws - /// Visiting `f64.sqrt` instruction. - mutating func visitF64Sqrt() throws - /// Visiting `f64.add` instruction. - mutating func visitF64Add() throws - /// Visiting `f64.sub` instruction. - mutating func visitF64Sub() throws - /// Visiting `f64.mul` instruction. - mutating func visitF64Mul() throws - /// Visiting `f64.div` instruction. - mutating func visitF64Div() throws - /// Visiting `f64.min` instruction. - mutating func visitF64Min() throws - /// Visiting `f64.max` instruction. - mutating func visitF64Max() throws - /// Visiting `f64.copysign` instruction. - mutating func visitF64Copysign() throws - /// Visiting `i32.wrap_i64` instruction. - mutating func visitI32WrapI64() throws - /// Visiting `i32.trunc_f32_s` instruction. - mutating func visitI32TruncF32S() throws - /// Visiting `i32.trunc_f32_u` instruction. - mutating func visitI32TruncF32U() throws - /// Visiting `i32.trunc_f64_s` instruction. - mutating func visitI32TruncF64S() throws - /// Visiting `i32.trunc_f64_u` instruction. - mutating func visitI32TruncF64U() throws - /// Visiting `i64.extend_i32_s` instruction. - mutating func visitI64ExtendI32S() throws - /// Visiting `i64.extend_i32_u` instruction. - mutating func visitI64ExtendI32U() throws - /// Visiting `i64.trunc_f32_s` instruction. - mutating func visitI64TruncF32S() throws - /// Visiting `i64.trunc_f32_u` instruction. - mutating func visitI64TruncF32U() throws - /// Visiting `i64.trunc_f64_s` instruction. - mutating func visitI64TruncF64S() throws - /// Visiting `i64.trunc_f64_u` instruction. - mutating func visitI64TruncF64U() throws - /// Visiting `f32.convert_i32_s` instruction. - mutating func visitF32ConvertI32S() throws - /// Visiting `f32.convert_i32_u` instruction. - mutating func visitF32ConvertI32U() throws - /// Visiting `f32.convert_i64_s` instruction. - mutating func visitF32ConvertI64S() throws - /// Visiting `f32.convert_i64_u` instruction. - mutating func visitF32ConvertI64U() throws - /// Visiting `f32.demote_f64` instruction. - mutating func visitF32DemoteF64() throws - /// Visiting `f64.convert_i32_s` instruction. - mutating func visitF64ConvertI32S() throws - /// Visiting `f64.convert_i32_u` instruction. - mutating func visitF64ConvertI32U() throws - /// Visiting `f64.convert_i64_s` instruction. - mutating func visitF64ConvertI64S() throws - /// Visiting `f64.convert_i64_u` instruction. - mutating func visitF64ConvertI64U() throws - /// Visiting `f64.promote_f32` instruction. - mutating func visitF64PromoteF32() throws - /// Visiting `i32.reinterpret_f32` instruction. - mutating func visitI32ReinterpretF32() throws - /// Visiting `i64.reinterpret_f64` instruction. - mutating func visitI64ReinterpretF64() throws - /// Visiting `f32.reinterpret_i32` instruction. - mutating func visitF32ReinterpretI32() throws - /// Visiting `f64.reinterpret_i64` instruction. - mutating func visitF64ReinterpretI64() throws - /// Visiting `i32.extend8_s` instruction. - mutating func visitI32Extend8S() throws - /// Visiting `i32.extend16_s` instruction. - mutating func visitI32Extend16S() throws - /// Visiting `i64.extend8_s` instruction. - mutating func visitI64Extend8S() throws - /// Visiting `i64.extend16_s` instruction. - mutating func visitI64Extend16S() throws - /// Visiting `i64.extend32_s` instruction. - mutating func visitI64Extend32S() throws + /// Visiting `unary` category instruction. + mutating func visitUnary(_: Instruction.Unary) throws + /// Visiting `binary` category instruction. + mutating func visitBinary(_: Instruction.Binary) throws + /// Visiting `conversion` category instruction. + mutating func visitConversion(_: Instruction.Conversion) throws /// Visiting `memory.init` instruction. mutating func visitMemoryInit(dataIndex: UInt32) throws /// Visiting `data.drop` instruction. @@ -1631,22 +398,6 @@ public protocol InstructionVisitor { mutating func visitTableGrow(table: UInt32) throws /// Visiting `table.size` instruction. mutating func visitTableSize(table: UInt32) throws - /// Visiting `i32.trunc_sat_f32_s` instruction. - mutating func visitI32TruncSatF32S() throws - /// Visiting `i32.trunc_sat_f32_u` instruction. - mutating func visitI32TruncSatF32U() throws - /// Visiting `i32.trunc_sat_f64_s` instruction. - mutating func visitI32TruncSatF64S() throws - /// Visiting `i32.trunc_sat_f64_u` instruction. - mutating func visitI32TruncSatF64U() throws - /// Visiting `i64.trunc_sat_f32_s` instruction. - mutating func visitI64TruncSatF32S() throws - /// Visiting `i64.trunc_sat_f32_u` instruction. - mutating func visitI64TruncSatF32U() throws - /// Visiting `i64.trunc_sat_f64_s` instruction. - mutating func visitI64TruncSatF64S() throws - /// Visiting `i64.trunc_sat_f64_u` instruction. - mutating func visitI64TruncSatF64U() throws } extension InstructionVisitor { @@ -1666,6 +417,8 @@ extension InstructionVisitor { case .return: return try visitReturn() case let .call(functionIndex): return try visitCall(functionIndex: functionIndex) case let .callIndirect(typeIndex, tableIndex): return try visitCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex) + case let .returnCall(functionIndex): return try visitReturnCall(functionIndex: functionIndex) + case let .returnCallIndirect(typeIndex, tableIndex): return try visitReturnCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex) case .drop: return try visitDrop() case .select: return try visitSelect() case let .typedSelect(type): return try visitTypedSelect(type: type) @@ -1674,29 +427,8 @@ extension InstructionVisitor { case let .localTee(localIndex): return try visitLocalTee(localIndex: localIndex) case let .globalGet(globalIndex): return try visitGlobalGet(globalIndex: globalIndex) case let .globalSet(globalIndex): return try visitGlobalSet(globalIndex: globalIndex) - case let .i32Load(memarg): return try visitI32Load(memarg: memarg) - case let .i64Load(memarg): return try visitI64Load(memarg: memarg) - case let .f32Load(memarg): return try visitF32Load(memarg: memarg) - case let .f64Load(memarg): return try visitF64Load(memarg: memarg) - case let .i32Load8S(memarg): return try visitI32Load8S(memarg: memarg) - case let .i32Load8U(memarg): return try visitI32Load8U(memarg: memarg) - case let .i32Load16S(memarg): return try visitI32Load16S(memarg: memarg) - case let .i32Load16U(memarg): return try visitI32Load16U(memarg: memarg) - case let .i64Load8S(memarg): return try visitI64Load8S(memarg: memarg) - case let .i64Load8U(memarg): return try visitI64Load8U(memarg: memarg) - case let .i64Load16S(memarg): return try visitI64Load16S(memarg: memarg) - case let .i64Load16U(memarg): return try visitI64Load16U(memarg: memarg) - case let .i64Load32S(memarg): return try visitI64Load32S(memarg: memarg) - case let .i64Load32U(memarg): return try visitI64Load32U(memarg: memarg) - case let .i32Store(memarg): return try visitI32Store(memarg: memarg) - case let .i64Store(memarg): return try visitI64Store(memarg: memarg) - case let .f32Store(memarg): return try visitF32Store(memarg: memarg) - case let .f64Store(memarg): return try visitF64Store(memarg: memarg) - case let .i32Store8(memarg): return try visitI32Store8(memarg: memarg) - case let .i32Store16(memarg): return try visitI32Store16(memarg: memarg) - case let .i64Store8(memarg): return try visitI64Store8(memarg: memarg) - case let .i64Store16(memarg): return try visitI64Store16(memarg: memarg) - case let .i64Store32(memarg): return try visitI64Store32(memarg: memarg) + case let .load(load, memarg): return try visitLoad(load, memarg: memarg) + case let .store(store, memarg): return try visitStore(store, memarg: memarg) case let .memorySize(memory): return try visitMemorySize(memory: memory) case let .memoryGrow(memory): return try visitMemoryGrow(memory: memory) case let .i32Const(value): return try visitI32Const(value: value) @@ -1707,133 +439,11 @@ extension InstructionVisitor { case .refIsNull: return try visitRefIsNull() case let .refFunc(functionIndex): return try visitRefFunc(functionIndex: functionIndex) case .i32Eqz: return try visitI32Eqz() - case .i32Eq: return try visitI32Eq() - case .i32Ne: return try visitI32Ne() - case .i32LtS: return try visitI32LtS() - case .i32LtU: return try visitI32LtU() - case .i32GtS: return try visitI32GtS() - case .i32GtU: return try visitI32GtU() - case .i32LeS: return try visitI32LeS() - case .i32LeU: return try visitI32LeU() - case .i32GeS: return try visitI32GeS() - case .i32GeU: return try visitI32GeU() + case let .cmp(cmp): return try visitCmp(cmp) case .i64Eqz: return try visitI64Eqz() - case .i64Eq: return try visitI64Eq() - case .i64Ne: return try visitI64Ne() - case .i64LtS: return try visitI64LtS() - case .i64LtU: return try visitI64LtU() - case .i64GtS: return try visitI64GtS() - case .i64GtU: return try visitI64GtU() - case .i64LeS: return try visitI64LeS() - case .i64LeU: return try visitI64LeU() - case .i64GeS: return try visitI64GeS() - case .i64GeU: return try visitI64GeU() - case .f32Eq: return try visitF32Eq() - case .f32Ne: return try visitF32Ne() - case .f32Lt: return try visitF32Lt() - case .f32Gt: return try visitF32Gt() - case .f32Le: return try visitF32Le() - case .f32Ge: return try visitF32Ge() - case .f64Eq: return try visitF64Eq() - case .f64Ne: return try visitF64Ne() - case .f64Lt: return try visitF64Lt() - case .f64Gt: return try visitF64Gt() - case .f64Le: return try visitF64Le() - case .f64Ge: return try visitF64Ge() - case .i32Clz: return try visitI32Clz() - case .i32Ctz: return try visitI32Ctz() - case .i32Popcnt: return try visitI32Popcnt() - case .i32Add: return try visitI32Add() - case .i32Sub: return try visitI32Sub() - case .i32Mul: return try visitI32Mul() - case .i32DivS: return try visitI32DivS() - case .i32DivU: return try visitI32DivU() - case .i32RemS: return try visitI32RemS() - case .i32RemU: return try visitI32RemU() - case .i32And: return try visitI32And() - case .i32Or: return try visitI32Or() - case .i32Xor: return try visitI32Xor() - case .i32Shl: return try visitI32Shl() - case .i32ShrS: return try visitI32ShrS() - case .i32ShrU: return try visitI32ShrU() - case .i32Rotl: return try visitI32Rotl() - case .i32Rotr: return try visitI32Rotr() - case .i64Clz: return try visitI64Clz() - case .i64Ctz: return try visitI64Ctz() - case .i64Popcnt: return try visitI64Popcnt() - case .i64Add: return try visitI64Add() - case .i64Sub: return try visitI64Sub() - case .i64Mul: return try visitI64Mul() - case .i64DivS: return try visitI64DivS() - case .i64DivU: return try visitI64DivU() - case .i64RemS: return try visitI64RemS() - case .i64RemU: return try visitI64RemU() - case .i64And: return try visitI64And() - case .i64Or: return try visitI64Or() - case .i64Xor: return try visitI64Xor() - case .i64Shl: return try visitI64Shl() - case .i64ShrS: return try visitI64ShrS() - case .i64ShrU: return try visitI64ShrU() - case .i64Rotl: return try visitI64Rotl() - case .i64Rotr: return try visitI64Rotr() - case .f32Abs: return try visitF32Abs() - case .f32Neg: return try visitF32Neg() - case .f32Ceil: return try visitF32Ceil() - case .f32Floor: return try visitF32Floor() - case .f32Trunc: return try visitF32Trunc() - case .f32Nearest: return try visitF32Nearest() - case .f32Sqrt: return try visitF32Sqrt() - case .f32Add: return try visitF32Add() - case .f32Sub: return try visitF32Sub() - case .f32Mul: return try visitF32Mul() - case .f32Div: return try visitF32Div() - case .f32Min: return try visitF32Min() - case .f32Max: return try visitF32Max() - case .f32Copysign: return try visitF32Copysign() - case .f64Abs: return try visitF64Abs() - case .f64Neg: return try visitF64Neg() - case .f64Ceil: return try visitF64Ceil() - case .f64Floor: return try visitF64Floor() - case .f64Trunc: return try visitF64Trunc() - case .f64Nearest: return try visitF64Nearest() - case .f64Sqrt: return try visitF64Sqrt() - case .f64Add: return try visitF64Add() - case .f64Sub: return try visitF64Sub() - case .f64Mul: return try visitF64Mul() - case .f64Div: return try visitF64Div() - case .f64Min: return try visitF64Min() - case .f64Max: return try visitF64Max() - case .f64Copysign: return try visitF64Copysign() - case .i32WrapI64: return try visitI32WrapI64() - case .i32TruncF32S: return try visitI32TruncF32S() - case .i32TruncF32U: return try visitI32TruncF32U() - case .i32TruncF64S: return try visitI32TruncF64S() - case .i32TruncF64U: return try visitI32TruncF64U() - case .i64ExtendI32S: return try visitI64ExtendI32S() - case .i64ExtendI32U: return try visitI64ExtendI32U() - case .i64TruncF32S: return try visitI64TruncF32S() - case .i64TruncF32U: return try visitI64TruncF32U() - case .i64TruncF64S: return try visitI64TruncF64S() - case .i64TruncF64U: return try visitI64TruncF64U() - case .f32ConvertI32S: return try visitF32ConvertI32S() - case .f32ConvertI32U: return try visitF32ConvertI32U() - case .f32ConvertI64S: return try visitF32ConvertI64S() - case .f32ConvertI64U: return try visitF32ConvertI64U() - case .f32DemoteF64: return try visitF32DemoteF64() - case .f64ConvertI32S: return try visitF64ConvertI32S() - case .f64ConvertI32U: return try visitF64ConvertI32U() - case .f64ConvertI64S: return try visitF64ConvertI64S() - case .f64ConvertI64U: return try visitF64ConvertI64U() - case .f64PromoteF32: return try visitF64PromoteF32() - case .i32ReinterpretF32: return try visitI32ReinterpretF32() - case .i64ReinterpretF64: return try visitI64ReinterpretF64() - case .f32ReinterpretI32: return try visitF32ReinterpretI32() - case .f64ReinterpretI64: return try visitF64ReinterpretI64() - case .i32Extend8S: return try visitI32Extend8S() - case .i32Extend16S: return try visitI32Extend16S() - case .i64Extend8S: return try visitI64Extend8S() - case .i64Extend16S: return try visitI64Extend16S() - case .i64Extend32S: return try visitI64Extend32S() + case let .unary(unary): return try visitUnary(unary) + case let .binary(binary): return try visitBinary(binary) + case let .conversion(conversion): return try visitConversion(conversion) case let .memoryInit(dataIndex): return try visitMemoryInit(dataIndex: dataIndex) case let .dataDrop(dataIndex): return try visitDataDrop(dataIndex: dataIndex) case let .memoryCopy(dstMem, srcMem): return try visitMemoryCopy(dstMem: dstMem, srcMem: srcMem) @@ -1846,14 +456,6 @@ extension InstructionVisitor { case let .tableSet(table): return try visitTableSet(table: table) case let .tableGrow(table): return try visitTableGrow(table: table) case let .tableSize(table): return try visitTableSize(table: table) - case .i32TruncSatF32S: return try visitI32TruncSatF32S() - case .i32TruncSatF32U: return try visitI32TruncSatF32U() - case .i32TruncSatF64S: return try visitI32TruncSatF64S() - case .i32TruncSatF64U: return try visitI32TruncSatF64U() - case .i64TruncSatF32S: return try visitI64TruncSatF32S() - case .i64TruncSatF32U: return try visitI64TruncSatF32U() - case .i64TruncSatF64S: return try visitI64TruncSatF64S() - case .i64TruncSatF64U: return try visitI64TruncSatF64U() } } } @@ -1873,6 +475,8 @@ extension InstructionVisitor { public mutating func visitReturn() throws {} public mutating func visitCall(functionIndex: UInt32) throws {} public mutating func visitCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws {} + public mutating func visitReturnCall(functionIndex: UInt32) throws {} + public mutating func visitReturnCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws {} public mutating func visitDrop() throws {} public mutating func visitSelect() throws {} public mutating func visitTypedSelect(type: ValueType) throws {} @@ -1881,29 +485,8 @@ extension InstructionVisitor { public mutating func visitLocalTee(localIndex: UInt32) throws {} public mutating func visitGlobalGet(globalIndex: UInt32) throws {} public mutating func visitGlobalSet(globalIndex: UInt32) throws {} - public mutating func visitI32Load(memarg: MemArg) throws {} - public mutating func visitI64Load(memarg: MemArg) throws {} - public mutating func visitF32Load(memarg: MemArg) throws {} - public mutating func visitF64Load(memarg: MemArg) throws {} - public mutating func visitI32Load8S(memarg: MemArg) throws {} - public mutating func visitI32Load8U(memarg: MemArg) throws {} - public mutating func visitI32Load16S(memarg: MemArg) throws {} - public mutating func visitI32Load16U(memarg: MemArg) throws {} - public mutating func visitI64Load8S(memarg: MemArg) throws {} - public mutating func visitI64Load8U(memarg: MemArg) throws {} - public mutating func visitI64Load16S(memarg: MemArg) throws {} - public mutating func visitI64Load16U(memarg: MemArg) throws {} - public mutating func visitI64Load32S(memarg: MemArg) throws {} - public mutating func visitI64Load32U(memarg: MemArg) throws {} - public mutating func visitI32Store(memarg: MemArg) throws {} - public mutating func visitI64Store(memarg: MemArg) throws {} - public mutating func visitF32Store(memarg: MemArg) throws {} - public mutating func visitF64Store(memarg: MemArg) throws {} - public mutating func visitI32Store8(memarg: MemArg) throws {} - public mutating func visitI32Store16(memarg: MemArg) throws {} - public mutating func visitI64Store8(memarg: MemArg) throws {} - public mutating func visitI64Store16(memarg: MemArg) throws {} - public mutating func visitI64Store32(memarg: MemArg) throws {} + public mutating func visitLoad(_ load: Instruction.Load, memarg: MemArg) throws {} + public mutating func visitStore(_ store: Instruction.Store, memarg: MemArg) throws {} public mutating func visitMemorySize(memory: UInt32) throws {} public mutating func visitMemoryGrow(memory: UInt32) throws {} public mutating func visitI32Const(value: Int32) throws {} @@ -1914,133 +497,11 @@ extension InstructionVisitor { public mutating func visitRefIsNull() throws {} public mutating func visitRefFunc(functionIndex: UInt32) throws {} public mutating func visitI32Eqz() throws {} - public mutating func visitI32Eq() throws {} - public mutating func visitI32Ne() throws {} - public mutating func visitI32LtS() throws {} - public mutating func visitI32LtU() throws {} - public mutating func visitI32GtS() throws {} - public mutating func visitI32GtU() throws {} - public mutating func visitI32LeS() throws {} - public mutating func visitI32LeU() throws {} - public mutating func visitI32GeS() throws {} - public mutating func visitI32GeU() throws {} + public mutating func visitCmp(_ cmp: Instruction.Cmp) throws {} public mutating func visitI64Eqz() throws {} - public mutating func visitI64Eq() throws {} - public mutating func visitI64Ne() throws {} - public mutating func visitI64LtS() throws {} - public mutating func visitI64LtU() throws {} - public mutating func visitI64GtS() throws {} - public mutating func visitI64GtU() throws {} - public mutating func visitI64LeS() throws {} - public mutating func visitI64LeU() throws {} - public mutating func visitI64GeS() throws {} - public mutating func visitI64GeU() throws {} - public mutating func visitF32Eq() throws {} - public mutating func visitF32Ne() throws {} - public mutating func visitF32Lt() throws {} - public mutating func visitF32Gt() throws {} - public mutating func visitF32Le() throws {} - public mutating func visitF32Ge() throws {} - public mutating func visitF64Eq() throws {} - public mutating func visitF64Ne() throws {} - public mutating func visitF64Lt() throws {} - public mutating func visitF64Gt() throws {} - public mutating func visitF64Le() throws {} - public mutating func visitF64Ge() throws {} - public mutating func visitI32Clz() throws {} - public mutating func visitI32Ctz() throws {} - public mutating func visitI32Popcnt() throws {} - public mutating func visitI32Add() throws {} - public mutating func visitI32Sub() throws {} - public mutating func visitI32Mul() throws {} - public mutating func visitI32DivS() throws {} - public mutating func visitI32DivU() throws {} - public mutating func visitI32RemS() throws {} - public mutating func visitI32RemU() throws {} - public mutating func visitI32And() throws {} - public mutating func visitI32Or() throws {} - public mutating func visitI32Xor() throws {} - public mutating func visitI32Shl() throws {} - public mutating func visitI32ShrS() throws {} - public mutating func visitI32ShrU() throws {} - public mutating func visitI32Rotl() throws {} - public mutating func visitI32Rotr() throws {} - public mutating func visitI64Clz() throws {} - public mutating func visitI64Ctz() throws {} - public mutating func visitI64Popcnt() throws {} - public mutating func visitI64Add() throws {} - public mutating func visitI64Sub() throws {} - public mutating func visitI64Mul() throws {} - public mutating func visitI64DivS() throws {} - public mutating func visitI64DivU() throws {} - public mutating func visitI64RemS() throws {} - public mutating func visitI64RemU() throws {} - public mutating func visitI64And() throws {} - public mutating func visitI64Or() throws {} - public mutating func visitI64Xor() throws {} - public mutating func visitI64Shl() throws {} - public mutating func visitI64ShrS() throws {} - public mutating func visitI64ShrU() throws {} - public mutating func visitI64Rotl() throws {} - public mutating func visitI64Rotr() throws {} - public mutating func visitF32Abs() throws {} - public mutating func visitF32Neg() throws {} - public mutating func visitF32Ceil() throws {} - public mutating func visitF32Floor() throws {} - public mutating func visitF32Trunc() throws {} - public mutating func visitF32Nearest() throws {} - public mutating func visitF32Sqrt() throws {} - public mutating func visitF32Add() throws {} - public mutating func visitF32Sub() throws {} - public mutating func visitF32Mul() throws {} - public mutating func visitF32Div() throws {} - public mutating func visitF32Min() throws {} - public mutating func visitF32Max() throws {} - public mutating func visitF32Copysign() throws {} - public mutating func visitF64Abs() throws {} - public mutating func visitF64Neg() throws {} - public mutating func visitF64Ceil() throws {} - public mutating func visitF64Floor() throws {} - public mutating func visitF64Trunc() throws {} - public mutating func visitF64Nearest() throws {} - public mutating func visitF64Sqrt() throws {} - public mutating func visitF64Add() throws {} - public mutating func visitF64Sub() throws {} - public mutating func visitF64Mul() throws {} - public mutating func visitF64Div() throws {} - public mutating func visitF64Min() throws {} - public mutating func visitF64Max() throws {} - public mutating func visitF64Copysign() throws {} - public mutating func visitI32WrapI64() throws {} - public mutating func visitI32TruncF32S() throws {} - public mutating func visitI32TruncF32U() throws {} - public mutating func visitI32TruncF64S() throws {} - public mutating func visitI32TruncF64U() throws {} - public mutating func visitI64ExtendI32S() throws {} - public mutating func visitI64ExtendI32U() throws {} - public mutating func visitI64TruncF32S() throws {} - public mutating func visitI64TruncF32U() throws {} - public mutating func visitI64TruncF64S() throws {} - public mutating func visitI64TruncF64U() throws {} - public mutating func visitF32ConvertI32S() throws {} - public mutating func visitF32ConvertI32U() throws {} - public mutating func visitF32ConvertI64S() throws {} - public mutating func visitF32ConvertI64U() throws {} - public mutating func visitF32DemoteF64() throws {} - public mutating func visitF64ConvertI32S() throws {} - public mutating func visitF64ConvertI32U() throws {} - public mutating func visitF64ConvertI64S() throws {} - public mutating func visitF64ConvertI64U() throws {} - public mutating func visitF64PromoteF32() throws {} - public mutating func visitI32ReinterpretF32() throws {} - public mutating func visitI64ReinterpretF64() throws {} - public mutating func visitF32ReinterpretI32() throws {} - public mutating func visitF64ReinterpretI64() throws {} - public mutating func visitI32Extend8S() throws {} - public mutating func visitI32Extend16S() throws {} - public mutating func visitI64Extend8S() throws {} - public mutating func visitI64Extend16S() throws {} - public mutating func visitI64Extend32S() throws {} + public mutating func visitUnary(_ unary: Instruction.Unary) throws {} + public mutating func visitBinary(_ binary: Instruction.Binary) throws {} + public mutating func visitConversion(_ conversion: Instruction.Conversion) throws {} public mutating func visitMemoryInit(dataIndex: UInt32) throws {} public mutating func visitDataDrop(dataIndex: UInt32) throws {} public mutating func visitMemoryCopy(dstMem: UInt32, srcMem: UInt32) throws {} @@ -2053,13 +514,5 @@ extension InstructionVisitor { public mutating func visitTableSet(table: UInt32) throws {} public mutating func visitTableGrow(table: UInt32) throws {} public mutating func visitTableSize(table: UInt32) throws {} - public mutating func visitI32TruncSatF32S() throws {} - public mutating func visitI32TruncSatF32U() throws {} - public mutating func visitI32TruncSatF64S() throws {} - public mutating func visitI32TruncSatF64U() throws {} - public mutating func visitI64TruncSatF32S() throws {} - public mutating func visitI64TruncSatF32U() throws {} - public mutating func visitI64TruncSatF64S() throws {} - public mutating func visitI64TruncSatF64U() throws {} } diff --git a/Sources/WasmParser/LEB.swift b/Sources/WasmParser/LEB.swift index 7c869428..7bbc0852 100644 --- a/Sources/WasmParser/LEB.swift +++ b/Sources/WasmParser/LEB.swift @@ -1,85 +1,83 @@ +@usableFromInline enum LEBError: Swift.Error, Equatable { case overflow case integerRepresentationTooLong case insufficientBytes } -extension FixedWidthInteger where Self: UnsignedInteger { - @inline(__always) - init(LEB stream: Stream) throws { - let firstByte = try stream.consumeAny() - var result: Self = Self(firstByte & 0b0111_1111) - if _fastPath(firstByte & 0b1000_0000 == 0) { - self = result - return - } +@inlinable +func decodeLEB128( + stream: Stream +) throws -> IntType where IntType: FixedWidthInteger, IntType: UnsignedInteger, Stream: ByteStream { + let firstByte = try stream.consumeAny() + var result: IntType = IntType(firstByte & 0b0111_1111) + if _fastPath(firstByte & 0b1000_0000 == 0) { + return result + } - var shift: UInt = 7 + var shift: UInt = 7 - while true { - let byte = try stream.consumeAny() - let slice = Self(byte & 0b0111_1111) - let nextShift = shift + 7 - if nextShift >= Self.bitWidth, (byte >> (UInt(Self.bitWidth) - shift)) != 0 { - throw LEBError.integerRepresentationTooLong - } - result |= slice << shift - shift = nextShift - - guard byte & 0b1000_0000 != 0 else { break } + while true { + let byte = try stream.consumeAny() + let slice = IntType(byte & 0b0111_1111) + let nextShift = shift + 7 + if nextShift >= IntType.bitWidth, (byte >> (UInt(IntType.bitWidth) - shift)) != 0 { + throw LEBError.integerRepresentationTooLong } + result |= slice << shift + shift = nextShift - self = result + guard byte & 0b1000_0000 != 0 else { break } } -} -extension FixedWidthInteger where Self: SignedInteger { - @inline(__always) - init(LEB stream: Stream) throws { - let firstByte = try stream.consumeAny() - var result: Self = Self(firstByte & 0b0111_1111) - if _fastPath(firstByte & 0b1000_0000 == 0) { - // Interpret Int${Self.bitWidth-1} as Int${Self.bitWidth} - self = (result << (Self.bitWidth - 7)) >> (Self.bitWidth - 7) - return - } + return result +} - var shift: Self = 7 +@inlinable +func decodeLEB128( + stream: Stream, bitWidth: Int = IntType.bitWidth +) throws -> IntType where IntType: FixedWidthInteger, IntType: RawSignedInteger, Stream: ByteStream { + let firstByte = try stream.consumeAny() + var result = IntType.Unsigned(firstByte & 0b0111_1111) + if _fastPath(firstByte & 0b1000_0000 == 0) { + // Interpret Int${Self.bitWidth-1} as Int${Self.bitWidth} + return (IntType(bitPattern: result) << (IntType.bitWidth - 7)) >> (IntType.bitWidth - 7) + } - var byte: UInt8 - repeat { - byte = try stream.consumeAny() + var shift: IntType = 7 - let slice = Self(byte & 0b0111_1111) - result |= slice << shift + var byte: UInt8 + repeat { + byte = try stream.consumeAny() - // When we don't have enough bit width - if shift > (Self.bitWidth - 7) { - let remainingBitWidth = Self.bitWidth - Int(shift) - let continuationBit = (byte & 0b1000_0000) != 0 - // When a next byte is expected - if continuationBit { - throw LEBError.integerRepresentationTooLong - } + let slice = IntType.Unsigned(byte & 0b0111_1111) + result |= slice << shift - let signAndDiscardingBits = Int8(bitPattern: byte << 1) >> remainingBitWidth - // When meaningful bits are discarded - if signAndDiscardingBits != 0 && signAndDiscardingBits != -1 { - throw LEBError.overflow - } - self = result - return + // When we don't have enough bit width + if shift > (bitWidth - 7) { + let remainingBitWidth = bitWidth - Int(shift) + let continuationBit = (byte & 0b1000_0000) != 0 + // When a next byte is expected + if continuationBit { + throw LEBError.integerRepresentationTooLong } - shift += 7 - } while byte & 0b1000_0000 != 0 - - // Sign flag is second high-order bit - if byte & 0b0100_0000 != 0 { - // Sign extend - result |= Self(~0) << shift + let signAndDiscardingBits = Int8(bitPattern: byte << 1) >> remainingBitWidth + // When meaningful bits are discarded + if signAndDiscardingBits != 0 && signAndDiscardingBits != -1 { + throw LEBError.overflow + } + return IntType(bitPattern: result) } - self = result + shift += 7 + } while byte & 0b1000_0000 != 0 + + // Sign flag is second high-order bit + if byte & 0b0100_0000 != 0 { + // Sign extend + result |= IntType.Unsigned(bitPattern: ~0) << shift } + + return IntType(bitPattern: result) } diff --git a/Sources/WasmParser/ParsingLimits.swift b/Sources/WasmParser/ParsingLimits.swift index 13c847fe..69d8d80b 100644 --- a/Sources/WasmParser/ParsingLimits.swift +++ b/Sources/WasmParser/ParsingLimits.swift @@ -1,10 +1,12 @@ /// Limits for parsing WebAssembly modules. +@usableFromInline struct ParsingLimits { /// Maximum number of locals in a function. + @usableFromInline var maxFunctionLocals: UInt64 /// The default limits for parsing. static var `default`: ParsingLimits { - return ParsingLimits(maxFunctionLocals: 50000) + return ParsingLimits(maxFunctionLocals: 100000) } } diff --git a/Sources/WasmParser/Stream/FileHandleStream.swift b/Sources/WasmParser/Stream/FileHandleStream.swift index e3a0f8d9..5da02bf6 100644 --- a/Sources/WasmParser/Stream/FileHandleStream.swift +++ b/Sources/WasmParser/Stream/FileHandleStream.swift @@ -1,4 +1,4 @@ -import SystemPackage +import struct SystemPackage.FileDescriptor public final class FileHandleStream: ByteStream { private(set) public var currentIndex: Int = 0 @@ -30,7 +30,7 @@ public final class FileHandleStream: ByteStream { @discardableResult public func consumeAny() throws -> UInt8 { guard let consumed = try peek() else { - throw WasmParserError.unexpectedEnd + throw WasmParserError(.unexpectedEnd, offset: currentIndex) } currentIndex = bytes.index(after: currentIndex) return consumed diff --git a/Sources/WasmParser/Stream/Stream.swift b/Sources/WasmParser/Stream/Stream.swift index bd65e85e..bf457aa4 100644 --- a/Sources/WasmParser/Stream/Stream.swift +++ b/Sources/WasmParser/Stream/Stream.swift @@ -1,3 +1,4 @@ +@usableFromInline enum StreamError: Swift.Error, Equatable where Element: Hashable { case unexpectedEnd(expected: Set?) case unexpected(Element, index: Int, expected: Set?) diff --git a/Sources/WasmParser/WasmParser.swift b/Sources/WasmParser/WasmParser.swift index 88861c37..2f8374b2 100644 --- a/Sources/WasmParser/WasmParser.swift +++ b/Sources/WasmParser/WasmParser.swift @@ -1,6 +1,8 @@ -import SystemPackage import WasmTypes +import struct SystemPackage.FileDescriptor +import struct SystemPackage.FilePath + #if os(Windows) import ucrt #endif @@ -11,28 +13,33 @@ import WasmTypes public struct Parser { @usableFromInline let stream: Stream - public private(set) var hasDataCount: Bool = false - public let features: WasmFeatureSet - let limits: ParsingLimits + @usableFromInline let limits: ParsingLimits + @usableFromInline var orderTracking = OrderTracking() + @usableFromInline enum NextParseTarget { case header case section } - private var nextParseTarget: NextParseTarget - @usableFromInline - var currentIndex: Int { + var nextParseTarget: NextParseTarget + + public let features: WasmFeatureSet + public var offset: Int { return stream.currentIndex } - public init(stream: Stream, features: WasmFeatureSet = .default, hasDataCount: Bool = false) { + public init(stream: Stream, features: WasmFeatureSet = .default) { self.stream = stream self.features = features - self.hasDataCount = hasDataCount self.nextParseTarget = .header self.limits = .default } + + @usableFromInline + internal func makeError(_ message: WasmParserError.Message) -> WasmParserError { + return WasmParserError(message, offset: offset) + } } extension Parser where Stream == StaticByteStream { @@ -115,50 +122,58 @@ extension Code { /// ```` @inlinable public func parseExpression(visitor: inout V) throws { - let parser = Parser(stream: StaticByteStream(bytes: self.expression), features: self.features, hasDataCount: self.hasDataCount) - var lastCode: InstructionCode? + var parser = Parser(stream: StaticByteStream(bytes: self.expression), features: self.features) + var lastIsEnd: Bool? while try !parser.stream.hasReachedEnd() { - lastCode = try parser.parseInstruction(visitor: &visitor) + lastIsEnd = try parser.parseInstruction(visitor: &visitor) } - guard lastCode == .end else { - throw WasmParserError.endOpcodeExpected + guard lastIsEnd == true else { + throw parser.makeError(.endOpcodeExpected) } } } -// TODO: Move `doParseInstruction` under `ExpressionParser` struct @_documentation(visibility: internal) public struct ExpressionParser { + /// The byte offset of the code in the module + let codeOffset: Int + /// The initial byte offset of the code buffer stream + /// NOTE: This might be different from `codeOffset` if the code buffer + /// is not a part of the initial `FileHandleStream` buffer + let initialStreamOffset: Int @usableFromInline - let parser: Parser + var parser: Parser @usableFromInline - var lastCode: InstructionCode? + var isLastEnd: Bool? public var offset: Int { - self.parser.currentIndex + self.codeOffset + self.parser.offset - self.initialStreamOffset } public init(code: Code) { self.parser = Parser( stream: StaticByteStream(bytes: code.expression), - features: code.features, - hasDataCount: code.hasDataCount + features: code.features ) + self.codeOffset = code.offset + self.initialStreamOffset = self.parser.offset } @inlinable public mutating func visit(visitor: inout V) throws -> Bool { - lastCode = try parser.parseInstruction(visitor: &visitor) + isLastEnd = try parser.parseInstruction(visitor: &visitor) let shouldContinue = try !parser.stream.hasReachedEnd() if !shouldContinue { - guard lastCode == .end else { - throw WasmParserError.endOpcodeExpected + guard isLastEnd == true else { + throw WasmParserError(.endOpcodeExpected, offset: offset) } } return shouldContinue } } +let WASM_MAGIC: [UInt8] = [0x00, 0x61, 0x73, 0x6D] + /// Flags for enabling/disabling WebAssembly features public struct WasmFeatureSet: OptionSet { /// The raw value of the feature set @@ -170,81 +185,151 @@ public struct WasmFeatureSet: OptionSet { } /// The WebAssembly memory64 proposal - public static let memory64 = WasmFeatureSet(rawValue: 1 << 0) + @_alwaysEmitIntoClient + public static var memory64: WasmFeatureSet { WasmFeatureSet(rawValue: 1 << 0) } /// The WebAssembly reference types proposal - public static let referenceTypes = WasmFeatureSet(rawValue: 1 << 1) + @_alwaysEmitIntoClient + public static var referenceTypes: WasmFeatureSet { WasmFeatureSet(rawValue: 1 << 1) } /// The WebAssembly threads proposal - public static let threads = WasmFeatureSet(rawValue: 1 << 2) + @_alwaysEmitIntoClient + public static var threads: WasmFeatureSet { WasmFeatureSet(rawValue: 1 << 2) } + /// The WebAssembly tail-call proposal + @_alwaysEmitIntoClient + public static var tailCall: WasmFeatureSet { WasmFeatureSet(rawValue: 1 << 3) } /// The default feature set public static let `default`: WasmFeatureSet = [.referenceTypes] /// The feature set with all features enabled - public static let all: WasmFeatureSet = [.memory64, .referenceTypes, .threads] + public static let all: WasmFeatureSet = [.memory64, .referenceTypes, .threads, .tailCall] +} + +/// An error that occurs during parsing of a WebAssembly binary +public struct WasmParserError: Swift.Error { + @usableFromInline + struct Message { + let text: String + + init(_ text: String) { + self.text = text + } + } + + let message: Message + let offset: Int + + @usableFromInline + init(_ message: Message, offset: Int) { + self.message = message + self.offset = offset + } +} + +extension WasmParserError: CustomStringConvertible { + public var description: String { + return "\"\(message)\" at offset 0x\(String(offset, radix: 16))" + } } -public enum WasmParserError: Swift.Error { - /// The magic number is not found or invalid - case invalidMagicNumber([UInt8]) - /// The version is not recognized - case unknownVersion([UInt8]) - /// The bytes are not valid UTF-8 - case invalidUTF8([UInt8]) - /// The section has an invalid size - case invalidSectionSize(UInt32) - /// The section ID is malformed - case malformedSectionID(UInt8) - /// The byte is expected to be zero, but it's not - case zeroExpected(actual: UInt8, index: Int) - /// The function and code length are inconsistent - case inconsistentFunctionAndCodeLength(functionCount: Int, codeCount: Int) - /// The data count and data section length are inconsistent - case inconsistentDataCountAndDataSectionLength(dataCount: UInt32, dataSection: Int) - /// The local count is too large - case tooManyLocals(UInt64, limit: UInt64) - /// The type is expected to be a reference type, but it's not - case expectedRefType(actual: ValueType) - /// The instruction is not implemented - case unimplementedInstruction(UInt8, suffix: UInt32? = nil) - /// The element kind is unexpected - case unexpectedElementKind(expected: UInt32, actual: UInt32) - /// The element kind is not recognized - case integerRepresentationTooLong - /// `end` opcode is expected but not found - case endOpcodeExpected - /// Unexpected end of the stream - case unexpectedEnd - /// The byte is not expected - case sectionSizeMismatch(expected: Int, actual: Int) - /// Illegal opcode is found - case illegalOpcode(UInt8) - /// Malformed mutability byte - case malformedMutability(UInt8) - /// Malformed function type byte - case malformedFunctionType(UInt8) - /// Sections in the module are out of order - case sectionOutOfOrder - /// The data count section is required but not found - case dataCountSectionRequired - /// Malformed limit byte - case malformedLimit(UInt8) - /// Malformed indirect call - case malformedIndirectCall - /// Invalid reference to a type section entry - case invalidTypeSectionReference - /// The data segment kind is malformed - case malformedDataSegmentKind(UInt32) - case raw(String) +extension WasmParserError.Message { + @usableFromInline + static func invalidMagicNumber(_ bytes: [UInt8]) -> Self { + Self("magic header not detected: expected \(WASM_MAGIC) but got \(bytes)") + } + + @usableFromInline + static func unknownVersion(_ bytes: [UInt8]) -> Self { + Self("unknown binary version: \(bytes)") + } + + static func invalidUTF8(_ bytes: [UInt8]) -> Self { + Self("malformed UTF-8 encoding: \(bytes)") + } + + @usableFromInline + static func invalidSectionSize(_ size: UInt32) -> Self { + // TODO: Remove size parameter + Self("unexpected end-of-file") + } + + @usableFromInline + static func malformedSectionID(_ id: UInt8) -> Self { + Self("malformed section id: \(id)") + } + + @usableFromInline static func zeroExpected(actual: UInt8) -> Self { + Self("Zero expected but got \(actual)") + } + + @usableFromInline + static func tooManyLocals(_ count: UInt64, limit: UInt64) -> Self { + Self("Too many locals: \(count) vs \(limit)") + } + + @usableFromInline static func expectedRefType(actual: ValueType) -> Self { + Self("Expected reference type but got \(actual)") + } + + @usableFromInline + static func unexpectedElementKind(expected: UInt32, actual: UInt32) -> Self { + Self("Unexpected element kind: expected \(expected) but got \(actual)") + } + + @usableFromInline + static let integerRepresentationTooLong = Self("Integer representation is too long") + + @usableFromInline + static let endOpcodeExpected = Self("`end` opcode expected but not found") + + @usableFromInline + static let unexpectedEnd = Self("Unexpected end of the stream") + + @usableFromInline + static func sectionSizeMismatch(expected: Int, actual: Int) -> Self { + Self("Section size mismatch: expected \(expected) but got \(actual)") + } + + @usableFromInline static func illegalOpcode(_ opcode: [UInt8]) -> Self { + Self("Illegal opcode: \(opcode)") + } + + @usableFromInline + static func malformedMutability(_ byte: UInt8) -> Self { + Self("Malformed mutability: \(byte)") + } + + @usableFromInline + static func malformedFunctionType(_ byte: UInt8) -> Self { + Self("Malformed function type: \(byte)") + } + + @usableFromInline + static let sectionOutOfOrder = Self("Sections in the module are out of order") @usableFromInline - init(_ message: String) { - self = .raw(message) + static func malformedLimit(_ byte: UInt8) -> Self { + Self("Malformed limit: \(byte)") + } + + @usableFromInline static let malformedIndirectCall = Self("Malformed indirect call") + + @usableFromInline static func malformedDataSegmentKind(_ kind: UInt32) -> Self { + Self("Malformed data segment kind: \(kind)") + } + + @usableFromInline static func invalidResultArity(expected: Int, actual: Int) -> Self { + Self("invalid result arity: expected \(expected) but got \(actual)") + } + + @usableFromInline static func invalidFunctionType(_ index: Int64) -> Self { + Self("invalid function type index: \(index), expected a unsigned 32-bit integer") } } /// > Note: /// extension ByteStream { - fileprivate func parseVector(content parser: () throws -> Content) throws -> [Content] { + @inlinable + func parseVector(content parser: () throws -> Content) throws -> [Content] { var contents = [Content]() let count: UInt32 = try parseUnsigned() for _ in 0.. Note: /// extension ByteStream { - @inline(__always) - @usableFromInline + @inlinable func parseUnsigned(_: T.Type = T.self) throws -> T { - return try T(LEB: self) + try decodeLEB128(stream: self) } - @inline(__always) - @usableFromInline - func parseSigned() throws -> T { - return try T(LEB: self) + @inlinable + func parseSigned() throws -> T { + try decodeLEB128(stream: self) } - @inline(__always) @usableFromInline - func parseInteger() throws -> T { - let signed: T.Signed = try parseSigned() - return signed.unsigned + func parseVarSigned33() throws -> Int64 { + try decodeLEB128(stream: self, bitWidth: 33) } } @@ -285,6 +366,7 @@ extension ByteStream { try consumeAny() } + // TODO(optimize): Utilize ASCII fast path in UTF8 decoder var name = "" var iterator = bytes.makeIterator() @@ -293,7 +375,7 @@ extension ByteStream { switch decoder.decode(&iterator) { case let .scalarValue(scalar): name.append(Character(scalar)) case .emptyInput: break Decode - case .error: throw WasmParserError.invalidUTF8(bytes) + case .error: throw WasmParserError(.invalidUTF8(bytes), offset: currentIndex) } } @@ -302,7 +384,7 @@ extension ByteStream { } extension Parser { - @usableFromInline + @inlinable func parseVector(content parser: () throws -> Content) throws -> [Content] { try stream.parseVector(content: parser) } @@ -313,16 +395,10 @@ extension Parser { try stream.parseUnsigned(T.self) } - @inline(__always) - @inlinable - func parseSigned() throws -> T { - try stream.parseSigned() - } - - @inline(__always) @inlinable func parseInteger() throws -> T { - try stream.parseInteger() + let signed: T.Signed = try stream.parseSigned() + return T(bitPattern: signed) } func parseName() throws -> String { @@ -366,19 +442,20 @@ extension Parser { case 0x7E: return .i64 case 0x7D: return .f32 case 0x7C: return .f64 + case 0x7B: return .f64 case 0x70: return .ref(.funcRef) case 0x6F: return .ref(.externRef) default: - throw StreamError.unexpected(b, index: currentIndex, expected: Set(0x7C...0x7F)) + throw StreamError.unexpected(b, index: offset, expected: Set(0x7C...0x7F)) } } /// > Note: /// - @usableFromInline + @inlinable func parseResultType() throws -> BlockType { guard let nextByte = try stream.peek() else { - throw WasmParserError.unexpectedEnd + throw makeError(.unexpectedEnd) } switch nextByte { case 0x40: @@ -387,22 +464,27 @@ extension Parser { case 0x7C...0x7F, 0x70, 0x6F: return try .type(parseValueType()) default: - return try .funcType(TypeIndex(stream.consumeAny())) + let rawIndex = try stream.parseVarSigned33() + guard let index = TypeIndex(exactly: rawIndex) else { + throw makeError(.invalidFunctionType(rawIndex)) + } + return .funcType(index) } } /// > Note: /// + @inlinable func parseFunctionType() throws -> FunctionType { let opcode = try stream.consumeAny() // XXX: spectest expects the first byte should be parsed as a LEB128 with 1 byte limit // but the spec itself doesn't require it, so just check the continue bit of LEB128 here. guard opcode & 0b10000000 == 0 else { - throw WasmParserError.integerRepresentationTooLong + throw makeError(.integerRepresentationTooLong) } guard opcode == 0x60 else { - throw WasmParserError.malformedFunctionType(opcode) + throw makeError(.malformedFunctionType(opcode)) } let parameters = try parseVector { try parseValueType() } @@ -412,6 +494,7 @@ extension Parser { /// > Note: /// + @usableFromInline func parseLimits() throws -> Limits { let b = try stream.consumeAny() let sharedMask: UInt8 = 0b0010 @@ -429,7 +512,7 @@ extension Parser { flagMask |= isMemory64Mask } guard (b & ~flagMask) == 0 else { - throw WasmParserError.malformedLimit(b) + throw makeError(.malformedLimit(b)) } let min: UInt64 @@ -457,6 +540,7 @@ extension Parser { /// > Note: /// + @inlinable func parseTableType() throws -> TableType { let elementType: ReferenceType let b = try stream.consumeAny() @@ -467,7 +551,7 @@ extension Parser { case 0x6F: elementType = .externRef default: - throw StreamError.unexpected(b, index: currentIndex, expected: [0x6F, 0x70]) + throw StreamError.unexpected(b, index: offset, expected: [0x6F, 0x70]) } let limits = try parseLimits() @@ -476,12 +560,14 @@ extension Parser { /// > Note: /// + @inlinable func parseGlobalType() throws -> GlobalType { let valueType = try parseValueType() let mutability = try parseMutability() return GlobalType(mutability: mutability, valueType: valueType) } + @inlinable func parseMutability() throws -> Mutability { let b = try stream.consumeAny() switch b { @@ -490,20 +576,20 @@ extension Parser { case 0x01: return .variable default: - throw WasmParserError.malformedMutability(b) + throw makeError(.malformedMutability(b)) } } /// > Note: /// - @usableFromInline + @inlinable func parseMemarg() throws -> MemArg { let align: UInt32 = try parseUnsigned() let offset: UInt64 = try features.contains(.memory64) ? parseUnsigned(UInt64.self) : UInt64(parseUnsigned(UInt32.self)) return MemArg(offset: offset, align: align) } - func parseVectorBytes() throws -> ArraySlice { + @inlinable func parseVectorBytes() throws -> ArraySlice { let count: UInt32 = try parseUnsigned() return try stream.consume(count: Int(count)) } @@ -511,357 +597,178 @@ extension Parser { /// > Note: /// -extension Parser { - @inlinable - func parseInstruction(visitor v: inout V) throws -> InstructionCode { - let rawCode = try stream.consumeAny() - guard let code = InstructionCode(rawValue: rawCode) else { - throw WasmParserError.illegalOpcode(rawCode) +extension Parser: BinaryInstructionDecoder { + @inlinable func parseMemoryIndex() throws -> UInt32 { + let zero = try stream.consumeAny() + guard zero == 0x00 else { + throw makeError(.zeroExpected(actual: zero)) } - try doParseInstruction(code: code, visitor: &v) - return code + return 0 } - @inlinable - func doParseInstruction(code: InstructionCode, visitor v: inout V) throws { - switch code { - case .unreachable: return try v.visitUnreachable() - case .nop: return try v.visitNop() - case .block: return try v.visitBlock(blockType: try parseResultType()) - case .loop: return try v.visitLoop(blockType: try parseResultType()) - case .if: return try v.visitIf(blockType: try parseResultType()) - case .else: return try v.visitElse() - case .end: return try v.visitEnd() - case .br: - let label: UInt32 = try parseUnsigned() - return try v.visitBr(relativeDepth: label) - case .br_if: - let label: UInt32 = try parseUnsigned() - return try v.visitBrIf(relativeDepth: label) - case .br_table: - let labelIndices: [UInt32] = try parseVector { try parseUnsigned() } - let labelIndex: UInt32 = try parseUnsigned() - return try v.visitBrTable(targets: BrTable(labelIndices: labelIndices, defaultIndex: labelIndex)) - case .return: - return try v.visitReturn() - case .call: - let index: UInt32 = try parseUnsigned() - return try v.visitCall(functionIndex: index) - case .call_indirect: - let typeIndex: TypeIndex = try parseUnsigned() - if try !features.contains(.referenceTypes) && stream.peek() != 0 { - // Check that reserved byte is zero when reference-types is disabled - throw WasmParserError.malformedIndirectCall - } - let tableIndex: TableIndex = try parseUnsigned() - return try v.visitCallIndirect(typeIndex: typeIndex, tableIndex: tableIndex) - case .drop: return try v.visitDrop() - case .select: return try v.visitSelect() - case .typed_select: - let results = try parseVector { try parseValueType() } - guard results.count == 1 else { - throw WasmParserError("Only single result type is allowed but got \(results)") - } - return try v.visitTypedSelect(type: results[0]) - - case .local_get: - let index: UInt32 = try parseUnsigned() - return try v.visitLocalGet(localIndex: index) - case .local_set: - let index: UInt32 = try parseUnsigned() - return try v.visitLocalSet(localIndex: index) - case .local_tee: - let index: UInt32 = try parseUnsigned() - return try v.visitLocalTee(localIndex: index) - case .global_get: - let index: UInt32 = try parseUnsigned() - return try v.visitGlobalGet(globalIndex: index) - case .global_set: - let index: UInt32 = try parseUnsigned() - return try v.visitGlobalSet(globalIndex: index) - - case .i32_load: return try v.visitI32Load(memarg: try parseMemarg()) - case .i64_load: return try v.visitI64Load(memarg: try parseMemarg()) - case .f32_load: return try v.visitF32Load(memarg: try parseMemarg()) - case .f64_load: return try v.visitF64Load(memarg: try parseMemarg()) - case .i32_load8_s: return try v.visitI32Load8S(memarg: try parseMemarg()) - case .i32_load8_u: return try v.visitI32Load8U(memarg: try parseMemarg()) - case .i32_load16_s: return try v.visitI32Load16S(memarg: try parseMemarg()) - case .i32_load16_u: return try v.visitI32Load16U(memarg: try parseMemarg()) - case .i64_load8_s: return try v.visitI64Load8S(memarg: try parseMemarg()) - case .i64_load8_u: return try v.visitI64Load8U(memarg: try parseMemarg()) - case .i64_load16_s: return try v.visitI64Load16S(memarg: try parseMemarg()) - case .i64_load16_u: return try v.visitI64Load16U(memarg: try parseMemarg()) - case .i64_load32_s: return try v.visitI64Load32S(memarg: try parseMemarg()) - case .i64_load32_u: return try v.visitI64Load32U(memarg: try parseMemarg()) - case .i32_store: return try v.visitI32Store(memarg: try parseMemarg()) - case .i64_store: return try v.visitI64Store(memarg: try parseMemarg()) - case .f32_store: return try v.visitF32Store(memarg: try parseMemarg()) - case .f64_store: return try v.visitF64Store(memarg: try parseMemarg()) - case .i32_store8: return try v.visitI32Store8(memarg: try parseMemarg()) - case .i32_store16: return try v.visitI32Store16(memarg: try parseMemarg()) - case .i64_store8: return try v.visitI64Store8(memarg: try parseMemarg()) - case .i64_store16: return try v.visitI64Store16(memarg: try parseMemarg()) - case .i64_store32: return try v.visitI64Store32(memarg: try parseMemarg()) - case .memory_size: - let zero = try stream.consumeAny() - guard zero == 0x00 else { - throw WasmParserError.zeroExpected(actual: zero, index: currentIndex) - } - return try v.visitMemorySize(memory: UInt32(zero)) - case .memory_grow: - let zero = try stream.consumeAny() - guard zero == 0x00 else { - throw WasmParserError.zeroExpected(actual: zero, index: currentIndex) - } - return try v.visitMemoryGrow(memory: UInt32(zero)) - - case .i32_const: - let n: UInt32 = try parseInteger() - return try v.visitI32Const(value: Int32(bitPattern: n)) - case .i64_const: - let n: UInt64 = try parseInteger() - return try v.visitI64Const(value: Int64(bitPattern: n)) - case .f32_const: - let n = try parseFloat() - return try v.visitF32Const(value: IEEE754.Float32(bitPattern: n)) - case .f64_const: - let n = try parseDouble() - return try v.visitF64Const(value: IEEE754.Float64(bitPattern: n)) - - case .i32_eqz: return try v.visitI32Eqz() - case .i32_eq: return try v.visitI32Eq() - case .i32_ne: return try v.visitI32Ne() - case .i32_lt_s: return try v.visitI32LtS() - case .i32_lt_u: return try v.visitI32LtU() - case .i32_gt_s: return try v.visitI32GtS() - case .i32_gt_u: return try v.visitI32GtU() - case .i32_le_s: return try v.visitI32LeS() - case .i32_le_u: return try v.visitI32LeU() - case .i32_ge_s: return try v.visitI32GeS() - case .i32_ge_u: return try v.visitI32GeU() - - case .i64_eqz: return try v.visitI64Eqz() - case .i64_eq: return try v.visitI64Eq() - case .i64_ne: return try v.visitI64Ne() - case .i64_lt_s: return try v.visitI64LtS() - case .i64_lt_u: return try v.visitI64LtU() - case .i64_gt_s: return try v.visitI64GtS() - case .i64_gt_u: return try v.visitI64GtU() - case .i64_le_s: return try v.visitI64LeS() - case .i64_le_u: return try v.visitI64LeU() - case .i64_ge_s: return try v.visitI64GeS() - case .i64_ge_u: return try v.visitI64GeU() - - case .f32_eq: return try v.visitF32Eq() - case .f32_ne: return try v.visitF32Ne() - case .f32_lt: return try v.visitF32Lt() - case .f32_gt: return try v.visitF32Gt() - case .f32_le: return try v.visitF32Le() - case .f32_ge: return try v.visitF32Ge() - - case .f64_eq: return try v.visitF64Eq() - case .f64_ne: return try v.visitF64Ne() - case .f64_lt: return try v.visitF64Lt() - case .f64_gt: return try v.visitF64Gt() - case .f64_le: return try v.visitF64Le() - case .f64_ge: return try v.visitF64Ge() - - case .i32_clz: return try v.visitI32Clz() - case .i32_ctz: return try v.visitI32Ctz() - case .i32_popcnt: return try v.visitI32Popcnt() - case .i32_add: return try v.visitI32Add() - case .i32_sub: return try v.visitI32Sub() - case .i32_mul: return try v.visitI32Mul() - case .i32_div_s: return try v.visitI32DivS() - case .i32_div_u: return try v.visitI32DivU() - case .i32_rem_s: return try v.visitI32RemS() - case .i32_rem_u: return try v.visitI32RemU() - case .i32_and: return try v.visitI32And() - case .i32_or: return try v.visitI32Or() - case .i32_xor: return try v.visitI32Xor() - case .i32_shl: return try v.visitI32Shl() - case .i32_shr_s: return try v.visitI32ShrS() - case .i32_shr_u: return try v.visitI32ShrU() - case .i32_rotl: return try v.visitI32Rotl() - case .i32_rotr: return try v.visitI32Rotr() - - case .i64_clz: return try v.visitI64Clz() - case .i64_ctz: return try v.visitI64Ctz() - case .i64_popcnt: return try v.visitI64Popcnt() - case .i64_add: return try v.visitI64Add() - case .i64_sub: return try v.visitI64Sub() - case .i64_mul: return try v.visitI64Mul() - case .i64_div_s: return try v.visitI64DivS() - case .i64_div_u: return try v.visitI64DivU() - case .i64_rem_s: return try v.visitI64RemS() - case .i64_rem_u: return try v.visitI64RemU() - case .i64_and: return try v.visitI64And() - case .i64_or: return try v.visitI64Or() - case .i64_xor: return try v.visitI64Xor() - case .i64_shl: return try v.visitI64Shl() - case .i64_shr_s: return try v.visitI64ShrS() - case .i64_shr_u: return try v.visitI64ShrU() - case .i64_rotl: return try v.visitI64Rotl() - case .i64_rotr: return try v.visitI64Rotr() - - case .f32_abs: return try v.visitF32Abs() - case .f32_neg: return try v.visitF32Neg() - case .f32_ceil: return try v.visitF32Ceil() - case .f32_floor: return try v.visitF32Floor() - case .f32_trunc: return try v.visitF32Trunc() - case .f32_nearest: return try v.visitF32Nearest() - case .f32_sqrt: return try v.visitF32Sqrt() - - case .f32_add: return try v.visitF32Add() - case .f32_sub: return try v.visitF32Sub() - case .f32_mul: return try v.visitF32Mul() - case .f32_div: return try v.visitF32Div() - case .f32_min: return try v.visitF32Min() - case .f32_max: return try v.visitF32Max() - case .f32_copysign: return try v.visitF32Copysign() - - case .f64_abs: return try v.visitF64Abs() - case .f64_neg: return try v.visitF64Neg() - case .f64_ceil: return try v.visitF64Ceil() - case .f64_floor: return try v.visitF64Floor() - case .f64_trunc: return try v.visitF64Trunc() - case .f64_nearest: return try v.visitF64Nearest() - case .f64_sqrt: return try v.visitF64Sqrt() - - case .f64_add: return try v.visitF64Add() - case .f64_sub: return try v.visitF64Sub() - case .f64_mul: return try v.visitF64Mul() - case .f64_div: return try v.visitF64Div() - case .f64_min: return try v.visitF64Min() - case .f64_max: return try v.visitF64Max() - case .f64_copysign: return try v.visitF64Copysign() - - case .i32_wrap_i64: return try v.visitI32WrapI64() - case .i32_trunc_f32_s: return try v.visitI32TruncF32S() - case .i32_trunc_f32_u: return try v.visitI32TruncF32U() - case .i32_trunc_f64_s: return try v.visitI32TruncF64S() - case .i32_trunc_f64_u: return try v.visitI32TruncF64U() - case .i64_extend_i32_s: return try v.visitI64ExtendI32S() - case .i64_extend_i32_u: return try v.visitI64ExtendI32U() - case .i64_trunc_f32_s: return try v.visitI64TruncF32S() - case .i64_trunc_f32_u: return try v.visitI64TruncF32U() - case .i64_trunc_f64_s: return try v.visitI64TruncF64S() - case .i64_trunc_f64_u: return try v.visitI64TruncF64U() - case .f32_convert_i32_s: return try v.visitF32ConvertI32S() - case .f32_convert_i32_u: return try v.visitF32ConvertI32U() - case .f32_convert_i64_s: return try v.visitF32ConvertI64S() - case .f32_convert_i64_u: return try v.visitF32ConvertI64U() - case .f32_demote_f64: return try v.visitF32DemoteF64() - case .f64_convert_i32_s: return try v.visitF64ConvertI32S() - case .f64_convert_i32_u: return try v.visitF64ConvertI32U() - case .f64_convert_i64_s: return try v.visitF64ConvertI64S() - case .f64_convert_i64_u: return try v.visitF64ConvertI64U() - case .f64_promote_f32: return try v.visitF64PromoteF32() - case .i32_reinterpret_f32: return try v.visitI32ReinterpretF32() - case .i64_reinterpret_f64: return try v.visitI64ReinterpretF64() - case .f32_reinterpret_i32: return try v.visitF32ReinterpretI32() - case .f64_reinterpret_i64: return try v.visitF64ReinterpretI64() - case .i32_extend8_s: return try v.visitI32Extend8S() - case .i32_extend16_s: return try v.visitI32Extend16S() - case .i64_extend8_s: return try v.visitI64Extend8S() - case .i64_extend16_s: return try v.visitI64Extend16S() - case .i64_extend32_s: return try v.visitI64Extend32S() - - case .ref_null: - let type = try parseValueType() - - guard case let .ref(refType) = type else { - throw WasmParserError.expectedRefType(actual: type) - } + @inlinable func visitUnknown(_ opcode: [UInt8]) throws { + throw makeError(.illegalOpcode(opcode)) + } - return try v.visitRefNull(type: refType) + @inlinable mutating func visitBlock() throws -> BlockType { try parseResultType() } + @inlinable mutating func visitLoop() throws -> BlockType { try parseResultType() } + @inlinable mutating func visitIf() throws -> BlockType { try parseResultType() } + @inlinable mutating func visitBr() throws -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitBrIf() throws -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitBrTable() throws -> BrTable { + let labelIndices: [UInt32] = try parseVector { try parseUnsigned() } + let labelIndex: UInt32 = try parseUnsigned() + return BrTable(labelIndices: labelIndices, defaultIndex: labelIndex) + } + @inlinable mutating func visitCall() throws -> UInt32 { try parseUnsigned() } - case .ref_is_null: return try v.visitRefIsNull() + @inlinable mutating func visitCallIndirect() throws -> (typeIndex: UInt32, tableIndex: UInt32) { + let typeIndex: TypeIndex = try parseUnsigned() + if try !features.contains(.referenceTypes) && stream.peek() != 0 { + // Check that reserved byte is zero when reference-types is disabled + throw makeError(.malformedIndirectCall) + } + let tableIndex: TableIndex = try parseUnsigned() + return (typeIndex, tableIndex) + } - case .ref_func: return try v.visitRefFunc(functionIndex: try parseUnsigned()) + @inlinable mutating func visitReturnCall() throws -> UInt32 { + try parseUnsigned() + } - case .table_get: return try v.visitTableGet(table: try parseUnsigned()) + @inlinable mutating func visitReturnCallIndirect() throws -> (typeIndex: UInt32, tableIndex: UInt32) { + let typeIndex: TypeIndex = try parseUnsigned() + let tableIndex: TableIndex = try parseUnsigned() + return (typeIndex, tableIndex) + } - case .table_set: return try v.visitTableSet(table: try parseUnsigned()) + @inlinable mutating func visitTypedSelect() throws -> WasmTypes.ValueType { + let results = try parseVector { try parseValueType() } + guard results.count == 1 else { + throw makeError(.invalidResultArity(expected: 1, actual: results.count)) + } + return results[0] + } - case .wasm2InstructionPrefix: - let codeSuffix: UInt32 = try parseUnsigned() - switch codeSuffix { - case 0: return try v.visitI32TruncSatF32S() - case 1: return try v.visitI32TruncSatF32U() - case 2: return try v.visitI32TruncSatF64S() - case 3: return try v.visitI32TruncSatF64U() - case 4: return try v.visitI64TruncSatF32S() - case 5: return try v.visitI64TruncSatF32U() - case 6: return try v.visitI64TruncSatF64S() - case 7: return try v.visitI64TruncSatF64U() - case 8: - let dataIndex: DataIndex = try parseUnsigned() - // memory.init requires data count section - // https://webassembly.github.io/spec/core/binary/modules.html#data-count-section - guard hasDataCount else { - throw WasmParserError.dataCountSectionRequired - } + @inlinable mutating func visitLocalGet() throws -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitLocalSet() throws -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitLocalTee() throws -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitGlobalGet() throws -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitGlobalSet() throws -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitLoad(_: Instruction.Load) throws -> MemArg { try parseMemarg() } + @inlinable mutating func visitStore(_: Instruction.Store) throws -> MemArg { try parseMemarg() } + @inlinable mutating func visitMemorySize() throws -> UInt32 { + try parseMemoryIndex() + } + @inlinable mutating func visitMemoryGrow() throws -> UInt32 { + try parseMemoryIndex() + } + @inlinable mutating func visitI32Const() throws -> Int32 { + let n: UInt32 = try parseInteger() + return Int32(bitPattern: n) + } + @inlinable mutating func visitI64Const() throws -> Int64 { + let n: UInt64 = try parseInteger() + return Int64(bitPattern: n) + } + @inlinable mutating func visitF32Const() throws -> IEEE754.Float32 { + let n = try parseFloat() + return IEEE754.Float32(bitPattern: n) + } + @inlinable mutating func visitF64Const() throws -> IEEE754.Float64 { + let n = try parseDouble() + return IEEE754.Float64(bitPattern: n) + } + @inlinable mutating func visitRefNull() throws -> WasmTypes.ReferenceType { + let type = try parseValueType() + guard case let .ref(refType) = type else { + throw makeError(.expectedRefType(actual: type)) + } + return refType + } - let zero = try stream.consumeAny() - guard zero == 0x00 else { - throw WasmParserError.zeroExpected(actual: zero, index: currentIndex) - } + @inlinable mutating func visitRefFunc() throws -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitMemoryInit() throws -> UInt32 { + let dataIndex: DataIndex = try parseUnsigned() + _ = try parseMemoryIndex() + return dataIndex + } - return try v.visitMemoryInit(dataIndex: dataIndex) - case 9: - // memory.drop requires data count section - // https://webassembly.github.io/spec/core/binary/modules.html#data-count-section - guard hasDataCount else { - throw WasmParserError.dataCountSectionRequired - } - return try v.visitDataDrop(dataIndex: try parseUnsigned()) - case 10: - let (zero1, zero2) = try (stream.consumeAny(), stream.consumeAny()) - guard zero1 == 0x00 && zero2 == 0x00 else { - throw WasmParserError.zeroExpected(actual: zero2, index: currentIndex) - } - return try v.visitMemoryCopy(dstMem: 0, srcMem: 0) - case 11: - let zero = try stream.consumeAny() - guard zero == 0x00 else { - throw WasmParserError.zeroExpected(actual: zero, index: currentIndex) - } + @inlinable mutating func visitDataDrop() throws -> UInt32 { + try parseUnsigned() + } - return try v.visitMemoryFill(memory: 0) - case 12: - let elementIndex: ElementIndex = try parseUnsigned() - let tableIndex: TableIndex = try parseUnsigned() - return try v.visitTableInit(elemIndex: elementIndex, table: tableIndex) - case 13: return try v.visitElemDrop(elemIndex: try parseUnsigned()) - case 14: - let destinationTableIndex: TableIndex = try parseUnsigned() - let sourceTableIndex: TableIndex = try parseUnsigned() - return try v.visitTableCopy(dstTable: destinationTableIndex, srcTable: sourceTableIndex) - case 15: return try v.visitTableGrow(table: try parseUnsigned()) - case 16: return try v.visitTableSize(table: try parseUnsigned()) - case 17: return try v.visitTableFill(table: try parseUnsigned()) - default: - throw WasmParserError.unimplementedInstruction(code.rawValue, suffix: codeSuffix) - } + @inlinable mutating func visitMemoryCopy() throws -> (dstMem: UInt32, srcMem: UInt32) { + _ = try parseMemoryIndex() + _ = try parseMemoryIndex() + return (0, 0) + } + + @inlinable mutating func visitMemoryFill() throws -> UInt32 { + let zero = try stream.consumeAny() + guard zero == 0x00 else { + throw makeError(.zeroExpected(actual: zero)) } + return 0 } + @inlinable mutating func visitTableInit() throws -> (elemIndex: UInt32, table: UInt32) { + let elementIndex: ElementIndex = try parseUnsigned() + let tableIndex: TableIndex = try parseUnsigned() + return (elementIndex, tableIndex) + } + @inlinable mutating func visitElemDrop() throws -> UInt32 { + try parseUnsigned() + } + @inlinable mutating func visitTableCopy() throws -> (dstTable: UInt32, srcTable: UInt32) { + let destination: TableIndex = try parseUnsigned() + let source: TableIndex = try parseUnsigned() + return (destination, source) + } + @inlinable mutating func visitTableFill() throws -> UInt32 { + try parseUnsigned() + } + @inlinable mutating func visitTableGet() throws -> UInt32 { + try parseUnsigned() + } + @inlinable mutating func visitTableSet() throws -> UInt32 { + try parseUnsigned() + } + @inlinable mutating func visitTableGrow() throws -> UInt32 { + try parseUnsigned() + } + @inlinable mutating func visitTableSize() throws -> UInt32 { + try parseUnsigned() + } + @inlinable func claimNextByte() throws -> UInt8 { + return try stream.consumeAny() + } + + @inline(__always) + @inlinable + mutating func parseInstruction(visitor v: inout V) throws -> Bool { + return try parseBinaryInstruction(visitor: &v, decoder: &self) + } + + @usableFromInline struct InstructionFactory: AnyInstructionVisitor { - var insts: [Instruction] = [] + @usableFromInline var insts: [Instruction] = [] + @inlinable init() {} + + @inlinable mutating func visit(_ instruction: Instruction) throws { insts.append(instruction) } } - func parseConstExpression() throws -> ConstExpression { + @usableFromInline + mutating func parseConstExpression() throws -> ConstExpression { var factory = InstructionFactory() - var inst: InstructionCode + var isEnd: Bool repeat { - inst = try self.parseInstruction(visitor: &factory) - } while inst != .end + isEnd = try self.parseInstruction(visitor: &factory) + } while !isEnd return factory.insts } } @@ -871,6 +778,7 @@ extension Parser { extension Parser { /// > Note: /// + @usableFromInline func parseCustomSection(size: UInt32) throws -> CustomSection { let preNameIndex = stream.currentIndex let name = try parseName() @@ -878,7 +786,7 @@ extension Parser { let contentSize = Int(size) - nameSize guard contentSize >= 0 else { - throw WasmParserError.invalidSectionSize(size) + throw makeError(.invalidSectionSize(size)) } let bytes = try stream.consume(count: contentSize) @@ -888,12 +796,14 @@ extension Parser { /// > Note: /// + @inlinable func parseTypeSection() throws -> [FunctionType] { return try parseVector { try parseFunctionType() } } /// > Note: /// + @usableFromInline func parseImportSection() throws -> [Import] { return try parseVector { let module = try parseName() @@ -919,25 +829,29 @@ extension Parser { /// > Note: /// + @inlinable func parseFunctionSection() throws -> [TypeIndex] { return try parseVector { try parseUnsigned() } } /// > Note: /// + @usableFromInline func parseTableSection() throws -> [Table] { return try parseVector { try Table(type: parseTableType()) } } /// > Note: /// + @usableFromInline func parseMemorySection() throws -> [Memory] { return try parseVector { try Memory(type: parseLimits()) } } /// > Note: /// - func parseGlobalSection() throws -> [Global] { + @usableFromInline + mutating func parseGlobalSection() throws -> [Global] { return try parseVector { let type = try parseGlobalType() let expression = try parseConstExpression() @@ -947,6 +861,7 @@ extension Parser { /// > Note: /// + @usableFromInline func parseExportSection() throws -> [Export] { return try parseVector { let name = try parseName() @@ -971,13 +886,15 @@ extension Parser { /// > Note: /// + @usableFromInline func parseStartSection() throws -> FunctionIndex { return try parseUnsigned() } /// > Note: /// - func parseElementSection() throws -> [ElementSegment] { + @inlinable + mutating func parseElementSection() throws -> [ElementSegment] { return try parseVector { let flag = try ElementSegment.Flag(rawValue: parseUnsigned()) @@ -1008,7 +925,7 @@ extension Parser { let valueType = try parseValueType() guard case let .ref(refType) = valueType else { - throw WasmParserError.expectedRefType(actual: valueType) + throw makeError(.expectedRefType(actual: valueType)) } type = refType @@ -1020,7 +937,7 @@ extension Parser { // `elemkind` parsing as defined in the spec let elemKind = try parseUnsigned() as UInt32 guard elemKind == 0x00 else { - throw WasmParserError.unexpectedElementKind(expected: 0x00, actual: elemKind) + throw makeError(.unexpectedElementKind(expected: 0x00, actual: elemKind)) } } @@ -1038,6 +955,7 @@ extension Parser { /// > Note: /// + @inlinable func parseCodeSection() throws -> [Code] { return try parseVector { let size = try parseUnsigned() as UInt32 @@ -1049,22 +967,27 @@ extension Parser { } let totalLocals = localTypes.reduce(UInt64(0)) { $0 + UInt64($1.n) } guard totalLocals < limits.maxFunctionLocals else { - throw WasmParserError.tooManyLocals(totalLocals, limit: limits.maxFunctionLocals) + throw makeError(.tooManyLocals(totalLocals, limit: limits.maxFunctionLocals)) } let locals = localTypes.flatMap { (n: UInt32, type: ValueType) in return Array(repeating: type, count: Int(n)) } + let expressionStart = stream.currentIndex let expressionBytes = try stream.consume( - count: Int(size) - (stream.currentIndex - bodyStart) + count: Int(size) - (expressionStart - bodyStart) + ) + return Code( + locals: locals, expression: expressionBytes, + offset: expressionStart, features: features ) - return Code(locals: locals, expression: expressionBytes, hasDataCount: hasDataCount, features: features) } } /// > Note: /// - func parseDataSection() throws -> [DataSegment] { + @inlinable + mutating func parseDataSection() throws -> [DataSegment] { return try parseVector { let kind: UInt32 = try parseUnsigned() switch kind { @@ -1082,13 +1005,14 @@ extension Parser { let initializer = try parseVectorBytes() return .active(.init(index: index, offset: offset, initializer: initializer)) default: - throw WasmParserError.malformedDataSegmentKind(kind) + throw makeError(.malformedDataSegmentKind(kind)) } } } /// > Note: /// + @usableFromInline func parseDataCountSection() throws -> UInt32 { return try parseUnsigned() } @@ -1116,23 +1040,57 @@ public enum ParsingPayload { extension Parser { /// > Note: /// + @usableFromInline func parseMagicNumber() throws { let magicNumber = try stream.consume(count: 4) - guard magicNumber == [0x00, 0x61, 0x73, 0x6D] else { - throw WasmParserError.invalidMagicNumber(.init(magicNumber)) + guard magicNumber.elementsEqual(WASM_MAGIC) else { + throw makeError(.invalidMagicNumber(.init(magicNumber))) } } /// > Note: /// + @usableFromInline func parseVersion() throws -> [UInt8] { let version = try Array(stream.consume(count: 4)) guard version == [0x01, 0x00, 0x00, 0x00] else { - throw WasmParserError.unknownVersion(.init(version)) + throw makeError(.unknownVersion(.init(version))) } return version } + @usableFromInline + struct OrderTracking { + @usableFromInline + enum Order: UInt8 { + case initial = 0 + case type + case _import + case function + case table + case memory + case tag + case global + case export + case start + case element + case dataCount + case code + case data + } + + @usableFromInline + var last: Order = .initial + + @inlinable + mutating func track(order: Order, parser: Parser) throws { + guard last.rawValue < order.rawValue else { + throw parser.makeError(.sectionOutOfOrder) + } + last = order + } + } + /// Attempts to parse a chunk of the Wasm binary stream. /// /// - Returns: A `ParsingPayload` if the parsing was successful, otherwise `nil`. @@ -1161,6 +1119,7 @@ extension Parser { /// } /// } /// ``` + @inlinable public mutating func parseNext() throws -> ParsingPayload? { switch nextParseTarget { case .header: @@ -1177,30 +1136,56 @@ extension Parser { let sectionStart = stream.currentIndex let payload: ParsingPayload + let order: OrderTracking.Order? switch sectionID { - case 0: payload = .customSection(try parseCustomSection(size: sectionSize)) - case 1: payload = .typeSection(try parseTypeSection()) - case 2: payload = .importSection(try parseImportSection()) - case 3: payload = .functionSection(try parseFunctionSection()) - case 4: payload = .tableSection(try parseTableSection()) - case 5: payload = .memorySection(try parseMemorySection()) - case 6: payload = .globalSection(try parseGlobalSection()) - case 7: payload = .exportSection(try parseExportSection()) - case 8: payload = .startSection(try parseStartSection()) - case 9: payload = .elementSection(try parseElementSection()) - case 10: payload = .codeSection(try parseCodeSection()) - case 11: payload = .dataSection(try parseDataSection()) + case 0: + order = nil + payload = .customSection(try parseCustomSection(size: sectionSize)) + case 1: + order = .type + payload = .typeSection(try parseTypeSection()) + case 2: + order = ._import + payload = .importSection(try parseImportSection()) + case 3: + order = .function + payload = .functionSection(try parseFunctionSection()) + case 4: + order = .table + payload = .tableSection(try parseTableSection()) + case 5: + order = .memory + payload = .memorySection(try parseMemorySection()) + case 6: + order = .global + payload = .globalSection(try parseGlobalSection()) + case 7: + order = .export + payload = .exportSection(try parseExportSection()) + case 8: + order = .start + payload = .startSection(try parseStartSection()) + case 9: + order = .element + payload = .elementSection(try parseElementSection()) + case 10: + order = .code + payload = .codeSection(try parseCodeSection()) + case 11: + order = .data + payload = .dataSection(try parseDataSection()) case 12: - hasDataCount = true + order = .dataCount payload = .dataCount(try parseDataCountSection()) default: - throw WasmParserError.malformedSectionID(sectionID) + throw makeError(.malformedSectionID(sectionID)) + } + if let order = order { + try orderTracking.track(order: order, parser: self) } let expectedSectionEnd = sectionStart + Int(sectionSize) guard expectedSectionEnd == stream.currentIndex else { - throw WasmParserError.sectionSizeMismatch( - expected: expectedSectionEnd, actual: stream.currentIndex - ) + throw makeError(.sectionSizeMismatch(expected: expectedSectionEnd, actual: offset)) } return payload } diff --git a/Sources/WasmParser/WasmTypes.swift b/Sources/WasmParser/WasmTypes.swift index 7a315e96..e58ae783 100644 --- a/Sources/WasmParser/WasmTypes.swift +++ b/Sources/WasmParser/WasmTypes.swift @@ -11,9 +11,17 @@ public struct Code { // Parser state used to parse the expression body lazily @usableFromInline - internal let hasDataCount: Bool + internal let offset: Int @usableFromInline internal let features: WasmFeatureSet + + @inlinable + init(locals: [ValueType], expression: ArraySlice, offset: Int, features: WasmFeatureSet) { + self.locals = locals + self.expression = expression + self.offset = offset + self.features = features + } } extension Code: Equatable { @@ -180,25 +188,27 @@ public struct Global: Equatable { /// > Note: /// public struct ElementSegment: Equatable { + @usableFromInline struct Flag: OptionSet { - let rawValue: UInt32 + @usableFromInline let rawValue: UInt32 + @inlinable init(rawValue: UInt32) { self.rawValue = rawValue } - var segmentHasElemKind: Bool { + @inlinable var segmentHasElemKind: Bool { !contains(.usesExpressions) && rawValue != 0 } - var segmentHasRefType: Bool { + @inlinable var segmentHasRefType: Bool { contains(.usesExpressions) && rawValue != 4 } - static let isPassiveOrDeclarative = Flag(rawValue: 1 << 0) - static let isDeclarative = Flag(rawValue: 1 << 1) - static let hasTableIndex = Flag(rawValue: 1 << 1) - static let usesExpressions = Flag(rawValue: 1 << 2) + @usableFromInline static let isPassiveOrDeclarative = Flag(rawValue: 1 << 0) + @usableFromInline static let isDeclarative = Flag(rawValue: 1 << 1) + @usableFromInline static let hasTableIndex = Flag(rawValue: 1 << 1) + @usableFromInline static let usesExpressions = Flag(rawValue: 1 << 2) } public enum Mode: Equatable { @@ -210,6 +220,12 @@ public struct ElementSegment: Equatable { public let type: ReferenceType public let initializer: [ConstExpression] public let mode: Mode + + public init(type: ReferenceType, initializer: [ConstExpression], mode: Mode) { + self.type = type + self.initializer = initializer + self.mode = mode + } } /// Data segment in a module @@ -220,6 +236,12 @@ public enum DataSegment: Equatable { public let index: UInt32 public let offset: ConstExpression public let initializer: ArraySlice + + @inlinable init(index: UInt32, offset: ConstExpression, initializer: ArraySlice) { + self.index = index + self.offset = offset + self.initializer = initializer + } } case passive(ArraySlice) @@ -322,8 +344,82 @@ extension RawUnsignedInteger { } } -extension RawSignedInteger { - var unsigned: Unsigned { - .init(bitPattern: self) +extension Instruction.Load { + /// The alignment to the storage size of the memory access + /// in log2 form. + @_alwaysEmitIntoClient + public var naturalAlignment: Int { + switch self { + case .i32Load: return 2 + case .i64Load: return 3 + case .f32Load: return 2 + case .f64Load: return 3 + case .i32Load8S: return 0 + case .i32Load8U: return 0 + case .i32Load16S: return 1 + case .i32Load16U: return 1 + case .i64Load8S: return 0 + case .i64Load8U: return 0 + case .i64Load16S: return 1 + case .i64Load16U: return 1 + case .i64Load32S: return 2 + case .i64Load32U: return 2 + } + } + + /// The type of the value loaded from memory + @_alwaysEmitIntoClient + public var type: ValueType { + switch self { + case .i32Load: return .i32 + case .i64Load: return .i64 + case .f32Load: return .f32 + case .f64Load: return .f64 + case .i32Load8S: return .i32 + case .i32Load8U: return .i32 + case .i32Load16S: return .i32 + case .i32Load16U: return .i32 + case .i64Load8S: return .i64 + case .i64Load8U: return .i64 + case .i64Load16S: return .i64 + case .i64Load16U: return .i64 + case .i64Load32S: return .i64 + case .i64Load32U: return .i64 + } + } +} + +extension Instruction.Store { + /// The alignment to the storage size of the memory access + /// in log2 form. + @_alwaysEmitIntoClient + public var naturalAlignment: Int { + switch self { + case .i32Store: return 2 + case .i64Store: return 3 + case .f32Store: return 2 + case .f64Store: return 3 + case .i32Store8: return 0 + case .i32Store16: return 1 + case .i64Store8: return 0 + case .i64Store16: return 1 + case .i64Store32: return 2 + } + } + + /// The type of the value stored to memory + @_alwaysEmitIntoClient + public var type: ValueType { + switch self { + case .i32Store: return .i32 + case .i64Store: return .i64 + case .f32Store: return .f32 + case .f64Store: return .f64 + case .i32Store8: return .i32 + case .i32Store16: return .i32 + case .i64Store8: return .i64 + case .i64Store16: return .i64 + case .i64Store32: return .i64 + } } } diff --git a/Sources/WasmTypes/WasmTypes.swift b/Sources/WasmTypes/WasmTypes.swift index 38cd1a7b..e3e1e589 100644 --- a/Sources/WasmTypes/WasmTypes.swift +++ b/Sources/WasmTypes/WasmTypes.swift @@ -31,6 +31,8 @@ public enum ValueType: Equatable, Hashable { case f32 /// 64-bit IEEE 754 floating-point number. case f64 + /// 128-bit vector of packed integer or floating-point data. + case v128 /// Reference value type. case ref(ReferenceType) } diff --git a/Sources/_CWasmKit/include/DirectThreadedCode.inc b/Sources/_CWasmKit/include/DirectThreadedCode.inc index 9aafa5cd..e52f81c1 100644 --- a/Sources/_CWasmKit/include/DirectThreadedCode.inc +++ b/Sources/_CWasmKit/include/DirectThreadedCode.inc @@ -24,35 +24,56 @@ SWIFT_CC(swiftasync) static inline void wasmkit_tc_call(Sp sp, Pc pc, Md md, Ms SWIFT_CC(swift) uint64_t wasmkit_execute_call(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_call(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_compilingCall(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_compilingCall(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_compilingCall(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_internalCall(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_internalCall(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_internalCall(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_callIndirect(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_callIndirect(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_callIndirect(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); + return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); +} +SWIFT_CC(swiftasync) static inline void wasmkit_tc_resizeFrameHeader(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { + SWIFT_CC(swift) uint64_t wasmkit_execute_resizeFrameHeader(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); + void * _Nullable error = NULL; uint64_t next; + INLINE_CALL next = wasmkit_execute_resizeFrameHeader(&sp, &pc, &md, &ms, state, &error); + if (error) return wasmkit_execution_state_set_error(error, sp, state); + return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); +} +SWIFT_CC(swiftasync) static inline void wasmkit_tc_returnCall(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { + SWIFT_CC(swift) uint64_t wasmkit_execute_returnCall(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); + void * _Nullable error = NULL; uint64_t next; + INLINE_CALL next = wasmkit_execute_returnCall(&sp, &pc, &md, &ms, state, &error); + if (error) return wasmkit_execution_state_set_error(error, sp, state); + return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); +} +SWIFT_CC(swiftasync) static inline void wasmkit_tc_returnCallIndirect(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { + SWIFT_CC(swift) uint64_t wasmkit_execute_returnCallIndirect(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); + void * _Nullable error = NULL; uint64_t next; + INLINE_CALL next = wasmkit_execute_returnCallIndirect(&sp, &pc, &md, &ms, state, &error); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_unreachable(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_unreachable(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_unreachable(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_nop(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { @@ -95,168 +116,168 @@ SWIFT_CC(swiftasync) static inline void wasmkit_tc_endOfExecution(Sp sp, Pc pc, SWIFT_CC(swift) uint64_t wasmkit_execute_endOfExecution(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_endOfExecution(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32Load(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32Load(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32Load(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Load(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Load(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Load(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_f32Load(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_f32Load(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_f32Load(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_f64Load(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_f64Load(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_f64Load(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32Load8S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32Load8S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32Load8S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32Load8U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32Load8U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32Load8U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32Load16S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32Load16S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32Load16S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32Load16U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32Load16U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32Load16U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Load8S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Load8S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Load8S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Load8U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Load8U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Load8U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Load16S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Load16S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Load16S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Load16U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Load16U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Load16U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Load32S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Load32S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Load32S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Load32U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Load32U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Load32U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32Store(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32Store(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32Store(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Store(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Store(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Store(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_f32Store(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_f32Store(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_f32Store(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_f64Store(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_f64Store(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_f64Store(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32Store8(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32Store8(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32Store8(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32Store16(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32Store16(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32Store16(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Store8(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Store8(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Store8(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Store16(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Store16(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Store16(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64Store32(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64Store32(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64Store32(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_memorySize(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { @@ -269,14 +290,14 @@ SWIFT_CC(swiftasync) static inline void wasmkit_tc_memoryGrow(Sp sp, Pc pc, Md m SWIFT_CC(swift) uint64_t wasmkit_execute_memoryGrow(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_memoryGrow(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_memoryInit(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_memoryInit(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_memoryInit(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_memoryDataDrop(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { @@ -289,14 +310,14 @@ SWIFT_CC(swiftasync) static inline void wasmkit_tc_memoryCopy(Sp sp, Pc pc, Md m SWIFT_CC(swift) uint64_t wasmkit_execute_memoryCopy(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_memoryCopy(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_memoryFill(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_memoryFill(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_memoryFill(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_const32(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { @@ -447,56 +468,56 @@ SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32DivS(Sp sp, Pc pc, Md md, SWIFT_CC(swift) uint64_t wasmkit_execute_i32DivS(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32DivS(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64DivS(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64DivS(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64DivS(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32DivU(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32DivU(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32DivU(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64DivU(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64DivU(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64DivU(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32RemS(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32RemS(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32RemS(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64RemS(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64RemS(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64RemS(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32RemU(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32RemU(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32RemU(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64RemU(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64RemU(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64RemU(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32Eq(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { @@ -719,112 +740,112 @@ SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32TruncF32S(Sp sp, Pc pc, Md SWIFT_CC(swift) uint64_t wasmkit_execute_i32TruncF32S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32TruncF32S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32TruncF32U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32TruncF32U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32TruncF32U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32TruncSatF32S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32TruncSatF32S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32TruncSatF32S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32TruncSatF32U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32TruncSatF32U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32TruncSatF32U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32TruncF64S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32TruncF64S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32TruncF64S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32TruncF64U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32TruncF64U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32TruncF64U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32TruncSatF64S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32TruncSatF64S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32TruncSatF64S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i32TruncSatF64U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i32TruncSatF64U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i32TruncSatF64U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64TruncF32S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64TruncF32S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64TruncF32S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64TruncF32U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64TruncF32U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64TruncF32U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64TruncSatF32S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64TruncSatF32S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64TruncSatF32S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64TruncSatF32U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64TruncSatF32U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64TruncSatF32U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64TruncF64S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64TruncF64S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64TruncF64S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64TruncF64U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64TruncF64U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64TruncF64U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64TruncSatF64S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64TruncSatF64S(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64TruncSatF64S(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_i64TruncSatF64U(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_i64TruncSatF64U(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_i64TruncSatF64U(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_f32ConvertI32S(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { @@ -1179,14 +1200,14 @@ SWIFT_CC(swiftasync) static inline void wasmkit_tc_tableGet(Sp sp, Pc pc, Md md, SWIFT_CC(swift) uint64_t wasmkit_execute_tableGet(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_tableGet(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_tableSet(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_tableSet(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_tableSet(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_tableSize(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { @@ -1199,28 +1220,28 @@ SWIFT_CC(swiftasync) static inline void wasmkit_tc_tableGrow(Sp sp, Pc pc, Md md SWIFT_CC(swift) uint64_t wasmkit_execute_tableGrow(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_tableGrow(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_tableFill(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_tableFill(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_tableFill(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_tableCopy(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_tableCopy(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_tableCopy(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_tableInit(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { SWIFT_CC(swift) uint64_t wasmkit_execute_tableInit(Sp *sp, Pc *pc, Md *md, Ms *ms, SWIFT_CONTEXT void *state, SWIFT_ERROR_RESULT void **error); void * _Nullable error = NULL; uint64_t next; INLINE_CALL next = wasmkit_execute_tableInit(&sp, &pc, &md, &ms, state, &error); - if (error) return wasmkit_execution_state_set_error(error, state); + if (error) return wasmkit_execution_state_set_error(error, sp, state); return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); } SWIFT_CC(swiftasync) static inline void wasmkit_tc_tableElementDrop(Sp sp, Pc pc, Md md, Ms ms, SWIFT_CONTEXT void *state) { @@ -1249,6 +1270,9 @@ static const uintptr_t wasmkit_tc_exec_handlers[] = { (uintptr_t)((wasmkit_tc_exec)&wasmkit_tc_compilingCall), (uintptr_t)((wasmkit_tc_exec)&wasmkit_tc_internalCall), (uintptr_t)((wasmkit_tc_exec)&wasmkit_tc_callIndirect), + (uintptr_t)((wasmkit_tc_exec)&wasmkit_tc_resizeFrameHeader), + (uintptr_t)((wasmkit_tc_exec)&wasmkit_tc_returnCall), + (uintptr_t)((wasmkit_tc_exec)&wasmkit_tc_returnCallIndirect), (uintptr_t)((wasmkit_tc_exec)&wasmkit_tc_unreachable), (uintptr_t)((wasmkit_tc_exec)&wasmkit_tc_nop), (uintptr_t)((wasmkit_tc_exec)&wasmkit_tc_br), diff --git a/Sources/_CWasmKit/include/InlineCode.h b/Sources/_CWasmKit/include/InlineCode.h index 4eb10c86..d288f7b4 100644 --- a/Sources/_CWasmKit/include/InlineCode.h +++ b/Sources/_CWasmKit/include/InlineCode.h @@ -11,7 +11,7 @@ #include -#include "Platform.h" +#include "_CWasmKit.h" #if WASMKIT_USE_DIRECT_THREADED_CODE // Inline instruction implementation written in Swift side into C handler @@ -29,7 +29,7 @@ // /// NOTE: We use error storage in the execution state rather than `swift_error_result` /// because it's not permitted with `swiftasynccc`. -SWIFT_CC(swift) void wasmkit_execution_state_set_error(void *trap, SWIFT_CONTEXT void *state); +SWIFT_CC(swift) void wasmkit_execution_state_set_error(void *trap, Sp sp, SWIFT_CONTEXT void *state); // Define instruction handler functions generated by // Utilities/Sources/VMGen.swift diff --git a/Tests/WASITests/IntegrationTests.swift b/Tests/WASITests/IntegrationTests.swift index ecb97913..0b64e634 100644 --- a/Tests/WASITests/IntegrationTests.swift +++ b/Tests/WASITests/IntegrationTests.swift @@ -6,6 +6,9 @@ import XCTest final class IntegrationTests: XCTestCase { func testRunAll() throws { + #if os(Android) + throw XCTSkip("unable to run spectest on Android due to missing files on emulator") + #endif let testDir = URL(fileURLWithPath: #filePath) .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() .appendingPathComponent("Vendor/wasi-testsuite") diff --git a/Tests/WASITests/TestSupport.swift b/Tests/WASITests/TestSupport.swift new file mode 100644 index 00000000..2dd1a01f --- /dev/null +++ b/Tests/WASITests/TestSupport.swift @@ -0,0 +1,68 @@ +import Foundation + +enum TestSupport { + struct Error: Swift.Error, CustomStringConvertible { + let description: String + + init(description: String) { + self.description = description + } + + init(errno: Int32) { + self.init(description: String(cString: strerror(errno))) + } + } + + class TemporaryDirectory { + let path: String + var url: URL { URL(fileURLWithPath: path) } + + init() throws { + let tempdir = URL(fileURLWithPath: NSTemporaryDirectory()) + let templatePath = tempdir.appendingPathComponent("WasmKit.XXXXXX") + var template = [UInt8](templatePath.path.utf8).map({ Int8($0) }) + [Int8(0)] + + #if os(Windows) + if _mktemp_s(&template, template.count) != 0 { + throw Error(errno: errno) + } + if _mkdir(template) != 0 { + throw Error(errno: errno) + } + #else + if mkdtemp(&template) == nil { + #if os(Android) + throw Error(errno: __errno().pointee) + #else + throw Error(errno: errno) + #endif + } + #endif + + self.path = String(cString: template) + } + + func createDir(at relativePath: String) throws { + let directoryURL = url.appendingPathComponent(relativePath) + try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + } + + func createFile(at relativePath: String, contents: String) throws { + let fileURL = url.appendingPathComponent(relativePath) + guard let data = contents.data(using: .utf8) else { return } + FileManager.default.createFile(atPath: fileURL.path, contents: data, attributes: nil) + } + + func createSymlink(at relativePath: String, to target: String) throws { + let linkURL = url.appendingPathComponent(relativePath) + try FileManager.default.createSymbolicLink( + atPath: linkURL.path, + withDestinationPath: target + ) + } + + deinit { + _ = try? FileManager.default.removeItem(atPath: path) + } + } +} diff --git a/Tests/WASITests/WASITests.swift b/Tests/WASITests/WASITests.swift new file mode 100644 index 00000000..68ef114b --- /dev/null +++ b/Tests/WASITests/WASITests.swift @@ -0,0 +1,137 @@ +import XCTest + +@testable import WASI + +final class WASITests: XCTestCase { + func testPathOpen() throws { + #if os(Windows) + try XCTSkipIf(true) + #endif + let t = try TestSupport.TemporaryDirectory() + + try t.createDir(at: "External") + try t.createDir(at: "External/secret-dir-b") + try t.createFile(at: "External/secret-a.txt", contents: "Secret A") + try t.createFile(at: "External/secret-dir-b/secret-c.txt", contents: "Secret C") + try t.createDir(at: "Sandbox") + try t.createFile(at: "Sandbox/hello.txt", contents: "Hello") + try t.createSymlink(at: "Sandbox/link-hello.txt", to: "hello.txt") + try t.createDir(at: "Sandbox/world.dir") + try t.createSymlink(at: "Sandbox/link-world.dir", to: "world.dir") + try t.createSymlink(at: "Sandbox/link-external-secret-a.txt", to: "../External/secret-a.txt") + try t.createSymlink(at: "Sandbox/link-secret-dir-b", to: "../External/secret-dir-b") + try t.createSymlink(at: "Sandbox/link-updown-hello.txt", to: "../Sandbox/link-updown-hello.txt") + try t.createSymlink(at: "Sandbox/link-external-non-existent.txt", to: "../External/non-existent.txt") + try t.createSymlink(at: "Sandbox/link-root", to: "/") + try t.createSymlink(at: "Sandbox/link-loop.txt", to: "link-loop.txt") + + let wasi = try WASIBridgeToHost( + preopens: ["/Sandbox": t.url.appendingPathComponent("Sandbox").path] + ) + let mntFd: WASIAbi.Fd = 3 + + func assertResolve(_ path: String, followSymlink: Bool, directory: Bool = false) throws { + let fd = try wasi.path_open( + dirFd: mntFd, + dirFlags: followSymlink ? [.SYMLINK_FOLLOW] : [], + path: path, + oflags: directory ? [.DIRECTORY] : [], + fsRightsBase: .DIRECTORY_BASE_RIGHTS, + fsRightsInheriting: .DIRECTORY_INHERITING_RIGHTS, + fdflags: [] + ) + try wasi.fd_close(fd: fd) + } + + func assertNotResolve( + _ path: String, + followSymlink: Bool, + directory: Bool = false, + file: StaticString = #file, + line: UInt = #line, + _ checkError: ((WASIAbi.Errno) throws -> Void)? + ) throws { + do { + _ = try wasi.path_open( + dirFd: mntFd, + dirFlags: followSymlink ? [.SYMLINK_FOLLOW] : [], + path: path, + oflags: directory ? [.DIRECTORY] : [], + fsRightsBase: .DIRECTORY_BASE_RIGHTS, + fsRightsInheriting: .DIRECTORY_INHERITING_RIGHTS, + fdflags: [] + ) + XCTFail("Expected not to be able to open \(path)", file: file, line: line) + } catch { + guard let error = error as? WASIAbi.Errno else { + XCTFail("Expected WASIAbi.Errno error but got \(error)", file: file, line: line) + return + } + try checkError?(error) + } + } + + try assertNotResolve("non-existent.txt", followSymlink: false) { error in + XCTAssertEqual(error, .ENOENT) + } + + try assertResolve("link-hello.txt", followSymlink: true) + try assertNotResolve("link-hello.txt", followSymlink: false) { error in + XCTAssertEqual(error, .ELOOP) + } + try assertNotResolve("link-hello.txt", followSymlink: true, directory: true) { error in + XCTAssertEqual(error, .ENOTDIR) + } + + try assertNotResolve("link-hello.txt/", followSymlink: true) { error in + XCTAssertEqual(error, .ENOTDIR) + } + + try assertResolve("link-world.dir", followSymlink: true) + try assertNotResolve("link-world.dir", followSymlink: false) { error in + XCTAssertEqual(error, .ELOOP) + } + + try assertNotResolve("link-external-secret-a.txt", followSymlink: true) { error in + XCTAssertEqual(error, .EPERM) + } + try assertNotResolve("link-external-secret-a.txt", followSymlink: false) { error in + XCTAssertEqual(error, .ELOOP) + } + + try assertNotResolve("link-external-non-existent.txt", followSymlink: true) { error in + XCTAssertEqual(error, .EPERM) + } + try assertNotResolve("link-external-non-existent.txt", followSymlink: false) { error in + XCTAssertEqual(error, .ELOOP) + } + + try assertNotResolve("link-updown-hello.txt", followSymlink: true) { error in + XCTAssertEqual(error, .EPERM) + } + try assertNotResolve("link-updown-hello.txt", followSymlink: false) { error in + XCTAssertEqual(error, .ELOOP) + } + + try assertNotResolve("link-secret-dir-b/secret-c.txt", followSymlink: true) { error in + XCTAssertEqual(error, .EPERM) + } + try assertNotResolve("link-secret-dir-b/secret-c.txt", followSymlink: false) { error in + XCTAssertEqual(error, .ENOTDIR) + } + + try assertNotResolve("link-root", followSymlink: true) { error in + XCTAssertEqual(error, .EPERM) + } + try assertNotResolve("link-root", followSymlink: false) { error in + XCTAssertEqual(error, .ELOOP) + } + + try assertNotResolve("link-loop.txt", followSymlink: false) { error in + XCTAssertEqual(error, .ELOOP) + } + try assertNotResolve("link-loop.txt", followSymlink: true) { error in + XCTAssertEqual(error, .ELOOP) + } + } +} diff --git a/Tests/WATTests/EncoderTests.swift b/Tests/WATTests/EncoderTests.swift index 8a91c11d..d5adb8ca 100644 --- a/Tests/WATTests/EncoderTests.swift +++ b/Tests/WATTests/EncoderTests.swift @@ -78,7 +78,7 @@ class EncoderTests: XCTestCase { assertEqual(watModule.id, expectedName) switch watModule.source { case .text(var watModule): - moduleBytes = try encode(module: &watModule) + moduleBytes = try encode(module: &watModule, options: .default) case .binary(let bytes): moduleBytes = bytes case .quote(let watText): @@ -116,7 +116,12 @@ class EncoderTests: XCTestCase { let wast2jsonProcess = try Process.run( wast2json, - arguments: [wastFile.path, "-o", json.path] + arguments: [ + wastFile.path, + "--enable-memory64", + "--enable-tail-call", + "-o", json.path, + ] ) wast2jsonProcess.waitUntilExit() @@ -135,4 +140,37 @@ class EncoderTests: XCTestCase { } #endif } + + func testEncodeNameSection() throws { + let bytes = try wat2wasm( + """ + (module + (func $foo) + (func) + (func $bar) + ) + """, + options: EncodeOptions(nameSection: true) + ) + + var parser = WasmParser.Parser(bytes: bytes) + var customSections: [CustomSection] = [] + while let payload = try parser.parseNext() { + guard case .customSection(let section) = payload else { + continue + } + customSections.append(section) + } + let nameSection = customSections.first(where: { $0.name == "name" }) + let nameParser = NameSectionParser( + stream: StaticByteStream(bytes: nameSection?.bytes ?? []) + ) + let names = try nameParser.parseAll() + XCTAssertEqual(names.count, 1) + guard case .functions(let functionNames) = try XCTUnwrap(names.first) else { + XCTFail() + return + } + XCTAssertEqual(functionNames, [0: "foo", 2: "bar"]) + } } diff --git a/Tests/WATTests/LexerTests.swift b/Tests/WATTests/LexerTests.swift index 8bd1ed0b..ac987110 100644 --- a/Tests/WATTests/LexerTests.swift +++ b/Tests/WATTests/LexerTests.swift @@ -25,6 +25,7 @@ class LexerTests: XCTestCase { } func testLexComment() { + XCTAssertEqual(try collectToken("(; foo ;)"), [.blockComment]) try XCTAssertEqual( collectToken( """ @@ -40,6 +41,12 @@ class LexerTests: XCTestCase { } + func testLexBrokenComment() { + XCTAssertThrowsError(try collectToken("(;)")) + XCTAssertThrowsError(try collectToken("(; foo )")) + XCTAssertThrowsError(try collectToken(";)")) + } + func testLexIdAndString() throws { try XCTAssertEqual(collectToken("$foo"), [.id]) try XCTAssertEqual(collectToken("\"foo\""), [.string(Array("foo".utf8))]) @@ -75,6 +82,12 @@ class LexerTests: XCTestCase { } func testLexSpectest() throws { + // NOTE: We do the same check as a part of the EncoderTests, so it's + // usually redundant and time-wasting to run this test every time. + // Keeping it here just for local unit testing purposes. + try XCTSkipIf( + ProcessInfo.processInfo.environment["WASMKIT_LEXER_SPECTEST"] != "1" + ) var failureCount = 0 for filePath in Spectest.wastFiles() { print("Lexing \(filePath.path)...") diff --git a/Tests/WATTests/ParserTests.swift b/Tests/WATTests/ParserTests.swift index c4d4e660..dd3d638c 100644 --- a/Tests/WATTests/ParserTests.swift +++ b/Tests/WATTests/ParserTests.swift @@ -178,6 +178,12 @@ class ParserTests: XCTestCase { } func testParseSpectest() throws { + // NOTE: We do the same check as a part of the EncoderTests, so it's + // usually redundant and time-wasting to run this test every time. + // Keeping it here just for local unit testing purposes. + try XCTSkipIf( + ProcessInfo.processInfo.environment["WASMKIT_PARSER_SPECTEST"] != "1" + ) var failureCount = 0 var totalCount = 0 for filePath in Spectest.wastFiles(include: []) { diff --git a/Tests/WATTests/Spectest.swift b/Tests/WATTests/Spectest.swift index 1cf76e65..bc743ecf 100644 --- a/Tests/WATTests/Spectest.swift +++ b/Tests/WATTests/Spectest.swift @@ -17,7 +17,12 @@ enum Spectest { } static func wastFiles(include: [String] = [], exclude: [String] = ["annotations.wast"]) -> AnyIterator { - var allFiles = [testsuitePath, testsuitePath.appendingPathComponent("proposals/memory64")].flatMap { + var allFiles = [ + testsuitePath, + testsuitePath.appendingPathComponent("proposals/memory64"), + testsuitePath.appendingPathComponent("proposals/tail-call"), + rootDirectory.appendingPathComponent("Tests/WasmKitTests/ExtraSuite"), + ].flatMap { try! FileManager.default.contentsOfDirectory(at: $0, includingPropertiesForKeys: nil) }.makeIterator() diff --git a/Tests/WATTests/TestSupport.swift b/Tests/WATTests/TestSupport.swift index 41a677fe..a514232a 100644 --- a/Tests/WATTests/TestSupport.swift +++ b/Tests/WATTests/TestSupport.swift @@ -29,7 +29,11 @@ enum TestSupport { } #else if mkdtemp(&template) == nil { - throw Error(errno: errno) + #if os(Android) + throw Error(errno: __errno().pointee) + #else + throw Error(errno: errno) + #endif } #endif diff --git a/Tests/WITExtractorPluginTests/PluginSmokeTests.swift b/Tests/WITExtractorPluginTests/PluginSmokeTests.swift index 391ae045..5687053c 100644 --- a/Tests/WITExtractorPluginTests/PluginSmokeTests.swift +++ b/Tests/WITExtractorPluginTests/PluginSmokeTests.swift @@ -2,6 +2,9 @@ import XCTest class PluginSmokeTests: XCTestCase { func testExtractPlugin() throws { + #if WASMKIT_CI_TOOLCHAIN_NIGHTLY + throw XCTSkip("XFAIL: https://github.com/swiftwasm/WasmKit/issues/184") + #endif guard ProcessInfo.processInfo.environment["__XCODE_BUILT_PRODUCTS_DIR_PATHS"] == nil else { throw XCTSkip( "\"swift package resolve\" somehow fails to clone git repository only when invoking from Xcode test runner" diff --git a/Tests/WITExtractorPluginTests/TestSupport.swift b/Tests/WITExtractorPluginTests/TestSupport.swift index 7fa542b3..69a6e49d 100644 --- a/Tests/WITExtractorPluginTests/TestSupport.swift +++ b/Tests/WITExtractorPluginTests/TestSupport.swift @@ -25,7 +25,11 @@ struct TestSupport { var template = [UInt8](templatePath.path.utf8).map({ Int8($0) }) + [Int8(0)] if mkdtemp(&template) == nil { - throw Error(errno: errno) + #if os(Android) + throw Error(errno: __errno().pointee) + #else + throw Error(errno: errno) + #endif } let path = String(cString: template) diff --git a/Tests/WITExtractorTests/TestSupport.swift b/Tests/WITExtractorTests/TestSupport.swift index 3b88bbe6..ab7d898d 100644 --- a/Tests/WITExtractorTests/TestSupport.swift +++ b/Tests/WITExtractorTests/TestSupport.swift @@ -56,7 +56,11 @@ struct TestSupport { } #else if mkdtemp(&template) == nil { - throw Error(errno: errno) + #if os(Android) + throw Error(errno: __errno().pointee) + #else + throw Error(errno: errno) + #endif } #endif diff --git a/Tests/WITOverlayGeneratorTests/HostGeneratorTests.swift b/Tests/WITOverlayGeneratorTests/HostGeneratorTests.swift index 8c8cc085..5929b394 100644 --- a/Tests/WITOverlayGeneratorTests/HostGeneratorTests.swift +++ b/Tests/WITOverlayGeneratorTests/HostGeneratorTests.swift @@ -7,6 +7,9 @@ class HostGeneratorTests: XCTestCase { // Host generators are already executed before running this test suite by SwiftPM build tool plugin, // but execute again here to collect coverage data. func testGenerateFromFixtures() throws { + #if os(Android) + throw XCTSkip("unable to run spectest on Android due to missing files on emulator") + #endif let fixturesDir = RuntimeTestHarness.testsDirectory.appendingPathComponent("Fixtures") for fixture in try FileManager.default.contentsOfDirectory(atPath: fixturesDir.path) { let inputFileDir = fixturesDir.appendingPathComponent(fixture).appendingPathComponent("wit") diff --git a/Tests/WITOverlayGeneratorTests/Runtime/RuntimeTestHarness.swift b/Tests/WITOverlayGeneratorTests/Runtime/RuntimeTestHarness.swift index 957d0060..4aadd441 100644 --- a/Tests/WITOverlayGeneratorTests/Runtime/RuntimeTestHarness.swift +++ b/Tests/WITOverlayGeneratorTests/Runtime/RuntimeTestHarness.swift @@ -165,6 +165,7 @@ struct RuntimeTestHarness { arguments: [ "-target", "wasm32-unknown-none-wasm", "-enable-experimental-feature", "Embedded", + "-enable-experimental-feature", "Extern", "-wmo", "-Xcc", "-fdeclspec", "-Xfrontend", "-disable-stack-protector", "-Xlinker", "--no-entry", "-Xclang-linker", "-nostdlib", @@ -176,6 +177,7 @@ struct RuntimeTestHarness { inputFiles: inputFiles, arguments: [ "-target", "wasm32-unknown-wasi", + "-enable-experimental-feature", "Extern", "-static-stdlib", "-Xclang-linker", "-mexec-model=reactor", "-resource-dir", configuration.wasiSwiftSDKPath.appendingPathComponent("/swift.xctoolchain/usr/lib/swift_static").path, diff --git a/Tests/WITTests/TextParser/ParseFunctionDeclTests.swift b/Tests/WITTests/TextParser/ParseFunctionDeclTests.swift index 671c5443..e8f7c1cd 100644 --- a/Tests/WITTests/TextParser/ParseFunctionDeclTests.swift +++ b/Tests/WITTests/TextParser/ParseFunctionDeclTests.swift @@ -8,7 +8,8 @@ class ParseFunctionDeclTests: XCTestCase { var lexer = Lexer(cursor: .init(input: text)) return try ResourceFunctionSyntax.parse( lexer: &lexer, - documents: DocumentsSyntax(comments: []) + documents: DocumentsSyntax(comments: []), + attributes: [] ) } diff --git a/Tests/WITTests/TextParser/ParseInterfaceTests.swift b/Tests/WITTests/TextParser/ParseInterfaceTests.swift index 5bf22dd3..9dcb7ceb 100644 --- a/Tests/WITTests/TextParser/ParseInterfaceTests.swift +++ b/Tests/WITTests/TextParser/ParseInterfaceTests.swift @@ -8,7 +8,7 @@ class ParseInterfaceTests: XCTestCase { var lexer = Lexer(cursor: .init(input: text)) return try InterfaceSyntax.parse( lexer: &lexer, - documents: DocumentsSyntax(comments: []) + documents: DocumentsSyntax(comments: []), attributes: [] ) } diff --git a/Tests/WITTests/TextParser/ParseTopTests.swift b/Tests/WITTests/TextParser/ParseTopTests.swift index 34e56408..b6642e4f 100644 --- a/Tests/WITTests/TextParser/ParseTopTests.swift +++ b/Tests/WITTests/TextParser/ParseTopTests.swift @@ -109,7 +109,7 @@ class ParseTopTests: XCTestCase { func testTopLevelUseAs() throws { var lexer = Lexer(cursor: .init(input: "use abc as xyz")) - let use = try TopLevelUseSyntax.parse(lexer: &lexer, documents: .init(comments: [])) + let use = try TopLevelUseSyntax.parse(lexer: &lexer, documents: .init(comments: []), attributes: []) XCTAssertEqual(use.item.name.text, "abc") XCTAssertEqual(use.asName?.text, "xyz") } @@ -132,7 +132,7 @@ class ParseTopTests: XCTestCase { func testUsePath() throws { var lexer = Lexer(cursor: .init(input: "use ns1:pkg1/item1@1.0.0")) - let use = try TopLevelUseSyntax.parse(lexer: &lexer, documents: .init(comments: [])) + let use = try TopLevelUseSyntax.parse(lexer: &lexer, documents: .init(comments: []), attributes: []) XCTAssertEqual(use.item.name.text, "item1") guard case let .package(id, _) = use.item else { XCTFail("expected package but got \(use.item)") @@ -141,6 +141,59 @@ class ParseTopTests: XCTestCase { XCTAssertEqual(id.namespace.text, "ns1") XCTAssertEqual(id.name.text, "pkg1") } + + func testAttributeSince() throws { + var lexer = Lexer( + cursor: .init( + input: """ + @since(version = 1.0.0) + @since(version = 1.0.0, feature = foo-bar) + """ + )) + let attributes = try AttributeSyntax.parseItems(lexer: &lexer) + guard attributes.count == 2 else { + XCTFail("expected 2 attributes but got \(attributes)") + return + } + do { + guard case let .since(attribute) = attributes[0] else { + XCTFail("expected since but got \(attributes[0])") + return + } + XCTAssertEqual(attribute.version.description, "1.0.0") + XCTAssertEqual(attribute.feature?.text, nil) + } + do { + guard case let .since(attribute) = attributes[1] else { + XCTFail("expected since but got \(attributes[1])") + return + } + XCTAssertEqual(attribute.version.description, "1.0.0") + XCTAssertEqual(attribute.feature?.text, "foo-bar") + } + } + + func testAttributeUnstable() throws { + var lexer = Lexer(cursor: .init(input: "@unstable(feature = foo)")) + let attributes = try AttributeSyntax.parseItems(lexer: &lexer) + XCTAssertEqual(attributes.count, 1) + guard case let .unstable(attribute) = attributes.first else { + XCTFail("expected since but got \(attributes)") + return + } + XCTAssertEqual(attribute.feature.text, "foo") + } + + func testAttributeDeprecated() throws { + var lexer = Lexer(cursor: .init(input: "@deprecated(version = 1.0.3)")) + let attributes = try AttributeSyntax.parseItems(lexer: &lexer) + XCTAssertEqual(attributes.count, 1) + guard case let .deprecated(attribute) = attributes.first else { + XCTFail("expected since but got \(attributes)") + return + } + XCTAssertEqual(attribute.version.description, "1.0.3") + } } extension SourceFileSyntax { diff --git a/Tests/WITTests/TextParser/ParseTypesTests.swift b/Tests/WITTests/TextParser/ParseTypesTests.swift index 85336e92..e8b2272a 100644 --- a/Tests/WITTests/TextParser/ParseTypesTests.swift +++ b/Tests/WITTests/TextParser/ParseTypesTests.swift @@ -48,7 +48,7 @@ class ParseTestsTests: XCTestCase { """ ) ) - let interface = try InterfaceSyntax.parse(lexer: &lexer, documents: .init(comments: [])) + let interface = try InterfaceSyntax.parse(lexer: &lexer, documents: .init(comments: []), attributes: []) XCTAssertEqual(interface.items.count, 30) } @@ -72,7 +72,7 @@ class ParseTestsTests: XCTestCase { """ ) ) - let typeDef = try TypeDefSyntax.parseResource(lexer: &lexer, documents: .init(comments: [])) + let typeDef = try TypeDefSyntax.parseResource(lexer: &lexer, documents: .init(comments: []), attributes: []) XCTAssertEqual(typeDef.name.text, "r1") guard case let .resource(resource) = typeDef.body else { XCTFail("unexpected type kind: \(typeDef.body)") @@ -93,7 +93,7 @@ class ParseTestsTests: XCTestCase { """ ) ) - let typeDef = try TypeDefSyntax.parseVariant(lexer: &lexer, documents: .init(comments: [])) + let typeDef = try TypeDefSyntax.parseVariant(lexer: &lexer, documents: .init(comments: []), attributes: []) XCTAssertEqual(typeDef.name.text, "r1") guard case let .variant(variant) = typeDef.body else { XCTFail("unexpected type kind: \(typeDef.body)") diff --git a/Tests/WITTests/TextParser/ParseWorldTests.swift b/Tests/WITTests/TextParser/ParseWorldTests.swift index e19d519d..689345c8 100644 --- a/Tests/WITTests/TextParser/ParseWorldTests.swift +++ b/Tests/WITTests/TextParser/ParseWorldTests.swift @@ -8,7 +8,8 @@ final class ParseWorldTests: XCTestCase { var lexer = Lexer(cursor: .init(input: text)) return try WorldSyntax.parse( lexer: &lexer, - documents: DocumentsSyntax(comments: []) + documents: DocumentsSyntax(comments: []), + attributes: [] ) } diff --git a/Tests/WasmKitTests/ExecutionTests.swift b/Tests/WasmKitTests/ExecutionTests.swift index 8c1b0a37..b4de3efc 100644 --- a/Tests/WasmKitTests/ExecutionTests.swift +++ b/Tests/WasmKitTests/ExecutionTests.swift @@ -51,4 +51,90 @@ final class ExecutionTests: XCTestCase { let results = try _start() XCTAssertEqual(results, [.i32(42)]) } + + func expectTrap(_ wat: String, assertTrap: (Trap) throws -> Void) throws { + let module = try parseWasm( + bytes: wat2wasm(wat, options: EncodeOptions(nameSection: true)) + ) + + let engine = Engine() + let store = Store(engine: engine) + var imports = Imports() + for importEntry in module.imports { + guard case .function(let type) = importEntry.descriptor else { continue } + let function = try Function( + store: store, + type: module.resolveFunctionType(type), + body: { _, _ in + return [] + } + ) + imports.define(importEntry, .function(function)) + } + let instance = try module.instantiate(store: store, imports: imports) + let _start = try XCTUnwrap(instance.exports[function: "_start"]) + + let trap: Trap + do { + try _start() + XCTFail("expect unreachable trap") + return + } catch let error { + trap = try XCTUnwrap(error as? Trap) + } + try assertTrap(trap) + } + + func testBacktraceBasic() throws { + try expectTrap( + """ + (module + (func $foo + unreachable + ) + (func $bar + (call $foo) + ) + (func (export "_start") + (call $bar) + ) + ) + """ + ) { trap in + XCTAssertEqual( + trap.backtrace?.symbols.compactMap(\.?.name), + [ + "foo", + "bar", + "_start", + ]) + } + } + + func testBacktraceWithImports() throws { + try expectTrap( + """ + (module + (func (import "env" "bar")) + (func + unreachable + ) + (func $bar + (call 1) + ) + (func (export "_start") + (call $bar) + ) + ) + """ + ) { trap in + XCTAssertEqual( + trap.backtrace?.symbols.compactMap(\.?.name), + [ + "wasm function[1]", + "bar", + "_start", + ]) + } + } } diff --git a/Tests/WasmKitTests/ExtraSuite/block_type.wast b/Tests/WasmKitTests/ExtraSuite/block_type.wast new file mode 100644 index 00000000..26cffae9 --- /dev/null +++ b/Tests/WasmKitTests/ExtraSuite/block_type.wast @@ -0,0 +1,78 @@ +(module + (type (;0;) (func (param))) + (type (;1;) (func (param i32))) + (type (;2;) (func (param i32 i32))) + (type (;3;) (func (param i32 i32 i32))) + (type (;4;) (func (param i32 i32 i32 i32))) + (type (;5;) (func (param i32 i32 i32 i32 i32))) + (type (;6;) (func (param i32 i32 i32 i32 i32 i32))) + (type (;7;) (func (param i32 i32 i32 i32 i32 i32 i32))) + (type (;8;) (func (param i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;9;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;10;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;11;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;12;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;13;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;14;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;15;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;16;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;17;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;18;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;19;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;20;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;21;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;22;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;23;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;24;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;25;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;26;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;27;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;28;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;29;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;30;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;31;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;32;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;33;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;34;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;35;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;36;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;37;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;38;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;39;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;40;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;41;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;42;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;43;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;44;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;45;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;46;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;47;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;48;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;49;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;50;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;51;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;52;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;53;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;54;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;55;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;56;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;57;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;58;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;59;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;60;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;61;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;62;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;63;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;64;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;65;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + + (func (export "empty") + (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) + (; 0x40 in signed leb128 encoding is multi-byte ([0xc0, 0x00]) and it should be parsed as s33 ;) + (block (type 0x40) + (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) (drop) + ) + ) +) + +(assert_return (invoke "empty")) diff --git a/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift b/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift index 9f76ea0f..ce03d9b7 100644 --- a/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift +++ b/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift @@ -1,8 +1,12 @@ import WasmKit +import WasmKitFuzzing import XCTest final class FuzzTranslatorRegressionTests: XCTestCase { - func testRunAll() async throws { + func testRunAll() throws { + #if os(Android) + throw XCTSkip("Test skipped due to absolute path #filePath unavailable on emulator") + #endif let sourceRoot = URL(fileURLWithPath: #filePath) .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() let failCasesDir = @@ -12,33 +16,11 @@ final class FuzzTranslatorRegressionTests: XCTestCase { for file in try FileManager.default.contentsOfDirectory(atPath: failCasesDir.path) { let path = failCasesDir.appendingPathComponent(file).path print("Fuzz regression test: \(path.dropFirst(sourceRoot.path.count + 1))") - let data = try Data(contentsOf: URL(fileURLWithPath: path)) do { - let module = try WasmKit.parseWasm(bytes: Array(data)) - let engine = Engine(configuration: EngineConfiguration(compilationMode: .eager)) - let store = Store(engine: engine) - var imports = Imports() - for importEntry in module.imports { - let value: ExternalValueConvertible - switch importEntry.descriptor { - case .function(let typeIndex): - let type = module.types[Int(typeIndex)] - value = Function(store: store, type: type) { _, _ in - fatalError("unreachable") - } - case .global(let globalType): - value = try Global(store: store, type: globalType, value: .i32(0)) - case .memory(let memoryType): - value = try Memory(store: store, type: memoryType) - case .table(let tableType): - value = try Table(store: store, type: tableType) - } - imports.define(module: importEntry.module, name: importEntry.name, value.externalValue) - } - _ = try module.instantiate(store: store, imports: imports) + try WasmKitFuzzing.fuzzInstantiation(bytes: Array(data)) } catch { - // Explicit errors are ok + // Skip exceptions without crash } } } diff --git a/Tests/WasmKitTests/Spectest/TestCase.swift b/Tests/WasmKitTests/Spectest/TestCase.swift index d4c351ee..2fb1809e 100644 --- a/Tests/WasmKitTests/Spectest/TestCase.swift +++ b/Tests/WasmKitTests/Spectest/TestCase.swift @@ -259,13 +259,9 @@ extension WastRunContext { do { _ = try instantiate(module: module) - } catch let error as InstantiationError { - guard error.assertionText.contains(message) else { - return .failed("assertion mismatch: expected: \(message), actual: \(error.assertionText)") - } } catch let error as Trap { - guard error.assertionText.contains(message) else { - return .failed("assertion mismatch: expected: \(message), actual: \(error.assertionText)") + guard error.reason.description.contains(message) else { + return .failed("assertion mismatch: expected: \(message), actual: \(error.reason.description)") } } catch { return .failed("\(error)") @@ -284,8 +280,8 @@ extension WastRunContext { _ = try wastExecute(execute: execute) return .failed("trap expected: \(message)") } catch let trap as Trap { - guard trap.assertionText.contains(message) else { - return .failed("assertion mismatch: expected: \(message), actual: \(trap.assertionText)") + guard trap.reason.description.contains(message) else { + return .failed("assertion mismatch: expected: \(message), actual: \(trap.reason.description)") } return .passed } catch { @@ -296,8 +292,8 @@ extension WastRunContext { _ = try wastInvoke(call: call) return .failed("trap expected: \(message)") } catch let trap as Trap { - guard trap.assertionText.contains(message) else { - return .failed("assertion mismatch: expected: \(message), actual: \(trap.assertionText)") + guard trap.reason.description.contains(message) else { + return .failed("assertion mismatch: expected: \(message), actual: \(trap.reason.description)") } return .passed } @@ -314,8 +310,8 @@ extension WastRunContext { do { _ = try instantiate(module: module) } catch let error as ImportError { - guard error.assertionText.contains(message) else { - return .failed("assertion mismatch: expected: \(message), actual: \(error.assertionText)") + guard error.message.text.contains(message) else { + return .failed("assertion mismatch: expected: \(message), actual: \(error.message.text)") } } catch { return .failed("\(error)") @@ -440,48 +436,7 @@ extension Array where Element == Value { extension Swift.Error { var text: String { if let error = self as? WasmParserError { - switch error { - case .invalidMagicNumber: - return "magic header not detected" - case .unknownVersion: - return "unknown binary version" - case .invalidUTF8: - return "malformed UTF-8 encoding" - case .zeroExpected: - return "zero byte expected" - case .inconsistentFunctionAndCodeLength: - return "function and code section have inconsistent lengths" - case .tooManyLocals: - return "too many locals" - case .invalidSectionSize: - // XXX: trailing "unexpected end" is just for making spectest happy - // The reference interpreter raises EOF error when the custom content - // size is negative[^1], and custom.wast contains a test case that depends - // on the behavior[^2]. - // [^1]: https://github.com/WebAssembly/spec/blob/653938a88c6f40eb886d5980ca315136eb861d03/interpreter/binary/decode.ml#L20 - // [^2]: https://github.com/WebAssembly/spec/blob/653938a88c6f40eb886d5980ca315136eb861d03/test/core/custom.wast#L76-L82 - return "invalid section size, unexpected end" - case .malformedSectionID: - return "malformed section id" - case .endOpcodeExpected: - return "END opcode expected" - case .unexpectedEnd: - return "unexpected end of section or function" - case .inconsistentDataCountAndDataSectionLength: - return "data count and data section have inconsistent lengths" - case .expectedRefType: - return "malformed reference type" - case .sectionSizeMismatch: - return "section size mismatch" - case .illegalOpcode: - return "illegal opcode" - case .malformedMutability: - return "malformed mutability" - case .integerRepresentationTooLong: - return "integer representation too long" - default: - return String(describing: error) - } + return error.description } return "unknown error: \(self)" diff --git a/Tests/WasmKitTests/SpectestTests.swift b/Tests/WasmKitTests/SpectestTests.swift index 5a5bec48..6fece81f 100644 --- a/Tests/WasmKitTests/SpectestTests.swift +++ b/Tests/WasmKitTests/SpectestTests.swift @@ -12,12 +12,16 @@ final class SpectestTests: XCTestCase { [ Self.testsuite.path, Self.testsuite.appendingPathComponent("proposals/memory64").path, + Self.testsuite.appendingPathComponent("proposals/tail-call").path, Self.projectDir.appendingPathComponent("Tests/WasmKitTests/ExtraSuite").path, ] } /// Run all the tests in the spectest suite. func testRunAll() async throws { + #if os(Android) + throw XCTSkip("unable to run spectest on Android due to missing files on emulator") + #endif let defaultConfig = EngineConfiguration() let ok = try await spectest( path: Self.testPaths, @@ -30,6 +34,9 @@ final class SpectestTests: XCTestCase { } func testRunAllWithTokenThreading() async throws { + #if os(Android) + throw XCTSkip("unable to run spectest on Android due to missing files on emulator") + #endif let defaultConfig = EngineConfiguration() guard defaultConfig.threadingModel != .token else { return } // Sanity check that non-default threading models work. diff --git a/Tests/WasmParserTests/LEBTests.swift b/Tests/WasmParserTests/LEBTests.swift index 69061182..e3fa905c 100644 --- a/Tests/WasmParserTests/LEBTests.swift +++ b/Tests/WasmParserTests/LEBTests.swift @@ -64,13 +64,13 @@ final class LEBTest: XCTestCase { extension FixedWidthInteger where Self: UnsignedInteger { fileprivate init(LEB bytes: [UInt8]) throws { let stream = StaticByteStream(bytes: bytes) - try self.init(LEB: stream) + self = try decodeLEB128(stream: stream) } } -extension FixedWidthInteger where Self: SignedInteger { +extension FixedWidthInteger where Self: RawSignedInteger { fileprivate init(LEB bytes: [UInt8]) throws { let stream = StaticByteStream(bytes: bytes) - try self.init(LEB: stream) + self = try decodeLEB128(stream: stream) } } diff --git a/Utilities/Instructions.json b/Utilities/Instructions.json index 01ecc9ab..953a4073 100644 --- a/Utilities/Instructions.json +++ b/Utilities/Instructions.json @@ -1,203 +1,205 @@ [ - ["mvp" , "unreachable" , null , "0x00", "unreachable" , "visitUnreachable" , [] ], - ["mvp" , "nop" , null , "0x01", "nop" , "visitNop" , [] ], - ["mvp" , "block" , null , "0x02", "block" , "visitBlock" , [["blockType", "BlockType"]] ], - ["mvp" , "loop" , null , "0x03", "loop" , "visitLoop" , [["blockType", "BlockType"]] ], - ["mvp" , "if" , null , "0x04", "if" , "visitIf" , [["blockType", "BlockType"]] ], - ["mvp" , "else" , null , "0x05", "else" , "visitElse" , [] ], - ["mvp" , "end" , null , "0x0B", "end" , "visitEnd" , [] ], - ["mvp" , "br" , null , "0x0C", "br" , "visitBr" , [["relativeDepth", "UInt32"]] ], - ["mvp" , "br_if" , null , "0x0D", "brIf" , "visitBrIf" , [["relativeDepth", "UInt32"]] ], - ["mvp" , "br_table" , null , "0x0E", "brTable" , "visitBrTable" , [["targets", "BrTable"]] ], - ["mvp" , "return" , null , "0x0F", "return" , "visitReturn" , [] ], - ["mvp" , "call" , null , "0x10", "call" , "visitCall" , [["functionIndex", "UInt32"]] ], - ["mvp" , "call_indirect" , null , "0x11", "callIndirect" , "visitCallIndirect" , [["typeIndex", "UInt32"], ["tableIndex", "UInt32"]]], - ["mvp" , "drop" , null , "0x1A", "drop" , "visitDrop" , [] ], - ["mvp" , "select" , null , "0x1B", "select" , "visitSelect" , [] ], - ["referenceTypes" , null , null , "0x1C", "typedSelect" , "visitTypedSelect" , [["type", "ValueType"]] ], - ["mvp" , "local.get" , null , "0x20", "localGet" , "visitLocalGet" , [["localIndex", "UInt32"]] ], - ["mvp" , "local.set" , null , "0x21", "localSet" , "visitLocalSet" , [["localIndex", "UInt32"]] ], - ["mvp" , "local.tee" , null , "0x22", "localTee" , "visitLocalTee" , [["localIndex", "UInt32"]] ], - ["mvp" , "global.get" , null , "0x23", "globalGet" , "visitGlobalGet" , [["globalIndex", "UInt32"]] ], - ["mvp" , "global.set" , null , "0x24", "globalSet" , "visitGlobalSet" , [["globalIndex", "UInt32"]] ], - ["mvp" , "i32.load" , null , "0x28", "i32Load" , "visitI32Load" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.load" , null , "0x29", "i64Load" , "visitI64Load" , [["memarg", "MemArg"]] ], - ["mvp" , "f32.load" , null , "0x2A", "f32Load" , "visitF32Load" , [["memarg", "MemArg"]] ], - ["mvp" , "f64.load" , null , "0x2B", "f64Load" , "visitF64Load" , [["memarg", "MemArg"]] ], - ["mvp" , "i32.load8_s" , null , "0x2C", "i32Load8S" , "visitI32Load8S" , [["memarg", "MemArg"]] ], - ["mvp" , "i32.load8_u" , null , "0x2D", "i32Load8U" , "visitI32Load8U" , [["memarg", "MemArg"]] ], - ["mvp" , "i32.load16_s" , null , "0x2E", "i32Load16S" , "visitI32Load16S" , [["memarg", "MemArg"]] ], - ["mvp" , "i32.load16_u" , null , "0x2F", "i32Load16U" , "visitI32Load16U" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.load8_s" , null , "0x30", "i64Load8S" , "visitI64Load8S" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.load8_u" , null , "0x31", "i64Load8U" , "visitI64Load8U" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.load16_s" , null , "0x32", "i64Load16S" , "visitI64Load16S" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.load16_u" , null , "0x33", "i64Load16U" , "visitI64Load16U" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.load32_s" , null , "0x34", "i64Load32S" , "visitI64Load32S" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.load32_u" , null , "0x35", "i64Load32U" , "visitI64Load32U" , [["memarg", "MemArg"]] ], - ["mvp" , "i32.store" , null , "0x36", "i32Store" , "visitI32Store" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.store" , null , "0x37", "i64Store" , "visitI64Store" , [["memarg", "MemArg"]] ], - ["mvp" , "f32.store" , null , "0x38", "f32Store" , "visitF32Store" , [["memarg", "MemArg"]] ], - ["mvp" , "f64.store" , null , "0x39", "f64Store" , "visitF64Store" , [["memarg", "MemArg"]] ], - ["mvp" , "i32.store8" , null , "0x3A", "i32Store8" , "visitI32Store8" , [["memarg", "MemArg"]] ], - ["mvp" , "i32.store16" , null , "0x3B", "i32Store16" , "visitI32Store16" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.store8" , null , "0x3C", "i64Store8" , "visitI64Store8" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.store16" , null , "0x3D", "i64Store16" , "visitI64Store16" , [["memarg", "MemArg"]] ], - ["mvp" , "i64.store32" , null , "0x3E", "i64Store32" , "visitI64Store32" , [["memarg", "MemArg"]] ], - ["mvp" , "memory.size" , null , "0x3F", "memorySize" , "visitMemorySize" , [["memory", "UInt32"]] ], - ["mvp" , "memory.grow" , null , "0x40", "memoryGrow" , "visitMemoryGrow" , [["memory", "UInt32"]] ], - ["mvp" , "i32.const" , null , "0x41", "i32Const" , "visitI32Const" , [["value", "Int32"]] ], - ["mvp" , "i64.const" , null , "0x42", "i64Const" , "visitI64Const" , [["value", "Int64"]] ], - ["mvp" , "f32.const" , null , "0x43", "f32Const" , "visitF32Const" , [["value", "IEEE754.Float32"]] ], - ["mvp" , "f64.const" , null , "0x44", "f64Const" , "visitF64Const" , [["value", "IEEE754.Float64"]] ], - ["referenceTypes" , "ref.null" , null , "0xD0", "refNull" , "visitRefNull" , [["type", "ReferenceType"]] ], - ["referenceTypes" , "ref.is_null" , null , "0xD1", "refIsNull" , "visitRefIsNull" , [] ], - ["referenceTypes" , "ref.func" , null , "0xD2", "refFunc" , "visitRefFunc" , [["functionIndex", "UInt32"]] ], - ["mvp" , "i32.eqz" , null , "0x45", "i32Eqz" , "visitI32Eqz" , [] ], - ["mvp" , "i32.eq" , null , "0x46", "i32Eq" , "visitI32Eq" , [] ], - ["mvp" , "i32.ne" , null , "0x47", "i32Ne" , "visitI32Ne" , [] ], - ["mvp" , "i32.lt_s" , null , "0x48", "i32LtS" , "visitI32LtS" , [] ], - ["mvp" , "i32.lt_u" , null , "0x49", "i32LtU" , "visitI32LtU" , [] ], - ["mvp" , "i32.gt_s" , null , "0x4A", "i32GtS" , "visitI32GtS" , [] ], - ["mvp" , "i32.gt_u" , null , "0x4B", "i32GtU" , "visitI32GtU" , [] ], - ["mvp" , "i32.le_s" , null , "0x4C", "i32LeS" , "visitI32LeS" , [] ], - ["mvp" , "i32.le_u" , null , "0x4D", "i32LeU" , "visitI32LeU" , [] ], - ["mvp" , "i32.ge_s" , null , "0x4E", "i32GeS" , "visitI32GeS" , [] ], - ["mvp" , "i32.ge_u" , null , "0x4F", "i32GeU" , "visitI32GeU" , [] ], - ["mvp" , "i64.eqz" , null , "0x50", "i64Eqz" , "visitI64Eqz" , [] ], - ["mvp" , "i64.eq" , null , "0x51", "i64Eq" , "visitI64Eq" , [] ], - ["mvp" , "i64.ne" , null , "0x52", "i64Ne" , "visitI64Ne" , [] ], - ["mvp" , "i64.lt_s" , null , "0x53", "i64LtS" , "visitI64LtS" , [] ], - ["mvp" , "i64.lt_u" , null , "0x54", "i64LtU" , "visitI64LtU" , [] ], - ["mvp" , "i64.gt_s" , null , "0x55", "i64GtS" , "visitI64GtS" , [] ], - ["mvp" , "i64.gt_u" , null , "0x56", "i64GtU" , "visitI64GtU" , [] ], - ["mvp" , "i64.le_s" , null , "0x57", "i64LeS" , "visitI64LeS" , [] ], - ["mvp" , "i64.le_u" , null , "0x58", "i64LeU" , "visitI64LeU" , [] ], - ["mvp" , "i64.ge_s" , null , "0x59", "i64GeS" , "visitI64GeS" , [] ], - ["mvp" , "i64.ge_u" , null , "0x5A", "i64GeU" , "visitI64GeU" , [] ], - ["mvp" , "f32.eq" , null , "0x5B", "f32Eq" , "visitF32Eq" , [] ], - ["mvp" , "f32.ne" , null , "0x5C", "f32Ne" , "visitF32Ne" , [] ], - ["mvp" , "f32.lt" , null , "0x5D", "f32Lt" , "visitF32Lt" , [] ], - ["mvp" , "f32.gt" , null , "0x5E", "f32Gt" , "visitF32Gt" , [] ], - ["mvp" , "f32.le" , null , "0x5F", "f32Le" , "visitF32Le" , [] ], - ["mvp" , "f32.ge" , null , "0x60", "f32Ge" , "visitF32Ge" , [] ], - ["mvp" , "f64.eq" , null , "0x61", "f64Eq" , "visitF64Eq" , [] ], - ["mvp" , "f64.ne" , null , "0x62", "f64Ne" , "visitF64Ne" , [] ], - ["mvp" , "f64.lt" , null , "0x63", "f64Lt" , "visitF64Lt" , [] ], - ["mvp" , "f64.gt" , null , "0x64", "f64Gt" , "visitF64Gt" , [] ], - ["mvp" , "f64.le" , null , "0x65", "f64Le" , "visitF64Le" , [] ], - ["mvp" , "f64.ge" , null , "0x66", "f64Ge" , "visitF64Ge" , [] ], - ["mvp" , "i32.clz" , null , "0x67", "i32Clz" , "visitI32Clz" , [] ], - ["mvp" , "i32.ctz" , null , "0x68", "i32Ctz" , "visitI32Ctz" , [] ], - ["mvp" , "i32.popcnt" , null , "0x69", "i32Popcnt" , "visitI32Popcnt" , [] ], - ["mvp" , "i32.add" , null , "0x6A", "i32Add" , "visitI32Add" , [] ], - ["mvp" , "i32.sub" , null , "0x6B", "i32Sub" , "visitI32Sub" , [] ], - ["mvp" , "i32.mul" , null , "0x6C", "i32Mul" , "visitI32Mul" , [] ], - ["mvp" , "i32.div_s" , null , "0x6D", "i32DivS" , "visitI32DivS" , [] ], - ["mvp" , "i32.div_u" , null , "0x6E", "i32DivU" , "visitI32DivU" , [] ], - ["mvp" , "i32.rem_s" , null , "0x6F", "i32RemS" , "visitI32RemS" , [] ], - ["mvp" , "i32.rem_u" , null , "0x70", "i32RemU" , "visitI32RemU" , [] ], - ["mvp" , "i32.and" , null , "0x71", "i32And" , "visitI32And" , [] ], - ["mvp" , "i32.or" , null , "0x72", "i32Or" , "visitI32Or" , [] ], - ["mvp" , "i32.xor" , null , "0x73", "i32Xor" , "visitI32Xor" , [] ], - ["mvp" , "i32.shl" , null , "0x74", "i32Shl" , "visitI32Shl" , [] ], - ["mvp" , "i32.shr_s" , null , "0x75", "i32ShrS" , "visitI32ShrS" , [] ], - ["mvp" , "i32.shr_u" , null , "0x76", "i32ShrU" , "visitI32ShrU" , [] ], - ["mvp" , "i32.rotl" , null , "0x77", "i32Rotl" , "visitI32Rotl" , [] ], - ["mvp" , "i32.rotr" , null , "0x78", "i32Rotr" , "visitI32Rotr" , [] ], - ["mvp" , "i64.clz" , null , "0x79", "i64Clz" , "visitI64Clz" , [] ], - ["mvp" , "i64.ctz" , null , "0x7A", "i64Ctz" , "visitI64Ctz" , [] ], - ["mvp" , "i64.popcnt" , null , "0x7B", "i64Popcnt" , "visitI64Popcnt" , [] ], - ["mvp" , "i64.add" , null , "0x7C", "i64Add" , "visitI64Add" , [] ], - ["mvp" , "i64.sub" , null , "0x7D", "i64Sub" , "visitI64Sub" , [] ], - ["mvp" , "i64.mul" , null , "0x7E", "i64Mul" , "visitI64Mul" , [] ], - ["mvp" , "i64.div_s" , null , "0x7F", "i64DivS" , "visitI64DivS" , [] ], - ["mvp" , "i64.div_u" , null , "0x80", "i64DivU" , "visitI64DivU" , [] ], - ["mvp" , "i64.rem_s" , null , "0x81", "i64RemS" , "visitI64RemS" , [] ], - ["mvp" , "i64.rem_u" , null , "0x82", "i64RemU" , "visitI64RemU" , [] ], - ["mvp" , "i64.and" , null , "0x83", "i64And" , "visitI64And" , [] ], - ["mvp" , "i64.or" , null , "0x84", "i64Or" , "visitI64Or" , [] ], - ["mvp" , "i64.xor" , null , "0x85", "i64Xor" , "visitI64Xor" , [] ], - ["mvp" , "i64.shl" , null , "0x86", "i64Shl" , "visitI64Shl" , [] ], - ["mvp" , "i64.shr_s" , null , "0x87", "i64ShrS" , "visitI64ShrS" , [] ], - ["mvp" , "i64.shr_u" , null , "0x88", "i64ShrU" , "visitI64ShrU" , [] ], - ["mvp" , "i64.rotl" , null , "0x89", "i64Rotl" , "visitI64Rotl" , [] ], - ["mvp" , "i64.rotr" , null , "0x8A", "i64Rotr" , "visitI64Rotr" , [] ], - ["mvp" , "f32.abs" , null , "0x8B", "f32Abs" , "visitF32Abs" , [] ], - ["mvp" , "f32.neg" , null , "0x8C", "f32Neg" , "visitF32Neg" , [] ], - ["mvp" , "f32.ceil" , null , "0x8D", "f32Ceil" , "visitF32Ceil" , [] ], - ["mvp" , "f32.floor" , null , "0x8E", "f32Floor" , "visitF32Floor" , [] ], - ["mvp" , "f32.trunc" , null , "0x8F", "f32Trunc" , "visitF32Trunc" , [] ], - ["mvp" , "f32.nearest" , null , "0x90", "f32Nearest" , "visitF32Nearest" , [] ], - ["mvp" , "f32.sqrt" , null , "0x91", "f32Sqrt" , "visitF32Sqrt" , [] ], - ["mvp" , "f32.add" , null , "0x92", "f32Add" , "visitF32Add" , [] ], - ["mvp" , "f32.sub" , null , "0x93", "f32Sub" , "visitF32Sub" , [] ], - ["mvp" , "f32.mul" , null , "0x94", "f32Mul" , "visitF32Mul" , [] ], - ["mvp" , "f32.div" , null , "0x95", "f32Div" , "visitF32Div" , [] ], - ["mvp" , "f32.min" , null , "0x96", "f32Min" , "visitF32Min" , [] ], - ["mvp" , "f32.max" , null , "0x97", "f32Max" , "visitF32Max" , [] ], - ["mvp" , "f32.copysign" , null , "0x98", "f32Copysign" , "visitF32Copysign" , [] ], - ["mvp" , "f64.abs" , null , "0x99", "f64Abs" , "visitF64Abs" , [] ], - ["mvp" , "f64.neg" , null , "0x9A", "f64Neg" , "visitF64Neg" , [] ], - ["mvp" , "f64.ceil" , null , "0x9B", "f64Ceil" , "visitF64Ceil" , [] ], - ["mvp" , "f64.floor" , null , "0x9C", "f64Floor" , "visitF64Floor" , [] ], - ["mvp" , "f64.trunc" , null , "0x9D", "f64Trunc" , "visitF64Trunc" , [] ], - ["mvp" , "f64.nearest" , null , "0x9E", "f64Nearest" , "visitF64Nearest" , [] ], - ["mvp" , "f64.sqrt" , null , "0x9F", "f64Sqrt" , "visitF64Sqrt" , [] ], - ["mvp" , "f64.add" , null , "0xA0", "f64Add" , "visitF64Add" , [] ], - ["mvp" , "f64.sub" , null , "0xA1", "f64Sub" , "visitF64Sub" , [] ], - ["mvp" , "f64.mul" , null , "0xA2", "f64Mul" , "visitF64Mul" , [] ], - ["mvp" , "f64.div" , null , "0xA3", "f64Div" , "visitF64Div" , [] ], - ["mvp" , "f64.min" , null , "0xA4", "f64Min" , "visitF64Min" , [] ], - ["mvp" , "f64.max" , null , "0xA5", "f64Max" , "visitF64Max" , [] ], - ["mvp" , "f64.copysign" , null , "0xA6", "f64Copysign" , "visitF64Copysign" , [] ], - ["mvp" , "i32.wrap_i64" , null , "0xA7", "i32WrapI64" , "visitI32WrapI64" , [] ], - ["mvp" , "i32.trunc_f32_s" , null , "0xA8", "i32TruncF32S" , "visitI32TruncF32S" , [] ], - ["mvp" , "i32.trunc_f32_u" , null , "0xA9", "i32TruncF32U" , "visitI32TruncF32U" , [] ], - ["mvp" , "i32.trunc_f64_s" , null , "0xAA", "i32TruncF64S" , "visitI32TruncF64S" , [] ], - ["mvp" , "i32.trunc_f64_u" , null , "0xAB", "i32TruncF64U" , "visitI32TruncF64U" , [] ], - ["mvp" , "i64.extend_i32_s" , null , "0xAC", "i64ExtendI32S" , "visitI64ExtendI32S" , [] ], - ["mvp" , "i64.extend_i32_u" , null , "0xAD", "i64ExtendI32U" , "visitI64ExtendI32U" , [] ], - ["mvp" , "i64.trunc_f32_s" , null , "0xAE", "i64TruncF32S" , "visitI64TruncF32S" , [] ], - ["mvp" , "i64.trunc_f32_u" , null , "0xAF", "i64TruncF32U" , "visitI64TruncF32U" , [] ], - ["mvp" , "i64.trunc_f64_s" , null , "0xB0", "i64TruncF64S" , "visitI64TruncF64S" , [] ], - ["mvp" , "i64.trunc_f64_u" , null , "0xB1", "i64TruncF64U" , "visitI64TruncF64U" , [] ], - ["mvp" , "f32.convert_i32_s" , null , "0xB2", "f32ConvertI32S" , "visitF32ConvertI32S" , [] ], - ["mvp" , "f32.convert_i32_u" , null , "0xB3", "f32ConvertI32U" , "visitF32ConvertI32U" , [] ], - ["mvp" , "f32.convert_i64_s" , null , "0xB4", "f32ConvertI64S" , "visitF32ConvertI64S" , [] ], - ["mvp" , "f32.convert_i64_u" , null , "0xB5", "f32ConvertI64U" , "visitF32ConvertI64U" , [] ], - ["mvp" , "f32.demote_f64" , null , "0xB6", "f32DemoteF64" , "visitF32DemoteF64" , [] ], - ["mvp" , "f64.convert_i32_s" , null , "0xB7", "f64ConvertI32S" , "visitF64ConvertI32S" , [] ], - ["mvp" , "f64.convert_i32_u" , null , "0xB8", "f64ConvertI32U" , "visitF64ConvertI32U" , [] ], - ["mvp" , "f64.convert_i64_s" , null , "0xB9", "f64ConvertI64S" , "visitF64ConvertI64S" , [] ], - ["mvp" , "f64.convert_i64_u" , null , "0xBA", "f64ConvertI64U" , "visitF64ConvertI64U" , [] ], - ["mvp" , "f64.promote_f32" , null , "0xBB", "f64PromoteF32" , "visitF64PromoteF32" , [] ], - ["mvp" , "i32.reinterpret_f32", null , "0xBC", "i32ReinterpretF32", "visitI32ReinterpretF32", [] ], - ["mvp" , "i64.reinterpret_f64", null , "0xBD", "i64ReinterpretF64", "visitI64ReinterpretF64", [] ], - ["mvp" , "f32.reinterpret_i32", null , "0xBE", "f32ReinterpretI32", "visitF32ReinterpretI32", [] ], - ["mvp" , "f64.reinterpret_i64", null , "0xBF", "f64ReinterpretI64", "visitF64ReinterpretI64", [] ], - ["signExtension" , "i32.extend8_s" , null , "0xC0", "i32Extend8S" , "visitI32Extend8S" , [] ], - ["signExtension" , "i32.extend16_s" , null , "0xC1", "i32Extend16S" , "visitI32Extend16S" , [] ], - ["signExtension" , "i64.extend8_s" , null , "0xC2", "i64Extend8S" , "visitI64Extend8S" , [] ], - ["signExtension" , "i64.extend16_s" , null , "0xC3", "i64Extend16S" , "visitI64Extend16S" , [] ], - ["signExtension" , "i64.extend32_s" , null , "0xC4", "i64Extend32S" , "visitI64Extend32S" , [] ], - ["bulkMemory" , "memory.init" , "0xFC", "0x08", "memoryInit" , "visitMemoryInit" , [["dataIndex", "UInt32"]] ], - ["bulkMemory" , "data.drop" , "0xFC", "0x09", "dataDrop" , "visitDataDrop" , [["dataIndex", "UInt32"]] ], - ["bulkMemory" , "memory.copy" , "0xFC", "0x0A", "memoryCopy" , "visitMemoryCopy" , [["dstMem", "UInt32"], ["srcMem", "UInt32"]] ], - ["bulkMemory" , "memory.fill" , "0xFC", "0x0B", "memoryFill" , "visitMemoryFill" , [["memory", "UInt32"]] ], - ["bulkMemory" , "table.init" , "0xFC", "0x0C", "tableInit" , "visitTableInit" , [["elemIndex", "UInt32"], ["table", "UInt32"]] ], - ["bulkMemory" , "elem.drop" , "0xFC", "0x0D", "elemDrop" , "visitElemDrop" , [["elemIndex", "UInt32"]] ], - ["bulkMemory" , "table.copy" , "0xFC", "0x0E", "tableCopy" , "visitTableCopy" , [["dstTable", "UInt32"], ["srcTable", "UInt32"]] ], - ["referenceTypes" , "table.fill" , "0xFC", "0x11", "tableFill" , "visitTableFill" , [["table", "UInt32"]] ], - ["referenceTypes" , "table.get" , null , "0x25", "tableGet" , "visitTableGet" , [["table", "UInt32"]] ], - ["referenceTypes" , "table.set" , null , "0x26", "tableSet" , "visitTableSet" , [["table", "UInt32"]] ], - ["referenceTypes" , "table.grow" , "0xFC", "0x0F", "tableGrow" , "visitTableGrow" , [["table", "UInt32"]] ], - ["referenceTypes" , "table.size" , "0xFC", "0x10", "tableSize" , "visitTableSize" , [["table", "UInt32"]] ], - ["saturatingFloatToInt", "i32.trunc_sat_f32_s", "0xFC", "0x00", "i32TruncSatF32S" , "visitI32TruncSatF32S" , [] ], - ["saturatingFloatToInt", "i32.trunc_sat_f32_u", "0xFC", "0x01", "i32TruncSatF32U" , "visitI32TruncSatF32U" , [] ], - ["saturatingFloatToInt", "i32.trunc_sat_f64_s", "0xFC", "0x02", "i32TruncSatF64S" , "visitI32TruncSatF64S" , [] ], - ["saturatingFloatToInt", "i32.trunc_sat_f64_u", "0xFC", "0x03", "i32TruncSatF64U" , "visitI32TruncSatF64U" , [] ], - ["saturatingFloatToInt", "i64.trunc_sat_f32_s", "0xFC", "0x04", "i64TruncSatF32S" , "visitI64TruncSatF32S" , [] ], - ["saturatingFloatToInt", "i64.trunc_sat_f32_u", "0xFC", "0x05", "i64TruncSatF32U" , "visitI64TruncSatF32U" , [] ], - ["saturatingFloatToInt", "i64.trunc_sat_f64_s", "0xFC", "0x06", "i64TruncSatF64S" , "visitI64TruncSatF64S" , [] ], - ["saturatingFloatToInt", "i64.trunc_sat_f64_u", "0xFC", "0x07", "i64TruncSatF64U" , "visitI64TruncSatF64U" , [] ] + ["mvp" , "unreachable" , ["0x00"] , [] , null ], + ["mvp" , "nop" , ["0x01"] , [] , null ], + ["mvp" , "block" , ["0x02"] , [["blockType", "BlockType"]] , null ], + ["mvp" , "loop" , ["0x03"] , [["blockType", "BlockType"]] , null ], + ["mvp" , "if" , ["0x04"] , [["blockType", "BlockType"]] , null ], + ["mvp" , "else" , ["0x05"] , [] , null ], + ["mvp" , "end" , ["0x0B"] , [] , null ], + ["mvp" , "br" , ["0x0C"] , [["relativeDepth", "UInt32"]] , null ], + ["mvp" , "br_if" , ["0x0D"] , [["relativeDepth", "UInt32"]] , null ], + ["mvp" , "br_table" , ["0x0E"] , [["targets", "BrTable"]] , null ], + ["mvp" , "return" , ["0x0F"] , [] , null ], + ["mvp" , "call" , ["0x10"] , [["functionIndex", "UInt32"]] , null ], + ["mvp" , "call_indirect" , ["0x11"] , [["typeIndex", "UInt32"], ["tableIndex", "UInt32"]], null ], + ["tailCall" , "return_call" , ["0x12"] , [["functionIndex", "UInt32"]] , null ], + ["tailCall" , "return_call_indirect" , ["0x13"] , [["typeIndex", "UInt32"], ["tableIndex", "UInt32"]], null ], + ["mvp" , "drop" , ["0x1A"] , [] , null ], + ["mvp" , "select" , ["0x1B"] , [] , null ], + ["referenceTypes" , {"enumCase": "typedSelect"}, ["0x1C"] , [["type", "ValueType"]] , null ], + ["mvp" , "local.get" , ["0x20"] , [["localIndex", "UInt32"]] , null ], + ["mvp" , "local.set" , ["0x21"] , [["localIndex", "UInt32"]] , null ], + ["mvp" , "local.tee" , ["0x22"] , [["localIndex", "UInt32"]] , null ], + ["mvp" , "global.get" , ["0x23"] , [["globalIndex", "UInt32"]] , null ], + ["mvp" , "global.set" , ["0x24"] , [["globalIndex", "UInt32"]] , null ], + ["mvp" , "i32.load" , ["0x28"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i64.load" , ["0x29"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "f32.load" , ["0x2A"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "f64.load" , ["0x2B"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i32.load8_s" , ["0x2C"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i32.load8_u" , ["0x2D"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i32.load16_s" , ["0x2E"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i32.load16_u" , ["0x2F"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i64.load8_s" , ["0x30"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i64.load8_u" , ["0x31"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i64.load16_s" , ["0x32"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i64.load16_u" , ["0x33"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i64.load32_s" , ["0x34"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i64.load32_u" , ["0x35"] , [["memarg", "MemArg"]] , "load" ], + ["mvp" , "i32.store" , ["0x36"] , [["memarg", "MemArg"]] , "store" ], + ["mvp" , "i64.store" , ["0x37"] , [["memarg", "MemArg"]] , "store" ], + ["mvp" , "f32.store" , ["0x38"] , [["memarg", "MemArg"]] , "store" ], + ["mvp" , "f64.store" , ["0x39"] , [["memarg", "MemArg"]] , "store" ], + ["mvp" , "i32.store8" , ["0x3A"] , [["memarg", "MemArg"]] , "store" ], + ["mvp" , "i32.store16" , ["0x3B"] , [["memarg", "MemArg"]] , "store" ], + ["mvp" , "i64.store8" , ["0x3C"] , [["memarg", "MemArg"]] , "store" ], + ["mvp" , "i64.store16" , ["0x3D"] , [["memarg", "MemArg"]] , "store" ], + ["mvp" , "i64.store32" , ["0x3E"] , [["memarg", "MemArg"]] , "store" ], + ["mvp" , "memory.size" , ["0x3F"] , [["memory", "UInt32"]] , null ], + ["mvp" , "memory.grow" , ["0x40"] , [["memory", "UInt32"]] , null ], + ["mvp" , "i32.const" , ["0x41"] , [["value", "Int32"]] , null ], + ["mvp" , "i64.const" , ["0x42"] , [["value", "Int64"]] , null ], + ["mvp" , "f32.const" , ["0x43"] , [["value", "IEEE754.Float32"]] , null ], + ["mvp" , "f64.const" , ["0x44"] , [["value", "IEEE754.Float64"]] , null ], + ["referenceTypes" , "ref.null" , ["0xD0"] , [["type", "ReferenceType"]] , null ], + ["referenceTypes" , "ref.is_null" , ["0xD1"] , [] , null ], + ["referenceTypes" , "ref.func" , ["0xD2"] , [["functionIndex", "UInt32"]] , null ], + ["mvp" , "i32.eqz" , ["0x45"] , [] , null ], + ["mvp" , "i32.eq" , ["0x46"] , [] , "cmp" ], + ["mvp" , "i32.ne" , ["0x47"] , [] , "cmp" ], + ["mvp" , "i32.lt_s" , ["0x48"] , [] , "cmp" ], + ["mvp" , "i32.lt_u" , ["0x49"] , [] , "cmp" ], + ["mvp" , "i32.gt_s" , ["0x4A"] , [] , "cmp" ], + ["mvp" , "i32.gt_u" , ["0x4B"] , [] , "cmp" ], + ["mvp" , "i32.le_s" , ["0x4C"] , [] , "cmp" ], + ["mvp" , "i32.le_u" , ["0x4D"] , [] , "cmp" ], + ["mvp" , "i32.ge_s" , ["0x4E"] , [] , "cmp" ], + ["mvp" , "i32.ge_u" , ["0x4F"] , [] , "cmp" ], + ["mvp" , "i64.eqz" , ["0x50"] , [] , null ], + ["mvp" , "i64.eq" , ["0x51"] , [] , "cmp" ], + ["mvp" , "i64.ne" , ["0x52"] , [] , "cmp" ], + ["mvp" , "i64.lt_s" , ["0x53"] , [] , "cmp" ], + ["mvp" , "i64.lt_u" , ["0x54"] , [] , "cmp" ], + ["mvp" , "i64.gt_s" , ["0x55"] , [] , "cmp" ], + ["mvp" , "i64.gt_u" , ["0x56"] , [] , "cmp" ], + ["mvp" , "i64.le_s" , ["0x57"] , [] , "cmp" ], + ["mvp" , "i64.le_u" , ["0x58"] , [] , "cmp" ], + ["mvp" , "i64.ge_s" , ["0x59"] , [] , "cmp" ], + ["mvp" , "i64.ge_u" , ["0x5A"] , [] , "cmp" ], + ["mvp" , "f32.eq" , ["0x5B"] , [] , "cmp" ], + ["mvp" , "f32.ne" , ["0x5C"] , [] , "cmp" ], + ["mvp" , "f32.lt" , ["0x5D"] , [] , "cmp" ], + ["mvp" , "f32.gt" , ["0x5E"] , [] , "cmp" ], + ["mvp" , "f32.le" , ["0x5F"] , [] , "cmp" ], + ["mvp" , "f32.ge" , ["0x60"] , [] , "cmp" ], + ["mvp" , "f64.eq" , ["0x61"] , [] , "cmp" ], + ["mvp" , "f64.ne" , ["0x62"] , [] , "cmp" ], + ["mvp" , "f64.lt" , ["0x63"] , [] , "cmp" ], + ["mvp" , "f64.gt" , ["0x64"] , [] , "cmp" ], + ["mvp" , "f64.le" , ["0x65"] , [] , "cmp" ], + ["mvp" , "f64.ge" , ["0x66"] , [] , "cmp" ], + ["mvp" , "i32.clz" , ["0x67"] , [] , "unary" ], + ["mvp" , "i32.ctz" , ["0x68"] , [] , "unary" ], + ["mvp" , "i32.popcnt" , ["0x69"] , [] , "unary" ], + ["mvp" , "i32.add" , ["0x6A"] , [] , "binary" ], + ["mvp" , "i32.sub" , ["0x6B"] , [] , "binary" ], + ["mvp" , "i32.mul" , ["0x6C"] , [] , "binary" ], + ["mvp" , "i32.div_s" , ["0x6D"] , [] , "binary" ], + ["mvp" , "i32.div_u" , ["0x6E"] , [] , "binary" ], + ["mvp" , "i32.rem_s" , ["0x6F"] , [] , "binary" ], + ["mvp" , "i32.rem_u" , ["0x70"] , [] , "binary" ], + ["mvp" , "i32.and" , ["0x71"] , [] , "binary" ], + ["mvp" , "i32.or" , ["0x72"] , [] , "binary" ], + ["mvp" , "i32.xor" , ["0x73"] , [] , "binary" ], + ["mvp" , "i32.shl" , ["0x74"] , [] , "binary" ], + ["mvp" , "i32.shr_s" , ["0x75"] , [] , "binary" ], + ["mvp" , "i32.shr_u" , ["0x76"] , [] , "binary" ], + ["mvp" , "i32.rotl" , ["0x77"] , [] , "binary" ], + ["mvp" , "i32.rotr" , ["0x78"] , [] , "binary" ], + ["mvp" , "i64.clz" , ["0x79"] , [] , "unary" ], + ["mvp" , "i64.ctz" , ["0x7A"] , [] , "unary" ], + ["mvp" , "i64.popcnt" , ["0x7B"] , [] , "unary" ], + ["mvp" , "i64.add" , ["0x7C"] , [] , "binary" ], + ["mvp" , "i64.sub" , ["0x7D"] , [] , "binary" ], + ["mvp" , "i64.mul" , ["0x7E"] , [] , "binary" ], + ["mvp" , "i64.div_s" , ["0x7F"] , [] , "binary" ], + ["mvp" , "i64.div_u" , ["0x80"] , [] , "binary" ], + ["mvp" , "i64.rem_s" , ["0x81"] , [] , "binary" ], + ["mvp" , "i64.rem_u" , ["0x82"] , [] , "binary" ], + ["mvp" , "i64.and" , ["0x83"] , [] , "binary" ], + ["mvp" , "i64.or" , ["0x84"] , [] , "binary" ], + ["mvp" , "i64.xor" , ["0x85"] , [] , "binary" ], + ["mvp" , "i64.shl" , ["0x86"] , [] , "binary" ], + ["mvp" , "i64.shr_s" , ["0x87"] , [] , "binary" ], + ["mvp" , "i64.shr_u" , ["0x88"] , [] , "binary" ], + ["mvp" , "i64.rotl" , ["0x89"] , [] , "binary" ], + ["mvp" , "i64.rotr" , ["0x8A"] , [] , "binary" ], + ["mvp" , "f32.abs" , ["0x8B"] , [] , "unary" ], + ["mvp" , "f32.neg" , ["0x8C"] , [] , "unary" ], + ["mvp" , "f32.ceil" , ["0x8D"] , [] , "unary" ], + ["mvp" , "f32.floor" , ["0x8E"] , [] , "unary" ], + ["mvp" , "f32.trunc" , ["0x8F"] , [] , "unary" ], + ["mvp" , "f32.nearest" , ["0x90"] , [] , "unary" ], + ["mvp" , "f32.sqrt" , ["0x91"] , [] , "unary" ], + ["mvp" , "f32.add" , ["0x92"] , [] , "binary" ], + ["mvp" , "f32.sub" , ["0x93"] , [] , "binary" ], + ["mvp" , "f32.mul" , ["0x94"] , [] , "binary" ], + ["mvp" , "f32.div" , ["0x95"] , [] , "binary" ], + ["mvp" , "f32.min" , ["0x96"] , [] , "binary" ], + ["mvp" , "f32.max" , ["0x97"] , [] , "binary" ], + ["mvp" , "f32.copysign" , ["0x98"] , [] , "binary" ], + ["mvp" , "f64.abs" , ["0x99"] , [] , "unary" ], + ["mvp" , "f64.neg" , ["0x9A"] , [] , "unary" ], + ["mvp" , "f64.ceil" , ["0x9B"] , [] , "unary" ], + ["mvp" , "f64.floor" , ["0x9C"] , [] , "unary" ], + ["mvp" , "f64.trunc" , ["0x9D"] , [] , "unary" ], + ["mvp" , "f64.nearest" , ["0x9E"] , [] , "unary" ], + ["mvp" , "f64.sqrt" , ["0x9F"] , [] , "unary" ], + ["mvp" , "f64.add" , ["0xA0"] , [] , "binary" ], + ["mvp" , "f64.sub" , ["0xA1"] , [] , "binary" ], + ["mvp" , "f64.mul" , ["0xA2"] , [] , "binary" ], + ["mvp" , "f64.div" , ["0xA3"] , [] , "binary" ], + ["mvp" , "f64.min" , ["0xA4"] , [] , "binary" ], + ["mvp" , "f64.max" , ["0xA5"] , [] , "binary" ], + ["mvp" , "f64.copysign" , ["0xA6"] , [] , "binary" ], + ["mvp" , "i32.wrap_i64" , ["0xA7"] , [] , "conversion"], + ["mvp" , "i32.trunc_f32_s" , ["0xA8"] , [] , "conversion"], + ["mvp" , "i32.trunc_f32_u" , ["0xA9"] , [] , "conversion"], + ["mvp" , "i32.trunc_f64_s" , ["0xAA"] , [] , "conversion"], + ["mvp" , "i32.trunc_f64_u" , ["0xAB"] , [] , "conversion"], + ["mvp" , "i64.extend_i32_s" , ["0xAC"] , [] , "conversion"], + ["mvp" , "i64.extend_i32_u" , ["0xAD"] , [] , "conversion"], + ["mvp" , "i64.trunc_f32_s" , ["0xAE"] , [] , "conversion"], + ["mvp" , "i64.trunc_f32_u" , ["0xAF"] , [] , "conversion"], + ["mvp" , "i64.trunc_f64_s" , ["0xB0"] , [] , "conversion"], + ["mvp" , "i64.trunc_f64_u" , ["0xB1"] , [] , "conversion"], + ["mvp" , "f32.convert_i32_s" , ["0xB2"] , [] , "conversion"], + ["mvp" , "f32.convert_i32_u" , ["0xB3"] , [] , "conversion"], + ["mvp" , "f32.convert_i64_s" , ["0xB4"] , [] , "conversion"], + ["mvp" , "f32.convert_i64_u" , ["0xB5"] , [] , "conversion"], + ["mvp" , "f32.demote_f64" , ["0xB6"] , [] , "conversion"], + ["mvp" , "f64.convert_i32_s" , ["0xB7"] , [] , "conversion"], + ["mvp" , "f64.convert_i32_u" , ["0xB8"] , [] , "conversion"], + ["mvp" , "f64.convert_i64_s" , ["0xB9"] , [] , "conversion"], + ["mvp" , "f64.convert_i64_u" , ["0xBA"] , [] , "conversion"], + ["mvp" , "f64.promote_f32" , ["0xBB"] , [] , "conversion"], + ["mvp" , "i32.reinterpret_f32" , ["0xBC"] , [] , "conversion"], + ["mvp" , "i64.reinterpret_f64" , ["0xBD"] , [] , "conversion"], + ["mvp" , "f32.reinterpret_i32" , ["0xBE"] , [] , "conversion"], + ["mvp" , "f64.reinterpret_i64" , ["0xBF"] , [] , "conversion"], + ["signExtension" , "i32.extend8_s" , ["0xC0"] , [] , "unary" ], + ["signExtension" , "i32.extend16_s" , ["0xC1"] , [] , "unary" ], + ["signExtension" , "i64.extend8_s" , ["0xC2"] , [] , "unary" ], + ["signExtension" , "i64.extend16_s" , ["0xC3"] , [] , "unary" ], + ["signExtension" , "i64.extend32_s" , ["0xC4"] , [] , "unary" ], + ["bulkMemory" , "memory.init" , ["0xFC", "0x08"], [["dataIndex", "UInt32"]] , null ], + ["bulkMemory" , "data.drop" , ["0xFC", "0x09"], [["dataIndex", "UInt32"]] , null ], + ["bulkMemory" , "memory.copy" , ["0xFC", "0x0A"], [["dstMem", "UInt32"], ["srcMem", "UInt32"]] , null ], + ["bulkMemory" , "memory.fill" , ["0xFC", "0x0B"], [["memory", "UInt32"]] , null ], + ["bulkMemory" , "table.init" , ["0xFC", "0x0C"], [["elemIndex", "UInt32"], ["table", "UInt32"]] , null ], + ["bulkMemory" , "elem.drop" , ["0xFC", "0x0D"], [["elemIndex", "UInt32"]] , null ], + ["bulkMemory" , "table.copy" , ["0xFC", "0x0E"], [["dstTable", "UInt32"], ["srcTable", "UInt32"]] , null ], + ["referenceTypes" , "table.fill" , ["0xFC", "0x11"], [["table", "UInt32"]] , null ], + ["referenceTypes" , "table.get" , ["0x25"] , [["table", "UInt32"]] , null ], + ["referenceTypes" , "table.set" , ["0x26"] , [["table", "UInt32"]] , null ], + ["referenceTypes" , "table.grow" , ["0xFC", "0x0F"], [["table", "UInt32"]] , null ], + ["referenceTypes" , "table.size" , ["0xFC", "0x10"], [["table", "UInt32"]] , null ], + ["saturatingFloatToInt", "i32.trunc_sat_f32_s" , ["0xFC", "0x00"], [] , "conversion"], + ["saturatingFloatToInt", "i32.trunc_sat_f32_u" , ["0xFC", "0x01"], [] , "conversion"], + ["saturatingFloatToInt", "i32.trunc_sat_f64_s" , ["0xFC", "0x02"], [] , "conversion"], + ["saturatingFloatToInt", "i32.trunc_sat_f64_u" , ["0xFC", "0x03"], [] , "conversion"], + ["saturatingFloatToInt", "i64.trunc_sat_f32_s" , ["0xFC", "0x04"], [] , "conversion"], + ["saturatingFloatToInt", "i64.trunc_sat_f32_u" , ["0xFC", "0x05"], [] , "conversion"], + ["saturatingFloatToInt", "i64.trunc_sat_f64_s" , ["0xFC", "0x06"], [] , "conversion"], + ["saturatingFloatToInt", "i64.trunc_sat_f64_u" , ["0xFC", "0x07"], [] , "conversion"] ] diff --git a/Utilities/Sources/VMGen.swift b/Utilities/Sources/VMGen.swift index 29aedd07..210098f7 100644 --- a/Utilities/Sources/VMGen.swift +++ b/Utilities/Sources/VMGen.swift @@ -378,7 +378,7 @@ enum VMGen { INLINE_CALL next = wasmkit_execute_\(inst.name)(\(params.map { "&\($0.label)" }.joined(separator: ", ")), state, &error);\n """ if inst.mayThrow { - output += " if (error) return wasmkit_execution_state_set_error(error, state);\n" + output += " if (error) return wasmkit_execution_state_set_error(error, sp, state);\n" } output += """ return ((wasmkit_tc_exec)next)(sp, pc, md, ms, state); diff --git a/Utilities/Sources/VMSpec/Instruction.swift b/Utilities/Sources/VMSpec/Instruction.swift index 91f23582..0243ed79 100644 --- a/Utilities/Sources/VMSpec/Instruction.swift +++ b/Utilities/Sources/VMSpec/Instruction.swift @@ -86,7 +86,6 @@ extension VMGen { self.useCurrentMemory = useCurrentMemory self.immediate = immediate self.immediateLayout = immediateLayout - assert(isControl || !mayUpdateFrame, "non-control instruction should not update frame") } init( @@ -544,6 +543,24 @@ extension VMGen { $0.field(name: "index", type: .VReg) $0.field(name: "spAddend", type: .VReg) }, + Instruction(name: "resizeFrameHeader", documentation: """ + Resize the frame header by increasing param/result slots and copying `sizeToCopy` + slots placed after the header + """, + mayThrow: true, mayUpdateFrame: true) { + $0.field(name: "delta", type: .VReg) + $0.field(name: "sizeToCopy", type: .VReg) + }, + Instruction(name: "returnCall", documentation: "WebAssembly Core Instruction `return_call`", + isControl: true, mayThrow: true, mayUpdateFrame: true, useCurrentMemory: .write) { + $0.field(name: "rawCallee", type: .UInt64) + }, + Instruction(name: "returnCallIndirect", documentation: "WebAssembly Core Instruction `return_call_indirect`", + isControl: true, mayThrow: true, mayUpdateFrame: true, useCurrentMemory: .write) { + $0.field(name: "tableIndex", type: .UInt32) + $0.field(name: "rawType", type: .UInt32) + $0.field(name: "index", type: .VReg) + }, Instruction(name: "unreachable", documentation: "WebAssembly Core Instruction `unreachable`", isControl: true, mayThrow: true), Instruction(name: "nop", documentation: "WebAssembly Core Instruction `nop`"), diff --git a/Utilities/Sources/WasmGen.swift b/Utilities/Sources/WasmGen.swift index 94d305cd..60b5275f 100644 --- a/Utilities/Sources/WasmGen.swift +++ b/Utilities/Sources/WasmGen.swift @@ -3,14 +3,62 @@ import Foundation /// A utility for generating Core Wasm instruction related code based on the `Instructions.json` file. enum WasmGen { + static func pascalCase(camelCase: String) -> String { + camelCase.prefix(1).uppercased() + camelCase.dropFirst() + } + struct Instruction: Decodable { let feature: String - let name: String? - let prefix: UInt8? - let opcode: UInt8 - let enumCaseName: String - let visitMethodName: String + let name: Name + let opcode: [UInt8] let immediates: [Immediate] + let category: String? + + var visitMethodName: String { + if let explicitCategory = category { + return "visit" + WasmGen.pascalCase(camelCase: explicitCategory) + } else { + return "visit" + WasmGen.pascalCase(camelCase: name.enumCase) + } + } + + enum Name: Decodable { + struct WithEnumCase: Decodable { + let enumCase: String + } + /// The instruction name in the Wasm text format. + case textual(String) + /// The instruction name in Swift enum case. + case withEnumCase(WithEnumCase) + + var text: String? { + switch self { + case let .textual(text): return text + case .withEnumCase: return nil + } + } + + var enumCase: String { + switch self { + case .textual(let name): + // e.g. i32.load -> i32Load, br_table -> brTable + let components = name.split(separator: ".").flatMap { + $0.split(separator: "_") + } + return components.first! + components.dropFirst().map(\.capitalized).joined() + case let .withEnumCase(name): return name.enumCase + } + } + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if let withEnumCase = try? container.decode(WithEnumCase.self) { + self = .withEnumCase(withEnumCase) + } else { + self = .textual(try container.decode(String.self)) + } + } + } struct Immediate: Comparable, Hashable { let label: String @@ -26,31 +74,16 @@ enum WasmGen { init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() - func decodeHex() throws -> UInt8 { - let hexString = try container.decode(String.self) - return UInt8(hexString.dropFirst(2), radix: 16)! + func decodeHexArray() throws -> [UInt8] { + let hexStrings = try container.decode([String].self) + return hexStrings.map { UInt8($0.dropFirst(2), radix: 16)! } } feature = try container.decode(String.self) - if (try? container.decodeNil()) == true { - name = nil - } else { - name = try container.decode(String.self) - } - if (try? container.decodeNil()) == true { - prefix = nil - } else { - prefix = try decodeHex() - } - opcode = try decodeHex() - enumCaseName = try container.decode(String.self) - visitMethodName = try container.decode(String.self) - let rawImmediates: [[String]] - if container.isAtEnd { - rawImmediates = [] - } else { - rawImmediates = try container.decode([[String]].self) - } + name = try container.decode(Name.self) + opcode = try decodeHexArray() + let rawImmediates = try container.decode([[String]].self) immediates = rawImmediates.map { Immediate(label: $0[0], type: $0[1]) } + category = try? container.decode(String.self) } } @@ -65,12 +98,12 @@ enum WasmGen { public protocol InstructionVisitor { """ - for instruction in instructions { + for instruction in instructions.categorized { code += "\n" - code += " /// Visiting `\(instruction.name ?? instruction.enumCaseName)` instruction.\n" + code += " /// Visiting \(instruction.description) instruction.\n" code += " mutating func \(instruction.visitMethodName)(" - code += instruction.immediates.map { i in - "\(i.label): \(i.type)" + code += instruction.associatedValues.map { i in + "\(i.argumentName ?? "_"): \(i.type)" }.joined(separator: ", ") code += ") throws" } @@ -90,15 +123,19 @@ enum WasmGen { """ - for instruction in instructions { - if instruction.immediates.isEmpty { + for instruction in instructions.categorized { + if instruction.associatedValues.isEmpty { code += " case .\(instruction.enumCaseName): return try \(instruction.visitMethodName)()\n" } else { code += " case let .\(instruction.enumCaseName)(" - code += instruction.immediates.map(\.label).joined(separator: ", ") + code += instruction.associatedValues.map(\.parameterName).joined(separator: ", ") code += "): return try \(instruction.visitMethodName)(" - code += instruction.immediates.map { - "\($0.label): \($0.label)" + code += instruction.associatedValues.map { + if let label = $0.argumentName { + return "\(label): \(label)" + } else { + return $0.parameterName + } }.joined(separator: ", ") code += ")\n" } @@ -114,10 +151,14 @@ enum WasmGen { extension InstructionVisitor { """ - for instruction in instructions { + for instruction in instructions.categorized { code += " public mutating func \(instruction.visitMethodName)(" - code += instruction.immediates.map { i in - "\(i.label): \(i.type)" + code += instruction.associatedValues.map { i in + if i.argumentName == i.parameterName { + return "\(i.parameterName): \(i.type)" + } else { + return "\(i.argumentName ?? "_") \(i.parameterName): \(i.type)" + } }.joined(separator: ", ") code += ") throws {}\n" } @@ -134,13 +175,29 @@ enum WasmGen { """ - for instruction in instructions { + let categorized = instructions.categorized + + for instruction in categorized { + guard let categoryTypeName = instruction.categoryTypeName else { continue } + code += " public enum \(categoryTypeName): Equatable {\n" + for sourceInstruction in instruction.sourceInstructions { + code += " case \(sourceInstruction.name.enumCase)\n" + } + code += " }\n" + } + + for instruction in categorized { code += " case `\(instruction.enumCaseName)`" - if !instruction.immediates.isEmpty { + let associatedValues = instruction.associatedValues + if !associatedValues.isEmpty { code += "(" - + instruction.immediates.map { - "\($0.label): \($0.type)" + + associatedValues.map { + if let label = $0.argumentName { + return "\(label): \($0.type)" + } else { + return $0.type + } }.joined(separator: ", ") + ")" } code += "\n" @@ -151,14 +208,18 @@ enum WasmGen { return code } - static func buildInstructionInstanceFromContext(_ instruction: Instruction) -> String { + static func buildInstructionInstanceFromContext(_ instruction: CategorizedInstruction) -> String { var code = "" - if instruction.immediates.isEmpty { + if instruction.associatedValues.isEmpty { code += ".\(instruction.enumCaseName)" } else { code += ".\(instruction.enumCaseName)(" - code += instruction.immediates.map { i in - "\(i.label): \(i.label)" + code += instruction.associatedValues.map { i in + if let label = i.argumentName { + return "\(label): \(label)" + } else { + return i.parameterName + } }.joined(separator: ", ") code += ")" } @@ -177,10 +238,14 @@ enum WasmGen { """ - for instruction in instructions { + for instruction in instructions.categorized { code += " public mutating func \(instruction.visitMethodName)(" - code += instruction.immediates.map { i in - "\(i.label): \(i.type)" + code += instruction.associatedValues.map { i in + if i.argumentName == i.parameterName { + return "\(i.parameterName): \(i.type)" + } else { + return "\(i.argumentName ?? "_") \(i.parameterName): \(i.type)" + } }.joined(separator: ", ") code += ") throws { " code += "return try self.visit(" + buildInstructionInstanceFromContext(instruction) + ")" @@ -213,18 +278,26 @@ enum WasmGen { """ - for instruction in instructions { + for instruction in instructions.categorized { code += " public mutating func \(instruction.visitMethodName)(" - code += instruction.immediates.map { i in - "\(i.label): \(i.type)" + code += instruction.associatedValues.map { i in + if i.argumentName == i.parameterName { + return "\(i.parameterName): \(i.type)" + } else { + return "\(i.argumentName ?? "_") \(i.parameterName): \(i.type)" + } }.joined(separator: ", ") code += ") throws {\n" code += " trace(" code += buildInstructionInstanceFromContext(instruction) code += ")\n" code += " return try visitor.\(instruction.visitMethodName)(" - code += instruction.immediates.map { i in - "\(i.label): \(i.label)" + code += instruction.associatedValues.map { i in + if let label = i.argumentName { + "\(label): \(i.parameterName)" + } else { + i.parameterName + } }.joined(separator: ", ") code += ")\n" code += " }\n" @@ -235,7 +308,7 @@ enum WasmGen { return code } - static func generateTextParser(_ instructions: InstructionSet) -> String { + static func generateTextInstructionParser(_ instructions: InstructionSet) -> String { var code = """ import WasmParser import WasmTypes @@ -253,7 +326,7 @@ enum WasmGen { """ for instruction in instructions { - guard let name = instruction.name else { + guard let name = instruction.name.text else { continue } code += " case \"\(name)\":" @@ -261,14 +334,30 @@ enum WasmGen { code += "\n" code += " let (" code += instruction.immediates.map(\.label).joined(separator: ", ") - code += ") = try expressionParser.\(instruction.visitMethodName)(wat: &wat)\n" + code += ") = try expressionParser.\(instruction.visitMethodName)(" + if instruction.category != nil { + code += ".\(instruction.name.enumCase), " + } + code += "wat: &wat" + code += ")\n" code += " " } else { code += " " } code += "return { return try $0.\(instruction.visitMethodName)(" - code += instruction.immediates.map { i in - "\(i.label): \(i.label)" + var arguments: [(label: String?, value: String)] = [] + if instruction.category != nil { + arguments.append((label: nil, value: ".\(instruction.name.enumCase)")) + } + for immediate in instruction.immediates { + arguments.append((label: immediate.label, value: immediate.label)) + } + code += arguments.map { i in + if let label = i.label { + return "\(label): \(i.value)" + } else { + return i.value + } }.joined(separator: ", ") code += ") }\n" } @@ -287,7 +376,7 @@ enum WasmGen { guard !instruction.immediates.isEmpty else { continue } - code += " mutating func \(instruction.visitMethodName)() throws -> " + code += " mutating func \(instruction.name.visitMethod)() throws -> " if instruction.immediates.count == 1 { code += instruction.immediates[0].type } else { @@ -304,15 +393,16 @@ enum WasmGen { return code } - static func generateInstructionEncoder(_ instructions: InstructionSet) -> String { + static func generateBinaryInstructionEncoder(_ instructions: InstructionSet) -> String { var code = """ import WasmParser import WasmTypes - /// An instruction encoder that is responsible for encoding opcodes and immediates. - protocol InstructionEncoder: InstructionVisitor { + /// An instruction encoder that is responsible for encoding opcodes and immediates + /// in Wasm binary format. + protocol BinaryInstructionEncoder: InstructionVisitor { /// Encodes an instruction opcode. - mutating func encodeInstruction(_ opcode: UInt8, _ prefix: UInt8?) throws + mutating func encodeInstruction(_ opcode: [UInt8]) throws // MARK: - Immediates encoding @@ -348,32 +438,51 @@ enum WasmGen { code += """ } - // InstructionEncoder implements the InstructionVisitor protocol to call the corresponding encode method. - extension InstructionEncoder { + // BinaryInstructionEncoder implements the InstructionVisitor protocol to call the corresponding encode method. + extension BinaryInstructionEncoder { """ - for instruction in instructions { - var encodeInstrCall = "try encodeInstruction(" - encodeInstrCall += [ - String(format: "0x%02X", instruction.opcode), - instruction.prefix.map { String(format: "0x%02X", $0) } ?? "nil", - ].joined(separator: ", ") - encodeInstrCall += ")" - + for instruction in instructions.categorized { code += " mutating func \(instruction.visitMethodName)(" - code += instruction.immediates.map { i in - "\(i.label): \(i.type)" + code += instruction.associatedValues.map { i in + if i.argumentName == i.parameterName { + return "\(i.parameterName): \(i.type)" + } else { + return "\(i.argumentName ?? "_") \(i.parameterName): \(i.type)" + } }.joined(separator: ", ") code += ") throws {" - if instruction.immediates.isEmpty { + + var encodeInstrCall: String + if let category = instruction.explicitCategory { + code += "\n" + code += " let opcode: [UInt8]\n" + code += " switch \(category) {\n" + for sourceInstruction in instruction.sourceInstructions { + code += " case .\(sourceInstruction.name.enumCase): opcode = [" + code += sourceInstruction.opcode.map { String(format: "0x%02X", $0) }.joined(separator: ", ") + code += "]\n" + } + code += " }\n" + encodeInstrCall = "try encodeInstruction(opcode)" + } else { + let instruction = instruction.sourceInstructions[0] + encodeInstrCall = "try encodeInstruction([" + encodeInstrCall += instruction.opcode.map { String(format: "0x%02X", $0) }.joined(separator: ", ") + encodeInstrCall += "])" + } + + if instruction.immediates.isEmpty, instruction.explicitCategory == nil { code += " \(encodeInstrCall) " } else { code += "\n" code += " \(encodeInstrCall)\n" - code += " try encodeImmediates(" - code += instruction.immediates.map { "\($0.label): \($0.label)" }.joined(separator: ", ") - code += ")\n" + if !instruction.immediates.isEmpty { + code += " try encodeImmediates(" + code += instruction.immediates.map { "\($0.label): \($0.label)" }.joined(separator: ", ") + code += ")\n" + } code += " " } @@ -385,43 +494,167 @@ enum WasmGen { return code } + static func generateBinaryInstructionDecoder(_ instructions: InstructionSet) -> String { + struct Trie { + var children: [UInt8: Trie] = [:] + /// An instruction corresponding to this terminal trie node + let instruction: Instruction? + + init(instruction: Instruction? = nil) { + self.instruction = instruction + } + + mutating func insert(_ opcode: S, instruction: Instruction) where S.Element == UInt8 { + guard let first = opcode.first else { return } + let isTermination = opcode.count == 1 + if isTermination { + assert(children[first] == nil) + children[first] = Trie(instruction: instruction) + } else { + children[first, default: Trie(instruction: nil)].insert(opcode.dropFirst(), instruction: instruction) + } + } + } + + var root = Trie() + for instruction in instructions { + root.insert(instruction.opcode, instruction: instruction) + } + var code = """ + import WasmTypes + + @usableFromInline + protocol BinaryInstructionDecoder { + /// Claim the next byte to be decoded + @inlinable func claimNextByte() throws -> UInt8 + /// Visit unknown instruction + @inlinable func visitUnknown(_ opcode: [UInt8]) throws + + """ + for instruction in instructions.categorized { + guard !instruction.immediates.isEmpty else { continue } + code += " /// Decode \(instruction.description) immediates\n" + code += " @inlinable mutating func \(instruction.visitMethodName)(" + if let categoryType = instruction.categoryTypeName { + code += "_: Instruction.\(categoryType)" + } + code += ") throws -> " + if instruction.immediates.count == 1 { + code += "\(instruction.immediates[0].type)" + } else { + code += "(" + instruction.immediates.map { "\($0.label): \($0.type)" }.joined(separator: ", ") + ")" + } + code += "\n" + } + code += """ + } + + """ + + code += """ + @inlinable + func parseBinaryInstruction(visitor: inout V, decoder: inout D) throws -> Bool { + """ + + func renderSwitchCase(_ root: Trie, depth: Int = 0) { + let indent = String(repeating: " ", count: (depth + 1) * 4) + func opcodeByteName(_ depth: Int) -> String { "opcode\(depth)" } + let opcodeByte = opcodeByteName(depth) + code += """ + + \(indent)let \(opcodeByte) = try decoder.claimNextByte() + \(indent)switch \(opcodeByte) { + + """ + for (opcode, trie) in root.children.sorted(by: { $0.key < $1.key }) { + code += "\(indent)case \(String(format: "0x%02X", opcode)):\n" + if let instruction = trie.instruction { + if !instruction.immediates.isEmpty { + code += "\(indent) let (" + code += instruction.immediates.map(\.label).joined(separator: ", ") + code += ") = try decoder.\(instruction.visitMethodName)(" + if instruction.category != nil { + code += ".\(instruction.name.enumCase)" + } + code += ")\n" + } + + code += "\(indent) try visitor.\(instruction.visitMethodName)(" + var arguments: [(label: String?, value: String)] = [] + if instruction.category != nil { + arguments.append((label: nil, value: ".\(instruction.name.enumCase)")) + } + for immediate in instruction.immediates { + arguments.append((label: immediate.label, value: immediate.label)) + } + code += arguments.map { i in + if let label = i.label { + return "\(label): \(i.value)" + } else { + return i.value + } + }.joined(separator: ", ") + code += ")\n" + if instruction.name.text == "end" { + code += "\(indent) return true\n" + } + } else { + renderSwitchCase(trie, depth: depth + 1) + } + } + code += "\(indent)default:\n" + code += "\(indent) try decoder.visitUnknown(" + code += "[" + (0...depth).map { opcodeByteName($0) }.joined(separator: ", ") + "]" + code += ")\n" + code += "\(indent)}\n" + } + + renderSwitchCase(root) + code += " return false\n" + code += "}\n" + return code + } + static func formatInstructionSet(_ instructions: InstructionSet) -> String { var json = "" json += "[\n" struct ColumnInfo { var header: String - var maxWidth: Int + var maxWidth: Int = 0 var value: (Instruction) -> String } var columns: [ColumnInfo] = [ - ColumnInfo(header: "Feature", maxWidth: 0, value: { "\"" + $0.feature + "\"" }), + ColumnInfo(header: "Feature", value: { "\"" + $0.feature + "\"" }), ColumnInfo( - header: "Name", maxWidth: 0, + header: "Name", value: { - $0.name.map { "\"" + $0 + "\"" } ?? "null" - }), - ColumnInfo( - header: "Prefix", maxWidth: 0, - value: { i in - if let prefix = i.prefix { - return "\"" + String(format: "0x%02X", prefix) + "\"" - } else { - return "null" + switch $0.name { + case .textual(let name): return "\"" + name + "\"" + case .withEnumCase(let name): return "{\"enumCase\": \"\(name.enumCase)\"}" } }), - ColumnInfo(header: "Opcode", maxWidth: 0, value: { "\"" + String(format: "0x%02X", $0.opcode) + "\"" }), - ColumnInfo(header: "Enum Case", maxWidth: 0, value: { "\"" + $0.enumCaseName + "\"" }), - ColumnInfo(header: "Visit Method", maxWidth: 0, value: { "\"" + $0.visitMethodName + "\"" }), + ColumnInfo(header: "Opcode", value: { + "[" + $0.opcode.map { "\"" + String(format: "0x%02X", $0) + "\"" }.joined(separator: ", ") + "]" + }), ColumnInfo( - header: "Immediates", maxWidth: 0, + header: "Immediates", value: { i in return "[" + i.immediates.map { i in "[\"\(i.label)\", \"\(i.type)\"]" }.joined(separator: ", ") + "]" }), + ColumnInfo( + header: "Category", + value: { i in + if let category = i.category { + return "\"" + category + "\"" + } else { + return "null" + } + }), ] for instruction in instructions { for columnIndex in columns.indices { @@ -433,7 +666,7 @@ enum WasmGen { } for (index, instruction) in instructions.enumerated() { - json += " [" + json += " [" for (columnIndex, column) in columns.enumerated() { let value = column.value(instruction) json += value.padding(toLength: column.maxWidth, withPad: " ", startingAt: 0) @@ -448,7 +681,7 @@ enum WasmGen { json += "],\n" } } - json += "]\n" + json += "]" return json } @@ -480,18 +713,20 @@ enum WasmGen { + "\n\n" + generateAnyInstructionVisitor(instructions) + "\n\n" - + generateTracingVisitor(instructions) - + "\n\n" + generateVisitorProtocol(instructions) + "\n" ), GeneratedFile( - projectSources + ["WAT", "ParseInstruction.swift"], - header + generateTextParser(instructions) + projectSources + ["WasmParser", "BinaryInstructionDecoder.swift"], + header + generateBinaryInstructionDecoder(instructions) ), GeneratedFile( - projectSources + ["WAT", "InstructionEncoder.swift"], - header + generateInstructionEncoder(instructions) + projectSources + ["WAT", "ParseTextInstruction.swift"], + header + generateTextInstructionParser(instructions) + ), + GeneratedFile( + projectSources + ["WAT", "BinaryInstructionEncoder.swift"], + header + generateBinaryInstructionEncoder(instructions) ), ] @@ -500,3 +735,72 @@ enum WasmGen { } } } + +extension WasmGen { + struct CategorizedInstruction { + let enumCaseName: String + let visitMethodName: String + let description: String + let immediates: [Instruction.Immediate] + let explicitCategory: String? + var sourceInstructions: [Instruction] = [] + + private var categoryValue: (argumentName: String?, parameterName: String, type: String)? { + guard let explicitCategory = explicitCategory else { + return nil + } + return (argumentName: nil, parameterName: explicitCategory, type: WasmGen.pascalCase(camelCase: explicitCategory)) + } + + var categoryTypeName: String? { + categoryValue?.type + } + + var associatedValues: [(argumentName: String?, parameterName: String, type: String)] { + var results: [(argumentName: String?, parameterName: String, type: String)] = [] + if var categoryValue = categoryValue { + categoryValue.type = "Instruction.\(categoryValue.type)" + results.append(categoryValue) + } + results += immediates.map { ($0.label, $0.label, $0.type) } + return results + } + } +} + +extension WasmGen.InstructionSet { + var categorized: [WasmGen.CategorizedInstruction] { + var categoryOrder: [String] = [] + var categories: [String: WasmGen.CategorizedInstruction] = [:] + + for instruction in self { + let category = instruction.category ?? instruction.name.enumCase + var categorized: WasmGen.CategorizedInstruction + if let existing = categories[category] { + categorized = existing + assert(categorized.immediates == instruction.immediates, "Inconsistent immediates for instruction \(instruction.name.text ?? instruction.name.enumCase) in category \(category)") + } else { + let enumCaseName: String + let description: String + if let explicitCategory = instruction.category { + enumCaseName = explicitCategory + description = "`\(explicitCategory)` category" + } else { + enumCaseName = instruction.name.enumCase + description = "`\(instruction.name.text ?? instruction.name.enumCase)`" + } + categorized = WasmGen.CategorizedInstruction( + enumCaseName: enumCaseName, + visitMethodName: instruction.visitMethodName, description: description, + immediates: instruction.immediates, + explicitCategory: instruction.category + ) + categoryOrder.append(category) + } + categorized.sourceInstructions.append(instruction) + categories[category] = categorized + } + + return categoryOrder.map { categories[$0]! } + } +} diff --git a/Vendor/checkout-dependency b/Vendor/checkout-dependency index f8493f54..bb896b82 100755 --- a/Vendor/checkout-dependency +++ b/Vendor/checkout-dependency @@ -34,10 +34,16 @@ class CheckoutDependency: print(f"Checking out '{dependency_name}' to {dependency['revision']}") subprocess.run(["git", "-C", dependency_path, "checkout", dependency["revision"]], check=True) + def checkout_category(self, category): + for dependency_name, dependency in self.dependencies.items(): + if category in dependency.get("categories", []): + self.checkout(dependency_name) + def checkout_all(self): for dependency_name in self.dependencies.keys(): self.checkout(dependency_name) + def main(): import argparse dependencies_file = os.path.join(os.path.dirname(__file__), "dependencies.json") @@ -46,13 +52,22 @@ def main(): parser = argparse.ArgumentParser(description="Checkout dependency repositories") available_dependencies = ", ".join(checkout_dependency.dependencies.keys()) parser.add_argument("names", nargs="*", help=f"Available dependencies: {available_dependencies}") + parser.add_argument("--all", action="store_true", help="Checkout all dependencies") + parser.add_argument("--category", action="append", dest="categories", + default=["default"], + help="Checkout dependencies by category") + args = parser.parse_args() if args.names: for dependency_name in args.names: checkout_dependency.checkout(dependency_name) - else: + elif args.all: checkout_dependency.checkout_all() + elif args.categories: + for category in args.categories: + checkout_dependency.checkout_category(category) + if __name__ == "__main__": main() diff --git a/Vendor/dependencies.json b/Vendor/dependencies.json index f3be9585..247c19ec 100644 --- a/Vendor/dependencies.json +++ b/Vendor/dependencies.json @@ -1,26 +1,37 @@ { "testsuite": { "repository": "https://github.com/WebAssembly/testsuite.git", - "revision": "53da17c0936a23f68f97cde4f9346a0a374dc35f" + "revision": "53da17c0936a23f68f97cde4f9346a0a374dc35f", + "categories": ["default"] }, "wasi-testsuite": { "repository": "https://github.com/WebAssembly/wasi-testsuite.git", - "revision": "c9c751586fd86b321d595bbef13f2c7403cfdbc5" + "revision": "c9c751586fd86b321d595bbef13f2c7403cfdbc5", + "categories": ["default"] }, "wasm-c-api": { "repository": "https://github.com/WebAssembly/wasm-c-api.git", - "revision": "2ce1367c9d1271c83fb63bef26d896a2f290cd23" + "revision": "2ce1367c9d1271c83fb63bef26d896a2f290cd23", + "categories": ["default"] }, "swift-format": { "repository": "https://github.com/swiftlang/swift-format.git", - "revision": "b268009b0d1182f9a573311c1a69eee9a7fd3d3f" + "revision": "c7a8b752e35d96577d1e9191d27cd9ea57dce2f2", + "categories": ["default"] }, "wish-you-were-fast": { "repository": "https://github.com/composablesys/wish-you-were-fast.git", - "revision": "65c968685d66eea3ae88ef747df95210690c6b46" + "revision": "65c968685d66eea3ae88ef747df95210690c6b46", + "categories": ["benchmark"] + }, + "swift-stringify-macro.wasm": { + "repository": "https://github.com/kateinoigakukun/swift-stringify-macro.wasm.git", + "revision": "576ef7ff3b9713d75519eab0cb1f2b738220b636", + "categories": ["benchmark"] }, "coremark": { "repository": "https://github.com/eembc/coremark.git", - "revision": "d5fad6bd094899101a4e5fd53af7298160ced6ab" + "revision": "d5fad6bd094899101a4e5fd53af7298160ced6ab", + "categories": ["benchmark"] } }