├── .gitattributes ├── .github └── workflows │ ├── linux.yml │ ├── macos.yml │ ├── static.yml │ └── windows.yml ├── .gitignore ├── .vscode └── settings.json ├── Changes ├── Changes.Wasm-Hook ├── README.md ├── author.yml ├── corpus ├── wasm │ ├── Math.pm │ └── Math.wat ├── wasm__linker │ └── lib │ │ ├── Foo │ │ └── Bar │ │ │ ├── X1.pm │ │ │ ├── X1.wat │ │ │ ├── X2.pm │ │ │ ├── X2.wat │ │ │ ├── X3.pm │ │ │ ├── X4.pm │ │ │ ├── X4.wat │ │ │ ├── X5.pm │ │ │ └── X5.wat │ │ ├── Module1.pm │ │ ├── Module1.wat │ │ ├── Module2.pm │ │ ├── Module2.wat │ │ ├── Module3.pm │ │ └── Module3.wat └── wasm_hook │ └── lib │ └── Foo │ └── Bar │ └── Baz │ ├── Math.wat │ └── Math2.wat ├── dist.ini ├── examples ├── README ├── synopsis │ ├── caller.pl │ ├── config.pl │ ├── engine.pl │ ├── exporttype.pl │ ├── extern.pl │ ├── externtype.pl │ ├── func1.pl │ ├── func2.pl │ ├── func3.pl │ ├── func4.pl │ ├── functype.pl │ ├── global.pl │ ├── global2.pl │ ├── global3.pl │ ├── globaltype.pl │ ├── importtype.pl │ ├── instance.pl │ ├── instance_exports.pl │ ├── linker.pl │ ├── memory.pl │ ├── memory2.pl │ ├── memory3.pl │ ├── memorytype.pl │ ├── module.pl │ ├── module_exports.pl │ ├── module_imports.pl │ ├── store.pl │ ├── table.pl │ ├── tabletype.pl │ ├── trap.pl │ ├── trap2.pl │ ├── valtype.pl │ ├── wasiconfig.pl │ ├── wasiinstance.pl │ ├── wasm.pl │ ├── wasmtime.pl │ └── wat2wasm.pl ├── wasm │ ├── gcd.pl │ ├── hello.pl │ ├── lib │ │ ├── Linking1.pm │ │ ├── Linking1.wat │ │ ├── Linking2.pm │ │ └── Linking2.wat │ ├── linking.pl │ ├── memory.pl │ └── multi.pl └── wasmtime │ ├── gcd.pl │ ├── gcd.wat │ ├── hello.pl │ ├── hello.wat │ ├── linking.pl │ ├── linking1.wat │ ├── linking2.wat │ ├── memory.pl │ ├── memory.wat │ ├── multi.pl │ └── multi.wat ├── lib ├── Test2 │ └── Plugin │ │ └── Wasm.pm ├── Wasm.pm └── Wasm │ ├── Func.pm │ ├── Global.pm │ ├── Hook.pm │ ├── Memory.pm │ ├── Table.pm │ ├── Trap.pm │ ├── Wasmtime.pm │ └── Wasmtime │ ├── Caller.pm │ ├── Config.pm │ ├── Engine.pm │ ├── ExportType.pm │ ├── Extern.pm │ ├── ExternType.pm │ ├── FFI.pm │ ├── Func.pm │ ├── FuncType.pm │ ├── Global.pm │ ├── GlobalType.pm │ ├── ImportType.pm │ ├── Instance.pm │ ├── Instance │ └── Exports.pm │ ├── Linker.pm │ ├── Memory.pm │ ├── MemoryType.pm │ ├── Module.pm │ ├── Module │ ├── Exports.pm │ └── Imports.pm │ ├── Store.pm │ ├── Table.pm │ ├── TableType.pm │ ├── Trap.pm │ ├── ValType.pm │ ├── WasiConfig.pm │ ├── WasiInstance.pm │ └── Wat2Wasm.pm ├── maint └── cip-before-install ├── perlcriticrc ├── t ├── 00_diag.t ├── lib │ └── Test2 │ │ └── Tools │ │ └── Wasm.pm ├── test2_plugin_wasm.t ├── wasm.t ├── wasm__linker.t ├── wasm_func.t ├── wasm_global.t ├── wasm_hook.t ├── wasm_memory.t ├── wasm_trap.t ├── wasm_wasmtime.t ├── wasm_wasmtime_bytevec.t ├── wasm_wasmtime_caller.t ├── wasm_wasmtime_config.t ├── wasm_wasmtime_engine.t ├── wasm_wasmtime_exporttype.t ├── wasm_wasmtime_extern.t ├── wasm_wasmtime_externtype.t ├── wasm_wasmtime_ffi.t ├── wasm_wasmtime_func.t ├── wasm_wasmtime_functype.t ├── wasm_wasmtime_global.t ├── wasm_wasmtime_globaltype.t ├── wasm_wasmtime_importtype.t ├── wasm_wasmtime_instance.t ├── wasm_wasmtime_instance_exports.t ├── wasm_wasmtime_linker.t ├── wasm_wasmtime_memory.t ├── wasm_wasmtime_memorytype.t ├── wasm_wasmtime_module.t ├── wasm_wasmtime_module_exports.t ├── wasm_wasmtime_module_imports.t ├── wasm_wasmtime_store.t ├── wasm_wasmtime_table.t ├── wasm_wasmtime_tabletype.t ├── wasm_wasmtime_trap.t ├── wasm_wasmtime_valtype.t ├── wasm_wasmtime_wasiconfig.t ├── wasm_wasmtime_wasiinstance.t └── wasm_wasmtime_wat2wasm.t └── xt └── author ├── critic.t ├── cycle.t └── examples.t /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pm linguist-language=Perl 2 | *.t linguist-language=Perl 3 | *.h linguist-language=C 4 | 5 | 6 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | tags-ignore: 8 | - '*' 9 | pull_request: 10 | 11 | jobs: 12 | perl: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | cip: 20 | - tag: "5.41" 21 | - tag: "5.40" 22 | # - tag: "5.40" 23 | # wasmtime: "v0.28.0" 24 | - tag: "5.38" 25 | - tag: "5.36" 26 | - tag: "5.34" 27 | - tag: "5.32" 28 | - tag: "5.30" 29 | - tag: "5.28" 30 | - tag: "5.26" 31 | - tag: "5.24" 32 | - tag: "5.22" 33 | - tag: "5.20" 34 | - tag: "5.18" 35 | - tag: "5.16" 36 | - tag: "5.14" 37 | - tag: "5.12" 38 | - tag: "5.10" 39 | 40 | env: 41 | CIP_TAG: ${{ matrix.cip.tag }} 42 | ALIEN_WASMTIME_VERSION: ${{ matrix.cip.wasmtime }} 43 | 44 | steps: 45 | - uses: actions/checkout@v2 46 | 47 | - name: Bootstrap CIP 48 | run: | 49 | curl -L https://raw.githubusercontent.com/uperl/cip/main/bin/github-bootstrap | bash 50 | 51 | - name: Cache-Key 52 | id: cache-key 53 | run: | 54 | echo -n '::set-output name=key::' 55 | cip cache-key 56 | 57 | - name: Cache CPAN modules 58 | uses: actions/cache@v2 59 | with: 60 | path: ~/.cip 61 | key: ${{ runner.os }}-build-${{ steps.cache-key.outputs.key }}-${{ matrix.cip.wasmtime }} 62 | restore-keys: | 63 | ${{ runner.os }}-build-${{ steps.cache-key.outputs.key }}-${{ matrix.cip.wasmtime }} 64 | 65 | - name: Start-Container 66 | run: | 67 | cip start 68 | 69 | - name: Diagnostics 70 | run: | 71 | cip diag 72 | 73 | - name: Install-Dependencies 74 | run: | 75 | cip install 76 | 77 | - name: Build + Test 78 | run: | 79 | cip script 80 | 81 | - name: CPAN log 82 | if: ${{ failure() }} 83 | run: | 84 | cip exec bash -c 'cat $HOME/.cpanm/latest-build/build.log' 85 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macos 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | tags-ignore: 8 | - '*' 9 | pull_request: 10 | 11 | env: 12 | PERL5LIB: /Users/runner/perl5/lib/perl5 13 | PERL_LOCAL_LIB_ROOT: /Users/runner/perl5 14 | PERL_MB_OPT: --install_base /Users/runner/perl5 15 | PERL_MM_OPT: INSTALL_BASE=/Users/runner/perl5 16 | 17 | jobs: 18 | perl: 19 | 20 | runs-on: macOS-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | 28 | - name: Set up Perl 29 | run: | 30 | brew install perl libffi libarchive 31 | brew install libffi 32 | brew install jq 33 | curl https://cpanmin.us | perl - App::cpanminus -n 34 | echo "/Users/runner/perl5/bin" >> $GITHUB_PATH 35 | 36 | - name: perl -V 37 | run: perl -V 38 | 39 | - name: Prepare for cache 40 | run: | 41 | perl -V > perlversion.txt 42 | brew info --installed --json libffi | jq '.[] | select(.name=="libffi") | .versions' >> perlversion.txt 43 | ls -l perlversion.txt 44 | 45 | - name: Cache CPAN modules 46 | uses: actions/cache@v1 47 | with: 48 | path: ~/perl5 49 | key: ${{ runner.os }}-build-${{ hashFiles('perlversion.txt') }} 50 | restore-keys: | 51 | ${{ runner.os }}-build-${{ hashFiles('perlversion.txt') }} 52 | 53 | - name: Install Static Dependencies 54 | run: | 55 | cpanm -n Dist::Zilla 56 | cpanm -n PeekPoke::FFI Carp::Assert 57 | dzil authordeps --missing | cpanm -n 58 | dzil listdeps --missing | cpanm -n 59 | 60 | - name: Install Dynamic Dependencies 61 | run: dzil run --no-build 'cpanm --installdeps .' 62 | 63 | - name: Run Tests 64 | run: dzil test -v 65 | 66 | - name: CPAN log 67 | if: ${{ failure() }} 68 | run: | 69 | cat ~/.cpanm/latest-build/build.log 70 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | name: static 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | tags-ignore: 8 | - '*' 9 | pull_request: 10 | 11 | jobs: 12 | perl: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | env: 17 | CIP_TAG: static 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: Bootstrap CIP 23 | run: | 24 | curl -L https://raw.githubusercontent.com/uperl/cip/main/bin/github-bootstrap | bash 25 | 26 | - name: Build + Test 27 | run: | 28 | cip script 29 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | tags-ignore: 8 | - '*' 9 | pull_request: 10 | 11 | env: 12 | PERL5LIB: c:\cx\lib\perl5 13 | PERL_LOCAL_LIB_ROOT: c:/cx 14 | PERL_MB_OPT: --install_base C:/cx 15 | PERL_MM_OPT: INSTALL_BASE=C:/cx 16 | 17 | jobs: 18 | perl: 19 | 20 | runs-on: windows-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | 25 | steps: 26 | - name: Set git to use LF 27 | run: | 28 | git config --global core.autocrlf false 29 | git config --global core.eol lf 30 | 31 | - uses: actions/checkout@v2 32 | 33 | - name: Set up Perl 34 | run: | 35 | choco install strawberryperl 36 | echo "C:\cx\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 37 | echo "C:\strawberry\c\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 38 | echo "C:\strawberry\perl\site\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 39 | echo "C:\strawberry\perl\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 40 | 41 | - name: Prepare for cache 42 | run: | 43 | perl -V > perlversion.txt 44 | 45 | - name: Cache CPAN modules 46 | uses: actions/cache@v1 47 | env: 48 | cache-name: cache-cpan-modules 49 | with: 50 | path: c:\cx 51 | key: ${{ runner.os }}-build-${{ hashFiles('perlversion.txt') }} 52 | restore-keys: | 53 | ${{ runner.os }}-build-${{ hashFiles('perlversion.txt') }} 54 | 55 | - name: perl -V 56 | run: perl -V 57 | 58 | - name: Install Static Dependencies 59 | run: | 60 | cpanm -n Dist::Zilla 61 | cpanm -n PeekPoke::FFI Carp::Assert 62 | dzil authordeps --missing | cpanm -n 63 | dzil listdeps --missing | cpanm -n 64 | 65 | - name: Install Dynamic Dependencies 66 | run: dzil run --no-build 'cpanm --installdeps .' 67 | 68 | - name: Run Tests 69 | run: dzil test -v 70 | 71 | 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Wasm-* 2 | /.build/ 3 | *.swp 4 | /.tmp/ 5 | *.wasm 6 | /jit-* 7 | /.teset-script* 8 | /.build 9 | *.old 10 | *.orig 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "pls.perlcritic.perlcriticrc": "perlcriticrc", 3 | "pls.inc": [ 4 | "$ROOT_PATH/lib" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /Changes.Wasm-Hook: -------------------------------------------------------------------------------- 1 | Revision history for Wasm-Hook 2 | 3 | After 0.02 Wasm-Hook was merged with Wasm 4 | 5 | 0.02 2020-04-11 11:08:56 -0600 6 | - Wasm subroutines are exportable using Exporter (gh#2) 7 | 8 | 0.01 2020-04-10 21:09:49 -0600 9 | - initial version 10 | 11 | 12 | -------------------------------------------------------------------------------- /author.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pod_spelling_system: 3 | skip: 0 4 | # list of words that are spelled correctly 5 | # (regardless of what spell check thinks) 6 | # or stuff that I like to spell incorrectly 7 | # intentionally 8 | stopwords: 9 | - Wasm 10 | - wasmtime 11 | - WebAssembly 12 | - wasm 13 | - wat 14 | - extern 15 | - api 16 | - mathstuff 17 | - SIMD 18 | - cranelift 19 | - profiler 20 | - params 21 | - ie 22 | - libc 23 | - WASI 24 | - TOML 25 | - interruptable 26 | - gc 27 | - finalizers 28 | 29 | pod_coverage: 30 | skip: 0 31 | # format is "Class#method" or "Class",regex allowed 32 | # for either Class or method. 33 | private: 34 | - Wasm::Wasmtime::ExternType#new 35 | - Wasm::Wasmtime::Extern#new 36 | - Wasm::Wasmtime::Table#new 37 | - Wasm::Wasmtime::Caller#new 38 | - Wasm::Wasmtime::Instance::Exports#new 39 | - Wasm::Wasmtime::Instance::Exports#can 40 | - Wasm::Wasmtime::Module::Exports#new 41 | - Wasm::Wasmtime::Module::Exports#can 42 | - Wasm::Wasmtime::Module::Imports#new 43 | - Wasm::Wasmtime::Module::Imports#can 44 | - Wasm::Memory#new 45 | - Test2::Plugin::Wasm 46 | -------------------------------------------------------------------------------- /corpus/wasm/Math.pm: -------------------------------------------------------------------------------- 1 | package Math; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm -api => 0, -self; 6 | 7 | 1; 8 | -------------------------------------------------------------------------------- /corpus/wasm/Math.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "add") (param i32 i32) (result i32) 3 | local.get 0 4 | local.get 1 5 | i32.add) 6 | (func (export "subtract") (param i32 i32) (result i32) 7 | local.get 0 8 | local.get 1 9 | i32.sub) 10 | (memory (export "frooble") 2 3) 11 | ) 12 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Foo/Bar/X1.pm: -------------------------------------------------------------------------------- 1 | package Foo::Bar::X1; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm -api => 0, -self; 6 | 7 | 1; 8 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Foo/Bar/X1.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (global (export "x1") (mut i32) (i32.const 42)) 3 | ) 4 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Foo/Bar/X2.pm: -------------------------------------------------------------------------------- 1 | package Foo::Bar::X2; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm -api => 0, -self; 6 | 7 | 1; 8 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Foo/Bar/X2.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (global $g (import "Foo::Bar::X1" "x1") (mut i32)) 3 | (func (export "get_x1") (result i32) 4 | (global.get $g)) 5 | (func (export "inc_x1") 6 | (global.set $g 7 | (i32.add (global.get $g) (i32.const 1)))) 8 | ) 9 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Foo/Bar/X3.pm: -------------------------------------------------------------------------------- 1 | package Foo::Bar::X3; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm 6 | -api => 0, 7 | -global => [ 'x3', 'i32', 'var', 42]; 8 | 9 | sub hello 10 | { 11 | print "hello, world!\n"; 12 | } 13 | 14 | 1; 15 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Foo/Bar/X4.pm: -------------------------------------------------------------------------------- 1 | package Foo::Bar::X4; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm -api => 0, -self; 6 | 7 | 1; 8 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Foo/Bar/X4.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (global $g (import "Foo::Bar::X3" "x3") (mut i32)) 3 | (func $hello (import "Foo::Bar::X3" "hello")) 4 | (func (export "get_x3") (result i32) 5 | (global.get $g)) 6 | (func (export "inc_x3") 7 | (global.set $g 8 | (i32.add (global.get $g) (i32.const 1)))) 9 | (func (export "run") (call $hello)) 10 | ) 11 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Foo/Bar/X5.pm: -------------------------------------------------------------------------------- 1 | package Foo::Bar::X5; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm -api => 0, -self; 6 | 7 | 1; 8 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Foo/Bar/X5.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (global $g (import "Foo::Bar::X3" "x3") (mut i32)) 3 | (func (export "get_x3") (result i32) 4 | (global.get $g)) 5 | (func (export "inc_x3") 6 | (global.set $g 7 | (i32.add (global.get $g) (i32.const 1)))) 8 | ) 9 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Module1.pm: -------------------------------------------------------------------------------- 1 | package Module1; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm -api => 0, -self; 6 | 7 | 1; 8 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Module1.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type $fd_write_ty (func (param i32 i32 i32 i32) (result i32))) 3 | (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (type $fd_write_ty))) 4 | 5 | (func (export "log") (param i32 i32) 6 | ;; store the pointer in the first iovec field 7 | i32.const 4 8 | local.get 0 9 | i32.store 10 | 11 | ;; store the length in the first iovec field 12 | i32.const 4 13 | local.get 1 14 | i32.store offset=4 15 | 16 | ;; call the `fd_write` import 17 | i32.const 1 ;; stdout fd 18 | i32.const 4 ;; iovs start 19 | i32.const 1 ;; number of iovs 20 | i32.const 0 ;; where to write nwritten bytes 21 | call $fd_write 22 | drop 23 | ) 24 | 25 | (memory (export "memory") 2) 26 | (global (export "memory_offset") i32 (i32.const 65536)) 27 | ) 28 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Module2.pm: -------------------------------------------------------------------------------- 1 | package Module2; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm -api => 0, -self; 6 | 7 | 1; 8 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Module2.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "Module1" "log" (func $log (param i32 i32))) 3 | (import "Module1" "memory" (memory 1)) 4 | (import "Module1" "memory_offset" (global $offset i32)) 5 | 6 | (func (export "run") 7 | ;; Our `data` segment initialized our imported memory, so let's print the 8 | ;; string there now. 9 | global.get $offset 10 | i32.const 14 11 | call $log 12 | ) 13 | 14 | (data (global.get $offset) "Hello, world!\n") 15 | ) 16 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Module3.pm: -------------------------------------------------------------------------------- 1 | package Module3; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm -api => 0, -self; 6 | 7 | 1; 8 | -------------------------------------------------------------------------------- /corpus/wasm__linker/lib/Module3.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "Bogus" "log" (func $log (param i32 i32))) 3 | (import "Bogus" "memory" (memory 1)) 4 | (import "Bogus" "memory_offset" (global $offset i32)) 5 | 6 | (func (export "run") 7 | ;; Our `data` segment initialized our imported memory, so let's print the 8 | ;; string there now. 9 | global.get $offset 10 | i32.const 14 11 | call $log 12 | ) 13 | 14 | (data (global.get $offset) "Hello, world!\n") 15 | ) 16 | -------------------------------------------------------------------------------- /corpus/wasm_hook/lib/Foo/Bar/Baz/Math.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "add") (param i32 i32) (result i32) 3 | local.get 0 4 | local.get 1 5 | i32.add) 6 | (func (export "subtract") (param i32 i32) (result i32) 7 | local.get 0 8 | local.get 1 9 | i32.sub) 10 | (memory (export "frooble") 2 3) 11 | ) 12 | -------------------------------------------------------------------------------- /corpus/wasm_hook/lib/Foo/Bar/Baz/Math2.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "add") (param i32 i32) (result i32) 3 | local.get 0 4 | local.get 1 5 | i32.add) 6 | (func (export "subtract") (param i32 i32) (result i32) 7 | local.get 0 8 | local.get 1 9 | i32.sub) 10 | (memory (export "frooble") 2 3) 11 | ) 12 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name = Wasm 2 | author = Graham Ollis 3 | license = Perl_5 4 | copyright_holder = Graham Ollis 5 | copyright_year = 2020-2024 6 | version = 0.23 7 | 8 | ; authordep Test::Memory::Cycle 9 | 10 | [@Author::Plicease] 11 | :version = 2.79 12 | release_tests = 1 13 | installer = Author::Plicease::MakeMaker 14 | github_user = perlwasm 15 | test2_v0 = 1 16 | irc = irc://irc.perl.org/#native 17 | diag = +Alien::wasmtime 18 | diag = +Test::Alien::Diag 19 | default_branch = main 20 | 21 | workflow = static 22 | workflow = linux 23 | workflow = windows 24 | workflow = macos 25 | 26 | 27 | diag_preamble = | $post_diag = sub { 28 | diag_preamble = | eval { require Test::Alien::Diag; require Alien::wasmtime; Test::Alien::Diag::alien_diag('Alien::wasmtime'); }; 29 | diag_preamble = | if($@) { 30 | diag_preamble = | eval { 31 | diag_preamble = | require Wasm::Wasmtime::FFI; 32 | diag_preamble = | diag "Wasm::Wasmtime::FFI->_lib = $_" for Wasm::Wasmtime::FFI->_lib; 33 | diag_preamble = | }; 34 | diag_preamble = | diag "error requiring Wasm::Wasmtime::FFI: $@" if $@; 35 | diag_preamble = | }; 36 | ;diag_preamble = | spacer(); 37 | ;diag_preamble = | require Wasm::Wasmtime::FFI; 38 | ;diag_preamble = | diag "is 0.23.0? = ", Wasm::Wasmtime::FFI::_v0_23_0(); 39 | diag_preamble = | }; 40 | 41 | [Author::Plicease::Core] 42 | 43 | [Prereqs / ConfigurePrereqs] 44 | -phase = configure 45 | FFI::CheckLib = 0.26 46 | 47 | [Prereqs / TestPrereqs] 48 | -phase = develop 49 | Carp::Assert = 0 50 | PeekPoke::FFI = 0 51 | FFI::CheckLib = 0.26 52 | 53 | [Prereqs] 54 | FFI::Platypus::Type::PtrObject = 0.02 55 | 56 | [DynamicPrereqs / AlienWasmtime] 57 | -condition = do { use FFI::CheckLib 0.26; !find_lib lib => 'wasmtime', symbol => ['wasmtime_func_as_funcref'] } 58 | -body = requires('Alien::wasmtime', '0.17') 59 | 60 | [Author::Plicease::Upload] 61 | cpan = 1 62 | 63 | [InsertExample] 64 | remove_boiler = 1 65 | 66 | [PruneFiles] 67 | filename = xt/author/pod_spelling_common.t 68 | match = \.wasm$ 69 | match = ^jit 70 | 71 | [RemovePrereqs] 72 | remove = Alien::wasmtime 73 | remove = Test::Alien::Diag 74 | remove = Math 75 | remove = Module1 76 | remove = Module2 77 | remove = Module3 78 | remove = Foo::Bar::X1 79 | remove = Foo::Bar::X2 80 | remove = Foo::Bar::X3 81 | remove = Foo::Bar::X4 82 | remove = Foo::Bar::X5 83 | remove = Foo::Bar::Baz::Math 84 | remove = Foo::Bar::Baz::Math2 85 | 86 | [MetaNoIndex] 87 | directory = corpus 88 | directory = examples 89 | -------------------------------------------------------------------------------- /examples/README: -------------------------------------------------------------------------------- 1 | This directory contains these subdirectories: 2 | 3 | synopsis/ 4 | - includes the synopsis examples from the POD 5 | of this distribution. Useful for showing 6 | basic usage 7 | wasmtime/ 8 | - Examples translated from the Python extension 9 | wasmtime-py into Perl using the lower level 10 | Wasm::Wasmtime interface 11 | wasm/ 12 | - Examples based on the wasmtime examples (and 13 | others) using the simpler Perlish Wasm.pm 14 | interface. 15 | 16 | -------------------------------------------------------------------------------- /examples/synopsis/caller.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | use Wasm::Wasmtime::Caller qw( wasmtime_caller ); 5 | 6 | { 7 | # this just uses Platypus to create a utility function 8 | # to convert a pointer to a C string into a Perl string. 9 | use FFI::Platypus 1.00; 10 | my $ffi = FFI::Platypus->new( api => 1 ); 11 | $ffi->attach_cast( 'cstring' => 'opaque' => 'string' ); 12 | } 13 | 14 | sub print_wasm_string 15 | { 16 | my $ptr = shift; 17 | my $caller = wasmtime_caller; 18 | my $memory = $caller->export_get('memory'); 19 | print cstring($ptr + $memory->data); 20 | } 21 | 22 | my $store = Wasm::Wasmtime::Store->new; 23 | my $instance = Wasm::Wasmtime::Instance->new( 24 | Wasm::Wasmtime::Module->new($store->engine, wat => q{ 25 | (module 26 | (import "" "print_wasm_string" (func $print_wasm_string (param i32))) 27 | (func (export "run") 28 | i32.const 0 29 | call $print_wasm_string 30 | ) 31 | (memory (export "memory") 1) 32 | (data (i32.const 0) "Hello, world!\n\00") 33 | ) 34 | }), 35 | $store, 36 | [\&print_wasm_string], 37 | ); 38 | 39 | $instance->exports->run->(); 40 | -------------------------------------------------------------------------------- /examples/synopsis/config.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $config = Wasm::Wasmtime::Config->new; 6 | $config->wasm_multi_value(1); 7 | my $engine = Wasm::Wasmtime::Engine->new($config); 8 | -------------------------------------------------------------------------------- /examples/synopsis/engine.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $engine = Wasm::Wasmtime::Engine->new; 6 | -------------------------------------------------------------------------------- /examples/synopsis/exporttype.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $module = Wasm::Wasmtime::Module->new(wat => q{ 6 | (module 7 | (func (export "foo") (param i32 i32) (result i32) 8 | local.get 0 9 | local.get 1 10 | i32.add) 11 | (memory (export "bar") 2 3) 12 | ) 13 | }); 14 | 15 | 16 | my($foo, $bar) = @{ $module->exports }; 17 | 18 | print $foo->name, "\n"; # foo 19 | print $foo->type->kind, "\n"; # func 20 | print $bar->name, "\n"; # bar 21 | print $bar->type->kind, "\n"; # memory 22 | -------------------------------------------------------------------------------- /examples/synopsis/extern.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | my $instance = Wasm::Wasmtime::Instance->new( 7 | Wasm::Wasmtime::Module->new($store->engine, wat => q{ 8 | (module 9 | (func (export "foo") (param i32 i32) (result i32) 10 | local.get 0 11 | local.get 1 12 | i32.add) 13 | (memory (export "bar") 2 3) 14 | ) 15 | }), 16 | $store, 17 | ); 18 | 19 | my $foo = $instance->exports->foo; 20 | print $foo->kind, "\n"; # func 21 | 22 | my $bar = $instance->exports->bar; 23 | print $bar->kind, "\n"; # memory 24 | -------------------------------------------------------------------------------- /examples/synopsis/externtype.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $module = Wasm::Wasmtime::Module->new(wat => q{ 6 | (module 7 | (func (export "foo") (param i32 i32) (result i32) 8 | local.get 0 9 | local.get 1 10 | i32.add) 11 | (memory (export "bar") 2 3) 12 | ) 13 | }); 14 | 15 | my $foo = $module->exports->foo; 16 | print $foo->kind, "\n"; # functype 17 | 18 | my $bar = $module->exports->bar; 19 | print $bar->kind, "\n"; # memorytype 20 | -------------------------------------------------------------------------------- /examples/synopsis/func1.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | # Call a wasm function from Perl 5 | use Wasm::Wasmtime; 6 | 7 | my $store = Wasm::Wasmtime::Store->new; 8 | my $module = Wasm::Wasmtime::Module->new( $store->engine, wat => q{ 9 | (module 10 | (func (export "add") (param i32 i32) (result i32) 11 | local.get 0 12 | local.get 1 13 | i32.add) 14 | ) 15 | }); 16 | 17 | my $instance = Wasm::Wasmtime::Instance->new($module, $store); 18 | my $add = $instance->exports->add; 19 | print $add->call(1,2), "\n"; # 3 20 | -------------------------------------------------------------------------------- /examples/synopsis/func2.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | # Call Perl from Wasm 5 | use Wasm::Wasmtime; 6 | 7 | my $store = Wasm::Wasmtime::Store->new; 8 | my $module = Wasm::Wasmtime::Module->new( $store->engine, wat => q{ 9 | (module 10 | (func $hello (import "" "hello")) 11 | (func (export "run") (call $hello)) 12 | ) 13 | }); 14 | 15 | my $hello = Wasm::Wasmtime::Func->new( 16 | $store, 17 | Wasm::Wasmtime::FuncType->new([],[]), 18 | sub { print "hello world!\n" }, 19 | ); 20 | 21 | my $instance = Wasm::Wasmtime::Instance->new($module, $store, [$hello]); 22 | $instance->exports->run->call(); # hello world! 23 | 24 | -------------------------------------------------------------------------------- /examples/synopsis/func3.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm 4 | -api => 0, 5 | -wat => q{ 6 | (module 7 | (func (export "add") (param i32 i32) (result i32) 8 | local.get 0 9 | local.get 1 10 | i32.add) 11 | ) 12 | } 13 | ; 14 | 15 | print add(1,2), "\n"; # 3 16 | -------------------------------------------------------------------------------- /examples/synopsis/func4.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | sub hello { 4 | print "hello world!\n"; 5 | } 6 | 7 | use Wasm 8 | -api => 0, 9 | -wat => q{ 10 | (module 11 | (func $hello (import "main" "hello")) 12 | (func (export "run") (call $hello)) 13 | ) 14 | } 15 | ; 16 | 17 | run(); # hello world! 18 | -------------------------------------------------------------------------------- /examples/synopsis/functype.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $functype = Wasm::Wasmtime::FuncType->new( 6 | # This function type takes a 32 bit and 64 bit 7 | # integer and returns a double floating point 8 | [ 'i32', 'i64' ] => [ 'f64' ], 9 | ); 10 | -------------------------------------------------------------------------------- /examples/synopsis/global.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | my $global = Wasm::Wasmtime::Global->new( 7 | $store, 8 | Wasm::Wasmtime::GlobalType->new('i32','var'), 9 | 42, 10 | ); 11 | 12 | print $global->get, "\n"; # 42 13 | $global->set(99); 14 | print $global->get, "\n"; # 99 15 | 16 | -------------------------------------------------------------------------------- /examples/synopsis/global2.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm 4 | -api => 0, 5 | -wat => q{ 6 | (module 7 | (global (export "global") (mut i32) (i32.const 42)) 8 | ) 9 | } 10 | ; 11 | 12 | print "$global\n"; # 42 13 | $global = 99; 14 | print "$global\n"; # 99 15 | 16 | -------------------------------------------------------------------------------- /examples/synopsis/global3.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | package Foo; 5 | 6 | use Wasm 7 | -api => 0, 8 | -global => [ 9 | 'foo', # name 10 | 'i32', # type 11 | 'var', # mutability 12 | 42, # initial value 13 | ] 14 | ; 15 | 16 | package Bar; 17 | 18 | use Wasm 19 | -api => 0, 20 | -wat => q{ 21 | (module 22 | (global $foo (import "Foo" "foo") (mut i32)) 23 | (func (export "get_foo") (result i32) 24 | (global.get $foo)) 25 | (func (export "inc_foo") 26 | (global.set $foo 27 | (i32.add (global.get $foo) (i32.const 1)))) 28 | ) 29 | } 30 | ; 31 | 32 | package main; 33 | 34 | print Bar::get_foo(), "\n"; # 42 35 | Bar::inc_foo(); 36 | print Bar::get_foo(), "\n"; # 43 37 | $Foo::foo = 0; 38 | print Bar::get_foo(), "\n"; # 0 39 | -------------------------------------------------------------------------------- /examples/synopsis/globaltype.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $globaltype = Wasm::Wasmtime::GlobalType->new('i32','var'); 6 | -------------------------------------------------------------------------------- /examples/synopsis/importtype.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $module = Wasm::Wasmtime::Module->new( wat => q{ 6 | (module 7 | (func $hello (import "xx" "hello")) 8 | ) 9 | }); 10 | 11 | my $hello = $module->imports->[0]; 12 | 13 | print $hello->module, "\n"; # xx 14 | print $hello->name, "\n"; # hello 15 | print $hello->type->kind, "\n"; # functype 16 | -------------------------------------------------------------------------------- /examples/synopsis/instance.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | my $module = Wasm::Wasmtime::Module->new($store->engine, wat => '(module)'); 7 | my $instance = Wasm::Wasmtime::Instance->new($module, $store, []); 8 | -------------------------------------------------------------------------------- /examples/synopsis/instance_exports.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | my $module = Wasm::Wasmtime::Module->new( $store->engine, wat => q{ 7 | (module 8 | (func (export "add") (param i32 i32) (result i32) 9 | local.get 0 10 | local.get 1 11 | i32.add) 12 | ) 13 | }); 14 | 15 | my $instance = Wasm::Wasmtime::Instance->new($module, $store); 16 | 17 | my $exports = $instance->exports; 18 | 19 | print $exports->add->call(1,2), "\n"; # 3 20 | print $exports->{add}->call(1,2), "\n"; # 3 21 | print $exports->[0]->call(1,2), "\n"; # 3 22 | -------------------------------------------------------------------------------- /examples/synopsis/linker.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | my $linker = Wasm::Wasmtime::Linker->new($store); 7 | 8 | # Instanciate and define a WASI instance 9 | my $wasi = Wasm::Wasmtime::WasiInstance->new( 10 | $store, 11 | "wasi_snapshot_preview1", 12 | Wasm::Wasmtime::WasiConfig 13 | ->new 14 | ->inherit_stdout 15 | ); 16 | $linker->define_wasi($wasi); 17 | 18 | # Create a logger module + instance 19 | my $logger = $linker->instantiate( 20 | Wasm::Wasmtime::Module->new( 21 | $store->engine, 22 | wat => q{ 23 | (module 24 | (type $fd_write_ty (func (param i32 i32 i32 i32) (result i32))) 25 | (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (type $fd_write_ty))) 26 | 27 | (func (export "log") (param i32 i32) 28 | ;; store the pointer in the first iovec field 29 | i32.const 4 30 | local.get 0 31 | i32.store 32 | 33 | ;; store the length in the first iovec field 34 | i32.const 4 35 | local.get 1 36 | i32.store offset=4 37 | 38 | ;; call the `fd_write` import 39 | i32.const 1 ;; stdout fd 40 | i32.const 4 ;; iovs start 41 | i32.const 1 ;; number of iovs 42 | i32.const 0 ;; where to write nwritten bytes 43 | call $fd_write 44 | drop 45 | ) 46 | 47 | (memory (export "memory") 2) 48 | (global (export "memory_offset") i32 (i32.const 65536)) 49 | ) 50 | }, 51 | ) 52 | ); 53 | $linker->define_instance("logger", $logger); 54 | 55 | # Create a caller module + instance 56 | my $caller = $linker->instantiate( 57 | Wasm::Wasmtime::Module->new( 58 | $store->engine, 59 | wat => q{ 60 | (module 61 | (import "logger" "log" (func $log (param i32 i32))) 62 | (import "logger" "memory" (memory 1)) 63 | (import "logger" "memory_offset" (global $offset i32)) 64 | 65 | (func (export "run") 66 | ;; Our `data` segment initialized our imported memory, so let's print the 67 | ;; string there now. 68 | global.get $offset 69 | i32.const 14 70 | call $log 71 | ) 72 | 73 | (data (global.get $offset) "Hello, world!\n") 74 | ) 75 | }, 76 | ), 77 | ); 78 | $caller->exports->run->(); 79 | -------------------------------------------------------------------------------- /examples/synopsis/memory.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | use PeekPoke::FFI qw( poke ); 5 | 6 | my $store = Wasm::Wasmtime::Store->new; 7 | 8 | # create a new memory object with a minumum 9 | # of 3 pages and maximum of 9 10 | my $memory = Wasm::Wasmtime::Memory->new( 11 | $store, 12 | Wasm::Wasmtime::MemoryType->new([3,9]), 13 | ); 14 | 15 | poke($memory->data + 10, 42); # store the byte 42 at offset 16 | # 10 inside the data region 17 | 18 | printf "data_size = %x\n", $memory->data_size; # 30000 19 | printf "size = %d\n", $memory->size; # 3 20 | 21 | $memory->grow(4); # increase data region by 4 pages 22 | 23 | printf "data_size = %x\n", $memory->data_size; # 70000 24 | printf "size = %d\n", $memory->size; # 7 25 | -------------------------------------------------------------------------------- /examples/synopsis/memory2.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use PeekPoke::FFI qw( peek poke ); 4 | use Wasm 5 | -api => 0, 6 | -wat => q{ 7 | (module 8 | (memory (export "memory") 3 9) 9 | ) 10 | } 11 | ; 12 | 13 | # $memory isa Wasm::Memory 14 | poke($memory->address + 10, 42); # store the byte 42 at offset 15 | # 10 inside the data region 16 | 17 | my($current, $min, $max) = $memory->limits; 18 | printf "size = %x\n", $memory->size; # 30000 19 | printf "current = %d\n", $current; # 3 20 | printf "min = %d\n", $min; # 3 21 | printf "max = %d\n", $max; # 9 22 | 23 | $memory->grow(4); # increase data region by 4 pages 24 | 25 | ($current, $min, $max) = $memory->limits; 26 | printf "size = %x\n", $memory->size; # 70000 27 | printf "current = %d\n", $current; # 7 28 | printf "min = %d\n", $min; # 3 29 | printf "max = %d\n", $max; # 9 30 | -------------------------------------------------------------------------------- /examples/synopsis/memory3.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Memory qw( wasm_caller_memory ); 4 | 5 | { 6 | # this just uses Platypus to create a utility function 7 | # to convert a pointer to a C string into a Perl string. 8 | use FFI::Platypus 1.00; 9 | my $ffi = FFI::Platypus->new( api => 1 ); 10 | $ffi->attach_cast( 'cstring' => 'opaque' => 'string' ); 11 | } 12 | 13 | sub print_wasm_string 14 | { 15 | my $ptr = shift; 16 | my $memory = wasm_caller_memory; 17 | print cstring($ptr + $memory->address); 18 | } 19 | 20 | use Wasm 21 | -api => 0, 22 | -wat => q{ 23 | (module 24 | (import "main" "print_wasm_string" (func $print_wasm_string (param i32))) 25 | (func (export "run") 26 | i32.const 0 27 | call $print_wasm_string 28 | ) 29 | (memory (export "memory") 1) 30 | (data (i32.const 0) "Hello, world!\n\00") 31 | ) 32 | }, 33 | ; 34 | 35 | run(); 36 | -------------------------------------------------------------------------------- /examples/synopsis/memorytype.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | # new memory type with minimum 3 and maximum 5 pages 6 | my $memorytype = Wasm::Wasmtime::MemoryType->new([3,5]); 7 | -------------------------------------------------------------------------------- /examples/synopsis/module.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $module = Wasm::Wasmtime::Module->new( wat => '(module)' ); 6 | -------------------------------------------------------------------------------- /examples/synopsis/module_exports.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $module = Wasm::Wasmtime::Module->new( wat => q{ 6 | (module 7 | (func (export "add") (param i32 i32) (result i32) 8 | local.get 0 9 | local.get 1 10 | i32.add) 11 | ) 12 | }); 13 | 14 | my $exports = $module->exports; # Wasm::Wasmtime::Module::Exports 15 | 16 | my $type1 = $exports->add; # this is the Wasm::Wasmtime::FuncType for add 17 | my $type2 = $exports->{add}; # this is also the Wasm::Wasmtime::FuncType for add 18 | my $exporttype = $exports->[0]; # this is the Wasm::Wasmtime::ExportType for add 19 | -------------------------------------------------------------------------------- /examples/synopsis/module_imports.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | # TODO 6 | -------------------------------------------------------------------------------- /examples/synopsis/store.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | -------------------------------------------------------------------------------- /examples/synopsis/table.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | my $instance = Wasm::Wasmtime::Instance->new( 7 | Wasm::Wasmtime::Module->new($store->engine, wat => q{ 8 | (module 9 | (table (export "table") 1 funcref) 10 | ) 11 | }), 12 | $store, 13 | ); 14 | 15 | my $table = $instance->exports->table; 16 | print $table->type->element->kind, "\n"; # funcref 17 | print $table->size, "\n"; # 1 18 | -------------------------------------------------------------------------------- /examples/synopsis/tabletype.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $tabletype = Wasm::Wasmtime::TableType->new('i32',[2,10]); 6 | -------------------------------------------------------------------------------- /examples/synopsis/trap.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | my $trap = Wasm::Wasmtime::Trap->new( 7 | $store, 8 | "something went bump in the night\0", 9 | ); 10 | -------------------------------------------------------------------------------- /examples/synopsis/trap2.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Trap; 4 | 5 | my $trap = Wasm::Trap->new( 6 | "something went bump in the night\0", 7 | ); 8 | -------------------------------------------------------------------------------- /examples/synopsis/valtype.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $valtype = Wasm::Wasmtime::ValType->new('i32'); 6 | -------------------------------------------------------------------------------- /examples/synopsis/wasiconfig.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | my $config = Wasm::Wasmtime::WasiConfig->new; 7 | 8 | # inherit everything, and provide access to the 9 | # host filesystem under /host (yikes!) 10 | $config->inherit_argv 11 | ->inherit_env 12 | ->inherit_stdin 13 | ->inherit_stdout 14 | ->inherit_stderr 15 | ->preopen_dir("/", "/host"); 16 | 17 | my $wasi = Wasm::Wasmtime::WasiInstance->new( 18 | $store, 19 | "wasi_snapshot_preview1", 20 | $config, 21 | ); 22 | -------------------------------------------------------------------------------- /examples/synopsis/wasiinstance.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | 5 | my $store = Wasm::Wasmtime::Store->new; 6 | my $wasi = Wasm::Wasmtime::WasiInstance->new( 7 | $store, 8 | "wasi_snapshot_preview1", 9 | ); 10 | -------------------------------------------------------------------------------- /examples/synopsis/wasm.pl: -------------------------------------------------------------------------------- 1 | package MathStuff; 2 | 3 | use strict; 4 | use warnings; 5 | use base qw( Exporter ); 6 | use Wasm 7 | -api => 0, 8 | -exporter => 'ok', 9 | -wat => q{ 10 | (module 11 | (func (export "add") (param i32 i32) (result i32) 12 | local.get 0 13 | local.get 1 14 | i32.add) 15 | (func (export "subtract") (param i32 i32) (result i32) 16 | local.get 0 17 | local.get 1 18 | i32.sub) 19 | (memory (export "frooble") 2 3) 20 | ) 21 | }; 22 | 23 | 1; 24 | -------------------------------------------------------------------------------- /examples/synopsis/wasmtime.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | use PeekPoke::FFI qw( poke ); 5 | 6 | my $store = Wasm::Wasmtime::Store->new; 7 | 8 | my $module = Wasm::Wasmtime::Module->new($store->engine, wat => q{ 9 | (module 10 | 11 | ;; callback we can make back into perl space 12 | (func $hello (import "" "hello")) 13 | (func (export "call_hello") (call $hello)) 14 | 15 | ;; plain WebAssembly function that we can call from Perl 16 | (func (export "gcd") (param i32 i32) (result i32) 17 | (local i32) 18 | block ;; label = @1 19 | block ;; label = @2 20 | local.get 0 21 | br_if 0 (;@2;) 22 | local.get 1 23 | local.set 2 24 | br 1 (;@1;) 25 | end 26 | loop ;; label = @2 27 | local.get 1 28 | local.get 0 29 | local.tee 2 30 | i32.rem_u 31 | local.set 0 32 | local.get 2 33 | local.set 1 34 | local.get 0 35 | br_if 0 (;@2;) 36 | end 37 | end 38 | local.get 2 39 | ) 40 | 41 | ;; memory region that can be accessed from 42 | ;; either Perl or WebAssembly 43 | (memory (export "memory") 2 3) 44 | (func (export "load") (param i32) (result i32) 45 | (i32.load8_s (local.get 0)) 46 | ) 47 | 48 | ) 49 | }); 50 | 51 | sub hello 52 | { 53 | print "hello world!\n"; 54 | } 55 | 56 | my $instance = Wasm::Wasmtime::Instance->new( $module, $store, [\&hello] ); 57 | 58 | # call a WebAssembly function that calls back into Perl space 59 | $instance->exports->call_hello; 60 | 61 | # call plain WebAssembly function 62 | my $gcd = $instance->exports->gcd; 63 | print $gcd->(6,27), "\n"; # 3 64 | 65 | # write to memory from Perl and read it from WebAssembly 66 | my $memory = $instance->exports->memory; 67 | poke($memory->data + 10, 42); # set offset 10 to 42 68 | my $load = $instance->exports->load; 69 | print $load->(10), "\n"; # 42 70 | poke($memory->data + 10, 52); # set offset 10 to 52 71 | print $load->(10), "\n"; # 52 72 | -------------------------------------------------------------------------------- /examples/synopsis/wat2wasm.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime::Wat2Wasm; 4 | 5 | my $wasm = wat2wasm('(module)'); 6 | -------------------------------------------------------------------------------- /examples/wasm/gcd.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm 4 | -api => 0, 5 | -wat => q{ 6 | (module 7 | (func $gcd (param i32 i32) (result i32) 8 | (local i32) 9 | block ;; label = @1 10 | block ;; label = @2 11 | local.get 0 12 | br_if 0 (;@2;) 13 | local.get 1 14 | local.set 2 15 | br 1 (;@1;) 16 | end 17 | loop ;; label = @2 18 | local.get 1 19 | local.get 0 20 | local.tee 2 21 | i32.rem_u 22 | local.set 0 23 | local.get 2 24 | local.set 1 25 | local.get 0 26 | br_if 0 (;@2;) 27 | end 28 | end 29 | local.get 2 30 | ) 31 | (export "gcd" (func $gcd)) 32 | ) 33 | }, 34 | ; 35 | 36 | print "gcd(6,27) = ", gcd(6,27), "\n"; 37 | -------------------------------------------------------------------------------- /examples/wasm/hello.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | sub hello 5 | { 6 | print "Hello from Perl!\n"; 7 | } 8 | 9 | use Wasm 10 | -api => 0, 11 | -wat => q{ 12 | (module 13 | (func $hello (import "main" "hello")) 14 | (func (export "run") (call $hello)) 15 | ) 16 | }, 17 | ; 18 | 19 | run(); 20 | -------------------------------------------------------------------------------- /examples/wasm/lib/Linking1.pm: -------------------------------------------------------------------------------- 1 | package Linking1; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm 6 | -api => 0, 7 | -exporter => 'all', 8 | -self; 9 | 10 | 1; 11 | -------------------------------------------------------------------------------- /examples/wasm/lib/Linking1.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "Linking2" "double" (func $double (param i32) (result i32))) 3 | (import "Linking2" "log" (func $log (param i32 i32))) 4 | (import "Linking2" "memory" (memory 1)) 5 | (import "Linking2" "memory_offset" (global $offset i32)) 6 | 7 | (func (export "run") 8 | ;; Call into the other module to double our number, and we could print it 9 | ;; here but for now we just drop it 10 | i32.const 2 11 | call $double 12 | drop 13 | 14 | ;; Our `data` segment initialized our imported memory, so let's print the 15 | ;; string there now. 16 | global.get $offset 17 | i32.const 14 18 | call $log 19 | ) 20 | 21 | (data (global.get $offset) "Hello, world!\n") 22 | ) 23 | 24 | -------------------------------------------------------------------------------- /examples/wasm/lib/Linking2.pm: -------------------------------------------------------------------------------- 1 | package Linking2; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm 6 | -api => 0, 7 | -self; 8 | 9 | 1; 10 | -------------------------------------------------------------------------------- /examples/wasm/lib/Linking2.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type $fd_write_ty (func (param i32 i32 i32 i32) (result i32))) 3 | (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (type $fd_write_ty))) 4 | 5 | (func (export "double") (param i32) (result i32) 6 | local.get 0 7 | i32.const 2 8 | i32.mul 9 | ) 10 | 11 | (func (export "log") (param i32 i32) 12 | ;; store the pointer in the first iovec field 13 | i32.const 4 14 | local.get 0 15 | i32.store 16 | 17 | ;; store the length in the first iovec field 18 | i32.const 4 19 | local.get 1 20 | i32.store offset=4 21 | 22 | ;; call the `fd_write` import 23 | i32.const 1 ;; stdout fd 24 | i32.const 4 ;; iovs start 25 | i32.const 1 ;; number of iovs 26 | i32.const 0 ;; where to write nwritten bytes 27 | call $fd_write 28 | drop 29 | ) 30 | 31 | (memory (export "memory") 2) 32 | (global (export "memory_offset") i32 (i32.const 65536)) 33 | ) 34 | -------------------------------------------------------------------------------- /examples/wasm/linking.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Path::Tiny qw( path ); 4 | use lib path(__FILE__)->parent->child('lib')->stringify; 5 | 6 | # unlike the Wasm:Wasmtime interface, Wasm.pm does all 7 | # the linking for us. Linking1.pm and Linking2.pm are 8 | # just thin wrappers around the .wat code. 9 | use Linking1; 10 | 11 | # Linking1 uses -export => 'all', which uses Exporter 12 | # to export to any module that uses Linking1 13 | run(); 14 | -------------------------------------------------------------------------------- /examples/wasm/memory.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Carp::Assert; 4 | use Path::Tiny qw( path ); 5 | use PeekPoke::FFI qw( peek poke ); 6 | use Wasm 7 | -api => 0, 8 | -wat => q{ 9 | (module 10 | (memory (export "memory") 2 3) 11 | 12 | (func (export "size") (result i32) (memory.size)) 13 | (func (export "load") (param i32) (result i32) 14 | (i32.load8_s (local.get 0)) 15 | ) 16 | (func (export "store") (param i32 i32) 17 | (i32.store8 (local.get 0) (local.get 1)) 18 | ) 19 | 20 | (data (i32.const 0x1000) "\01\02\03\04") 21 | ) 22 | } 23 | ; 24 | 25 | print "Checking memory...\n"; 26 | assert([$memory->limits]->[0] == 2); 27 | assert($memory->size == 0x20000); 28 | 29 | # Note that usage of `data` is unsafe! This is a raw C pointer which is not 30 | # bounds checked at all. We checked our `data_size` above but you'll want to be 31 | # very careful when accessing data through `data()` 32 | assert(peek($memory->address + 0x0000) == 0); 33 | assert(peek($memory->address + 0x1000) == 1); 34 | assert(peek($memory->address + 0x1003) == 4); 35 | 36 | assert(size() == 2); 37 | assert(load(0) == 0); 38 | assert(load(0x1000) == 1); 39 | assert(load(0x1003) == 4); 40 | assert(load(0x1ffff) == 0); 41 | 42 | # out of bounds trap 43 | { 44 | local $@; 45 | eval { load(0x20000) }; 46 | assert($@ =~ /wasm trap: out of bounds memory/); 47 | } 48 | 49 | print "Mutating memory...\n"; 50 | poke($memory->address + 0x1003, 5); 51 | store(0x1002, 6); 52 | 53 | #out of bounds trap 54 | { 55 | local $@; 56 | eval { store(0x20000, 0) }; 57 | assert($@ =~ /wasm trap: out of bounds memory access/); 58 | } 59 | 60 | assert(peek($memory->address + 0x1002) == 6); 61 | assert(peek($memory->address + 0x1003) == 5); 62 | assert(load(0x1002) == 6); 63 | assert(peek($memory->address + 0x1003) == 5); 64 | 65 | # Grow memory 66 | assert($memory->grow(1)); 67 | assert([$memory->limits]->[0] == 3); 68 | assert($memory->size == 0x30000); 69 | 70 | assert(load(0x20000) == 0); 71 | store(0x20000, 0); 72 | { 73 | local $@; 74 | eval { load(0x30000) }; 75 | assert($@ =~ /wasm trap: out of bounds memory/); 76 | } 77 | { 78 | local $@; 79 | eval { store(0x30000, 0) }; 80 | assert($@ =~ /wasm trap: out of bounds memory/); 81 | } 82 | 83 | # Memory can fail to grow 84 | assert(!$memory->grow(1)); 85 | assert($memory->grow(0)); 86 | -------------------------------------------------------------------------------- /examples/wasm/multi.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Carp::Assert; 4 | 5 | sub f 6 | { 7 | my($x,$y) = @_; 8 | ($y+1, $x+1); 9 | } 10 | 11 | use Wasm 12 | -api => 0, 13 | -wat => q{ 14 | (module 15 | (func $f (import "main" "f") (param i32 i64) (result i64 i32)) 16 | 17 | (func $g (export "g") (param i32 i64) (result i64 i32) 18 | (call $f (local.get 0) (local.get 1)) 19 | ) 20 | 21 | (func $round_trip_many 22 | (export "round_trip_many") 23 | (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) 24 | (result i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) 25 | 26 | local.get 0 27 | local.get 1 28 | local.get 2 29 | local.get 3 30 | local.get 4 31 | local.get 5 32 | local.get 6 33 | local.get 7 34 | local.get 8 35 | local.get 9) 36 | ) 37 | }, 38 | ; 39 | 40 | print "Calling export \"g\"...\n"; 41 | my @results = g(1,3); 42 | printf "> %d %d\n", @results; 43 | 44 | assert($results[0] == 4); 45 | assert($results[1] == 2); 46 | 47 | print "Calling export \"round_trip_many\"...\n"; 48 | @results = round_trip_many(0,1,2,3,4,5,6,7,8,9); 49 | 50 | print "Printing results...\n"; 51 | print "> @results\n"; 52 | assert(scalar @results == 10); 53 | for my $i (0..9) 54 | { 55 | assert($results[$i] == $i); 56 | } 57 | -------------------------------------------------------------------------------- /examples/wasmtime/gcd.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Path::Tiny qw( path ); 4 | use Wasm::Wasmtime; 5 | 6 | my $store = Wasm::Wasmtime::Store->new; 7 | my $module = Wasm::Wasmtime::Module->new( $store->engine, file => path(__FILE__)->parent->child('gcd.wat') ); 8 | my $instance = Wasm::Wasmtime::Instance->new($module, $store); 9 | my $gcd = $instance->exports->gcd; 10 | 11 | print "gcd(6,27) = @{[ $gcd->(6,27) ]}\n"; 12 | -------------------------------------------------------------------------------- /examples/wasmtime/gcd.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $gcd (param i32 i32) (result i32) 3 | (local i32) 4 | block ;; label = @1 5 | block ;; label = @2 6 | local.get 0 7 | br_if 0 (;@2;) 8 | local.get 1 9 | local.set 2 10 | br 1 (;@1;) 11 | end 12 | loop ;; label = @2 13 | local.get 1 14 | local.get 0 15 | local.tee 2 16 | i32.rem_u 17 | local.set 0 18 | local.get 2 19 | local.set 1 20 | local.get 0 21 | br_if 0 (;@2;) 22 | end 23 | end 24 | local.get 2 25 | ) 26 | (export "gcd" (func $gcd)) 27 | ) 28 | 29 | -------------------------------------------------------------------------------- /examples/wasmtime/hello.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | use Path::Tiny qw( path ); 5 | 6 | # Almost all operations in wasmtime require a contextual "store" argument to be 7 | # shared amongst objects 8 | my $store = Wasm::Wasmtime::Store->new; 9 | 10 | # Here we can compile a `Module` which is then ready for instantiation 11 | # afterwards 12 | my $module = Wasm::Wasmtime::Module->new( $store->engine, file => path(__FILE__)->parent->child('hello.wat') ); 13 | 14 | # Our module needs one import, so we'll create that here. 15 | sub say_hello 16 | { 17 | print "Hello from Perl!\n"; 18 | } 19 | 20 | ## And with all that we can instantiate our module and call the export! 21 | my $instance = Wasm::Wasmtime::Instance->new($module, $store, [\&say_hello]); 22 | $instance->exports->run->(); 23 | -------------------------------------------------------------------------------- /examples/wasmtime/hello.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $hello (import "" "hello")) 3 | (func (export "run") (call $hello)) 4 | ) 5 | -------------------------------------------------------------------------------- /examples/wasmtime/linking.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | use Path::Tiny qw( path ); 5 | 6 | # Example of instantiating two modules which link to each other. 7 | my $store = Wasm::Wasmtime::Store->new; 8 | 9 | # First set up our linker which is going to be linking modules together. We 10 | # want our linker to have wasi available, so we set that up here as well. 11 | my $linker = Wasm::Wasmtime::Linker->new($store); 12 | my $wasi = Wasm::Wasmtime::WasiInstance->new( 13 | $store, 14 | "wasi_snapshot_preview1", 15 | Wasm::Wasmtime::WasiConfig 16 | ->new 17 | ->inherit_stdout 18 | ); 19 | $linker->define_wasi($wasi); 20 | 21 | # Load and compile our two modules 22 | my $module1 = Wasm::Wasmtime::Module->new($store->engine, file => path(__FILE__)->parent->child('linking1.wat') ); 23 | my $module2 = Wasm::Wasmtime::Module->new($store->engine, file => path(__FILE__)->parent->child('linking2.wat') ); 24 | 25 | # Instantiate our first module which only uses WASI, then register that 26 | # instance with the linker since the next linking will use it. 27 | my $instance2 = $linker->instantiate($module2); 28 | $linker->define_instance("linking2", $instance2); 29 | 30 | # And with that we can perform the final link and the execute the module. 31 | my $instance1 = $linker->instantiate($module1); 32 | my $run = $instance1->exports->run; 33 | $run->(); 34 | -------------------------------------------------------------------------------- /examples/wasmtime/linking1.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "linking2" "double" (func $double (param i32) (result i32))) 3 | (import "linking2" "log" (func $log (param i32 i32))) 4 | (import "linking2" "memory" (memory 1)) 5 | (import "linking2" "memory_offset" (global $offset i32)) 6 | 7 | (func (export "run") 8 | ;; Call into the other module to double our number, and we could print it 9 | ;; here but for now we just drop it 10 | i32.const 2 11 | call $double 12 | drop 13 | 14 | ;; Our `data` segment initialized our imported memory, so let's print the 15 | ;; string there now. 16 | global.get $offset 17 | i32.const 14 18 | call $log 19 | ) 20 | 21 | (data (global.get $offset) "Hello, world!\n") 22 | ) 23 | 24 | -------------------------------------------------------------------------------- /examples/wasmtime/linking2.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type $fd_write_ty (func (param i32 i32 i32 i32) (result i32))) 3 | (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (type $fd_write_ty))) 4 | 5 | (func (export "double") (param i32) (result i32) 6 | local.get 0 7 | i32.const 2 8 | i32.mul 9 | ) 10 | 11 | (func (export "log") (param i32 i32) 12 | ;; store the pointer in the first iovec field 13 | i32.const 4 14 | local.get 0 15 | i32.store 16 | 17 | ;; store the length in the first iovec field 18 | i32.const 4 19 | local.get 1 20 | i32.store offset=4 21 | 22 | ;; call the `fd_write` import 23 | i32.const 1 ;; stdout fd 24 | i32.const 4 ;; iovs start 25 | i32.const 1 ;; number of iovs 26 | i32.const 0 ;; where to write nwritten bytes 27 | call $fd_write 28 | drop 29 | ) 30 | 31 | (memory (export "memory") 2) 32 | (global (export "memory_offset") i32 (i32.const 65536)) 33 | ) 34 | -------------------------------------------------------------------------------- /examples/wasmtime/memory.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Carp::Assert; 4 | use Path::Tiny qw( path ); 5 | use Wasm::Wasmtime; 6 | use PeekPoke::FFI qw( peek poke ); 7 | 8 | my $wasm_store = Wasm::Wasmtime::Store->new; 9 | my $module = Wasm::Wasmtime::Module->new( $wasm_store->engine, file => path(__FILE__)->parent->child('memory.wat') ); 10 | my $instance = Wasm::Wasmtime::Instance->new($module, $wasm_store); 11 | 12 | my $memory = $instance->exports->memory; 13 | my $size = $instance->exports->size; 14 | my $load = $instance->exports->load; 15 | my $store = $instance->exports->store; 16 | 17 | print "Checking memory...\n"; 18 | assert($memory->size == 2); 19 | assert($memory->data_size == 0x20000); 20 | 21 | # Note that usage of `data` is unsafe! This is a raw C pointer which is not 22 | # bounds checked at all. We checked our `data_size` above but you'll want to be 23 | # very careful when accessing data through `data()` 24 | assert(peek($memory->data + 0x0000) == 0); 25 | assert(peek($memory->data + 0x1000) == 1); 26 | assert(peek($memory->data + 0x1003) == 4); 27 | 28 | assert($size->() == 2); 29 | assert($load->(0) == 0); 30 | assert($load->(0x1000) == 1); 31 | assert($load->(0x1003) == 4); 32 | assert($load->(0x1ffff) == 0); 33 | 34 | # out of bounds trap 35 | { 36 | local $@; 37 | eval { $load->(0x20000) }; 38 | assert($@ =~ /wasm trap: out of bounds memory/); 39 | } 40 | 41 | print "Mutating memory...\n"; 42 | poke($memory->data + 0x1003, 5); 43 | $store->(0x1002, 6); 44 | 45 | #out of bounds trap 46 | { 47 | local $@; 48 | eval { $store->(0x20000, 0) }; 49 | assert($@ =~ /wasm trap: out of bounds memory access/); 50 | } 51 | 52 | assert(peek($memory->data + 0x1002) == 6); 53 | assert(peek($memory->data + 0x1003) == 5); 54 | assert($load->(0x1002) == 6); 55 | assert(peek($memory->data + 0x1003) == 5); 56 | 57 | # Grow memory 58 | assert($memory->grow(1)); 59 | assert($memory->size == 3); 60 | assert($memory->data_size == 0x30000); 61 | 62 | assert($load->(0x20000) == 0); 63 | $store->(0x20000, 0); 64 | { 65 | local $@; 66 | eval { $load->(0x30000) }; 67 | assert($@ =~ /wasm trap: out of bounds memory/); 68 | } 69 | { 70 | local $@; 71 | eval { $store->(0x30000, 0) }; 72 | assert($@ =~ /wasm trap: out of bounds memory/); 73 | } 74 | 75 | # Memory can fail to grow 76 | assert(!$memory->grow(1)); 77 | assert($memory->grow(0)); 78 | 79 | print "Creating stand-alone memory...\n"; 80 | my $memory2 = Wasm::Wasmtime::Memory->new( 81 | Wasm::Wasmtime::Store->new, 82 | Wasm::Wasmtime::MemoryType->new([5,5]), 83 | ); 84 | assert($memory2->size() == 5); 85 | assert(!$memory2->grow(1)); 86 | assert($memory2->grow(0)); 87 | -------------------------------------------------------------------------------- /examples/wasmtime/memory.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (memory (export "memory") 2 3) 3 | 4 | (func (export "size") (result i32) (memory.size)) 5 | (func (export "load") (param i32) (result i32) 6 | (i32.load8_s (local.get 0)) 7 | ) 8 | (func (export "store") (param i32 i32) 9 | (i32.store8 (local.get 0) (local.get 1)) 10 | ) 11 | 12 | (data (i32.const 0x1000) "\01\02\03\04") 13 | ) 14 | -------------------------------------------------------------------------------- /examples/wasmtime/multi.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Wasm::Wasmtime; 4 | use Carp::Assert; 5 | use Path::Tiny qw( path ); 6 | 7 | # This is an example of working with mulit-value modules and dealing with 8 | # multi-value functions. 9 | 10 | # Configure our `Store`, but be sure to use a `Config` that enables the 11 | # wasm multi-value feature since it's not stable yet. 12 | print "Initializing...\n"; 13 | my $store = Wasm::Wasmtime::Store->new( 14 | Wasm::Wasmtime::Engine->new( 15 | Wasm::Wasmtime::Config->new 16 | ->wasm_multi_value(1), 17 | ) 18 | ); 19 | 20 | print "Compiling module...\n"; 21 | my $module = Wasm::Wasmtime::Module->new( $store->engine, file => path(__FILE__)->parent->child('multi.wat') ); 22 | 23 | print "Creating callback...\n"; 24 | sub callback_func 25 | { 26 | my($x,$y) = @_; 27 | return ($y+1, $x+1); 28 | } 29 | 30 | print "Instantiating module...\n"; 31 | my $instance = Wasm::Wasmtime::Instance->new( 32 | $module, $store, [\&callback_func], 33 | ); 34 | 35 | print "Extracting export...\n"; 36 | my $g = $instance->exports->g; 37 | 38 | print "Calling export \"g\"...\n"; 39 | my @results = $g->(1,3); 40 | printf "> %d %d\n", @results; 41 | 42 | assert($results[0] == 4); 43 | assert($results[1] == 2); 44 | 45 | print "Calling export \"round_trip_many\"...\n"; 46 | my $round_trip_many = $instance->exports->round_trip_many; 47 | @results = $round_trip_many->(0,1,2,3,4,5,6,7,8,9); 48 | 49 | print "Printing results...\n"; 50 | print "> @results\n"; 51 | assert(scalar @results == 10); 52 | for my $i (0..9) 53 | { 54 | assert($results[$i] == $i); 55 | } 56 | -------------------------------------------------------------------------------- /examples/wasmtime/multi.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $f (import "" "f") (param i32 i64) (result i64 i32)) 3 | 4 | (func $g (export "g") (param i32 i64) (result i64 i32) 5 | (call $f (local.get 0) (local.get 1)) 6 | ) 7 | 8 | (func $round_trip_many 9 | (export "round_trip_many") 10 | (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) 11 | (result i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) 12 | 13 | local.get 0 14 | local.get 1 15 | local.get 2 16 | local.get 3 17 | local.get 4 18 | local.get 5 19 | local.get 6 20 | local.get 7 21 | local.get 8 22 | local.get 9) 23 | ) 24 | 25 | -------------------------------------------------------------------------------- /lib/Test2/Plugin/Wasm.pm: -------------------------------------------------------------------------------- 1 | package Test2::Plugin::Wasm; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Test2::API qw( context ); 7 | use Test2::Mock; 8 | use Data::Dumper (); 9 | use FFI::C::Util qw( c_to_perl ); 10 | 11 | # ABSTRACT: Test2 plugin for WebAssembly extensions 12 | # VERSION 13 | 14 | =head1 SYNOPSIS 15 | 16 | use Test2::Plugin::Wasm; 17 | 18 | =head1 DESCRIPTION 19 | 20 | This L plugin is for testing L extensions. Right now, it sets C resource 21 | limits to work on systems with a low virtual memory address limit. It may do other thing that 22 | make sense for all L extensions running in a testing context in the future. 23 | 24 | =head1 SEE ALSO 25 | 26 | =over 4 27 | 28 | =item L 29 | 30 | Write Perl extensions using WebAssembly. 31 | 32 | =back 33 | 34 | =cut 35 | 36 | sub get_virtual_memory_limit 37 | { 38 | my $ctx = context(); 39 | if($^O eq 'linux') 40 | { 41 | require FFI::Platypus; 42 | require FFI::C::StructDef; 43 | my $ffi = FFI::Platypus->new( api => 1, lib => [undef] ); 44 | my $rlimit; 45 | if($ffi->find_symbol('getrlimit')) 46 | { 47 | $ctx->note("linux : found getrlimit") if $ENV{TEST2_PLUGIN_WASM_DEBUG}; 48 | $rlimit = FFI::C::StructDef->new( 49 | $ffi, 50 | name => 'rlimit', 51 | members => [ 52 | rlim_cur => 'uint32', 53 | rlim_max => 'uint32', 54 | ], 55 | )->create; 56 | my $ret = $ffi->function( getrlimit => [ 'int', 'rlimit' ] => 'int' )->call(9,$rlimit); 57 | if($ret == -1) 58 | { 59 | $ctx->note("getrlimit failed: $!") if $ENV{TEST2_PLUGIN_WASM_DEBUG}; 60 | undef $rlimit; 61 | } 62 | } 63 | if(defined $rlimit) 64 | { 65 | my $cur = $rlimit->rlim_cur; 66 | $ctx->note("rlimit = " . Data::Dumper->new( 67 | [c_to_perl($rlimit)])->Terse(1)->Indent(0)->Dump 68 | ) if $ENV{TEST2_PLUGIN_WASM_DEBUG}; 69 | $ctx->release; 70 | $cur = 0 if $cur == 0xffffffff; 71 | return $cur; 72 | } 73 | } 74 | $ctx->release; 75 | return undef; 76 | } 77 | 78 | our $config_mock; 79 | 80 | sub import 81 | { 82 | my $ctx = context(); 83 | my $vm_limit = get_virtual_memory_limit(); 84 | if($vm_limit) 85 | { 86 | require Wasm::Wasmtime::Config; 87 | $config_mock = Test2::Mock->new( 88 | class => 'Wasm::Wasmtime::Config', 89 | around => [ 90 | new => sub { 91 | my $orig = shift; 92 | my $self = shift->$orig(@_); 93 | my $ctx = context(); 94 | $ctx->note("virtual memory address limit detected, try to set limits to zero"); 95 | $self->static_memory_maximum_size(0); 96 | $self->static_memory_guard_size(0); 97 | $self->dynamic_memory_guard_size(0); 98 | $ctx->release; 99 | $self; 100 | }, 101 | ], 102 | ); 103 | } 104 | $ctx->release; 105 | } 106 | 107 | 1; 108 | -------------------------------------------------------------------------------- /lib/Wasm/Func.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Func; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | 7 | # ABSTRACT: Interface to WebAssembly functions 8 | # VERSION 9 | 10 | =head1 SYNOPSIS 11 | 12 | Call Wasm from Perl: 13 | 14 | # EXAMPLE: examples/synopsis/func3.pl 15 | 16 | Call Perl from Wasm: 17 | 18 | # EXAMPLE: examples/synopsis/func4.pl 19 | 20 | =head1 DESCRIPTION 21 | 22 | B: WebAssembly and Wasmtime are a moving target and the 23 | interface for these modules is under active development. Use with 24 | caution. 25 | 26 | This documents the interface to functions for L. 27 | Each function exported from WebAssembly is automatically 28 | imported into Perl space as a Perl subroutine. Wasm modules 29 | can import Perl subroutines using their standard import process. 30 | 31 | =head1 SEE ALSO 32 | 33 | =over 4 34 | 35 | =item L 36 | 37 | =back 38 | 39 | =cut 40 | 41 | 1; 42 | -------------------------------------------------------------------------------- /lib/Wasm/Global.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Global; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | 7 | # ABSTRACT: Interface to WebAssembly global variables 8 | # VERSION 9 | 10 | =head1 SYNOPSIS 11 | 12 | Import globals into Perl from WebAssembly 13 | 14 | # EXAMPLE: examples/synopsis/global2.pl 15 | 16 | Import globals from Perl into WebAssembly 17 | 18 | # EXAMPLE: examples/synopsis/global3.pl 19 | 20 | =head1 DESCRIPTION 21 | 22 | B: WebAssembly and Wasmtime are a moving target and the 23 | interface for these modules is under active development. Use with 24 | caution. 25 | 26 | This documents the interface to global variables for L. 27 | Each global variable exported from WebAssembly is automatically 28 | imported into Perl space as a tied scalar, which allows you to get 29 | and set the variable easily from Perl. Going the other way 30 | requires a bit more boilerplate, but is almost as easy. Using 31 | the C<-global> option on the L module, you can define global 32 | variables in Pure Perl modules that can be imported into WebAssembly 33 | from Perl. 34 | 35 | =head1 CAVEATS 36 | 37 | Note that depending on the 38 | storage of the global variable setting might be lossy and round-trip 39 | isn't guaranteed. For example for integer types, if you set a string 40 | value it will be converted to an integer using the normal Perl string 41 | to integer conversion, and when it comes back you will just have 42 | the integer value. 43 | 44 | =head1 SEE ALSO 45 | 46 | =over 4 47 | 48 | =item L 49 | 50 | =back 51 | 52 | =cut 53 | 54 | 1; 55 | -------------------------------------------------------------------------------- /lib/Wasm/Hook.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Hook; 2 | 3 | use strict; 4 | use warnings; 5 | use autodie; 6 | use 5.008004; 7 | use Wasm; 8 | use Ref::Util qw( is_ref ); 9 | use Path::Tiny qw( path ); 10 | use Scalar::Util qw( refaddr ); 11 | 12 | # ABSTRACT: Automatically load WebAssembly modules without a Perl wrapper 13 | # VERSION 14 | 15 | =head1 SYNOPSIS 16 | 17 | use Wasm::Hook; 18 | use Foo::Bar; # will load Foo/Bar.wasm or Foo/Bar.wat if no Foo/Bar.pm is found 19 | no Wasm::Hook; # turns off automatic wasm / wat loading 20 | 21 | =head1 DESCRIPTION 22 | 23 | B: WebAssembly and Wasmtime are a moving target and the 24 | interface for these modules is under active development. Use with 25 | caution. 26 | 27 | This module installs an C<@INC> hook that automatically loads WebAssembly (Wasm) 28 | files so that they can be used like a Perl module, without: 29 | 30 | =over 4 31 | 32 | =item 33 | 34 | Having to write a boilerplate C<.pm> file that loads the WebAssembly 35 | 36 | =item 37 | 38 | The caller needing to even know or care that the module is implemented in something other than Perl. 39 | 40 | =back 41 | 42 | This module will only load a WebAssembly module if there is no Perl Module (C<.pm> file) with the appropriate name. 43 | 44 | =head1 SEE ALSO 45 | 46 | =over 4 47 | 48 | =item L 49 | 50 | =item L 51 | 52 | =back 53 | 54 | The functions inside the WebAssembly module are exportable via the L 55 | module. C<@EXPORT_OK> is used, so you will need to explicitly export functions. 56 | 57 | =cut 58 | 59 | sub _hook 60 | { 61 | my(undef, $file) = @_; 62 | foreach my $inc (@INC) 63 | { 64 | next if ref $inc; 65 | my $pm = path($inc)->child($file); 66 | return () if -f $pm; 67 | my $basename = $pm->basename; 68 | $basename =~ s/\.pm$//; 69 | my($wa) = sort { $b->stat->mtime <=> $a->stat->mtime } 70 | grep { -f $_ } 71 | map { $pm->parent->child($basename . $_) } 72 | qw( .wasm .wat ); 73 | next unless defined $wa; 74 | if(-f $wa) 75 | { 76 | my $package = $file; 77 | $package =~ s/\.pm$//; 78 | $package =~ s/\//::/g; 79 | my $perl = qq{package $package; use Wasm -api => 0, -exporter => 'ok', -file => "$wa"; 1;\n}; 80 | my $fh; 81 | open $fh, '<', \$perl; 82 | return ($fh); 83 | } 84 | } 85 | return (); 86 | } 87 | 88 | sub import 89 | { 90 | __PACKAGE__->unimport; 91 | push @INC, \&_hook; 92 | } 93 | 94 | sub unimport 95 | { 96 | @INC = grep { !is_ref($_) || refaddr($_) != refaddr(\&_hook) } @INC; 97 | } 98 | 99 | 1; 100 | -------------------------------------------------------------------------------- /lib/Wasm/Memory.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Memory; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::Caller (); 7 | use base qw( Exporter ); 8 | 9 | our @EXPORT_OK = qw( wasm_caller_memory ); 10 | 11 | # ABSTRACT: Interface to WebAssembly Memory 12 | # VERSION 13 | 14 | =head1 SYNOPSIS 15 | 16 | Use WebAssembly memory from plain Perl: 17 | 18 | # EXAMPLE: examples/synopsis/memory2.pl 19 | 20 | Use WebAssembly memory from Perl in callback from WebAssembly: 21 | 22 | # EXAMPLE: examples/synopsis/memory3.pl 23 | 24 | =head1 DESCRIPTION 25 | 26 | B: WebAssembly and Wasmtime are a moving target and the 27 | interface for these modules is under active development. Use with 28 | caution. 29 | 30 | This class represents a region of memory exported from a WebAssembly 31 | module. A L instance is automatically imported into 32 | Perl space for each WebAssembly memory region with the same name. 33 | 34 | =head1 FUNCTIONS 35 | 36 | =head2 wasm_caller_memory 37 | 38 | my $memory = wasm_caller_memory; 39 | 40 | Returns the memory region of the WebAssembly caller, if Perl has been 41 | called by Wasm, otherwise it returns C. 42 | 43 | This function can be exported by request via L. 44 | 45 | =cut 46 | 47 | sub wasm_caller_memory 48 | { 49 | my $caller = Wasm::Wasmtime::Caller::wasmtime_caller(); 50 | defined $caller 51 | ? do { 52 | my $wm = $caller->export_get('memory'); 53 | defined $wm && $wm->is_memory 54 | ? __PACKAGE__->new($wm) 55 | : undef; 56 | } : undef; 57 | } 58 | 59 | sub new 60 | { 61 | my($class, $memory) = @_; 62 | bless \$memory, $class; 63 | } 64 | 65 | =head1 METHODS 66 | 67 | =head2 address 68 | 69 | my $pointer = $memory->address; 70 | 71 | Returns an opaque pointer to the start of memory. 72 | 73 | =head2 size 74 | 75 | my $size = $memory->size; 76 | 77 | Returns the size of the memory in bytes. 78 | 79 | =cut 80 | 81 | sub address { ${shift()}->data } 82 | sub size { ${shift()}->data_size } 83 | 84 | =head2 limits 85 | 86 | my($current, $min, $max) = $memory->limits; 87 | 88 | Returns the current memory limit, the minimum and maximum. All sizes 89 | are in pages. 90 | 91 | =cut 92 | 93 | sub limits 94 | { 95 | my $self = shift; 96 | my $memory = $$self; 97 | my $type = $memory->type; 98 | ($memory->size, @{ $type->limits }); 99 | } 100 | 101 | =head2 grow 102 | 103 | $memory->grow($count); 104 | 105 | Grown the memory region by C<$count> pages. 106 | 107 | =cut 108 | 109 | sub grow 110 | { 111 | my($self, $count) = @_; 112 | ${$self}->grow($count); 113 | } 114 | 115 | 1; 116 | 117 | =head1 SEE ALSO 118 | 119 | =over 4 120 | 121 | =item L 122 | 123 | =back 124 | 125 | =cut 126 | -------------------------------------------------------------------------------- /lib/Wasm/Table.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Table; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | 7 | # ABSTRACT: Interface to WebAssembly Tables 8 | # VERSION 9 | 10 | =head1 DESCRIPTION 11 | 12 | B: WebAssembly and Wasmtime are a moving target and the 13 | interface for these modules is under active development. Use with 14 | caution. 15 | 16 | L doesn't yet provide an interface to WebAssembly Tables, 17 | but one day this is where its interface will be documented. 18 | 19 | =head1 SEE ALSO 20 | 21 | =over 4 22 | 23 | =item L 24 | 25 | =back 26 | 27 | =cut 28 | 29 | 1; 30 | -------------------------------------------------------------------------------- /lib/Wasm/Trap.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Trap; 2 | 3 | use strict; 4 | use warnings; 5 | use Wasm::Wasmtime::Trap; 6 | use 5.008004; 7 | 8 | # ABSTRACT: Wasm trap class 9 | # VERSION 10 | 11 | =head1 SYNOPSIS 12 | 13 | # EXAMPLE: examples/synopsis/trap2.pl 14 | 15 | =head1 DESCRIPTION 16 | 17 | B: WebAssembly and Wasmtime are a moving target and the 18 | interface for these modules is under active development. Use with 19 | caution. 20 | 21 | This class represents a trap thrown into or out of WebAssembly. It 22 | can be thrown back to WebAssembly via die in a Perl function called 23 | from WebAssembly. It can be caught in Perl via eval around WebAssembly. 24 | 25 | The actual implementation may be a super or subclass. As of this 26 | writing it is a simple wrapper around L, but 27 | relying on that is undefined behavior. In order to catch a trap from 28 | WebAssembly, use this class name like so: 29 | 30 | use Ref::Util qw( is_blessed_ref ); 31 | 32 | local $@ = ''; 33 | eval { 34 | web_assembly_func(); 35 | }; 36 | if(my $error = $@) 37 | { 38 | if(is_blessed_ref $error && $error->isa('Wasm::Trap')) 39 | { 40 | my $message = $error->message; 41 | my $exit_value = $error->exit_value; 42 | print "message = $message\n"; 43 | print "exit_value = $exit_value\n"; 44 | } 45 | } 46 | 47 | To throw from Perl: 48 | 49 | use Wasm::Trap; 50 | 51 | sub perl_from_wasm 52 | { 53 | die Wasm::Trap->new("diagnostic\0"); 54 | } 55 | 56 | =head1 CONSTRUCTORS 57 | 58 | =head2 new 59 | 60 | my $trap = Wasm::Trap->new($message); 61 | 62 | This creates a new trap object. 63 | 64 | =cut 65 | 66 | sub new 67 | { 68 | my(undef, $message) = @_; 69 | require Wasm; 70 | my $linker = Wasm::_linker(); 71 | Wasm::Wasmtime::Trap->new($linker->store, $message); 72 | } 73 | 74 | push @Wasm::Wasmtime::Trap::ISA, __PACKAGE__; 75 | 76 | =head1 METHODS 77 | 78 | =head2 message 79 | 80 | my $message = $trap->message; 81 | 82 | Returns the trap message as a string. 83 | 84 | =head2 exit_status 85 | 86 | my $status = $trap->exit_status; 87 | 88 | If the trap was triggered by an C call, this will return the exist status code. 89 | If it wasn't triggered by an C call it will return C. 90 | 91 | =head1 SEE ALSO 92 | 93 | =over 4 94 | 95 | =item L 96 | 97 | =back 98 | 99 | =cut 100 | 101 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::Caller; 7 | use Wasm::Wasmtime::Config; 8 | use Wasm::Wasmtime::Engine; 9 | use Wasm::Wasmtime::ExportType; 10 | use Wasm::Wasmtime::Extern; 11 | use Wasm::Wasmtime::ExternType; 12 | use Wasm::Wasmtime::Func; 13 | use Wasm::Wasmtime::FuncType; 14 | use Wasm::Wasmtime::Global; 15 | use Wasm::Wasmtime::GlobalType; 16 | use Wasm::Wasmtime::ImportType; 17 | use Wasm::Wasmtime::Instance; 18 | use Wasm::Wasmtime::Linker; 19 | use Wasm::Wasmtime::Module; 20 | use Wasm::Wasmtime::Store; 21 | use Wasm::Wasmtime::TableType; 22 | use Wasm::Wasmtime::Trap; 23 | use Wasm::Wasmtime::ValType; 24 | use Wasm::Wasmtime::WasiConfig; 25 | use Wasm::Wasmtime::WasiInstance; 26 | 27 | # ABSTRACT: Perl interface to Wasmtime 28 | # VERSION 29 | 30 | =head1 SYNOPSIS 31 | 32 | # EXAMPLE: examples/synopsis/wasmtime.pl 33 | 34 | =head1 DESCRIPTION 35 | 36 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 37 | is under active development. Use with caution. 38 | 39 | This module pre-loads all the relevant Wasmtime modules so that you can just start using the 40 | appropriate classes. 41 | 42 | If you are just getting your feet wet with WebAssembly and Perl then you probably want to 43 | take a look at L, which is a simple interface that automatically imports functions 44 | from Wasm space into Perl space. 45 | 46 | =head1 ENVIRONMENT 47 | 48 | =head2 PERL_WASM_WASMTIME_MEMORY 49 | 50 | This environment variable, if set, should be a colon separated list of values for 51 | C, C and C. 52 | See L for more details on these limits. 53 | 54 | =head1 SEE ALSO 55 | 56 | =over 4 57 | 58 | =item L 59 | 60 | Simplified interface to WebAssembly that imports WebAssembly functions into Perl space. 61 | 62 | =item L 63 | 64 | Interface to WebAssembly module. 65 | 66 | =item L 67 | 68 | Interface to a WebAssembly module instance. 69 | 70 | =item L 71 | 72 | Interface to WebAssembly function. 73 | 74 | =item L 75 | 76 | Link together multiple WebAssembly modules into one program. 77 | 78 | =item L 79 | 80 | Tool to convert WebAssembly Text (WAT) to WebAssembly binary (Wasm). 81 | 82 | =item L 83 | 84 | WebAssembly System Interface (WASI). 85 | 86 | =item L 87 | 88 | The rust library used by this module, via its C API, via FFI. 89 | 90 | =item L 91 | 92 | These bindings here heavily influenced by the Python Wasmtime bindings. 93 | 94 | =back 95 | 96 | =cut 97 | 98 | 1; 99 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Caller.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Caller; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | use Wasm::Wasmtime::Extern; 8 | use base qw( Exporter ); 9 | 10 | our @EXPORT = qw( wasmtime_caller ); 11 | 12 | $ffi_prefix = 'wasmtime_caller_'; 13 | $ffi->load_custom_type('::PtrObject' => 'wasmtime_caller_t' => __PACKAGE__); 14 | 15 | # ABSTRACT: Wasmtime caller interface 16 | # VERSION 17 | 18 | =head1 SYNOPSIS 19 | 20 | # EXAMPLE: examples/synopsis/caller.pl 21 | 22 | =head1 DESCRIPTION 23 | 24 | This class represents the caller's context when calling a Perl L from 25 | WebAssembly. The primary purpose of this structure is to provide access to the caller's 26 | exported memory. This allows functions which take pointers as arguments to easily read the 27 | memory the pointers point into. 28 | 29 | This is intended to be a pretty temporary mechanism for accessing the Caller's memory until 30 | interface types has been fully standardized and implemented. 31 | 32 | =head1 FUNCTIONS 33 | 34 | =head2 wasmtime_caller 35 | 36 | my $caller = wasmtime_caller; 37 | my $caller = wasmtime_caller $index; 38 | 39 | This returns the current caller context (an instance of L). If 40 | the current Perl code wasn't called from WebAssembly, then it will return C. If 41 | C<$index> is given, then that indicates how many WebAssembly call frames to go back 42 | before the current one. (This is vaguely similar to how the Perl C function 43 | works). 44 | 45 | This function is exported by default using L. 46 | 47 | =cut 48 | 49 | our @callers; 50 | 51 | sub wasmtime_caller (;$) 52 | { 53 | $callers[$_[0]||0] 54 | } 55 | 56 | =head1 METHODS 57 | 58 | =head2 export_get 59 | 60 | my $extern = $caller->export_get($name); 61 | 62 | Returns the L for the named export C<$name>. As of this writing, 63 | only L types are supported. 64 | 65 | =cut 66 | 67 | sub new 68 | { 69 | my($class, $ptr) = @_; 70 | bless { 71 | ptr => $ptr, 72 | }, $class; 73 | } 74 | 75 | $ffi->attach( export_get => ['wasmtime_caller_t','wasm_byte_vec_t*'] => 'wasm_extern_t' => sub { 76 | my $xsub = shift; 77 | my $self = shift; 78 | return undef unless $self->{ptr}; 79 | my $name = Wasm::Wasmtime::ByteVec->new($_[0]); 80 | $xsub->($self, $name); 81 | }); 82 | 83 | 1; 84 | 85 | =head1 SEE ALSO 86 | 87 | =over 4 88 | 89 | =item L 90 | 91 | =item L 92 | 93 | =back 94 | 95 | =cut 96 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Engine.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Engine; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | use Wasm::Wasmtime::Config; 8 | 9 | # ABSTRACT: Wasmtime engine class 10 | # VERSION 11 | 12 | =head1 SYNOPSIS 13 | 14 | # EXAMPLE: examples/synopsis/engine.pl 15 | 16 | =head1 DESCRIPTION 17 | 18 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 19 | is under active development. Use with caution. 20 | 21 | This class represents the main WebAssembly engine. It can optionally 22 | be configured with a L object. 23 | 24 | =cut 25 | 26 | $ffi_prefix = 'wasm_engine_'; 27 | $ffi->load_custom_type('::PtrObject' => 'wasm_engine_t' => __PACKAGE__ ); 28 | 29 | =head1 CONSTRUCTOR 30 | 31 | =head2 new 32 | 33 | my $engine = Wasm::Wasmtime::Engine->new; 34 | my $engine = Wasm::Wasmtime::Engine->new( 35 | $config, # Wasm::Wasmtime::Config 36 | ); 37 | 38 | Creates a new instance of the engine class. 39 | 40 | =cut 41 | 42 | $ffi->attach( [ 'new_with_config' => 'new' ] => ['wasm_config_t'] => 'wasm_engine_t' => sub { 43 | my($xsub, $class, $config) = @_; 44 | $config ||= Wasm::Wasmtime::Config->new; 45 | if(defined $ENV{PERL_WASM_WASMTIME_MEMORY}) 46 | { 47 | my($static_memory_maximum_size, $static_memory_guard_size, $dynamic_memory_guard_size) = split /:/, $ENV{PERL_WASM_WASMTIME_MEMORY}; 48 | $config->static_memory_maximum_size($static_memory_maximum_size); 49 | $config->static_memory_guard_size($static_memory_guard_size); 50 | $config->dynamic_memory_guard_size($dynamic_memory_guard_size); 51 | } 52 | my $self = $xsub->($config), 53 | delete $config->{ptr}; 54 | $self; 55 | }); 56 | 57 | _generate_destroy(); 58 | 59 | 1; 60 | 61 | =head1 SEE ALSO 62 | 63 | =over 4 64 | 65 | =item L 66 | 67 | =item L 68 | 69 | =back 70 | 71 | =cut 72 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/ExportType.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::ExportType; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Ref::Util qw( is_blessed_ref ); 7 | use Wasm::Wasmtime::FFI; 8 | use Wasm::Wasmtime::ExternType; 9 | 10 | # ABSTRACT: Wasmtime export type class 11 | # VERSION 12 | 13 | =head1 SYNOPSIS 14 | 15 | # EXAMPLE: examples/synopsis/exporttype.pl 16 | 17 | =head1 DESCRIPTION 18 | 19 | This class represents an export from a module. It is essentially a name 20 | and an L. The latter gives you the function 21 | signature and other configuration details for exportable objects. 22 | 23 | =cut 24 | 25 | $ffi_prefix = 'wasm_exporttype_'; 26 | $ffi->load_custom_type('::PtrObject' => 'wasm_exporttype_t' => __PACKAGE__); 27 | 28 | =head1 CONSTRUCTOR 29 | 30 | =head2 new 31 | 32 | my $exporttype = Wasm::Wasmtime::ExportType->new( 33 | $name, # string 34 | $externtype, # Wasm::Wasmtime::ExternType 35 | ); 36 | 37 | Creates a new export type object. 38 | 39 | =cut 40 | 41 | $ffi->attach( new => ['wasm_byte_vec_t*', 'opaque'] => 'wasm_exporttype_t' => sub { 42 | my $xsub = shift; 43 | my $class = shift; 44 | 45 | # not sure this is actually useful... 46 | if(defined $_[1] && is_blessed_ref $_[1] && $_[1]->isa('Wasm::Wasmtime::ExternType')) 47 | { 48 | my $name = Wasm::Wasmtime::ByteVec->new(shift); 49 | my $externtype = shift; 50 | my $self = $xsub->($name, $externtype->{ptr}); 51 | $name->delete; 52 | return $self; 53 | } 54 | else 55 | { 56 | my ($ptr,$owner) = @_; 57 | return bless { 58 | ptr => $ptr, 59 | owner => $owner, 60 | }, $class; 61 | } 62 | }); 63 | 64 | =head1 METHODS 65 | 66 | =head2 name 67 | 68 | my $name = $exporttype->name; 69 | 70 | Returns the name of the export. 71 | 72 | =cut 73 | 74 | $ffi->attach( name => ['wasm_exporttype_t'] => 'wasm_byte_vec_t*' => sub { 75 | my($xsub, $self) = @_; 76 | my $name = $xsub->($self); 77 | $name->get; 78 | }); 79 | 80 | =head2 type 81 | 82 | my $externtype = $exporttype->type; 83 | 84 | Returns the L for the export. 85 | 86 | =cut 87 | 88 | $ffi->attach( type => ['wasm_exporttype_t'] => 'wasm_externtype_t' => sub { 89 | my($xsub, $self) = @_; 90 | my $type = $xsub->($self); 91 | $type->{owner} = $self->{owner} || $self; 92 | $type; 93 | }); 94 | 95 | =head2 to_string 96 | 97 | my $string = $exporttype->to_string; 98 | 99 | Converts the type into a string for diagnostics. 100 | 101 | =cut 102 | 103 | sub to_string 104 | { 105 | my($self) = @_; 106 | my $kind = $self->type->kind; 107 | $kind =~ s/type$//; 108 | # TODO: escape strings ? 109 | sprintf '(%s (export "%s") %s)', $kind, $self->name, $self->type->to_string; 110 | } 111 | 112 | _generate_destroy(); 113 | _generate_vec_class(); 114 | 115 | 1; 116 | 117 | =head1 SEE ALSO 118 | 119 | =over 4 120 | 121 | =item L 122 | 123 | =item L 124 | 125 | =back 126 | 127 | =cut 128 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Extern.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Extern; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | 8 | require Wasm::Wasmtime::Func; 9 | require Wasm::Wasmtime::Global; 10 | require Wasm::Wasmtime::Table; 11 | require Wasm::Wasmtime::Memory; 12 | 13 | # ABSTRACT: Wasmtime extern class 14 | # VERSION 15 | 16 | =head1 SYNOPSIS 17 | 18 | # EXAMPLE: examples/synopsis/extern.pl 19 | 20 | =head1 DESCRIPTION 21 | 22 | This class represents an object exported from or imported into a L. 23 | This class cannot be created independently, but subclasses of this class can be retrieved from 24 | the L object. This is a base class and cannot be instantiated on its own. 25 | 26 | It is a base class. 27 | 28 | =head1 METHODS 29 | 30 | =head2 kind 31 | 32 | my $string = $extern->kind; 33 | 34 | Returns the extern kind as a string. This will be one of: 35 | 36 | =over 4 37 | 38 | =item C L 39 | 40 | =item C L 41 | 42 | =item C L 43 | 44 | =item C L 45 | 46 | =back 47 | 48 | =head2 is_func 49 | 50 | my $bool = $extern->is_func; 51 | 52 | Returns true if it is a function. 53 | 54 | =head2 is_global 55 | 56 | my $bool = $extern->is_global; 57 | 58 | Returns true if it is a global. 59 | 60 | =head2 is_table 61 | 62 | my $bool = $extern->is_table; 63 | 64 | Returns true if it is a table. 65 | 66 | =head2 is_memory 67 | 68 | my $bool = $extern->is_memory; 69 | 70 | Returns true if it is a memory. 71 | 72 | =cut 73 | 74 | $ffi_prefix = 'wasm_extern_'; 75 | 76 | $ffi->attach( [ kind => '_kind' ] => ['opaque'] => 'uint8' ); 77 | 78 | my @cast; 79 | 80 | sub _cast 81 | { 82 | my(undef, $index) = @_; 83 | my $caller = caller; 84 | my($name) = map { lc $_ } $caller =~ /::([a-z]+)$/i; 85 | $cast[$index] = $ffi->function( "wasm_extern_as_$name" => ['opaque'] => "wasm_${name}_t" )->sub_ref; 86 | } 87 | 88 | $ffi->custom_type('wasm_extern_t' => { 89 | native_type => 'opaque', 90 | native_to_perl => sub { 91 | my $extern = shift; 92 | Carp::croak("extern error") unless defined $extern; 93 | my $kind = _kind($extern); 94 | $cast[$kind]->($extern); 95 | }, 96 | }); 97 | 98 | $ffi->attach_cast('new', 'opaque', 'wasm_extern_t', sub { 99 | my($xsub, undef, $ptr, $owner) = @_; 100 | my $self = $xsub->($ptr); 101 | $self->{owner} = $owner; 102 | $self; 103 | }); 104 | 105 | use constant is_func => 0; 106 | use constant is_global => 0; 107 | use constant is_table => 0; 108 | use constant is_memory => 0; 109 | 110 | sub kind { die "internal error" }; 111 | 112 | _generate_vec_class(); 113 | 114 | 1; 115 | 116 | =head1 SEE ALSO 117 | 118 | =over 4 119 | 120 | =item L 121 | 122 | =item L 123 | 124 | =back 125 | 126 | =cut 127 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/ExternType.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::ExternType; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | 8 | require Wasm::Wasmtime::FuncType; 9 | require Wasm::Wasmtime::GlobalType; 10 | require Wasm::Wasmtime::TableType; 11 | require Wasm::Wasmtime::MemoryType; 12 | 13 | # ABSTRACT: Wasmtime extern type class 14 | # VERSION 15 | 16 | =head1 SYNOPSIS 17 | 18 | # EXAMPLE: examples/synopsis/externtype.pl 19 | 20 | =head1 DESCRIPTION 21 | 22 | This class represents an extern type. This class cannot be created independently, but subclasses of this class can be retrieved from the L object. 23 | This is a base class and cannot be instantiated on its own. 24 | 25 | =head1 METHODS 26 | 27 | =head2 kind 28 | 29 | my $string = $externtype->kind; 30 | 31 | Returns the extern type kind as a string. This will be one of: 32 | 33 | =over 4 34 | 35 | =item C L 36 | 37 | =item C L 38 | 39 | =item C L 40 | 41 | =item C L 42 | 43 | =back 44 | 45 | =head2 is_functype 46 | 47 | my $bool = $externtype->is_functype; 48 | 49 | Returns true if it is a function type. 50 | 51 | =head2 is_globaltype 52 | 53 | my $bool = $externtype->is_globaltype; 54 | 55 | Returns true if it is a global type. 56 | 57 | =head2 is_tabletype 58 | 59 | my $bool = $externtype->is_tabletype; 60 | 61 | Returns true if it is a table type. 62 | 63 | =head2 is_memorytype 64 | 65 | my $bool = $externtype->is_memorytype; 66 | 67 | Returns true if it is a memory type. 68 | 69 | =cut 70 | 71 | sub kind { die "internal error" }; 72 | use constant is_functype => 0; 73 | use constant is_globaltype => 0; 74 | use constant is_tabletype => 0; 75 | use constant is_memorytype => 0; 76 | 77 | $ffi_prefix = 'wasm_externtype_'; 78 | 79 | $ffi->attach( [ kind => '_kind' ] => ['opaque'] => 'uint8' ); 80 | 81 | my @cast; 82 | 83 | sub _cast 84 | { 85 | my(undef, $index) = @_; 86 | my $caller = caller; 87 | my($name) = map { lc $_ } $caller =~ /::([a-z]+Type)$/i; 88 | $cast[$index] = $ffi->function( "wasm_externtype_as_$name" => ['opaque'] => "wasm_${name}_t" )->sub_ref; 89 | } 90 | 91 | $ffi->custom_type('wasm_externtype_t' => { 92 | native_type => 'opaque', 93 | native_to_perl => sub { 94 | my $externtype = shift; 95 | Carp::croak("externtype error") unless defined $externtype; 96 | my $kind = _kind($externtype); 97 | $cast[$kind]->($externtype); 98 | }, 99 | }); 100 | 101 | =head2 to_string 102 | 103 | my $string = $externtype->to_string; 104 | 105 | Converts the type into a string for diagnostics. 106 | 107 | =cut 108 | 109 | sub to_string 110 | { 111 | die "internal error"; # pure virtual ish 112 | } 113 | 114 | 1; 115 | 116 | =head1 SEE ALSO 117 | 118 | =over 4 119 | 120 | =item L 121 | 122 | =item L 123 | 124 | =back 125 | 126 | =cut 127 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/FuncType.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::FuncType; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use base qw( Wasm::Wasmtime::ExternType ); 7 | use Ref::Util qw( is_arrayref ); 8 | use Wasm::Wasmtime::FFI; 9 | use Wasm::Wasmtime::ValType; 10 | use constant is_functype => 1; 11 | use constant kind => 'functype'; 12 | 13 | # ABSTRACT: Wasmtime function type class 14 | # VERSION 15 | 16 | =head1 SYNOPSIS 17 | 18 | # EXAMPLE: examples/synopsis/functype.pl 19 | 20 | =head1 DESCRIPTION 21 | 22 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 23 | is under active development. Use with caution. 24 | 25 | The function type class represents a function signature, that is the parameter and return 26 | types that a function will take. 27 | 28 | =cut 29 | 30 | $ffi_prefix = 'wasm_functype_'; 31 | $ffi->load_custom_type('::PtrObject' => 'wasm_functype_t' => __PACKAGE__); 32 | 33 | =head1 CONSTRUCTOR 34 | 35 | =head2 new 36 | 37 | my $functype = Wasm::Wasmtime::FuncType->new(\@params, \@results); 38 | 39 | Creates a new function type instance. C<@params> and C<@results> should be a list of 40 | either L objects, or the string representation of those types 41 | (C, C, etc). 42 | 43 | =cut 44 | 45 | $ffi->attach( new => ['wasm_valtype_vec_t*', 'wasm_valtype_vec_t*'] => 'wasm_functype_t' => sub { 46 | my $xsub = shift; 47 | my $class = shift; 48 | if(is_arrayref $_[0] && is_arrayref $_[1]) 49 | { 50 | # try not to think too much about all of the maps here 51 | my($params, $results) = map { my $rec = Wasm::Wasmtime::ValTypeVec->new; $rec->set($_) } 52 | map { [map { delete $_->{ptr} } map { Wasm::Wasmtime::ValType->new($_) } @$_] } @_; 53 | my $self = $xsub->($params, $results); 54 | return $self; 55 | } 56 | else 57 | { 58 | my($ptr, $owner) = @_; 59 | bless { 60 | ptr => $ptr, 61 | owner => $owner, 62 | }, $class; 63 | } 64 | }); 65 | 66 | =head1 METHODS 67 | 68 | =head2 params 69 | 70 | my @params = $functype->params; 71 | 72 | Returns a list of the parameter types for the function type, as L objects. 73 | 74 | =cut 75 | 76 | $ffi->attach( params => ['wasm_functype_t'] => 'wasm_valtype_vec_t*' => sub { 77 | my($xsub, $self) = @_; 78 | $xsub->($self)->to_list; 79 | }); 80 | 81 | =head2 results 82 | 83 | my @params = $functype->results; 84 | 85 | Returns a list of the result types for the function type, as L objects. 86 | 87 | =cut 88 | 89 | $ffi->attach( results => ['wasm_functype_t'] => 'wasm_valtype_vec_t*' => sub { 90 | my($xsub, $self) = @_; 91 | $xsub->($self)->to_list; 92 | }); 93 | 94 | =head2 to_string 95 | 96 | my $string = $functype->to_string; 97 | 98 | Converts the type into a string for diagnostics. 99 | 100 | =cut 101 | 102 | sub to_string 103 | { 104 | my($self) = @_; 105 | my @params = map { $_->to_string } $self->params; 106 | my @results = map { $_->to_string } $self->results; 107 | 108 | my $string = ''; 109 | $string .= "(param @params)" if @params; 110 | $string .= ' ' if $string && @results; 111 | $string .= "(result @results)" if @results; 112 | $string; 113 | } 114 | 115 | __PACKAGE__->_cast(0); 116 | _generate_destroy(); 117 | 118 | 1; 119 | 120 | =head1 SEE ALSO 121 | 122 | =over 4 123 | 124 | =item L 125 | 126 | =item L 127 | 128 | =back 129 | 130 | =cut 131 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Global.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Global; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use base qw( Wasm::Wasmtime::Extern ); 7 | use Ref::Util qw( is_ref ); 8 | use Wasm::Wasmtime::FFI; 9 | use Wasm::Wasmtime::Store; 10 | use Wasm::Wasmtime::GlobalType; 11 | use constant is_global => 1; 12 | use constant kind => 'global'; 13 | 14 | # ABSTRACT: Wasmtime global class 15 | # VERSION 16 | 17 | =head1 SYNOPSIS 18 | 19 | # EXAMPLE: examples/synopsis/global.pl 20 | 21 | =head1 DESCRIPTION 22 | 23 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 24 | is under active development. Use with caution. 25 | 26 | This class represents a WebAssembly global object. 27 | 28 | =cut 29 | 30 | $ffi_prefix = 'wasm_global_'; 31 | $ffi->load_custom_type('::PtrObject' => 'wasm_global_t' => __PACKAGE__); 32 | 33 | =head1 CONSTRUCTOR 34 | 35 | =head2 new 36 | 37 | my $global = Wasm::Wasmtime::Global->new( 38 | $store, # Wasm::Wasmtime::Store 39 | $globaltype, # Wasm::Wasmtime::GlobalType 40 | ); 41 | 42 | Creates a new global object. 43 | 44 | =cut 45 | 46 | $ffi->attach( new => ['wasm_store_t', 'wasm_globaltype_t', 'wasm_val_t'] => 'wasm_global_t' => sub { 47 | my $xsub = shift; 48 | my $class = shift; 49 | if(is_ref $_[0]) 50 | { 51 | my($store, $globaltype, $value) = @_; 52 | $value = Wasm::Wasmtime::Val->new({ 53 | kind => $globaltype->content->kind_num, 54 | of => { $globaltype->content->kind => $value }, 55 | }); 56 | my $self = $xsub->($store, $globaltype, $value); 57 | $self->{store} = $store; 58 | return $self; 59 | } 60 | else 61 | { 62 | my($ptr, $owner) = @_; 63 | bless { 64 | ptr => $ptr, 65 | owner => $owner, 66 | }, $class; 67 | } 68 | }); 69 | 70 | =head1 METHODS 71 | 72 | =head2 type 73 | 74 | my $globaltype = $global->type; 75 | 76 | Returns the L object for this global object. 77 | 78 | =cut 79 | 80 | $ffi->attach( type => ['wasm_global_t'] => 'wasm_globaltype_t' => sub { 81 | my($xsub, $self) = @_; 82 | my $type = $xsub->($self); 83 | $type->{owner} = $self->{owner} || $self; 84 | $type; 85 | }); 86 | 87 | =head2 get 88 | 89 | my $value = $global->get; 90 | 91 | Gets the global value. 92 | 93 | =cut 94 | 95 | $ffi->attach( get => ['wasm_global_t', 'wasm_val_t'] => sub { 96 | my($xsub, $self) = @_; 97 | my $value = Wasm::Wasmtime::Val->new; 98 | $xsub->($self, $value); 99 | $value->to_perl; 100 | }); 101 | 102 | =head2 set 103 | 104 | my $global->set($value); 105 | 106 | Sets the global to the given value. 107 | 108 | =cut 109 | 110 | $ffi->attach( set => ['wasm_global_t','wasm_val_t'] => sub { 111 | my($xsub, $self, $value) = @_; 112 | $value = Wasm::Wasmtime::Val->new({ 113 | kind => $self->type->content->kind_num, 114 | of => { $self->type->content->kind => $value }, 115 | }); 116 | $xsub->($self, $value); 117 | }); 118 | 119 | =head2 tie 120 | 121 | my $ref = $global->tie; 122 | 123 | Returns a reference to a tied scalar that can be used to get/set the global. 124 | 125 | =cut 126 | 127 | sub tie 128 | { 129 | my $self = shift; 130 | my $ref; 131 | tie $ref, __PACKAGE__, $self; 132 | \$ref; 133 | } 134 | 135 | sub TIESCALAR { $_[1] } 136 | *FETCH = \&get; 137 | *STORE = \&set; 138 | 139 | __PACKAGE__->_cast(1); 140 | _generate_destroy(); 141 | 142 | 1; 143 | 144 | =head1 SEE ALSO 145 | 146 | =over 4 147 | 148 | =item L 149 | 150 | =item L 151 | 152 | =back 153 | 154 | =cut 155 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/GlobalType.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::GlobalType; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use base qw( Wasm::Wasmtime::ExternType ); 7 | use Wasm::Wasmtime::FFI; 8 | use Wasm::Wasmtime::ValType; 9 | use Ref::Util qw( is_ref ); 10 | use Carp (); 11 | use constant is_globaltype => 1; 12 | use constant kind => 'globaltype'; 13 | 14 | # ABSTRACT: Wasmtime global type class 15 | # VERSION 16 | 17 | =head1 SYNOPSIS 18 | 19 | # EXAMPLE: examples/synopsis/globaltype.pl 20 | 21 | =head1 DESCRIPTION 22 | 23 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 24 | is under active development. Use with caution. 25 | 26 | This class represents a module global type. 27 | 28 | =cut 29 | 30 | $ffi_prefix = 'wasm_globaltype_'; 31 | $ffi->load_custom_type('::PtrObject' => 'wasm_globaltype_t' => __PACKAGE__); 32 | 33 | =head1 CONSTRUCTOR 34 | 35 | =head2 new 36 | 37 | my $globaltype = Wasm::Wasmtime::GlobalType->new( 38 | $valtype, # Wasm::Wasmtime::ValType 39 | $mutability, # 'const' or 'var' 40 | ); 41 | 42 | Creates a new global type object. 43 | 44 | As a shortcut, the type names (ie C, etc) maybe used instead of a L 45 | for C<$valtype>. 46 | 47 | C<$mutability> must be one of 48 | 49 | =over 4 50 | 51 | =item C 52 | 53 | =item C 54 | 55 | =back 56 | 57 | =cut 58 | 59 | my %mutability = ( 60 | const => 0, 61 | var => 1, 62 | ); 63 | 64 | $ffi->attach( new => ['wasm_valtype_t','uint8'] => 'wasm_globaltype_t' => sub { 65 | my $xsub = shift; 66 | my $class = shift; 67 | my $ptr; 68 | my $owner; 69 | if(defined $_[0] && !is_ref($_[0]) && $_[0] =~ /^[0-9]+$/) 70 | { 71 | my($ptr, $owner) = @_; 72 | return bless { 73 | ptr => $ptr, 74 | owner => $owner, 75 | }, $class; 76 | } 77 | else 78 | { 79 | my($valtype, $mutability) = @_; 80 | if(ref($valtype) eq 'Wasm::Wasmtime::ValType') 81 | { 82 | $valtype = Wasm::Wasmtime::ValType->new($valtype->kind); 83 | } 84 | else 85 | { 86 | $valtype = Wasm::Wasmtime::ValType->new($valtype); 87 | } 88 | Carp::croak("mutability must be one of 'const' or 'var'") unless defined $mutability{$mutability}; 89 | my $self = $xsub->($valtype, $mutability{$mutability}); 90 | delete $valtype->{ptr}; 91 | return $self; 92 | } 93 | }); 94 | 95 | =head2 content 96 | 97 | my $valtype = $globaltype->content; 98 | 99 | Returns the L for this global type. 100 | 101 | =cut 102 | 103 | $ffi->attach( content => ['wasm_globaltype_t'] => 'wasm_valtype_t' => sub { 104 | my($xsub, $self) = @_; 105 | my $valtype = $xsub->($self); 106 | $valtype->{owner} = $self; 107 | $valtype; 108 | }); 109 | 110 | =head2 mutability 111 | 112 | my $mutable = $globaltype->mutability; 113 | 114 | Returns the mutability for this global type. One of either C or C. 115 | 116 | =cut 117 | 118 | my @mutability = ( 119 | 'const', 120 | 'var', 121 | ); 122 | 123 | $ffi->attach( mutability => ['wasm_globaltype_t'] => 'uint8' => sub { 124 | my($xsub, $self) = @_; 125 | $mutability[$xsub->($self)]; 126 | }); 127 | 128 | =head2 to_string 129 | 130 | my $string = $globaltype->to_string; 131 | 132 | Converts the type into a string for diagnostics. 133 | 134 | =cut 135 | 136 | sub to_string 137 | { 138 | my($self) = @_; 139 | return sprintf("(%s %s)", $self->mutability, $self->content->to_string); 140 | } 141 | 142 | __PACKAGE__->_cast(1); 143 | _generate_destroy(); 144 | 145 | 1; 146 | 147 | =head1 SEE ALSO 148 | 149 | =over 4 150 | 151 | =item L 152 | 153 | =item L 154 | 155 | =back 156 | 157 | =cut 158 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/ImportType.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::ImportType; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Carp (); 7 | use Ref::Util qw( is_blessed_ref ); 8 | use Wasm::Wasmtime::FFI; 9 | use Wasm::Wasmtime::ExternType; 10 | 11 | # ABSTRACT: Wasmtime import type class 12 | # VERSION 13 | 14 | =head1 SYNOPSIS 15 | 16 | # EXAMPLE: examples/synopsis/importtype.pl 17 | 18 | =head1 DESCRIPTION 19 | 20 | This class represents an import from a module. It is essentially a name 21 | and an L. The latter gives you the function 22 | signature and other configuration details for import objects. 23 | 24 | =cut 25 | 26 | $ffi_prefix = 'wasm_importtype_'; 27 | $ffi->load_custom_type('::PtrObject' => 'wasm_importtype_t' => __PACKAGE__); 28 | 29 | =head1 CONSTRUCTOR 30 | 31 | =head2 new 32 | 33 | my $importtype = Wasm::Wasmtime::ImportType->new( 34 | $module, # Wasm::Wasmtime::Module 35 | $name, # string 36 | $externtype, # Wasm::Wasmtime::FuncType, ::MemoryType, ::GlobalType or ::TableType 37 | ); 38 | 39 | Creates a new import type object. 40 | 41 | =cut 42 | 43 | $ffi->attach( new => ['wasm_byte_vec_t*', 'wasm_byte_vec_t*', 'opaque'] => 'wasm_importtype_t' => sub { 44 | my $xsub = shift; 45 | my $class = shift; 46 | if(defined $_[2] && is_blessed_ref $_[2]) 47 | { 48 | my $externtype = $_[2]; 49 | # not sure this is actually useful? 50 | if(is_blessed_ref($externtype) && $externtype->isa('Wasm::Wasmtime::ExternType')) 51 | { 52 | my $module = Wasm::Wasmtime::ByteVec->new(shift); 53 | my $name = Wasm::Wasmtime::ByteVec->new(shift); 54 | my $self = $xsub->($module, $name, $externtype->{ptr}); 55 | $module->delete; 56 | $name->delete; 57 | return $self; 58 | } 59 | else 60 | { 61 | Carp::croak("Not an externtype"); 62 | } 63 | } 64 | else 65 | { 66 | my($ptr,$owner) = @_; 67 | return bless { 68 | ptr => $ptr, 69 | owner => $owner, 70 | }, $class; 71 | } 72 | }); 73 | 74 | =head1 METHODS 75 | 76 | =head2 name 77 | 78 | my $name = $importtype->name; 79 | 80 | Returns the name of the import. 81 | 82 | =cut 83 | 84 | $ffi->attach( name => ['wasm_importtype_t'] => 'wasm_byte_vec_t*' => sub { 85 | my($xsub, $self) = @_; 86 | my $name = $xsub->($self); 87 | $name->get; 88 | }); 89 | 90 | =head2 type 91 | 92 | my $externtype = $importtype->type; 93 | 94 | Returns the L for the import. 95 | 96 | =cut 97 | 98 | $ffi->attach( type => ['wasm_importtype_t'] => 'wasm_externtype_t' => sub { 99 | my($xsub, $self) = @_; 100 | my $type = $xsub->($self); 101 | $type->{owner} = $self->{owner} || $self; 102 | $type; 103 | }); 104 | 105 | =head2 module 106 | 107 | my $name = $importtype->module; 108 | 109 | Returns the name of the module for the import. 110 | 111 | =cut 112 | 113 | $ffi->attach( module => ['wasm_importtype_t'] => 'wasm_byte_vec_t*' => sub { 114 | my($xsub, $self) = @_; 115 | my $name = $xsub->($self); 116 | $name->get; 117 | }); 118 | 119 | =head2 to_string 120 | 121 | my $string = $importtype->to_string; 122 | 123 | Converts the type into a string for diagnostics. 124 | 125 | =cut 126 | 127 | sub to_string 128 | { 129 | my($self) = @_; 130 | my $kind = $self->type->kind; 131 | $kind =~ s/type$//; 132 | # TODO: escape strings ? 133 | sprintf '(%s (import "%s" "%s") %s)', $kind, $self->module, $self->name, $self->type->to_string; 134 | } 135 | 136 | _generate_destroy(); 137 | _generate_vec_class(); 138 | 139 | 1; 140 | 141 | =head1 SEE ALSO 142 | 143 | =over 4 144 | 145 | =item L 146 | 147 | =item L 148 | 149 | =back 150 | 151 | =cut 152 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Instance.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Instance; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | use Wasm::Wasmtime::Module; 8 | use Wasm::Wasmtime::Extern; 9 | use Wasm::Wasmtime::Func; 10 | use Wasm::Wasmtime::Trap; 11 | use Wasm::Wasmtime::Instance::Exports; 12 | use Ref::Util qw( is_ref is_blessed_ref is_plain_coderef is_plain_scalarref ); 13 | use Carp (); 14 | 15 | # ABSTRACT: Wasmtime instance class 16 | # VERSION 17 | 18 | =head1 SYNOPSIS 19 | 20 | # EXAMPLE: examples/synopsis/instance.pl 21 | 22 | =head1 DESCRIPTION 23 | 24 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 25 | is under active development. Use with caution. 26 | 27 | This class represents an instance of a WebAssembly module L. 28 | 29 | =cut 30 | 31 | $ffi_prefix = 'wasm_instance_'; 32 | $ffi->load_custom_type('::PtrObject' => 'wasm_instance_t' => __PACKAGE__); 33 | 34 | =head1 CONSTRUCTOR 35 | 36 | =head2 new 37 | 38 | my $instance = Wasm::Wasmtime::Instance->new( 39 | $module, # Wasm::Wasmtime::Module 40 | $store # Wasm::Wasmtime::Store 41 | ); 42 | my $instance = Wasm::Wasmtime::Instance->new( 43 | $module, # Wasm::Wasmtime::Module 44 | $store, # Wasm::Wasmtime::Store 45 | \@imports, # array reference of Wasm::Wasmtime::Extern 46 | ); 47 | 48 | Create a new instance of the instance class. C<@imports> should match the 49 | imports specified by C<$module>. You can use a few shortcuts when specifying 50 | imports: 51 | 52 | =over 4 53 | 54 | =item code reference 55 | 56 | For a function import, you can provide a plan Perl subroutine, since we can 57 | determine the function signature from the C<$module>. 58 | 59 | =item scalar reference 60 | 61 | For a memory import, a memory object will be created and the referred scalar 62 | will be set to it. 63 | 64 | =item C 65 | 66 | For a memory import, a memory object will be created. You won't be able to 67 | access it from Perl space, but at least it won't die. 68 | 69 | =back 70 | 71 | =cut 72 | 73 | sub _cast_import 74 | { 75 | my($ii, $mi, $store, $keep) = @_; 76 | if(ref($ii) eq 'Wasm::Wasmtime::Extern') 77 | { 78 | return $ii->{ptr}; 79 | } 80 | elsif(is_blessed_ref($ii) && $ii->isa('Wasm::Wasmtime::Extern')) 81 | { 82 | return $ii->{ptr}; 83 | } 84 | elsif(is_plain_coderef($ii)) 85 | { 86 | if($mi->type->kind eq 'functype') 87 | { 88 | my $f = Wasm::Wasmtime::Func->new( 89 | $store, 90 | $mi->type, 91 | $ii, 92 | ); 93 | push @$keep, $f; 94 | return $f->{ptr}; 95 | } 96 | } 97 | elsif(is_plain_scalarref($ii) || !defined $ii) 98 | { 99 | if($mi->type->kind eq 'memorytype') 100 | { 101 | my $m = Wasm::Wasmtime::Memory->new( 102 | $store, 103 | $mi->type, 104 | ); 105 | $$ii = $m if defined $ii; 106 | push @$keep, $m; 107 | return $m->{ptr}; 108 | } 109 | } 110 | Carp::croak("Non-extern object as import"); 111 | } 112 | 113 | require FFI::Platypus::Memory; 114 | 115 | $ffi->attach( [ wasmtime_instance_new => 'new' ] => ['wasm_store_t','wasm_module_t','record(Wasm::Wasmtime::Vec)*','opaque*','opaque*'] => 'wasmtime_error_t' => sub { 116 | my $xsub = shift; 117 | my $class = shift; 118 | my $module = shift; 119 | my $store = is_blessed_ref($_[0]) && $_[0]->isa('Wasm::Wasmtime::Store') 120 | ? shift 121 | : Carp::croak('Creating a Wasm::Wasmtime::Instance instance without a Wasm::Wasmtime::Store object is no longer allowed'); 122 | 123 | my $ptr; 124 | my @keep; 125 | 126 | if(defined $_[0] && !is_ref($_[0])) 127 | { 128 | ($ptr) = @_; 129 | return bless { 130 | ptr => $ptr, 131 | module => $module, 132 | keep => \@keep, 133 | }, $class; 134 | } 135 | else 136 | { 137 | my($imports) = @_; 138 | 139 | $imports ||= []; 140 | Carp::confess("imports is not an array reference") unless ref($imports) eq 'ARRAY'; 141 | my @imports = @$imports; 142 | my $trap; 143 | 144 | { 145 | my @mi = @{ $module->imports }; 146 | if(@mi != @imports) 147 | { 148 | Carp::croak("Got @{[ scalar @imports ]} imports, but expected @{[ scalar @mi ]}"); 149 | } 150 | 151 | @imports = map { _cast_import($_, shift @mi, $store, \@keep) } @imports; 152 | } 153 | 154 | my $imports_vec = Wasm::Wasmtime::Vec->new( 155 | size => scalar @imports, 156 | data => scalar(@imports) > 0 ? do { 157 | my $count = scalar @imports; 158 | my $ptr = FFI::Platypus::Memory::malloc($ffi->sizeof('opaque') * $count); 159 | # void *memcpy(void *dest, const void *src, size_t n) 160 | FFI::Platypus->new( lib => [undef] )->function( 'memcpy' => [ 'opaque', "opaque[$count]", 'size_t' ] => 'opaque' )->call($ptr, \@imports, $ffi->sizeof('opaque') * $count); 161 | } : undef, 162 | ); 163 | 164 | my $ptr; 165 | if(my $error = $xsub->($store, $module, $imports_vec, \$ptr, \$trap)) 166 | { 167 | FFI::Platypus::Memory::free($imports_vec->data) if defined $imports_vec->data; 168 | Carp::croak("error creating module: " . $error->message); 169 | } 170 | else 171 | { 172 | FFI::Platypus::Memory::free($imports_vec->data) if defined $imports_vec->data; 173 | if($trap) 174 | { 175 | $trap = Wasm::Wasmtime::Trap->new($trap); 176 | die $trap; 177 | } 178 | else 179 | { 180 | return bless { 181 | ptr => $ptr, 182 | module => $module, 183 | keep => \@keep, 184 | }, $class; 185 | } 186 | } 187 | } 188 | 189 | }); 190 | 191 | =head1 METHODS 192 | 193 | =head2 module 194 | 195 | my $module = $instance->module; 196 | 197 | Returns the L for this instance. 198 | 199 | =cut 200 | 201 | sub module { shift->{module} } 202 | 203 | =head2 exports 204 | 205 | my $exports = $instance->exports; 206 | 207 | Returns the L object for this instance. 208 | This can be used to query and call exports from the instance. 209 | 210 | =cut 211 | 212 | sub exports 213 | { 214 | Wasm::Wasmtime::Instance::Exports->new(shift); 215 | } 216 | 217 | $ffi->attach( [ exports => '_exports' ] => ['wasm_instance_t','wasm_extern_vec_t*'] => sub { 218 | my($xsub, $self) = @_; 219 | my $externs = Wasm::Wasmtime::ExternVec->new; 220 | $xsub->($self, $externs); 221 | $externs->to_list; 222 | }); 223 | 224 | _generate_destroy(); 225 | 226 | 1; 227 | 228 | =head1 SEE ALSO 229 | 230 | =over 4 231 | 232 | =item L 233 | 234 | =item L 235 | 236 | =back 237 | 238 | =cut 239 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Instance/Exports.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Instance::Exports; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Carp (); 7 | use Hash::Util (); 8 | use overload 9 | '%{}' => sub { 10 | my $self = shift; 11 | my $instance = $$self; 12 | $instance->{exports}; 13 | }, 14 | '@{}' => sub { 15 | my $self = shift; 16 | my $instance = $$self; 17 | my @exports = $instance->_exports; 18 | Internals::SvREADONLY @exports, 1; 19 | Internals::SvREADONLY $exports[$_], 1 for 0..$#exports; 20 | \@exports; 21 | }, 22 | bool => sub { 1 }, 23 | fallback => 1; 24 | 25 | # ABSTRACT: Wasmtime instance exports class 26 | # VERSION 27 | 28 | =head1 SYNOPSIS 29 | 30 | # EXAMPLE: examples/synopsis/instance_exports.pl 31 | 32 | =head1 DESCRIPTION 33 | 34 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 35 | is under active development. Use with caution. 36 | 37 | This class represents the exports from an instance. It can be used in a number of different ways. 38 | 39 | =over 4 40 | 41 | =item autoload methods 42 | 43 | my $foo = $instance->exports->foo; 44 | 45 | Calling the name of an export as a method returns the L for the 46 | export. 47 | 48 | =item As a hash reference 49 | 50 | my $foo = $instance->exports->{foo}; 51 | 52 | Using the Exports class as a hash reference allows you to get exports that might clash with 53 | common Perl methods like C, C, C, etc. The L 54 | will be returned. 55 | 56 | =item An array reference 57 | 58 | my $foo = $instance->exports->[0]; 59 | 60 | This will give you the list of exports in the order that they are defined in your WebAssembly. 61 | The object returned is a L, which is essentially a name and a 62 | L. 63 | 64 | =back 65 | 66 | =cut 67 | 68 | sub new 69 | { 70 | my($class, $instance) = @_; 71 | 72 | $instance->{exports} ||= do { 73 | my @exports = $instance->_exports; 74 | my @module_exports = @{ $instance->module->exports }; 75 | my %exports; 76 | foreach my $i (0..$#exports) 77 | { 78 | $exports{$module_exports[$i]->name} = $exports[$i]; 79 | } 80 | Hash::Util::lock_hash(%exports); 81 | \%exports; 82 | }; 83 | 84 | bless \$instance, $class; 85 | } 86 | 87 | sub can 88 | { 89 | my($self, $name) = @_; 90 | my $instance = $$self; 91 | exists $instance->{exports}->{$name} 92 | ? sub { $self->$name } 93 | : $self->SUPER::can($name); 94 | } 95 | 96 | sub AUTOLOAD 97 | { 98 | our $AUTOLOAD; 99 | my $self = shift; 100 | 101 | my $name = $AUTOLOAD; 102 | $name=~ s/^.*:://; 103 | 104 | my $instance = $$self; 105 | Carp::croak("no export $name") unless exists $instance->{exports}->{$name}; 106 | $instance->{exports}->{$name}; 107 | } 108 | 109 | sub DESTROY 110 | { 111 | # needed because of AUTOLOAD 112 | } 113 | 114 | =head1 SEE ALSO 115 | 116 | =over 4 117 | 118 | =item L 119 | 120 | =item L 121 | 122 | =back 123 | 124 | =cut 125 | 126 | 1; 127 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Memory.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Memory; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use base qw( Wasm::Wasmtime::Extern ); 7 | use Ref::Util qw( is_ref is_plain_arrayref ); 8 | use Wasm::Wasmtime::FFI; 9 | use Wasm::Wasmtime::Store; 10 | use Wasm::Wasmtime::MemoryType; 11 | use constant is_memory => 1; 12 | use constant kind => 'memory'; 13 | 14 | # ABSTRACT: Wasmtime memory class 15 | # VERSION 16 | 17 | =head1 SYNOPSIS 18 | 19 | # EXAMPLE: examples/synopsis/memory.pl 20 | 21 | =head1 DESCRIPTION 22 | 23 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 24 | is under active development. Use with caution. 25 | 26 | This class represents a WebAssembly memory object. 27 | 28 | =cut 29 | 30 | $ffi_prefix = 'wasm_memory_'; 31 | $ffi->load_custom_type('::PtrObject' => 'wasm_memory_t' => __PACKAGE__); 32 | 33 | =head1 CONSTRUCTOR 34 | 35 | =head2 new 36 | 37 | my $memory = Wasm::Wasmtime::Memory->new( 38 | $store, # Wasm::Wasmtime::Store 39 | $memorytype, # Wasm::Wasmtime::MemoryType 40 | ); 41 | 42 | Creates a new memory object. 43 | 44 | =cut 45 | 46 | $ffi->attach( new => ['wasm_store_t', 'wasm_memorytype_t'] => 'wasm_memory_t' => sub { 47 | my $xsub = shift; 48 | my $class = shift; 49 | if(is_ref $_[0]) 50 | { 51 | my($store, $memorytype) = @_; 52 | $memorytype = Wasm::Wasmtime::MemoryType->new($memorytype) 53 | if is_plain_arrayref $memorytype; 54 | return $xsub->($store, $memorytype); 55 | } 56 | else 57 | { 58 | my($ptr, $owner) = @_; 59 | return bless { 60 | ptr => $ptr, 61 | owner => $owner, 62 | }, $class; 63 | } 64 | }); 65 | 66 | =head1 METHODS 67 | 68 | =head2 type 69 | 70 | my $memorytype = $memory->type; 71 | 72 | Returns the L object for this memory object. 73 | 74 | =cut 75 | 76 | $ffi->attach( type => ['wasm_memory_t'] => 'wasm_memorytype_t' => sub { 77 | my($xsub, $self) = @_; 78 | my $type = $xsub->($self); 79 | $type->{owner} = $self->{owner} || $self if $type; 80 | $type; 81 | }); 82 | 83 | =head2 data 84 | 85 | my $pointer = $memory->data; 86 | 87 | Returns a pointer to the start of the memory. 88 | 89 | =cut 90 | 91 | $ffi->attach( data => ['wasm_memory_t'] => 'opaque' => sub { 92 | my($xsub, $self) = @_; 93 | $xsub->($self); 94 | }); 95 | 96 | =head2 data_size 97 | 98 | my $size = $memory->data_size; 99 | 100 | Returns the current size of the memory in bytes. 101 | 102 | =cut 103 | 104 | $ffi->attach( data_size => ['wasm_memory_t'] => 'size_t' => sub { 105 | my($xsub, $self) = @_; 106 | $xsub->($self); 107 | }); 108 | 109 | =head2 size 110 | 111 | my $size = $memory->size; 112 | 113 | Returns the current size of the memory in pages. 114 | 115 | =cut 116 | 117 | $ffi->attach( size => ['wasm_memory_t'] => 'uint32' => sub { 118 | my($xsub, $self) = @_; 119 | $xsub->($self); 120 | }); 121 | 122 | =head2 grow 123 | 124 | my $bool = $memory->grow($delta); 125 | 126 | Tries to increase the page size by the given C<$delta>. Returns true on success, false otherwise. 127 | 128 | =cut 129 | 130 | $ffi->attach( grow => ['wasm_memory_t', 'uint32'] => 'bool' => sub { 131 | my($xsub, $self, $delta) = @_; 132 | $xsub->($self, $delta); 133 | }); 134 | 135 | __PACKAGE__->_cast(3); 136 | _generate_destroy(); 137 | 138 | 1; 139 | 140 | =head1 SEE ALSO 141 | 142 | =over 4 143 | 144 | =item L 145 | 146 | =item L 147 | 148 | =back 149 | 150 | =cut 151 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/MemoryType.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::MemoryType; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use base qw( Wasm::Wasmtime::ExternType ); 7 | use Ref::Util qw( is_ref is_plain_arrayref ); 8 | use Wasm::Wasmtime::FFI; 9 | use constant is_memorytype => 1; 10 | use constant kind => 'memorytype'; 11 | 12 | # ABSTRACT: Wasmtime memory type class 13 | # VERSION 14 | 15 | =head1 SYNOPSIS 16 | 17 | # EXAMPLE: examples/synopsis/memorytype.pl 18 | 19 | =head1 DESCRIPTION 20 | 21 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 22 | is under active development. Use with caution. 23 | 24 | This class represents a module memory type. It models the minimum and 25 | maximum number of pages. 26 | 27 | =cut 28 | 29 | $ffi_prefix = 'wasm_memorytype_'; 30 | $ffi->load_custom_type('::PtrObject' => 'wasm_memorytype_t' => __PACKAGE__); 31 | 32 | =head1 CONSTRUCTOR 33 | 34 | =head2 new 35 | 36 | my $memorytype = Wasm::Wasmtime::MemoryType->new([ 37 | $min, # minumum number of pages 38 | $max # maximum number of pages 39 | ]); 40 | 41 | Creates a new memory type object. 42 | 43 | =cut 44 | 45 | $ffi->attach( new => ['uint32[2]'] => 'wasm_memorytype_t' => sub { 46 | my $xsub = shift; 47 | my $class = shift; 48 | if(is_ref $_[0]) 49 | { 50 | my $limit = shift; 51 | Carp::croak("bad limits") unless is_plain_arrayref($limit); 52 | Carp::croak("no minumum in limit") unless defined $limit->[0]; 53 | $limit->[1] = 0xffffffff unless defined $limit->[1]; 54 | return $xsub->($limit); 55 | } 56 | else 57 | { 58 | my ($ptr, $owner) = @_; 59 | return bless { 60 | ptr => $ptr, 61 | owner => $owner, 62 | }, $class; 63 | } 64 | }); 65 | 66 | =head2 limits 67 | 68 | my $limits = $memorytype->limits; 69 | 70 | Returns the minimum and maximum number of pages as an array reference. 71 | 72 | =cut 73 | 74 | $ffi->attach( limits => ['wasm_memorytype_t'] => 'uint32[2]' => sub { 75 | my($xsub, $self) = @_; 76 | my $limits = $xsub->($self); 77 | $limits; 78 | }); 79 | 80 | =head2 to_string 81 | 82 | my $string = $memorytype->to_string; 83 | 84 | Converts the type into a string for diagnostics. 85 | 86 | =cut 87 | 88 | sub to_string 89 | { 90 | my($self) = @_; 91 | my($min, $max) = @{ $self->limits }; 92 | my $string = "$min"; 93 | $string .= " $max" if $max != 0xffffffff; 94 | return $string; 95 | } 96 | 97 | __PACKAGE__->_cast(3); 98 | _generate_destroy(); 99 | 100 | 1; 101 | 102 | =head1 SEE ALSO 103 | 104 | =over 4 105 | 106 | =item L 107 | 108 | =item L 109 | 110 | =back 111 | 112 | =cut 113 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Module/Exports.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Module::Exports; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Carp (); 7 | use Hash::Util (); 8 | use overload 9 | '%{}' => sub { 10 | my $self = shift; 11 | my $module = $$self; 12 | $module->{exports}; 13 | }, 14 | '@{}' => sub { 15 | my $self = shift; 16 | my $module = $$self; 17 | my @exports = $module->_exports; 18 | Internals::SvREADONLY @exports, 1; 19 | Internals::SvREADONLY $exports[$_], 1 for 0..$#exports; 20 | \@exports; 21 | }, 22 | bool => sub { 1 }, 23 | fallback => 1; 24 | 25 | # ABSTRACT: Wasmtime module exports class 26 | # VERSION 27 | 28 | =head1 SYNOPSIS 29 | 30 | # EXAMPLE: examples/synopsis/module_exports.pl 31 | 32 | =head1 DESCRIPTION 33 | 34 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 35 | is under active development. Use with caution. 36 | 37 | This class represents the exports from a module. It can be used in a number of different ways. 38 | 39 | =over 4 40 | 41 | =item autoload methods 42 | 43 | my $foo = $module->exports->foo; 44 | 45 | Calling the name of an export as a method returns the L for the 46 | export. 47 | 48 | =item As a hash reference 49 | 50 | my $foo = $module->exports->{foo}; 51 | 52 | Using the Exports class as a hash reference allows you to get exports that might clash with 53 | common Perl methods like C, C, C, etc. The L 54 | will be returned. 55 | 56 | =item An array reference 57 | 58 | my $foo = $module->exports->[0]; 59 | 60 | This will give you the list of exports in the order that they are defined in your WebAssembly. 61 | The object returned is a L, which is essentially a name and a 62 | L. 63 | 64 | =back 65 | 66 | =cut 67 | 68 | sub new 69 | { 70 | my($class, $module) = @_; 71 | 72 | $module->{exports} ||= do { 73 | my @exports = $module->_exports; 74 | my %exports; 75 | foreach my $export (@exports) 76 | { 77 | $exports{$export->name} = $export->type; 78 | } 79 | Hash::Util::lock_hash(%exports); 80 | \%exports; 81 | }; 82 | 83 | bless \$module, $class; 84 | } 85 | 86 | sub can 87 | { 88 | my($self, $name) = @_; 89 | my $module = $$self; 90 | exists $module->{exports}->{$name} 91 | ? sub { $self->$name } 92 | : $self->SUPER::can($name); 93 | } 94 | 95 | sub AUTOLOAD 96 | { 97 | our $AUTOLOAD; 98 | my $self = shift; 99 | 100 | my $name = $AUTOLOAD; 101 | $name=~ s/^.*:://; 102 | 103 | my $module = $$self; 104 | Carp::croak("no export $name") unless exists $module->{exports}->{$name}; 105 | $module->{exports}->{$name}; 106 | } 107 | 108 | sub DESTROY 109 | { 110 | # needed because of AUTOLOAD 111 | } 112 | 113 | =head1 SEE ALSO 114 | 115 | =over 4 116 | 117 | =item L 118 | 119 | =item L 120 | 121 | =back 122 | 123 | =cut 124 | 125 | 1; 126 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Module/Imports.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Module::Imports; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Carp (); 7 | use Hash::Util (); 8 | use overload 9 | '%{}' => sub { 10 | my $self = shift; 11 | my $module = $$self; 12 | $module->{imports}; 13 | }, 14 | '@{}' => sub { 15 | my $self = shift; 16 | my $module = $$self; 17 | my @imports = $module->_imports; 18 | Internals::SvREADONLY @imports, 1; 19 | Internals::SvREADONLY $imports[$_], 1 for 0..$#imports; 20 | \@imports; 21 | }, 22 | bool => sub { 1 }, 23 | fallback => 1; 24 | 25 | # ABSTRACT: Wasmtime module imports class 26 | # VERSION 27 | 28 | =head1 SYNOPSIS 29 | 30 | # EXAMPLE: examples/synopsis/module_imports.pl 31 | 32 | =head1 DESCRIPTION 33 | 34 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 35 | is under active development. Use with caution. 36 | 37 | This class represents the imports from a module. It can be used in a number of different ways. 38 | 39 | =over 4 40 | 41 | =item autoload methods 42 | 43 | my $foo = $module->imports->foo; 44 | 45 | Calling the name of an export as a method returns the L for the 46 | export. 47 | 48 | =item As a hash reference 49 | 50 | my $foo = $module->imports->{foo}; 51 | 52 | Using the Imports class as a hash reference allows you to get imports that might clash with 53 | common Perl methods like C, C, C, etc. The L 54 | will be returned. 55 | 56 | =item An array reference 57 | 58 | my $foo = $module->imports->[0]; 59 | 60 | This will give you the list of imports in the order that they are defined in your WebAssembly. 61 | The object returned is a L, which is essentially a name and a 62 | L. 63 | 64 | =back 65 | 66 | =cut 67 | 68 | sub new 69 | { 70 | my($class, $module) = @_; 71 | 72 | $module->{imports} ||= do { 73 | my @imports = $module->_imports; 74 | my %imports; 75 | foreach my $export (@imports) 76 | { 77 | $imports{$export->name} = $export->type; 78 | } 79 | Hash::Util::lock_hash(%imports); 80 | \%imports; 81 | }; 82 | 83 | bless \$module, $class; 84 | } 85 | 86 | sub can 87 | { 88 | my($self, $name) = @_; 89 | my $module = $$self; 90 | exists $module->{imports}->{$name} 91 | ? sub { $self->$name } 92 | : $self->SUPER::can($name); 93 | } 94 | 95 | sub AUTOLOAD 96 | { 97 | our $AUTOLOAD; 98 | my $self = shift; 99 | 100 | my $name = $AUTOLOAD; 101 | $name=~ s/^.*:://; 102 | 103 | my $module = $$self; 104 | Carp::croak("no export $name") unless exists $module->{imports}->{$name}; 105 | $module->{imports}->{$name}; 106 | } 107 | 108 | sub DESTROY 109 | { 110 | # needed because of AUTOLOAD 111 | } 112 | 113 | =head1 SEE ALSO 114 | 115 | =over 4 116 | 117 | =item L 118 | 119 | =item L 120 | 121 | =back 122 | 123 | =cut 124 | 125 | 1; 126 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Store.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Store; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | use Wasm::Wasmtime::Engine; 8 | 9 | # TODO: wasmtime_store_add_fuel 10 | # TODO: wasmtime_store_fuel_consumed 11 | 12 | # ABSTRACT: Wasmtime store class 13 | # VERSION 14 | 15 | =head1 SYNOPSIS 16 | 17 | # EXAMPLE: examples/synopsis/store.pl 18 | 19 | =head1 DESCRIPTION 20 | 21 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 22 | is under active development. Use with caution. 23 | 24 | This class represents storage used by the WebAssembly engine. 25 | 26 | =cut 27 | 28 | $ffi_prefix = 'wasm_store_'; 29 | $ffi->load_custom_type('::PtrObject' => 'wasm_store_t' => __PACKAGE__); 30 | 31 | =head1 CONSTRUCTOR 32 | 33 | =head2 new 34 | 35 | my $store = Wasm::Wasmtime::Store->new; 36 | my $store = Wasm::Wasmtime::Store->new( 37 | $engine, # Wasm::Wasmtime::Engine 38 | ); 39 | 40 | Creates a new storage instance. If the optional L object 41 | isn't provided, then a new one will be created. 42 | 43 | =cut 44 | 45 | $ffi->attach( new => ['wasm_engine_t'] => 'wasm_store_t' => sub { 46 | my($xsub, $class, $engine) = @_; 47 | $engine ||= Wasm::Wasmtime::Engine->new; 48 | my $self = $xsub->($engine); 49 | $self->{engine} = $engine; 50 | $self; 51 | }); 52 | 53 | =head2 gc 54 | 55 | $store->gc; 56 | 57 | Garbage collects Cs that are used within this store. Any 58 | Cs that are discovered to be unreachable by other code or objects 59 | will have their finalizers run. 60 | 61 | =cut 62 | 63 | $ffi->attach( [ wasmtime_store_gc => 'gc' ] => ['wasm_store_t'] => 'void' ); 64 | 65 | =head2 engine 66 | 67 | my $engine = $store->engine; 68 | 69 | Returns the L object for this storage object. 70 | 71 | =cut 72 | 73 | sub engine { shift->{engine} } 74 | 75 | _generate_destroy(); 76 | 77 | 1; 78 | 79 | =head1 SEE ALSO 80 | 81 | =over 4 82 | 83 | =item L 84 | 85 | =item L 86 | 87 | =back 88 | 89 | =cut 90 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Table.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Table; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use base qw( Wasm::Wasmtime::Extern ); 7 | use Ref::Util qw( is_ref ); 8 | use Wasm::Wasmtime::FFI; 9 | use Wasm::Wasmtime::Store; 10 | use Wasm::Wasmtime::TableType; 11 | use constant is_table => 1; 12 | use constant kind => 'table'; 13 | 14 | # ABSTRACT: Wasmtime table class 15 | # VERSION 16 | 17 | =head1 SYNOPSIS 18 | 19 | # EXAMPLE: examples/synopsis/table.pl 20 | 21 | =head1 DESCRIPTION 22 | 23 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 24 | is under active development. Use with caution. 25 | 26 | This class represents a WebAssembly table object. 27 | 28 | =cut 29 | 30 | $ffi_prefix = 'wasm_table_'; 31 | $ffi->load_custom_type('::PtrObject' => 'wasm_table_t' => __PACKAGE__); 32 | 33 | sub new 34 | { 35 | # TODO: add wasm_table_new for standalone support 36 | # TODO: add wasm_table_set 37 | # TODO: add wasm_table_get 38 | # TODO: add wasm_table_grow 39 | my($class, $ptr, $owner) = @_; 40 | bless { 41 | ptr => $ptr, 42 | owner => $owner, 43 | }, $class; 44 | } 45 | 46 | =head1 METHODS 47 | 48 | =head2 type 49 | 50 | my $tabletype = $table->type; 51 | 52 | Returns the L object for this table object. 53 | 54 | =cut 55 | 56 | $ffi->attach( type => ['wasm_table_t'] => 'wasm_tabletype_t' => sub { 57 | my($xsub, $self) = @_; 58 | my $type = $xsub->($self); 59 | $type->{owner} = $self->{owner} || $self; 60 | $type; 61 | }); 62 | 63 | =head2 size 64 | 65 | my $size = $table->size; 66 | 67 | Returns the size of the table. 68 | 69 | =cut 70 | 71 | $ffi->attach( size => ['wasm_table_t'] => 'uint32' ); 72 | 73 | __PACKAGE__->_cast(2); 74 | _generate_destroy(); 75 | 76 | 1; 77 | 78 | =head1 SEE ALSO 79 | 80 | =over 4 81 | 82 | =item L 83 | 84 | =item L 85 | 86 | =back 87 | 88 | =cut 89 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/TableType.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::TableType; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use base qw( Wasm::Wasmtime::ExternType ); 7 | use Wasm::Wasmtime::FFI; 8 | use Wasm::Wasmtime::ValType; 9 | use Ref::Util qw( is_ref is_plain_arrayref ); 10 | use constant is_tabletype => 1; 11 | use constant kind => 'tabletype'; 12 | 13 | # ABSTRACT: Wasmtime table type class 14 | # VERSION 15 | 16 | =head1 SYNOPSIS 17 | 18 | # EXAMPLE: examples/synopsis/tabletype.pl 19 | 20 | =head1 DESCRIPTION 21 | 22 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 23 | is under active development. Use with caution. 24 | 25 | This class represents a module table type. 26 | 27 | =cut 28 | 29 | $ffi_prefix = 'wasm_tabletype_'; 30 | $ffi->load_custom_type('::PtrObject' => 'wasm_tabletype_t' => __PACKAGE__); 31 | 32 | =head1 CONSTRUCTOR 33 | 34 | =head2 new 35 | 36 | my $tabletype = Wasm::Wasmtime::TableType->new( 37 | $valtype, # Wasm::Wasmtime::ValType 38 | [$min,$max], # integer limits 39 | ); 40 | 41 | Creates a new table type object. 42 | 43 | As a shortcut, the type names (ie C, etc) maybe used instead of a L 44 | for C<$valtype>. 45 | 46 | =cut 47 | 48 | $ffi->attach( new => ['wasm_valtype_t','uint32[2]'] => 'wasm_tabletype_t' => sub { 49 | my $xsub = shift; 50 | my $class = shift; 51 | if(defined $_[0] && !is_ref($_[0]) && $_[0] =~ /^[0-9]+$/) 52 | { 53 | my($ptr, $owner) = @_; 54 | return bless { 55 | ptr => $ptr, 56 | owner => $owner, 57 | }, $class; 58 | } 59 | else 60 | { 61 | my($valtype, $limit) = @_; 62 | if(ref($valtype) eq 'Wasm::Wasmtime::ValType') 63 | { 64 | $valtype = Wasm::Wasmtime::ValType->new($valtype->kind); 65 | } 66 | else 67 | { 68 | $valtype = Wasm::Wasmtime::ValType->new($valtype); 69 | } 70 | Carp::croak("bad limits") unless is_plain_arrayref($limit); 71 | Carp::croak("no minumum in limit") unless defined $limit->[0]; 72 | $limit->[1] = 0xffffffff unless defined $limit->[1]; 73 | my $self = $xsub->($valtype, $limit); 74 | delete $valtype->{ptr}; 75 | return $self; 76 | } 77 | }); 78 | 79 | =head2 element 80 | 81 | my $valtype = $tabletype->element; 82 | 83 | Returns the L for this table type. 84 | 85 | =cut 86 | 87 | $ffi->attach( element => ['wasm_tabletype_t'] => 'wasm_valtype_t' => sub { 88 | my($xsub, $self) = @_; 89 | my $valtype = $xsub->($self); 90 | $valtype->{owner} = $self; 91 | $valtype; 92 | }); 93 | 94 | =head2 limits 95 | 96 | my $limits = $tabletype->limits; 97 | 98 | Returns the limits as an array reference. 99 | 100 | =cut 101 | 102 | $ffi->attach( limits => ['wasm_tabletype_t'] => 'uint32[2]' => sub { 103 | my($xsub, $self) = @_; 104 | $xsub->($self); 105 | }); 106 | 107 | =head2 to_string 108 | 109 | my $string = $tabletype->to_string; 110 | 111 | Converts the type into a string for diagnostics. 112 | 113 | =cut 114 | 115 | sub to_string 116 | { 117 | my($self) = @_; 118 | my($min, $max) = @{ $self->limits }; 119 | my $string = "$min "; 120 | $string .= "$max " if $max != 0xffffffff; 121 | $string .= $self->element->to_string; 122 | } 123 | 124 | __PACKAGE__->_cast(2); 125 | _generate_destroy(); 126 | 127 | 1; 128 | 129 | =head1 SEE ALSO 130 | 131 | =over 4 132 | 133 | =item L 134 | 135 | =item L 136 | 137 | =back 138 | 139 | =cut 140 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Trap.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Trap; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | use Wasm::Wasmtime::Store; 8 | use overload 9 | '""' => sub { shift->message . "\n" }, 10 | bool => sub { 1 }, 11 | fallback => 1; 12 | 13 | # ABSTRACT: Wasmtime trap class 14 | # VERSION 15 | 16 | =head1 SYNOPSIS 17 | 18 | # EXAMPLE: examples/synopsis/trap.pl 19 | 20 | =head1 DESCRIPTION 21 | 22 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 23 | is under active development. Use with caution. 24 | 25 | This class represents a trap, usually something unexpected that happened in Wasm land. 26 | This is usually converted into an exception in Perl land, but you can create your 27 | own trap here. 28 | 29 | =cut 30 | 31 | $ffi_prefix = 'wasm_trap_'; 32 | $ffi->load_custom_type('::PtrObject' => 'wasm_trap_t' => __PACKAGE__); 33 | 34 | =head1 CONSTRUCTORS 35 | 36 | =head2 new 37 | 38 | my $trap = Wasm::Wasmtime::Trap->new( 39 | $store, # Wasm::Wasmtime::Store 40 | $message, # Null terminated string 41 | ); 42 | 43 | Create a trap instance. C<$message> MUST be null terminated. 44 | 45 | =cut 46 | 47 | $ffi->attach( new => [ 'wasm_store_t', 'wasm_byte_vec_t*' ] => 'wasm_trap_t' => sub { 48 | my $xsub = shift; 49 | my $class = shift; 50 | if(@_ == 1) 51 | { 52 | my $ptr = shift; 53 | return bless { 54 | ptr => $ptr, 55 | }, $class; 56 | } 57 | else 58 | { 59 | my $store = shift; 60 | my $message = Wasm::Wasmtime::ByteVec->new($_[0]); 61 | return $xsub->($store, $message); 62 | } 63 | }); 64 | 65 | =head1 METHODS 66 | 67 | =head2 message 68 | 69 | my $message = $trap->message; 70 | 71 | Returns the trap message as a string. 72 | 73 | =cut 74 | 75 | $ffi->attach( message => ['wasm_trap_t', 'wasm_byte_vec_t*'] => sub { 76 | my($xsub, $self) = @_; 77 | my $message = Wasm::Wasmtime::ByteVec->new; 78 | $xsub->($self, $message); 79 | my $ret = $message->get; 80 | $ret =~ s/\0$//; 81 | $message->delete; 82 | $ret; 83 | }); 84 | 85 | =head2 exit_status 86 | 87 | my $status = $trap->exit_status; 88 | 89 | If the trap was triggered by an C call, this will return the exist status code. 90 | If it wasn't triggered by an C call it will return C. 91 | 92 | =cut 93 | 94 | $ffi->attach( [ wasmtime_trap_exit_status => 'exit_status' ] => ['wasm_trap_t', 'int*'] => 'bool' => sub { 95 | my($xsub, $self) = @_; 96 | my $status; 97 | $xsub->($self, \$status) 98 | ? $status 99 | : undef; 100 | }); 101 | 102 | _generate_destroy(); 103 | 104 | 1; 105 | 106 | =head1 SEE ALSO 107 | 108 | =over 4 109 | 110 | =item L 111 | 112 | =item L 113 | 114 | =back 115 | 116 | =cut 117 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/ValType.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::ValType; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | 8 | # ABSTRACT: Wasmtime value type class 9 | # VERSION 10 | 11 | =head1 SYNOPSIS 12 | 13 | # EXAMPLE: examples/synopsis/valtype.pl 14 | 15 | =head1 DESCRIPTION 16 | 17 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 18 | is under active development. Use with caution. 19 | 20 | This class represents a Wasm type. 21 | 22 | =cut 23 | 24 | $ffi_prefix = 'wasm_valtype_'; 25 | $ffi->load_custom_type('::PtrObject' => 'wasm_valtype_t' => __PACKAGE__); 26 | 27 | my %kind = ( 28 | 0 => 'i32', 29 | 1 => 'i64', 30 | 2 => 'f32', 31 | 3 => 'f64', 32 | 128 => 'anyref', 33 | 129 => 'funcref', 34 | ); 35 | 36 | my %rkind; 37 | foreach my $key (keys %kind) 38 | { 39 | my $value = $kind{$key}; 40 | $rkind{$value} = $key; 41 | } 42 | 43 | =head1 CONSTRUCTOR 44 | 45 | =head2 new 46 | 47 | my $valtype = Wasm::Wasmtime::ValType->new($type); 48 | 49 | Creates a new value type instance. Acceptable values for C<$type> are: 50 | 51 | =over 4 52 | 53 | =item C 54 | 55 | Signed 32 bit integer. 56 | 57 | =item C 58 | 59 | Signed 64 bit integer. 60 | 61 | =item C 62 | 63 | Floating point. 64 | 65 | =item C 66 | 67 | Double precision floating point. 68 | 69 | =item C 70 | 71 | A pointer. 72 | 73 | =item C 74 | 75 | A function pointer. 76 | 77 | =back 78 | 79 | =cut 80 | 81 | $ffi->attach( new => ['uint8'] => 'wasm_valtype_t' => sub { 82 | my $xsub = shift; 83 | my $class = shift; 84 | if($_[0] =~ /^[0-9]+$/) 85 | { 86 | my($ptr, $owner) = @_; 87 | return bless { 88 | ptr => $ptr, 89 | owner => $owner, 90 | }, $class; 91 | } 92 | else 93 | { 94 | my($kind) = @_; 95 | my $kind_num = $rkind{$kind}; 96 | Carp::croak("no such value type: $kind") unless defined $kind_num; 97 | return $xsub->($kind_num); 98 | } 99 | }); 100 | 101 | =head1 METHODS 102 | 103 | =head2 kind 104 | 105 | my $kind = $valtype->kind; 106 | 107 | Returns the value type as a string (ie C). 108 | 109 | =cut 110 | 111 | sub kind { $kind{shift->kind_num} } 112 | 113 | =head2 kind_num 114 | 115 | my $kind = $valtype->kind_num; 116 | 117 | Returns the number used internally to represent the type. 118 | 119 | =cut 120 | 121 | $ffi->attach( [kind => 'kind_num'] => ['wasm_valtype_t'] => 'uint8' ); 122 | 123 | =head2 to_string 124 | 125 | my $string = $valtype->to_string; 126 | 127 | Converts the type into a string for diagnostics. 128 | For this class, this does the same thing as the kind 129 | method. 130 | 131 | =cut 132 | 133 | *to_string = \&kind; 134 | 135 | _generate_destroy(); 136 | _generate_vec_class( delete => 0 ); 137 | 138 | $ffi->attach( [ wasm_valtype_vec_new => 'Wasm::Wasmtime::ValTypeVec::set' ] => ['wasm_valtype_vec_t*','size_t','opaque[]'] => sub { 139 | my($xsub, $self, $valtypes) = @_; 140 | $xsub->($self, scalar(@$valtypes), $valtypes); 141 | $self; 142 | }); 143 | 144 | 1; 145 | 146 | =head1 SEE ALSO 147 | 148 | =over 4 149 | 150 | =item L 151 | 152 | =item L 153 | 154 | =back 155 | 156 | =cut 157 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/WasiConfig.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::WasiConfig; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | 8 | # ABSTRACT: WASI Configuration 9 | # VERSION 10 | 11 | =head1 SYNOPSIS 12 | 13 | # EXAMPLE: examples/synopsis/wasiconfig.pl 14 | 15 | =head1 DESCRIPTION 16 | 17 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 18 | is under active development. Use with caution. 19 | 20 | This class represents the WebAssembly System Interface (WASI) configuration. For WebAssembly WASI 21 | is the equivalent to the part of libc that interfaces with the system. As such it allows you to 22 | configure if and how the WebAssembly program has access to program arguments, environment, 23 | standard streams and file system directories. 24 | 25 | =cut 26 | 27 | $ffi_prefix = 'wasi_config_'; 28 | $ffi->load_custom_type('::PtrObject' => 'wasi_config_t' => __PACKAGE__); 29 | 30 | =head1 CONSTRUCTOR 31 | 32 | =head2 new 33 | 34 | my $config = Wasm::Wasmtime::WasiConfig->new; 35 | 36 | Creates a new WASI config object. 37 | 38 | =head1 METHODS 39 | 40 | =head2 set_argv 41 | 42 | $config->set_argv(@argv); 43 | 44 | Sets the program arguments. 45 | 46 | =head2 inherit_argv 47 | 48 | $config->inherit_argv; 49 | 50 | Configures WASI to use the host program's arguments. 51 | 52 | =head2 set_env 53 | 54 | $config->set_env(\%env); 55 | 56 | Sets the program environment variables. 57 | 58 | =head2 inherit_env 59 | 60 | $config->inherit_env; 61 | 62 | Configures WASI to use the host program's environment variables. 63 | 64 | =head2 set_stdin_file 65 | 66 | $config->set_stdin_file($path); 67 | 68 | Sets the program standard input to use the given file path. 69 | 70 | =head2 inherit_stdin 71 | 72 | $config->inherit_stdin; 73 | 74 | Configures WASI to use the host program's standard input. 75 | 76 | =head2 set_stdout_file 77 | 78 | $config->set_stdout_file($path); 79 | 80 | Sets the program standard output to use the given file path. 81 | 82 | =head2 inherit_stdout 83 | 84 | $config->inherit_stdout; 85 | 86 | Configures WASI to use the host program's standard output. 87 | 88 | =head2 set_stderr_file 89 | 90 | $config->set_stderr_file($path); 91 | 92 | Sets the program standard error to use the given file path. 93 | 94 | =head2 inherit_stderr 95 | 96 | $config->inherit_stderr; 97 | 98 | Configures WASI to use the host program's standard error. 99 | 100 | =head2 preopen_dir 101 | 102 | $config->preopen_dir($host_path, $guest_path); 103 | 104 | Pre-open the given directory from the host's C<$host_path> to the guest's C<$guest_path>. 105 | 106 | =cut 107 | 108 | sub _wrapper 109 | { 110 | my $xsub = shift; 111 | my $self = shift; 112 | $xsub->($self, @_); 113 | $self; 114 | } 115 | 116 | $ffi->attach( new => [] => 'wasi_config_t' ); 117 | $ffi->attach( set_stdin_file => ['wasi_config_t','string'] => 'void', \&_wrapper ); 118 | $ffi->attach( set_stdout_file => ['wasi_config_t','string'] => 'void', \&_wrapper ); 119 | $ffi->attach( set_stderr_file => ['wasi_config_t','string'] => 'void', \&_wrapper ); 120 | $ffi->attach( preopen_dir => ['wasi_config_t','string','string'] => 'void', \&_wrapper ); 121 | 122 | foreach my $name (qw( argv env stdin stdout stderr )) 123 | { 124 | $ffi->attach( "inherit_$name" => ['wasi_config_t'], \&_wrapper ); 125 | } 126 | 127 | $ffi->attach( set_argv => ['wasi_config_t', 'int', 'string[]'] => sub { 128 | my($xsub, $self, @argv) = @_; 129 | $xsub->($self, scalar(@argv), \@argv); 130 | $self; 131 | }); 132 | 133 | $ffi->attach( set_env => ['wasi_config_t','int','string[]','string[]'] => sub { 134 | my($xsub, $self, %env) = @_; 135 | my @names; 136 | my @values; 137 | foreach my $name (keys %env) 138 | { 139 | push @names, $name; 140 | push @values, $env{$name}; 141 | } 142 | $xsub->($self, scalar(@names), \@names, \@values); 143 | $self; 144 | }); 145 | 146 | _generate_destroy(); 147 | 148 | 1; 149 | 150 | =head1 SEE ALSO 151 | 152 | =over 4 153 | 154 | =item L 155 | 156 | =item L 157 | 158 | =back 159 | 160 | =cut 161 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/WasiInstance.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::WasiInstance; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | use Wasm::Wasmtime::Store; 8 | use Wasm::Wasmtime::Trap; 9 | use Wasm::Wasmtime::WasiConfig; 10 | 11 | # ABSTRACT: WASI instance class 12 | # VERSION 13 | 14 | =head1 SYNOPSIS 15 | 16 | # EXAMPLE: examples/synopsis/wasiinstance.pl 17 | 18 | =head1 DESCRIPTION 19 | 20 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 21 | is under active development. Use with caution. 22 | 23 | This class represents the WebAssembly System Interface (WASI). For WebAssembly WASI is the 24 | equivalent to the part of libc that interfaces with the system. 25 | 26 | To configure if and how the WASI accesses program argument, environment, standard streams 27 | and file system directories, see L. 28 | 29 | For a complete example of using WASI from WebAssembly, see the synopsis for 30 | L. 31 | 32 | =cut 33 | 34 | $ffi_prefix = 'wasi_instance_'; 35 | $ffi->load_custom_type('::PtrObject' => 'wasi_instance_t' => __PACKAGE__); 36 | 37 | =head1 CONSTRUCTOR 38 | 39 | =head2 new 40 | 41 | my $wasi = Wasm::Wasmtime::WasiInstance->new( 42 | $store, # Wasm::Wasmtime::Store, 43 | $name, # string 44 | $config, # Wasm::Wasmtime::WasiConfig, 45 | ); 46 | my $wasi = Wasm::Wasmtime::WasiInstance->new( 47 | $store, # Wasm::Wasmtime::Store, 48 | $name, # string 49 | ); 50 | 51 | Create a new WASI instance. 52 | 53 | =cut 54 | 55 | $ffi->attach( new => ['wasm_store_t', 'string', 'wasi_config_t', 'opaque*'] => 'wasi_instance_t' => sub { 56 | my $xsub = shift; 57 | my $class = shift; 58 | my $store = shift; 59 | my $name = shift; 60 | my $config = defined $_[0] && ref($_[0]) eq 'Wasm::Wasmtime::WasiConfig' ? shift : Wasm::Wasmtime::WasiConfig->new; 61 | my $trap; 62 | my $instance = $xsub->($store, $name, $config, \$trap); 63 | delete $config->{ptr}; 64 | unless($instance) 65 | { 66 | if($trap) 67 | { 68 | die Wasm::Wasmtime::Trap->new($trap); 69 | } 70 | Carp::croak("failed to create wasi instance"); 71 | } 72 | $instance; 73 | }); 74 | 75 | # TODO: bind_import 76 | 77 | _generate_destroy(); 78 | 79 | 1; 80 | 81 | =head1 SEE ALSO 82 | 83 | =over 4 84 | 85 | =item L 86 | 87 | =item L 88 | 89 | =back 90 | 91 | =cut 92 | -------------------------------------------------------------------------------- /lib/Wasm/Wasmtime/Wat2Wasm.pm: -------------------------------------------------------------------------------- 1 | package Wasm::Wasmtime::Wat2Wasm; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Wasm::Wasmtime::FFI; 7 | use base qw( Exporter ); 8 | 9 | # ABSTRACT: Convert WebAssembly Text to Wasm 10 | # VERSION 11 | 12 | =head1 SYNOPSIS 13 | 14 | # EXAMPLE: examples/synopsis/wat2wasm.pl 15 | 16 | =head1 DESCRIPTION 17 | 18 | B: WebAssembly and Wasmtime are a moving target and the interface for these modules 19 | is under active development. Use with caution. 20 | 21 | This module provides C, a function for converting WebAssembly Text to WebAssembly binary format (Wasm). 22 | It is exported by default. 23 | 24 | =cut 25 | 26 | our @EXPORT = qw( wat2wasm ); 27 | 28 | $ffi_prefix = 'wasmtime_'; 29 | 30 | =head1 FUNCTIONS 31 | 32 | =head2 wat2wasm 33 | 34 | my $wasm = wat2wasm($wat); 35 | 36 | Takes WebAssembly Text C<$wat> and converts it into the WebAssembly binary C<$wasm>. 37 | 38 | =cut 39 | 40 | $ffi->attach( wat2wasm => ['wasm_byte_vec_t*','wasm_byte_vec_t*'] => 'wasmtime_error_t' => sub { 41 | my $xsub = shift; 42 | my $wat = Wasm::Wasmtime::ByteVec->new($_[0]); 43 | my $ret = Wasm::Wasmtime::ByteVec->new; 44 | my $error = $xsub->($wat, $ret); 45 | if($error) 46 | { 47 | Carp::croak($error->message . "\nwat2wasm error"); 48 | } 49 | else 50 | { 51 | my $wasm = $ret->get; 52 | $ret->delete; 53 | return $wasm; 54 | } 55 | }); 56 | 57 | 1; 58 | 59 | =head1 SEE ALSO 60 | 61 | =over 4 62 | 63 | =item L 64 | 65 | =item L 66 | 67 | =back 68 | 69 | =cut 70 | -------------------------------------------------------------------------------- /maint/cip-before-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | cip sudo apt-get update 6 | cip sudo apt-get install libffi-dev 7 | cip exec cpanm -n PeekPoke::FFI Carp::Assert 8 | 9 | if [ ! -z "$ALIEN_WASMTIME_VERSION" ]; then 10 | cip exec env ALIEN_WASMTIME_VERSION=$ALIEN_WASMTIME_VERSION cpanm -n --reinstall Alien::wasmtime 11 | else 12 | echo "no ALIEN_WASMTIME_VERSION set" 13 | fi 14 | -------------------------------------------------------------------------------- /perlcriticrc: -------------------------------------------------------------------------------- 1 | severity = 1 2 | only = 1 3 | 4 | [Community::ArrayAssignAref] 5 | [Community::BarewordFilehandles] 6 | [Community::ConditionalDeclarations] 7 | [Community::ConditionalImplicitReturn] 8 | [Community::DeprecatedFeatures] 9 | [Community::DiscouragedModules] 10 | [Community::DollarAB] 11 | [Community::Each] 12 | [Community::EmptyReturn] 13 | [Community::IndirectObjectNotation] 14 | [Community::LexicalForeachIterator] 15 | [Community::LoopOnHash] 16 | [Community::ModPerl] 17 | [Community::OpenArgs] 18 | [Community::OverloadOptions] 19 | [Community::POSIXImports] 20 | [Community::PackageMatchesFilename] 21 | [Community::PreferredAlternatives] 22 | [Community::StrictWarnings] 23 | extra_importers = Test2::V0 24 | [Community::Threads] 25 | [Community::Wantarray] 26 | [Community::WarningsSwitch] 27 | [Community::WhileDiamondDefaultAssignment] 28 | 29 | [BuiltinFunctions::ProhibitBooleanGrep] 30 | [BuiltinFunctions::ProhibitStringyEval] 31 | [BuiltinFunctions::ProhibitStringySplit] 32 | [BuiltinFunctions::ProhibitVoidGrep] 33 | [BuiltinFunctions::ProhibitVoidMap] 34 | [ClassHierarchies::ProhibitExplicitISA] 35 | [ClassHierarchies::ProhibitOneArgBless] 36 | [CodeLayout::ProhibitHardTabs] 37 | allow_leading_tabs = 0 38 | [CodeLayout::ProhibitTrailingWhitespace] 39 | [CodeLayout::RequireConsistentNewlines] 40 | [ControlStructures::ProhibitLabelsWithSpecialBlockNames] 41 | [ControlStructures::ProhibitMutatingListFunctions] 42 | [ControlStructures::ProhibitUnreachableCode] 43 | [InputOutput::ProhibitBarewordFileHandles] 44 | [InputOutput::ProhibitJoinedReadline] 45 | [InputOutput::ProhibitTwoArgOpen] 46 | [Miscellanea::ProhibitFormats] 47 | [Miscellanea::ProhibitUselessNoCritic] 48 | [Modules::ProhibitConditionalUseStatements] 49 | ;[Modules::RequireEndWithOne] 50 | [Modules::RequireNoMatchVarsWithUseEnglish] 51 | [Objects::ProhibitIndirectSyntax] 52 | [RegularExpressions::ProhibitUselessTopic] 53 | [Subroutines::ProhibitNestedSubs] 54 | [ValuesAndExpressions::ProhibitLeadingZeros] 55 | [ValuesAndExpressions::ProhibitMixedBooleanOperators] 56 | [ValuesAndExpressions::ProhibitSpecialLiteralHeredocTerminator] 57 | [ValuesAndExpressions::RequireUpperCaseHeredocTerminator] 58 | [Variables::ProhibitPerl4PackageNames] 59 | [Variables::ProhibitUnusedVariables] 60 | 61 | 62 | -------------------------------------------------------------------------------- /t/00_diag.t: -------------------------------------------------------------------------------- 1 | use Test2::V0 -no_srand => 1; 2 | use Config; 3 | 4 | eval { require 'Test/More.pm' }; 5 | 6 | # This .t file is generated. 7 | # make changes instead to dist.ini 8 | 9 | my %modules; 10 | my $post_diag; 11 | 12 | $modules{$_} = $_ for qw( 13 | Alien::wasmtime 14 | Capture::Tiny 15 | Devel::GlobalDestruction 16 | ExtUtils::MakeMaker 17 | FFI::C 18 | FFI::C::StructDef 19 | FFI::C::Util 20 | FFI::CheckLib 21 | FFI::Platypus 22 | FFI::Platypus::Buffer 23 | FFI::Platypus::Memory 24 | FFI::Platypus::Record 25 | FFI::Platypus::Type::PtrObject 26 | Path::Tiny 27 | Ref::Util 28 | Sub::Install 29 | Test2::API 30 | Test2::Mock 31 | Test2::V0 32 | Test::Alien::Diag 33 | YAML 34 | autodie 35 | ); 36 | 37 | $post_diag = sub { 38 | eval { require Test::Alien::Diag; require Alien::wasmtime; Test::Alien::Diag::alien_diag('Alien::wasmtime'); }; 39 | if($@) { 40 | eval { 41 | require Wasm::Wasmtime::FFI; 42 | diag "Wasm::Wasmtime::FFI->_lib = $_" for Wasm::Wasmtime::FFI->_lib; 43 | }; 44 | diag "error requiring Wasm::Wasmtime::FFI: $@" if $@; 45 | }; 46 | }; 47 | 48 | my @modules = sort keys %modules; 49 | 50 | sub spacer () 51 | { 52 | diag ''; 53 | diag ''; 54 | diag ''; 55 | } 56 | 57 | pass 'okay'; 58 | 59 | my $max = 1; 60 | $max = $_ > $max ? $_ : $max for map { length $_ } @modules; 61 | our $format = "%-${max}s %s"; 62 | 63 | spacer; 64 | 65 | my @keys = sort grep /(MOJO|PERL|\A(LC|HARNESS)_|\A(SHELL|LANG)\Z)/i, keys %ENV; 66 | 67 | if(@keys > 0) 68 | { 69 | diag "$_=$ENV{$_}" for @keys; 70 | 71 | if($ENV{PERL5LIB}) 72 | { 73 | spacer; 74 | diag "PERL5LIB path"; 75 | diag $_ for split $Config{path_sep}, $ENV{PERL5LIB}; 76 | 77 | } 78 | elsif($ENV{PERLLIB}) 79 | { 80 | spacer; 81 | diag "PERLLIB path"; 82 | diag $_ for split $Config{path_sep}, $ENV{PERLLIB}; 83 | } 84 | 85 | spacer; 86 | } 87 | 88 | diag sprintf $format, 'perl', "$] $^O $Config{archname}"; 89 | 90 | foreach my $module (sort @modules) 91 | { 92 | my $pm = "$module.pm"; 93 | $pm =~ s{::}{/}g; 94 | if(eval { require $pm; 1 }) 95 | { 96 | my $ver = eval { $module->VERSION }; 97 | $ver = 'undef' unless defined $ver; 98 | diag sprintf $format, $module, $ver; 99 | } 100 | else 101 | { 102 | diag sprintf $format, $module, '-'; 103 | } 104 | } 105 | 106 | if($post_diag) 107 | { 108 | spacer; 109 | $post_diag->(); 110 | } 111 | 112 | spacer; 113 | 114 | done_testing; 115 | 116 | -------------------------------------------------------------------------------- /t/lib/Test2/Tools/Wasm.pm: -------------------------------------------------------------------------------- 1 | package Test2::Tools::Wasm; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008004; 6 | use Ref::Util qw( is_plain_arrayref ); 7 | use Test2::API qw( context ); 8 | use base qw( Exporter ); 9 | 10 | our @EXPORT = qw( wasm_store wasm_module_ok wasm_instance_ok wasm_func_ok ); 11 | 12 | sub _module 13 | { 14 | my $name = shift; 15 | my $wat = shift; 16 | 17 | require Wasm::Wasmtime::Module; 18 | 19 | my $ctx = context(); 20 | 21 | local $@ = ''; 22 | my $store = eval { wasm_store() }; 23 | return $ctx->fail_and_release($name, "error creating store object", "$@") if $@; 24 | 25 | my $module = eval { Wasm::Wasmtime::Module->new($store->engine, wat => $wat) }; 26 | return $ctx->fail_and_release($name, "error loading module", "$@") if $@; 27 | 28 | $ctx->release; 29 | $module; 30 | } 31 | 32 | sub _instance 33 | { 34 | my $module = _module(@_); 35 | my $name = shift; 36 | my $imports = is_plain_arrayref($_[-1]) ? pop : undef; 37 | 38 | return 0 unless $module; 39 | 40 | require Wasm::Wasmtime::Instance; 41 | 42 | my $ctx = context(); 43 | 44 | my $instance = eval { Wasm::Wasmtime::Instance->new($module, wasm_store(), $imports) }; 45 | return $ctx->fail_and_release($name, "error creating instance", "$@") if $@; 46 | 47 | $ctx->release; 48 | $instance; 49 | } 50 | 51 | sub wasm_module_ok ($;$) 52 | { 53 | my($wat,$name) = @_; 54 | 55 | $name ||= "module ok"; 56 | 57 | my $ctx = context(); 58 | my $module = _module($name, $wat); 59 | 60 | if($module) 61 | { 62 | $ctx->pass_and_release($name); 63 | return $module; 64 | } 65 | else 66 | { 67 | $ctx->release; 68 | return 0; 69 | } 70 | } 71 | 72 | sub wasm_instance_ok ($$;$) 73 | { 74 | my($imports, $wat, $name) = @_; 75 | 76 | $name ||= "instance ok"; 77 | 78 | my $ctx = context(); 79 | my $instance = _instance($name, $wat, $imports); 80 | 81 | if($instance) 82 | { 83 | $ctx->pass_and_release($name); 84 | return $instance; 85 | } 86 | else 87 | { 88 | $ctx->release; 89 | } 90 | } 91 | 92 | sub wasm_func_ok ($$;$) 93 | { 94 | my $f = shift; 95 | my $wat = shift; 96 | my $name = shift; 97 | 98 | require Wasm::Wasmtime::Func; 99 | 100 | my $ctx = context(); 101 | $name ||= "function $f"; 102 | my $instance = _instance($name, $wat); 103 | 104 | unless($instance) 105 | { 106 | $ctx->release; 107 | return 0; 108 | } 109 | 110 | my $extern = eval { $instance->exports->{$f} }; 111 | return $ctx->fail_and_release($name, "no export $f") unless $extern; 112 | 113 | my $kind = $extern->kind; 114 | return $ctx->fail_and_release($name, "$f is a $kind, expected a func") unless $kind eq 'func'; 115 | 116 | $ctx->pass_and_release($name); 117 | 118 | return $extern; 119 | } 120 | 121 | my $store; 122 | 123 | sub wasm_store 124 | { 125 | $store ||= do { 126 | my $config = Wasm::Wasmtime::Config->new; 127 | $config->wasm_multi_value(1); 128 | my $engine = Wasm::Wasmtime::Engine->new($config); 129 | Wasm::Wasmtime::Store->new($engine); 130 | }; 131 | } 132 | 133 | 1; 134 | -------------------------------------------------------------------------------- /t/test2_plugin_wasm.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | 5 | ok 1; 6 | 7 | done_testing; 8 | -------------------------------------------------------------------------------- /t/wasm.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use Wasm; 5 | use YAML qw( Dump ); 6 | 7 | try_ok { Wasm->import( -api => 0 ); } 'works with -api => 0 '; 8 | is(dies { Wasm->import( -api => 2 ); }, match qr/Currently only -api => 0 is supported/, 'dies with non 0 api level'); 9 | is(dies { Wasm->import( -foo => 'bar'); }, match qr/You MUST specify an api level as the first option/, 10 | 'bad key '); 11 | is(dies { Wasm->import( -api => 0, -api => 0 ) }, 12 | match qr/Specified -api more than once/, 'api more than once'); 13 | try_ok { package Empty; Wasm->import( -api => 0, -wat => '(module)' ) } 'empty module'; 14 | 15 | { 16 | package Foo0; 17 | use Wasm -api => 0, -wat => q{ 18 | (module 19 | (func (export "add") (param i32 i32) (result i32) 20 | local.get 0 21 | local.get 1 22 | i32.add) 23 | (func (export "subtract") (param i32 i32) (result i32) 24 | local.get 0 25 | local.get 1 26 | i32.sub) 27 | (memory (export "frooble") 2 3) 28 | ) 29 | }; 30 | } 31 | 32 | is( Foo0::add(1,2), 3, '1+2=3' ); 33 | is( Foo0::subtract(3,2), 1, '3-2=1' ); 34 | is( 35 | $Foo0::frooble, 36 | object { 37 | call [ isa => 'Wasm::Memory' ] => T(); 38 | call_list limits => [ 2, 2, 3 ]; 39 | }, 40 | 'memory region exported', 41 | ); 42 | 43 | { 44 | package Foo1; 45 | use Wasm -api => 0, -file => 'corpus/wasm/Math.wat'; 46 | } 47 | 48 | is( Foo1::add(1,2), 3, '1+2=3' ); 49 | is( Foo1::subtract(3,2), 1, '3-2=1' ); 50 | 51 | { 52 | package Foo2; 53 | use File::Temp qw( tempdir ); 54 | use Path::Tiny qw( path ); 55 | use Wasm -api => 0, -file => do { 56 | my $wat = path('corpus/wasm/Math.wat'); 57 | #my $wasm = path(tempdir( CLEANUP => 1 ))->child('math.wasm'); 58 | my $wasm = path('corpus/wasm/Math.wasm'); 59 | require Wasm::Wasmtime::Wat2Wasm; 60 | $wasm->spew_raw(Wasm::Wasmtime::Wat2Wasm::wat2wasm($wat->slurp_utf8)); 61 | $wasm->stringify; 62 | }; 63 | } 64 | 65 | is( Foo2::add(1,2), 3, '1+2=3' ); 66 | is( Foo2::subtract(3,2), 1, '3-2=1' ); 67 | 68 | require './corpus/wasm/Math.pm'; 69 | 70 | is( Math::add(1,2), 3, '1+2=3' ); 71 | is( Math::subtract(3,2), 1, '3-2=1' ); 72 | 73 | { 74 | package Foo3; 75 | use Wasm -api => 0, -package => 'Foo4', -file => 'corpus/wasm/Math.wat'; 76 | } 77 | 78 | ok( !Foo3->can('add'), 'did not export into Foo3' ); 79 | is( Foo4::add(1,2), 3, '1+2=3' ); 80 | is( Foo4::subtract(3,2), 1, '3-2=1' ); 81 | 82 | { 83 | package Foo5; 84 | use Wasm -api => 0, -exporter => 'ok', -file => 'corpus/wasm/Math.wat'; 85 | BEGIN { $INC{'Foo5.pm'} = __FILE__ } 86 | } 87 | 88 | { 89 | package Foo6; 90 | use Foo5 qw( add subtract ); 91 | } 92 | 93 | { 94 | package Foo7; 95 | use Foo5; 96 | } 97 | 98 | ok( !Foo7->can('add'), 'did not export into Foo7' ); 99 | is( Foo6::add(1,2), 3, '1+2=3' ); 100 | is( Foo6::subtract(3,2), 1, '3-2=1' ); 101 | 102 | { 103 | package Foo8; 104 | use Wasm -api => 0, -exporter => 'all', -file => 'corpus/wasm/Math.wat'; 105 | BEGIN { $INC{'Foo8.pm'} = __FILE__ } 106 | } 107 | 108 | { 109 | package Foo9; 110 | use Foo8; 111 | } 112 | 113 | is( Foo9::add(1,2), 3, '1+2=3' ); 114 | is( Foo9::subtract(3,2), 1, '3-2=1' ); 115 | 116 | note Dump(\%Wasm::WASM); 117 | 118 | done_testing; 119 | -------------------------------------------------------------------------------- /t/wasm__linker.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use Capture::Tiny qw( capture ); 5 | use lib 'corpus/wasm__linker/lib'; 6 | use YAML qw( Dump ); 7 | 8 | is( dies { require Module2 }, U(), 'require Module2'); 9 | is( dies { require Module3 }, match qr/module required by WebAssembly at.*Module3\.wat/, 'require Module3'); 10 | 11 | is 12 | [ capture { Module2::run() } ], 13 | ["Hello, world!\n", ''], 14 | 'run it!', 15 | ; 16 | 17 | is( dies { require Foo::Bar::X2 }, U(), 'require Foo::Bar::X2'); 18 | is( $Foo::Bar::X1::x1, 42, "global x1 is set to 42"); 19 | is( Foo::Bar::X2::get_x1(), 42, "get_x1() = 42"); 20 | Foo::Bar::X2::inc_x1(); 21 | is( Foo::Bar::X2::get_x1(), 43, "get_x1() = 43"); 22 | is( $Foo::Bar::X1::x1, 43, "global x1 is set to 43"); 23 | 24 | is( dies { require Foo::Bar::X4 }, U(), 'require Foo::Bar::X4'); 25 | is( $Foo::Bar::X3::x3, 42, "global x3 is set to 42"); 26 | is( Foo::Bar::X4::get_x3(), 42, "get_x3() = 42"); 27 | Foo::Bar::X4::inc_x3(); 28 | is( $Foo::Bar::X3::x3, 43, "global x3 is set to 43"); 29 | is( Foo::Bar::X4::get_x3(), 43, "get_x3() = 43"); 30 | 31 | is 32 | [ capture { Foo::Bar::X4::run() } ], 33 | ["hello, world!\n", ''], 34 | 'run it to Perl!', 35 | ; 36 | 37 | is( dies { require Foo::Bar::X5 }, U(), 'require Foo::Bar::X5'); 38 | Foo::Bar::X5::inc_x3(); 39 | is( $Foo::Bar::X3::x3, 44, "global x3 is set to 44"); 40 | is( Foo::Bar::X4::get_x3(), 44, "get_x3() = 44"); 41 | is( Foo::Bar::X5::get_x3(), 44, "get_x3() = 44"); 42 | 43 | note Dump(do { no warnings 'once'; \%Wasm::WASM }); 44 | 45 | done_testing; 46 | -------------------------------------------------------------------------------- /t/wasm_func.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Func; 4 | 5 | # no interface, just documentation. 6 | ok 1; 7 | 8 | done_testing; 9 | -------------------------------------------------------------------------------- /t/wasm_global.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Global; 4 | 5 | # no interface, just documentation. 6 | ok 1; 7 | 8 | done_testing; 9 | -------------------------------------------------------------------------------- /t/wasm_hook.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use Wasm::Hook; 5 | use lib 'corpus/wasm_hook/lib'; 6 | use Foo::Bar::Baz::Math; 7 | 8 | is(Foo::Bar::Baz::Math::add(1,2), 3); 9 | 10 | Wasm::Hook->unimport; 11 | 12 | is( 13 | dies { require Foo::Bar::Baz::Math2 }, 14 | match qr/Can't locate Foo\/Bar\/Baz\/Math2\.pm/, 15 | ); 16 | 17 | { package Frooble; 18 | use Foo::Bar::Baz::Math qw( add subtract ); 19 | } 20 | 21 | ok(!__PACKAGE__->can('add')); 22 | is(Frooble::add(1,2), 3); 23 | 24 | done_testing; 25 | 26 | 27 | -------------------------------------------------------------------------------- /t/wasm_memory.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use Wasm::Wasmtime::Store; 5 | use Wasm::Wasmtime::Memory; 6 | use Wasm::Memory qw( wasm_caller_memory ); 7 | 8 | my $store = Wasm::Wasmtime::Store->new; 9 | 10 | is( 11 | Wasm::Memory->new( 12 | Wasm::Wasmtime::Memory->new( 13 | $store, [5,10], 14 | ) 15 | ), 16 | object { 17 | call [ isa => 'Wasm::Memory' ] => T(); 18 | call address => match qr/^[0-9]+$/; 19 | call size => 327680; 20 | call_list limits => [ 5, 5, 10 ]; 21 | call [ grow => 2 ] => T(); 22 | call_list limits => [ 7, 5, 10 ]; 23 | }, 24 | 'create a memory object' 25 | ); 26 | 27 | imported_ok 'wasm_caller_memory'; 28 | 29 | { 30 | my $memory; 31 | { 32 | sub hello 33 | { 34 | $memory = wasm_caller_memory; 35 | } 36 | 37 | use Wasm 38 | -api => 0, 39 | -wat => q{ 40 | (module 41 | (func $hello (import "main" "hello")) 42 | (func (export "run") (call $hello)) 43 | (memory (export "memory") 2 3) 44 | ) 45 | } 46 | ; 47 | run(); 48 | } 49 | 50 | isa_ok $memory, 'Wasm::Memory'; 51 | is wasm_caller_memory(), U(); 52 | } 53 | 54 | done_testing; 55 | -------------------------------------------------------------------------------- /t/wasm_trap.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Trap; 4 | 5 | is( 6 | Wasm::Trap->new("foo\0"), 7 | object { 8 | call [isa => 'Wasm::Trap'] => T(); 9 | call message => 'foo'; 10 | call sub { my $trap = shift; "$trap" } => "foo\n"; 11 | call exit_status => undef; 12 | }, 13 | 'created trap ok', 14 | ); 15 | 16 | { 17 | package Frooble; 18 | 19 | require Wasm; 20 | Wasm->import( 21 | -api => 0, 22 | -wat => q{ 23 | (module 24 | (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32))) 25 | (memory 10) 26 | (export "memory" (memory 0)) 27 | (func (export "do_exit") 28 | (call $proc_exit (i32.const 7)) 29 | ) 30 | ) 31 | }, 32 | ); 33 | } 34 | 35 | is( 36 | dies { Frooble::do_exit() }, 37 | object { 38 | call [ isa => 'Wasm::Trap' ] => T(); 39 | call exit_status => 7; 40 | }, 41 | ); 42 | 43 | done_testing; 44 | -------------------------------------------------------------------------------- /t/wasm_wasmtime.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use Wasm::Wasmtime; 5 | use Path::Tiny qw( path ); 6 | 7 | pass 'preload does not crash'; 8 | 9 | foreach my $pm (map { $_->relative('lib') } path('lib/Wasm/Wasmtime')->children) 10 | { 11 | next unless $pm->basename =~ /\.pm$/; 12 | next if $pm->basename eq 'Wat2Wasm.pm'; 13 | is($INC{"$pm"}, T(), "loaded $pm"); 14 | } 15 | 16 | done_testing; 17 | 18 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_bytevec.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::FFI (); 4 | 5 | my $vec = Wasm::Wasmtime::ByteVec->new("foo"); 6 | isa_ok $vec, 'Wasm::Wasmtime::ByteVec'; 7 | is $vec->get, 'foo', 'roundtrip'; 8 | 9 | done_testing; 10 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_caller.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use Wasm::Wasmtime::Caller; 5 | 6 | imported_ok 'wasmtime_caller'; 7 | is(wasmtime_caller(), U()); 8 | 9 | { 10 | require Wasm::Wasmtime; 11 | my $store = Wasm::Wasmtime::Store->new; 12 | my $module = Wasm::Wasmtime::Module->new( $store->engine, wat => q{ 13 | (module 14 | (func $hello (import "" "hello")) 15 | (func (export "run") (call $hello)) 16 | (memory (export "memory") 2 3) 17 | ) 18 | }); 19 | 20 | my $caller; 21 | my $memory; 22 | my $hello = Wasm::Wasmtime::Func->new( 23 | $store, 24 | Wasm::Wasmtime::FuncType->new([],[]), 25 | sub { 26 | $caller = wasmtime_caller; 27 | $memory = $caller->export_get('memory'); 28 | note "hello world!\n" 29 | }, 30 | ); 31 | 32 | my $instance = Wasm::Wasmtime::Instance->new($module, $store, [$hello]); 33 | $instance->exports->run->call(); # hello world! 34 | 35 | isa_ok $caller, 'Wasm::Wasmtime::Caller'; 36 | isa_ok $memory, 'Wasm::Wasmtime::Memory'; 37 | is $caller->export_get('memory'), U(); 38 | } 39 | 40 | done_testing; 41 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_config.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Config; 4 | use File::Glob qw( bsd_glob ); 5 | 6 | my $config = Wasm::Wasmtime::Config->new; 7 | isa_ok $config, 'Wasm::Wasmtime::Config'; 8 | 9 | $config->debug_info(0); 10 | $config->debug_info(1); 11 | pass 'debug_info'; 12 | 13 | $config->wasm_threads(0); 14 | $config->wasm_threads(1); 15 | pass 'wasm_threads'; 16 | 17 | $config->wasm_reference_types(0); 18 | $config->wasm_reference_types(1); 19 | pass 'wasm_reference_types'; 20 | 21 | $config->wasm_simd(0); 22 | $config->wasm_simd(1); 23 | pass 'wasm_simd'; 24 | 25 | $config->wasm_bulk_memory(0); 26 | $config->wasm_bulk_memory(1); 27 | pass 'wasm_bulk_memory'; 28 | 29 | $config->wasm_multi_value(0); 30 | $config->wasm_multi_value(1); 31 | pass 'wasm_multi_value'; 32 | 33 | $config->interruptable(0); 34 | $config->interruptable(1); 35 | pass 'interruptable'; 36 | 37 | $config->max_wasm_stack(1024); 38 | pass 'max_wasm_stack'; 39 | 40 | $config->consume_fuel(1); 41 | pass 'consume_fuel'; 42 | 43 | foreach my $strategy (qw( auto cranelift lightbeam )) 44 | { 45 | if(my $e = dies { $config->strategy($strategy) }) 46 | { 47 | is( 48 | $e, 49 | mismatch qr/unknown strategy:/, 50 | "strategy($strategy) = fail", 51 | ); 52 | note "exception: $e"; 53 | } 54 | else 55 | { 56 | pass "strategy($strategy) = ok"; 57 | } 58 | } 59 | 60 | is 61 | dies { $config->strategy('foo') }, 62 | match qr/unknown strategy: foo/, 63 | 'strategy: unknown strategy' 64 | ; 65 | 66 | $config->cranelift_debug_verifier(0); 67 | $config->cranelift_debug_verifier(1); 68 | pass 'cranelift_debug_verifier'; 69 | 70 | foreach my $cranelift_opt_level (qw( none speed speed_and_size )) 71 | { 72 | $config->cranelift_opt_level($cranelift_opt_level); 73 | pass "cranelift_opt_level($cranelift_opt_level) = ok"; 74 | } 75 | 76 | is 77 | dies { $config->cranelift_opt_level('foo') }, 78 | match qr/unknown cranelift_opt_level: foo/, 79 | 'cranelift_opt_level: unknown cranelift_opt_level' 80 | ; 81 | 82 | foreach my $profiler (qw( none jitdump )) 83 | { 84 | if(my $e = dies { $config->profiler($profiler) }) 85 | { 86 | is( 87 | $e, 88 | mismatch qr/unknown profiler:/, 89 | "profiler($profiler) = fail", 90 | ); 91 | note "exception: $e"; 92 | } 93 | else 94 | { 95 | pass "profiler($profiler) = ok"; 96 | } 97 | } 98 | 99 | is 100 | dies { $config->profiler('foo') }, 101 | match qr/unknown profiler: foo/, 102 | 'profiler: unknown profiler' 103 | ; 104 | 105 | foreach my $prop (qw( static_memory_maximum_size static_memory_guard_size dynamic_memory_guard_size )) 106 | { 107 | eval { 108 | $config->$prop(1024); 109 | }; 110 | if(my $error = $@) 111 | { 112 | is($error, match qr/property $prop is not available/, "$prop(1024)"); 113 | note "not available"; 114 | } 115 | else 116 | { 117 | pass "$prop(1024)"; 118 | } 119 | } 120 | 121 | unlink $_ for bsd_glob('jit-*.dump'); 122 | 123 | done_testing; 124 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_engine.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Config; 4 | use Wasm::Wasmtime::Engine; 5 | 6 | is( 7 | Wasm::Wasmtime::Engine->new, 8 | object { 9 | call ['isa','Wasm::Wasmtime::Engine'] => T(); 10 | }, 11 | 'default config', 12 | ); 13 | 14 | is( 15 | Wasm::Wasmtime::Engine->new(Wasm::Wasmtime::Config->new), 16 | object { 17 | call ['isa','Wasm::Wasmtime::Engine'] => T(); 18 | }, 19 | 'explicit config', 20 | ); 21 | 22 | done_testing; 23 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_exporttype.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use lib 't/lib'; 4 | use Test2::Tools::Wasm; 5 | use Wasm::Wasmtime::ExportType; 6 | 7 | is( 8 | wasm_module_ok(q{ 9 | (module 10 | (func (export "foo") (param i32 i32) (result i32) 11 | local.get 0 12 | local.get 1 13 | i32.add) 14 | (memory (export "bar") 2 3) 15 | ) 16 | }), 17 | object { 18 | call_list sub { @{ shift->exports } } => array { 19 | item object { 20 | call [ isa => 'Wasm::Wasmtime::ExportType' ] => T(); 21 | call name => 'foo'; 22 | call type => object { 23 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 24 | }; 25 | call to_string => '(func (export "foo") (param i32 i32) (result i32))'; 26 | }; 27 | item object { 28 | call [ isa => 'Wasm::Wasmtime::ExportType' ] => T(); 29 | call name => 'bar'; 30 | call type => object { 31 | call [ isa => 'Wasm::Wasmtime::MemoryType' ] => T(); 32 | }; 33 | call to_string => '(memory (export "bar") 2 3)'; 34 | }; 35 | end; 36 | }; 37 | }, 38 | 'export types okay', 39 | ); 40 | 41 | done_testing; 42 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_extern.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use lib 't/lib'; 5 | use Test2::Tools::Wasm; 6 | use Wasm::Wasmtime::Extern; 7 | 8 | is( 9 | wasm_instance_ok([], q{ 10 | (module 11 | (func (export "foo") (param i32 i32) (result i32) 12 | local.get 0 13 | local.get 1 14 | i32.add) 15 | (memory (export "bar") 2 3) 16 | (table (export "frooble") 1 funcref) 17 | (global (export "hi") (mut i32) (i32.const 1)) 18 | ) 19 | }), 20 | object { 21 | call exports => object { 22 | call foo => object { 23 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 24 | call type => object { 25 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 26 | }; 27 | call kind => 'func'; 28 | }; 29 | call hi => object { 30 | call [ isa => 'Wasm::Wasmtime::Global' ] => T(); 31 | call type => object { 32 | call [ isa => 'Wasm::Wasmtime::GlobalType' ] => T(); 33 | }; 34 | call kind => 'global'; 35 | }; 36 | call frooble => object { 37 | call [ isa => 'Wasm::Wasmtime::Table' ] => T(); 38 | call type => object { 39 | call [ isa => 'Wasm::Wasmtime::TableType' ] => T(); 40 | }; 41 | call kind => 'table'; 42 | }; 43 | call bar => object { 44 | call [ isa => 'Wasm::Wasmtime::Memory' ] => T(); 45 | call type => object { 46 | call [ isa => 'Wasm::Wasmtime::MemoryType' ] => T(); 47 | }; 48 | call kind => 'memory'; 49 | }; 50 | }; 51 | }, 52 | 'exter objects', 53 | ); 54 | 55 | done_testing; 56 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_externtype.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use lib 't/lib'; 4 | use Test2::Tools::Wasm; 5 | use Wasm::Wasmtime::ExternType; 6 | 7 | is( 8 | wasm_module_ok(q{ 9 | (module 10 | (func (export "foo") (param i32 i32) (result i32) 11 | local.get 0 12 | local.get 1 13 | i32.add) 14 | (memory (export "bar") 2 3) 15 | (global (export "baz") (mut i32) (i32.const 1)) 16 | (table (export "frooble") 1 3 funcref) 17 | ) 18 | }), 19 | object { 20 | call exports => object { 21 | call foo => object { 22 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 23 | call kind => 'functype'; 24 | call is_functype => T(); 25 | call is_globaltype => F(); 26 | call is_tabletype => F(); 27 | call is_memorytype => F(); 28 | }; 29 | call baz => object { 30 | call [ isa => 'Wasm::Wasmtime::GlobalType' ] => T(); 31 | call kind => 'globaltype'; 32 | call is_functype => F(); 33 | call is_globaltype => T(); 34 | call is_tabletype => F(); 35 | call is_memorytype => F(); 36 | call content => object { 37 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 38 | call kind => 'i32'; 39 | }; 40 | call mutability => 'var'; 41 | }; 42 | call frooble => object { 43 | call [ isa => 'Wasm::Wasmtime::TableType' ] => T(); 44 | call kind => 'tabletype'; 45 | call is_functype => F(); 46 | call is_globaltype => F(); 47 | call is_tabletype => T(); 48 | call is_memorytype => F(); 49 | call element => object { 50 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 51 | call kind => 'funcref'; 52 | }; 53 | call limits => [ 1, 3 ]; 54 | }; 55 | call bar => object { 56 | call [ isa => 'Wasm::Wasmtime::MemoryType' ] => T(); 57 | call kind => 'memorytype'; 58 | call is_functype => F(); 59 | call is_globaltype => F(); 60 | call is_tabletype => F(); 61 | call is_memorytype => T(); 62 | }; 63 | }; 64 | }, 65 | 'test extern types' 66 | ); 67 | 68 | done_testing; 69 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_ffi.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::FFI; 4 | 5 | imported_ok '$ffi'; 6 | isa_ok $ffi, 'FFI::Platypus'; 7 | 8 | done_testing; 9 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_func.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use lib 't/lib'; 4 | use Test2::Tools::Wasm; 5 | use Wasm::Wasmtime::Store; 6 | use Wasm::Wasmtime::Func; 7 | use Wasm::Wasmtime::FuncType; 8 | 9 | my $add; 10 | 11 | is( 12 | $add = wasm_func_ok( add => q{ 13 | (module 14 | (func (export "add") (param i32 i32) (result i32) 15 | local.get 0 16 | local.get 1 17 | i32.add) 18 | ) 19 | }), 20 | object { 21 | call [ call => 1, 2 ] => 3; 22 | call [ call => 3, 4 ] => 7; 23 | call type => object { 24 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 25 | }; 26 | call param_arity => 2; 27 | call result_arity => 1; 28 | 29 | call is_func => T(); 30 | call is_global => F(); 31 | call is_table => F(); 32 | call is_memory => F(); 33 | call kind => 'func'; 34 | }, 35 | 'call add', 36 | ); 37 | 38 | if($add) 39 | { 40 | { package Foo::Bar; 41 | $add->attach('baz'); 42 | sub optimus {} 43 | } 44 | ok(Foo::Bar->can('baz'), 'attached using caller'); 45 | is(Foo::Bar::baz(9,9), 18, 'calling attached Foo::Bar::baz'); 46 | 47 | is( 48 | warnings { $add->attach('Foo::Bar', 'optimus') }, 49 | bag { 50 | item match qr/attaching Foo::Bar::optimus replaces existing subroutine .*wasm_wasmtime_func\.t/; 51 | etc; 52 | }, 53 | 'warns about redefine' 54 | ); 55 | 56 | ok(Foo::Bar->can('optimus'), 'attached using caller'); 57 | is(Foo::Bar::optimus(9,9), 18, 'calling attached Foo::Bar::optimus'); 58 | 59 | { package Foo; 60 | $add->attach('Bar', 'baz'); 61 | } 62 | undef $add; 63 | ok(!Foo->can('baz'), 'attach using explicit package does not install in caller'); 64 | ok(Bar->can('baz'), 'attach using explicit package'); 65 | is(Bar::baz(9,9), 18, 'calling attached Bar::baz'); 66 | 67 | } 68 | 69 | is( 70 | wasm_func_ok( round_trip_many => q{ 71 | (module 72 | (func $round_trip_many 73 | (export "round_trip_many") 74 | (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) 75 | (result i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) 76 | 77 | local.get 0 78 | local.get 1 79 | local.get 2 80 | local.get 3 81 | local.get 4 82 | local.get 5 83 | local.get 6 84 | local.get 7 85 | local.get 8 86 | local.get 9) 87 | ) 88 | }), 89 | object { 90 | call_list [ call => 0,1,2,3,4,5,6,7,8,9 ] => [0,1,2,3,4,5,6,7,8,9]; 91 | }, 92 | 'call round_trip_many', 93 | ); 94 | 95 | { 96 | my $it_worked; 97 | 98 | my $f = Wasm::Wasmtime::Func->new( 99 | Wasm::Wasmtime::Store->new, 100 | Wasm::Wasmtime::FuncType->new([],[]), 101 | sub { $it_worked = 1 }, 102 | ); 103 | 104 | is( 105 | $f, 106 | object { 107 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 108 | }, 109 | 'create functon with no arguments/results', 110 | ); 111 | 112 | try_ok { $f->call } 'call function'; 113 | 114 | is( 115 | $it_worked, 116 | T(), 117 | 'it worked', 118 | ); 119 | } 120 | 121 | { 122 | my $it_worked; 123 | 124 | my $f = Wasm::Wasmtime::Func->new( 125 | Wasm::Wasmtime::Store->new, 126 | [],[], 127 | sub { $it_worked = 1 }, 128 | ); 129 | 130 | is( 131 | $f, 132 | object { 133 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 134 | }, 135 | 'create functon with no arguments/results', 136 | ); 137 | 138 | try_ok { $f->call } 'call function'; 139 | 140 | is( 141 | $it_worked, 142 | T(), 143 | 'it worked', 144 | ); 145 | } 146 | 147 | { 148 | my @it_worked; 149 | 150 | my $f = Wasm::Wasmtime::Func->new( 151 | Wasm::Wasmtime::Store->new, 152 | ['i32','i32'],[], 153 | sub { @it_worked = @_ }, 154 | ); 155 | 156 | is( 157 | $f, 158 | object { 159 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 160 | }, 161 | 'create functon with arguments', 162 | ); 163 | 164 | try_ok { $f->call(1,2) } 'call function'; 165 | 166 | is( 167 | \@it_worked, 168 | [1,2], 169 | 'it worked', 170 | ); 171 | } 172 | 173 | { 174 | my $it_worked; 175 | 176 | my $f = Wasm::Wasmtime::Func->new( 177 | Wasm::Wasmtime::Store->new, 178 | [],['i32'], 179 | sub { return 42 }, 180 | ); 181 | 182 | is( 183 | $f, 184 | object { 185 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 186 | }, 187 | 'create functon without arguments', 188 | ); 189 | 190 | try_ok { $it_worked = $f->call } 'call function'; 191 | 192 | is( 193 | $it_worked, 194 | 42, 195 | 'it worked', 196 | ); 197 | } 198 | 199 | { 200 | my $f = Wasm::Wasmtime::Func->new( 201 | Wasm::Wasmtime::Store->new, 202 | Wasm::Wasmtime::FuncType->new([],[]), 203 | sub { die 'it dies' }, 204 | ); 205 | 206 | is( 207 | $f, 208 | object { 209 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 210 | }, 211 | 'create functon with an exception', 212 | ); 213 | 214 | my $error; 215 | 216 | is( 217 | $error = dies { $f->call }, 218 | object { 219 | call message => match qr/^it dies/; 220 | }, 221 | 'it died', 222 | ); 223 | 224 | note $error; 225 | 226 | } 227 | 228 | { 229 | my $store = Wasm::Wasmtime::Store->new; 230 | my $f = Wasm::Wasmtime::Func->new( 231 | $store, 232 | Wasm::Wasmtime::FuncType->new([],[]), 233 | sub { die Wasm::Wasmtime::Trap->new($store, "it dies\0") }, 234 | ); 235 | 236 | is( 237 | $f, 238 | object { 239 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 240 | }, 241 | 'create functon with an exception', 242 | ); 243 | 244 | my $error; 245 | 246 | is( 247 | $error = dies { $f->call }, 248 | object { 249 | call message => 'it dies'; 250 | }, 251 | 'it died', 252 | ); 253 | 254 | note $error; 255 | 256 | } 257 | 258 | done_testing; 259 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_functype.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::FuncType; 4 | 5 | is( 6 | Wasm::Wasmtime::FuncType->new([] => []), 7 | object { 8 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 9 | call_list params => []; 10 | call_list results => []; 11 | }, 12 | 'functype with no args or return type', 13 | ); 14 | 15 | is( 16 | Wasm::Wasmtime::FuncType->new(['i32','f64'] => ['i64','f32']), 17 | object { 18 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 19 | call_list params => array { 20 | item object { 21 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 22 | call kind => 'i32'; 23 | }; 24 | item object { 25 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 26 | call kind => 'f64'; 27 | }; 28 | end; 29 | }; 30 | call_list results => array { 31 | item object { 32 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 33 | call kind => 'i64'; 34 | }; 35 | item object { 36 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 37 | call kind => 'f32'; 38 | }; 39 | end; 40 | }; 41 | 42 | call to_string => "(param i32 f64) (result i64 f32)"; 43 | 44 | call is_functype => T(); 45 | call is_globaltype => F(); 46 | call is_tabletype => F(); 47 | call is_memorytype => F(); 48 | call kind => 'functype'; 49 | }, 50 | '(i32,f64)->(i64,f32)', 51 | ); 52 | 53 | done_testing; 54 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_global.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Global; 4 | 5 | my $global = Wasm::Wasmtime::Global->new( 6 | Wasm::Wasmtime::Store->new, 7 | Wasm::Wasmtime::GlobalType->new( 8 | 'i32', 9 | 'var', 10 | ), 11 | 42 12 | ); 13 | 14 | 15 | is( 16 | $global, 17 | object { 18 | call [ isa => 'Wasm::Wasmtime::Global' ] => T(); 19 | call type => object { 20 | call [ isa => 'Wasm::Wasmtime::GlobalType' ] => T(); 21 | }; 22 | call get => 42; 23 | call [ set => 99 ] => U(); 24 | call get => 99; 25 | 26 | call is_func => F(); 27 | call is_global => T(); 28 | call is_table => F(); 29 | call is_memory => F(); 30 | call kind => 'global'; 31 | }, 32 | ); 33 | 34 | our $tied; 35 | *tied = $global->tie; 36 | is $tied, 99, 'tied.FETCH == 99'; 37 | is $tied = 100, 100, 'tied.STORE == 100'; 38 | is $tied, 100, 'tied.FETCH == 100'; 39 | is $global->get, 100, 'globa.get == 100'; 40 | 41 | done_testing; 42 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_globaltype.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::GlobalType; 4 | 5 | is( 6 | Wasm::Wasmtime::GlobalType->new('i32','const'), 7 | object { 8 | call [ isa => 'Wasm::Wasmtime::GlobalType' ] => T(); 9 | call content => object { 10 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 11 | call kind => 'i32'; 12 | }; 13 | call mutability => 'const'; 14 | call to_string => "(const i32)"; 15 | 16 | call is_functype => F(); 17 | call is_globaltype => T(); 18 | call is_tabletype => F(); 19 | call is_memorytype => F(); 20 | call kind => 'globaltype'; 21 | }, 22 | 'i32,const', 23 | ); 24 | 25 | is( 26 | Wasm::Wasmtime::GlobalType->new('i64','var'), 27 | object { 28 | call [ isa => 'Wasm::Wasmtime::GlobalType' ] => T(); 29 | call content => object { 30 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 31 | call kind => 'i64'; 32 | }; 33 | call mutability => 'var'; 34 | call to_string => "(var i64)"; 35 | }, 36 | 'i64,var', 37 | ); 38 | 39 | is( 40 | Wasm::Wasmtime::GlobalType->new(Wasm::Wasmtime::ValType->new('f32'),'var'), 41 | object { 42 | call [ isa => 'Wasm::Wasmtime::GlobalType' ] => T(); 43 | call content => object { 44 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 45 | call kind => 'f32'; 46 | }; 47 | call mutability => 'var'; 48 | call to_string => "(var f32)"; 49 | }, 50 | '(i64),var', 51 | ); 52 | 53 | done_testing; 54 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_importtype.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use lib 't/lib'; 4 | use Test2::Tools::Wasm; 5 | use Wasm::Wasmtime::ImportType; 6 | 7 | is( 8 | wasm_module_ok(q{ 9 | (module 10 | (func $hello (import "xx" "hello")) 11 | ) 12 | }), 13 | object { 14 | call_list sub { @{ shift->imports } } => array { 15 | item object { 16 | call [ isa => 'Wasm::Wasmtime::ImportType' ] => T(); 17 | call name => 'hello'; 18 | call type => object { 19 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 20 | }; 21 | call module => 'xx'; 22 | call to_string => '(func (import "xx" "hello") )'; 23 | }; 24 | end; 25 | }; 26 | }, 27 | 'import types are good' 28 | ); 29 | 30 | is( 31 | wasm_module_ok(q{ 32 | (module 33 | (func $hello (import "xx" "hello") (param i32 i32 i32) (result f32)) 34 | ) 35 | }), 36 | object { 37 | call_list sub { @{ shift->imports } } => array { 38 | item object { 39 | call [ isa => 'Wasm::Wasmtime::ImportType' ] => T(); 40 | call name => 'hello'; 41 | call type => object { 42 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 43 | }; 44 | call module => 'xx'; 45 | call to_string => '(func (import "xx" "hello") (param i32 i32 i32) (result f32))'; 46 | }; 47 | end; 48 | }; 49 | }, 50 | 'import with function arguments' 51 | ); 52 | 53 | done_testing; 54 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_instance.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use lib 't/lib'; 5 | use Test2::Tools::Wasm; 6 | use Wasm::Wasmtime::Module; 7 | use Wasm::Wasmtime::Instance; 8 | 9 | my $store = wasm_store(); 10 | 11 | is( 12 | Wasm::Wasmtime::Instance->new(Wasm::Wasmtime::Module->new($store->engine, wat => '(module)'), $store), 13 | object { 14 | call [ isa => 'Wasm::Wasmtime::Instance' ] => T(); 15 | call module => object { 16 | call [ isa => 'Wasm::Wasmtime::Module' ] => T(); 17 | }; 18 | }, 19 | 'created instance instance' 20 | ); 21 | 22 | is( 23 | Wasm::Wasmtime::Instance->new(Wasm::Wasmtime::Module->new($store->engine, wat => q{ 24 | (module 25 | (func (export "add") (param i32 i32) (result i32) 26 | local.get 0 27 | local.get 1 28 | i32.add) 29 | (func (export "sub") (param i64 i64) (result i64) 30 | local.get 0 31 | local.get 1 32 | i64.sub) 33 | (memory (export "frooble") 2 3) 34 | ) 35 | }), $store), 36 | object { 37 | call [ isa => 'Wasm::Wasmtime::Instance' ] => T(); 38 | call exports => object { 39 | call add => object { 40 | call [isa => 'Wasm::Wasmtime::Func'] => T(); 41 | }; 42 | }; 43 | call_list sub { @{ shift->exports } } => array { 44 | item object { 45 | call [isa => 'Wasm::Wasmtime::Func'] => T(); 46 | call type => object { 47 | call [isa => 'Wasm::Wasmtime::FuncType'] => T(); 48 | call kind => 'functype'; 49 | call_list params => array { 50 | item object { call kind => 'i32' }; 51 | item object { call kind => 'i32' }; 52 | end; 53 | }; 54 | }; 55 | call type => object { 56 | call [isa => 'Wasm::Wasmtime::FuncType'] => T(); 57 | }; 58 | call param_arity => 2; 59 | call result_arity => 1; 60 | call [call => 1, 2] => 3; 61 | call_list [call => 1, 2] => [3]; 62 | }; 63 | item object { 64 | call [isa => 'Wasm::Wasmtime::Func'] => T(); 65 | call type => object { 66 | call [isa => 'Wasm::Wasmtime::FuncType'] => T(); 67 | call kind => 'functype'; 68 | call_list params => array { 69 | item object { call kind => 'i64' }; 70 | item object { call kind => 'i64' }; 71 | end; 72 | }; 73 | }; 74 | call type => object { 75 | call [isa => 'Wasm::Wasmtime::FuncType'] => T(); 76 | }; 77 | call param_arity => 2; 78 | call result_arity => 1; 79 | call [call => 3, 1] => 2; 80 | call_list [call => 3, 1] => [2]; 81 | }; 82 | item object { 83 | call [isa => 'Wasm::Wasmtime::Memory'] => T(); 84 | call type => object { 85 | call [isa => 'Wasm::Wasmtime::MemoryType'] => T(); 86 | call kind => 'memorytype'; 87 | }; 88 | }; 89 | end; 90 | }; 91 | }, 92 | 'created exports' 93 | ); 94 | 95 | wasm_instance_ok [], '(module)'; 96 | 97 | is( 98 | dies { 99 | Wasm::Wasmtime::Instance->new( 100 | Wasm::Wasmtime::Module->new( $store->engine, wat => q{ 101 | (module 102 | (func $hello (import "" "hello")) 103 | (func (export "run") (call $hello)) 104 | ) 105 | }), 106 | $store, 107 | ); 108 | }, 109 | match qr/Got 0 imports, but expected 1/, 110 | 'import count mismatch', 111 | ); 112 | 113 | { 114 | my $it_works; 115 | 116 | my $store = Wasm::Wasmtime::Store->new; 117 | my $module = Wasm::Wasmtime::Module->new( $store->engine, wat => q{ 118 | (module 119 | (func $hello (import "" "hello")) 120 | (func (export "run") (call $hello)) 121 | ) 122 | }); 123 | 124 | my $hello = Wasm::Wasmtime::Func->new( 125 | $store, 126 | Wasm::Wasmtime::FuncType->new([],[]), 127 | sub { $it_works = 1 }, 128 | ); 129 | 130 | my $instance = Wasm::Wasmtime::Instance->new($module, $store, [$hello]); 131 | $instance->exports->run->(); 132 | 133 | is $it_works, T(), 'callback called'; 134 | } 135 | 136 | { 137 | my $it_works; 138 | 139 | is( 140 | wasm_instance_ok([sub { $it_works = 1 }], q{ 141 | (module 142 | (func $hello (import "" "hello")) 143 | (func (export "run") (call $hello)) 144 | ) 145 | }), 146 | object { 147 | call exports => object { 148 | call run => object { 149 | call call => U(); 150 | }; 151 | }; 152 | }, 153 | 'pass func as code ref' 154 | ); 155 | 156 | is($it_works, T(), 'verified that we called the callback'); 157 | } 158 | 159 | { 160 | wasm_instance_ok([undef], q{ 161 | (module 162 | (import "" "" (memory 1)) 163 | ) 164 | }); 165 | } 166 | 167 | { 168 | my $memory; 169 | wasm_instance_ok([\$memory], q{ 170 | (module 171 | (import "" "" (memory 1)) 172 | ) 173 | }); 174 | 175 | isa_ok $memory, 'Wasm::Wasmtime::Memory'; 176 | } 177 | 178 | done_testing; 179 | 180 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_instance_exports.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Store; 4 | use Wasm::Wasmtime::Instance; 5 | use Wasm::Wasmtime::Instance::Exports; 6 | use YAML qw( Dump ); 7 | 8 | { 9 | my $store = Wasm::Wasmtime::Store->new; 10 | my $module = Wasm::Wasmtime::Module->new($store->engine, wat => q{ 11 | (module 12 | (func (export "add") (param i32 i32) (result i32) 13 | local.get 0 14 | local.get 1 15 | i32.add) 16 | ) 17 | }); 18 | my $instance = Wasm::Wasmtime::Instance->new($module, $store, []); 19 | my $exports = Wasm::Wasmtime::Instance::Exports->new($instance); 20 | is( 21 | $exports, 22 | object { 23 | call [ isa => 'Wasm::Wasmtime::Instance::Exports' ] => T(); 24 | call add => object { 25 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 26 | call [ call => 1,2 ] => 3; 27 | }; 28 | 29 | # test %{} overload 30 | call sub { \%{ shift() } } => hash { 31 | field add => object { 32 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 33 | call [ call => 3,4 ] => 7; 34 | }; 35 | end; 36 | }; 37 | 38 | # test that you can't insert a new key 39 | call sub { my $exports = shift; dies { $exports->{foo} = 1 } } => D(); 40 | 41 | # test that you can't replace an existing key 42 | call sub { my $exports = shift; dies { $exports->{add} = 1 } } => D(); 43 | 44 | # hopefully we can still modify the values themselves? 45 | call sub { my $exports = shift; dies { $exports->{add}->{rando1} = 1 } } => U(); 46 | 47 | call sub { \@{ shift() } } => array { 48 | item object { 49 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 50 | call [ call => 10,15 ] => 25; 51 | call type => object { 52 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 53 | }; 54 | }; 55 | end; 56 | }; 57 | 58 | call sub { my $exports = shift; dies { $exports->[0] = 1 } } => D(); 59 | call sub { my $exports = shift; dies { $exports->[1] = 1 } } => D(); 60 | call sub { my $exports = shift; dies { $exports->[0]->{rando2} = 1 } } => U(); 61 | }, 62 | 'exports object looks good' 63 | ); 64 | note Dump($exports); 65 | } 66 | 67 | done_testing; 68 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_linker.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use lib 't/lib'; 5 | use Test2::Tools::Wasm; 6 | use Wasm::Wasmtime::Linker; 7 | use Wasm::Wasmtime::WasiInstance; 8 | 9 | my $instance = wasm_instance_ok( [], q{ 10 | (module 11 | (func (export "add") (param i32 i32) (result i32) 12 | local.get 0 13 | local.get 1 14 | i32.add) 15 | (func (export "sub") (param i64 i64) (result i64) 16 | local.get 0 17 | local.get 1 18 | i64.sub) 19 | (memory (export "memory") 2 3) 20 | ) 21 | }); 22 | 23 | my $module = $instance->module; 24 | my $store = wasm_store(); 25 | my $wasi = Wasm::Wasmtime::WasiInstance->new( 26 | $store, "wasi_snapshot_preview1", 27 | ); 28 | 29 | my $instance2 = Wasm::Wasmtime::Instance->new( 30 | Wasm::Wasmtime::Module->new($store->engine, wat => '(module)' ), 31 | $store, 32 | ); 33 | 34 | my $module2 = Wasm::Wasmtime::Module->new($store->engine, wat => '(module)' ); 35 | 36 | is( 37 | Wasm::Wasmtime::Linker->new( 38 | $store, 39 | ), 40 | object { 41 | call [ isa => 'Wasm::Wasmtime::Linker' ] => T(); 42 | call [ allow_shadowing => 1 ] => D(); 43 | call [ allow_shadowing => 0 ] => D(); 44 | call [ define => 'xx', 'add0', $instance->exports->add ] => D(); 45 | call [ define_wasi => $wasi ] => T(); 46 | call [ define_instance => "foo", $instance2 ] => T(); 47 | call [ instantiate => $module2 ] => object { 48 | call [ isa => 'Wasm::Wasmtime::Instance' ] => T(); 49 | }; 50 | call store => object { 51 | call [ isa => 'Wasm::Wasmtime::Store' ] => T(); 52 | }; 53 | 54 | call [ get_default => 'foo' ] => object { 55 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 56 | call call => undef; 57 | }; 58 | 59 | call sub { 60 | my $linker = shift; 61 | my $instance3 = $linker->instantiate($module, $store); 62 | $linker->define_instance("yy", $instance3); 63 | 1; 64 | } => T(); 65 | 66 | call [ get_one_by_name => 'yy', 'add' ] => object { 67 | call [ isa => 'Wasm::Wasmtime::Func' ] => T(); 68 | call [ call => 1, 2 ] => 3; 69 | }; 70 | 71 | call sub { 72 | my $linker = shift; 73 | dies { $linker->get_one_by_name('zz','add') }; 74 | } => match qr/./; 75 | }, 76 | 'basics' 77 | ); 78 | 79 | done_testing; 80 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_memory.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Test2::Plugin::Wasm; 4 | use lib 't/lib'; 5 | use Test2::Tools::Wasm; 6 | use Wasm::Wasmtime::Memory; 7 | use Wasm::Wasmtime::Store; 8 | use Wasm::Wasmtime::MemoryType; 9 | 10 | is( 11 | wasm_instance_ok([], q{ 12 | (module 13 | (memory (export "frooble") 2 6) 14 | ) 15 | }), 16 | object { 17 | call exports => object { 18 | call frooble => object { 19 | call [ isa => 'Wasm::Wasmtime::Memory' ] => T(); 20 | call type => object { 21 | call [ isa => 'Wasm::Wasmtime::MemoryType' ] => T(); 22 | }; 23 | call data => match qr/^[0-9]+$/; 24 | call data_size => match qr/^[0-9]+$/; 25 | call size => 2; 26 | call [ grow => 3] => T(); 27 | call size => 5; 28 | call is_func => F(); 29 | call is_global => F(); 30 | call is_table => F(); 31 | call is_memory => T(); 32 | call kind => 'memory'; 33 | }; 34 | }; 35 | }, 36 | 'memory class basics', 37 | ); 38 | 39 | is( 40 | Wasm::Wasmtime::Memory->new( 41 | Wasm::Wasmtime::Store->new, 42 | Wasm::Wasmtime::MemoryType->new([1,2]), 43 | ), 44 | object { 45 | call [ isa => 'Wasm::Wasmtime::Memory' ] => T(); 46 | }, 47 | 'standalone', 48 | ); 49 | 50 | is( 51 | Wasm::Wasmtime::Memory->new( 52 | Wasm::Wasmtime::Store->new, 53 | [1,2], 54 | ), 55 | object { 56 | call [ isa => 'Wasm::Wasmtime::Memory' ] => T(); 57 | }, 58 | 'standalone (ii)', 59 | ); 60 | 61 | done_testing; 62 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_memorytype.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use lib 't/lib'; 4 | use Test2::Tools::Wasm; 5 | use Wasm::Wasmtime::MemoryType; 6 | 7 | is( 8 | wasm_module_ok(q{ 9 | (module 10 | (memory (export "frooble") 2 6) 11 | ) 12 | }), 13 | object { 14 | call exports => object { 15 | call frooble => object { 16 | call [ isa => 'Wasm::Wasmtime::MemoryType' ] => T(); 17 | call limits => [2,6]; 18 | call is_functype => F(); 19 | call is_globaltype => F(); 20 | call is_tabletype => F(); 21 | call is_memorytype => T(); 22 | call kind => 'memorytype'; 23 | call to_string => '2 6'; 24 | }; 25 | }; 26 | }, 27 | 'memorytype class basics', 28 | ); 29 | 30 | is( 31 | Wasm::Wasmtime::MemoryType->new([2,3]), 32 | object { 33 | call [ isa => 'Wasm::Wasmtime::MemoryType' ] => T(); 34 | call limits => [2,3]; 35 | call to_string => '2 3'; 36 | }, 37 | 'standalone', 38 | ); 39 | 40 | done_testing; 41 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_module_exports.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Module; 4 | use Wasm::Wasmtime::Module::Exports; 5 | use YAML qw( Dump ); 6 | 7 | { 8 | my $module = Wasm::Wasmtime::Module->new(wat => q{ 9 | (module 10 | (func (export "add") (param i32 i32) (result i32) 11 | local.get 0 12 | local.get 1 13 | i32.add) 14 | ) 15 | }); 16 | my $exports = Wasm::Wasmtime::Module::Exports->new($module); 17 | is( 18 | $exports, 19 | object { 20 | call [ isa => 'Wasm::Wasmtime::Module::Exports' ] => T(); 21 | call add => object { 22 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 23 | }; 24 | 25 | # test %{} overload 26 | call sub { \%{ shift() } } => hash { 27 | field add => object { 28 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 29 | }; 30 | end; 31 | }; 32 | 33 | # test that you can't insert a new key 34 | call sub { my $exports = shift; dies { $exports->{foo} = 1 } } => D(); 35 | 36 | # test that you can't replace an existing key 37 | call sub { my $exports = shift; dies { $exports->{add} = 1 } } => D(); 38 | 39 | # hopefully we can still modify the values themselves? 40 | call sub { my $exports = shift; lives { $exports->{add}->{rando1} = 1 } } => T(); 41 | 42 | call sub { \@{ shift() } } => array { 43 | item object { 44 | call [ isa => 'Wasm::Wasmtime::ExportType' ] => T(); 45 | call name => 'add'; 46 | call type => object { 47 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 48 | }; 49 | }; 50 | end; 51 | }; 52 | 53 | call sub { my $exports = shift; dies { $exports->[0] = 1 } } => D(); 54 | call sub { my $exports = shift; dies { $exports->[1] = 1 } } => D(); 55 | call sub { my $exports = shift; lives { $exports->[0]->{rando2} = 1 } } => T(); 56 | }, 57 | 'exports object looks good' 58 | ); 59 | note Dump($exports); 60 | } 61 | 62 | done_testing; 63 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_module_imports.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Module; 4 | use Wasm::Wasmtime::Module::Imports; 5 | use YAML qw( Dump ); 6 | 7 | { 8 | my $module = Wasm::Wasmtime::Module->new(wat => q{ 9 | (module 10 | (func $hello (import "" "add")) 11 | ) 12 | }); 13 | my $imports = Wasm::Wasmtime::Module::Imports->new($module); 14 | is( 15 | $imports, 16 | object { 17 | call [ isa => 'Wasm::Wasmtime::Module::Imports' ] => T(); 18 | 19 | call add => object { 20 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 21 | }; 22 | 23 | # test %{} overload 24 | call sub { \%{ shift() } } => hash { 25 | field add => object { 26 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 27 | }; 28 | end; 29 | }; 30 | 31 | # test that you can't insert a new key 32 | call sub { my $exports = shift; dies { $exports->{foo} = 1 } } => D(); 33 | 34 | # test that you can't replace an existing key 35 | call sub { my $exports = shift; dies { $exports->{add} = 1 } } => D(); 36 | 37 | # hopefully we can still modify the values themselves? 38 | call sub { my $exports = shift; lives { $exports->{add}->{rando1} = 1 } } => T(); 39 | 40 | call sub { \@{ shift() } } => array { 41 | item object { 42 | call [ isa => 'Wasm::Wasmtime::ImportType' ] => T(); 43 | call name => 'add'; 44 | call type => object { 45 | call [ isa => 'Wasm::Wasmtime::FuncType' ] => T(); 46 | }; 47 | }; 48 | end; 49 | }; 50 | 51 | call sub { my $exports = shift; dies { $exports->[0] = 1 } } => D(); 52 | call sub { my $exports = shift; dies { $exports->[1] = 1 } } => D(); 53 | call sub { my $exports = shift; lives { $exports->[0]->{rando2} = 1 } } => T(); 54 | 55 | }, 56 | 'imports object looks good' 57 | ); 58 | note Dump($imports); 59 | } 60 | 61 | done_testing; 62 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_store.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Engine; 4 | use Wasm::Wasmtime::Store; 5 | 6 | is( 7 | Wasm::Wasmtime::Store->new, 8 | object { 9 | call ['isa','Wasm::Wasmtime::Store'] => T(); 10 | call engine => object { 11 | call ['isa','Wasm::Wasmtime::Engine'] => T(); 12 | }; 13 | call_list 'gc' => []; 14 | }, 15 | 'default engine', 16 | ); 17 | 18 | is( 19 | Wasm::Wasmtime::Store->new(Wasm::Wasmtime::Engine->new), 20 | object { 21 | call ['isa','Wasm::Wasmtime::Store'] => T(); 22 | call engine => object { 23 | call ['isa','Wasm::Wasmtime::Engine'] => T(); 24 | }; 25 | }, 26 | 'explicit engine', 27 | ); 28 | 29 | done_testing; 30 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_table.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use lib 't/lib'; 4 | use Test2::Tools::Wasm; 5 | use Wasm::Wasmtime::Table; 6 | 7 | is( 8 | wasm_instance_ok([], q{ 9 | (module (table (export "frooble") 1 funcref)) 10 | }), 11 | object { 12 | call exports => object { 13 | call frooble => object { 14 | call [ isa => 'Wasm::Wasmtime::Table' ] => T(); 15 | call type => object { 16 | call [ isa => 'Wasm::Wasmtime::TableType' ] => T(); 17 | }; 18 | call size => 1; 19 | call is_func => F(); 20 | call is_global => F(); 21 | call is_table => T(); 22 | call is_memory => F(); 23 | call kind => 'table'; 24 | }; 25 | }; 26 | }, 27 | 'table good' 28 | ); 29 | 30 | done_testing; 31 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_tabletype.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::TableType; 4 | 5 | is( 6 | Wasm::Wasmtime::TableType->new('i32',[3,4]), 7 | object { 8 | call [ isa => 'Wasm::Wasmtime::TableType' ] => T(); 9 | call element => object { 10 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 11 | call kind => 'i32'; 12 | }; 13 | call limits => [3,4]; 14 | call is_functype => F(); 15 | call is_globaltype => F(); 16 | call is_tabletype => T(); 17 | call is_memorytype => F(); 18 | call kind => 'tabletype'; 19 | 20 | call to_string => '3 4 i32'; 21 | }, 22 | 'i32,const', 23 | ); 24 | 25 | is( 26 | Wasm::Wasmtime::TableType->new('i64',[9,undef]), 27 | object { 28 | call [ isa => 'Wasm::Wasmtime::TableType' ] => T(); 29 | call element => object { 30 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 31 | call kind => 'i64'; 32 | }; 33 | call limits => [9,0xffffffff]; 34 | call to_string => '9 i64'; 35 | }, 36 | 'i64,var', 37 | ); 38 | 39 | is( 40 | Wasm::Wasmtime::TableType->new(Wasm::Wasmtime::ValType->new('f32'),[1,6]), 41 | object { 42 | call [ isa => 'Wasm::Wasmtime::TableType' ] => T(); 43 | call element => object { 44 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 45 | call kind => 'f32'; 46 | }; 47 | call limits => [1,6]; 48 | call to_string => '1 6 f32'; 49 | }, 50 | '(i64),var', 51 | ); 52 | 53 | done_testing; 54 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_trap.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Store; 4 | use Wasm::Wasmtime::Trap; 5 | 6 | is( 7 | Wasm::Wasmtime::Trap->new(Wasm::Wasmtime::Store->new, "foo\0"), 8 | object { 9 | call [isa => 'Wasm::Wasmtime::Trap'] => T(); 10 | call message => 'foo'; 11 | call sub { my $trap = shift; "$trap" } => "foo\n"; 12 | call exit_status => undef; 13 | }, 14 | 'created trap ok', 15 | ); 16 | 17 | { 18 | require Wasm::Wasmtime; 19 | my $store = Wasm::Wasmtime::Store->new; 20 | my $linker = Wasm::Wasmtime::Linker->new($store); 21 | 22 | my $wasi = Wasm::Wasmtime::WasiInstance->new( 23 | $store, 24 | "wasi_snapshot_preview1", 25 | Wasm::Wasmtime::WasiConfig->new, 26 | ); 27 | $linker->define_wasi($wasi); 28 | my $module = Wasm::Wasmtime::Module->new($store->engine, wat => q{ 29 | (module 30 | (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32))) 31 | (memory 10) 32 | (export "memory" (memory 0)) 33 | (func $main 34 | (call $proc_exit (i32.const 7)) 35 | ) 36 | (start $main) 37 | ) 38 | }); 39 | is( 40 | dies { $linker->instantiate($module) }, 41 | object { 42 | call [ isa => 'Wasm::Wasmtime::Trap' ] => T(); 43 | call exit_status => 7; 44 | }, 45 | ); 46 | } 47 | 48 | done_testing; 49 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_valtype.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::ValType; 4 | 5 | foreach my $name (qw( i32 i64 f32 f64 anyref funcref )) 6 | { 7 | my $vt = Wasm::Wasmtime::ValType->new($name); 8 | pass 'created $name'; 9 | is( 10 | $vt, 11 | object { 12 | call [ isa => 'Wasm::Wasmtime::ValType' ] => T(); 13 | call kind => $name; 14 | call to_string => $name; 15 | call kind_num => match qr/^[0-9]+$/; 16 | }, 17 | "use $name", 18 | ); 19 | undef $vt; 20 | pass "deleted $name"; 21 | } 22 | 23 | done_testing; 24 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_wasiconfig.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::WasiConfig; 4 | use File::Temp qw( tempdir ); 5 | use Path::Tiny qw( path ); 6 | 7 | is( 8 | Wasm::Wasmtime::WasiConfig->new, 9 | object { 10 | call [ isa => 'Wasm::Wasmtime::WasiConfig' ] => T(); 11 | }, 12 | 'default' 13 | ); 14 | 15 | foreach my $method (map { "inherit_$_" } qw( argv env stdin stdout stderr )) 16 | { 17 | try_ok { 18 | Wasm::Wasmtime::WasiConfig 19 | ->new 20 | ->$method 21 | } "$method"; 22 | } 23 | 24 | try_ok { 25 | Wasm::Wasmtime::WasiConfig 26 | ->new 27 | ->set_argv("hello","world") 28 | } "set_argv"; 29 | 30 | try_ok { 31 | Wasm::Wasmtime::WasiConfig 32 | ->new 33 | ->set_env(foo => "bar", baz => 2) 34 | } "set_env"; 35 | 36 | try_ok { 37 | Wasm::Wasmtime::WasiConfig 38 | ->new 39 | ->set_stdin_file(__FILE__) 40 | } "set_stdin_file"; 41 | 42 | try_ok { 43 | my $file = path(tempdir(CLEANUP => 1))->child('foo.txt'); 44 | Wasm::Wasmtime::WasiConfig 45 | ->new 46 | ->set_stdout_file("$file") 47 | } "set_stdout_file"; 48 | 49 | try_ok { 50 | my $file = path(tempdir(CLEANUP => 1))->child('foo.txt'); 51 | Wasm::Wasmtime::WasiConfig 52 | ->new 53 | ->set_stderr_file("$file") 54 | } "set_stderr_file"; 55 | 56 | try_ok { 57 | my $dir = path(tempdir(CLEANUP => 1)); 58 | Wasm::Wasmtime::WasiConfig 59 | ->new 60 | ->preopen_dir("$dir", "/foo/bar") 61 | } "preopen_dir"; 62 | 63 | done_testing; 64 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_wasiinstance.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Store; 4 | use Wasm::Wasmtime::WasiConfig; 5 | use Wasm::Wasmtime::WasiInstance; 6 | 7 | is( 8 | Wasm::Wasmtime::WasiInstance->new(Wasm::Wasmtime::Store->new, "wasi_snapshot_preview1"), 9 | object { 10 | call [ isa => 'Wasm::Wasmtime::WasiInstance' ] => T(); 11 | }, 12 | 'explicit: store', 13 | ); 14 | 15 | is( 16 | Wasm::Wasmtime::WasiInstance->new(Wasm::Wasmtime::Store->new, "wasi_snapshot_preview1", Wasm::Wasmtime::WasiConfig->new), 17 | object { 18 | call [ isa => 'Wasm::Wasmtime::WasiInstance' ] => T(); 19 | }, 20 | 'explicit: store + config', 21 | ); 22 | 23 | done_testing; 24 | -------------------------------------------------------------------------------- /t/wasm_wasmtime_wat2wasm.t: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use Test2::V0 -no_srand => 1; 3 | use Wasm::Wasmtime::Wat2Wasm; 4 | 5 | imported_ok 'wat2wasm'; 6 | 7 | is( 8 | wat2wasm('(module)'), 9 | D(), 10 | 'okay with good module', 11 | ); 12 | 13 | is( 14 | dies { wat2wasm('f00f') }, 15 | match qr/wat2wasm error/, 16 | 'dies with bad input', 17 | ); 18 | 19 | done_testing; 20 | -------------------------------------------------------------------------------- /xt/author/critic.t: -------------------------------------------------------------------------------- 1 | use Test2::Require::Module 'Test2::Tools::PerlCritic'; 2 | use Test2::Require::Module 'Perl::Critic'; 3 | use Test2::Require::Module 'Perl::Critic::Community'; 4 | use Test2::V0; 5 | use Perl::Critic; 6 | use Test2::Tools::PerlCritic; 7 | 8 | my $critic = Perl::Critic->new( 9 | -profile => 'perlcriticrc', 10 | ); 11 | 12 | perl_critic_ok ['examples','lib','t'], $critic; 13 | 14 | done_testing; 15 | 16 | 17 | -------------------------------------------------------------------------------- /xt/author/cycle.t: -------------------------------------------------------------------------------- 1 | use Test2::V0 -no_srand => 1; 2 | use Test2::Require::Module 'Test::Memory::Cycle'; 3 | use Test::Memory::Cycle; 4 | use Wasm::Wasmtime; 5 | use YAML qw( Dump ); 6 | 7 | skip_all 'not tested with ciperl:static' if defined $ENV{CIPSTATIC} && $ENV{CIPSTATIC} eq 'true'; 8 | 9 | subtest 'module' => sub { 10 | 11 | my $module = Wasm::Wasmtime::Module->new( 12 | wat => q{ 13 | (module 14 | (func (export "add") (param i32 i32) (result i32) 15 | local.get 0 16 | local.get 1 17 | i32.add) 18 | ) 19 | }, 20 | ); 21 | 22 | $module->imports; 23 | $module->exports; 24 | 25 | memory_cycle_ok $module; 26 | 27 | note Dump($module); 28 | }; 29 | 30 | 31 | subtest 'instance' => sub { 32 | 33 | my $store = Wasm::Wasmtime::Store->new; 34 | my $instance = Wasm::Wasmtime::Instance->new( 35 | Wasm::Wasmtime::Module->new( 36 | $store->engine, 37 | wat => q{ 38 | (module 39 | (func (export "add") (param i32 i32) (result i32) 40 | local.get 0 41 | local.get 1 42 | i32.add) 43 | ) 44 | }, 45 | ), 46 | $store, 47 | [], 48 | ); 49 | 50 | $instance->exports; 51 | 52 | memory_cycle_ok $instance; 53 | 54 | note Dump($instance); 55 | }; 56 | 57 | done_testing; 58 | -------------------------------------------------------------------------------- /xt/author/examples.t: -------------------------------------------------------------------------------- 1 | use Test2::V0 -no_srand => 1; 2 | use Test2::Require::Module 'Test::Script'; 3 | use Test::Script; 4 | use File::Glob qw( bsd_glob ); 5 | 6 | skip_all 'not tested with ciperl:static' if defined $ENV{CIPSTATIC} && $ENV{CIPSTATIC} eq 'true'; 7 | 8 | foreach my $example (map { bsd_glob "examples/$_/*.pl" } qw( synopsis wasmtime wasm )) 9 | { 10 | my $out = ''; 11 | my $err = ''; 12 | script_compiles $example; 13 | script_runs $example, { stdout => \$out, stderr => \$err }; 14 | note "[out]\n$out" if $out ne ''; 15 | note "[err]\n$err" if $err ne ''; 16 | unlike $err, qr/deprecated/, "no deprecation warnings ($example)"; 17 | } 18 | 19 | done_testing; 20 | --------------------------------------------------------------------------------