orca runtime initial commit

This commit is contained in:
Martin Fouilleul 2023-04-12 16:21:03 +02:00
commit 17c0f02962
318 changed files with 50440 additions and 0 deletions

20
.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
.DS_Store
*.dSYM
bin/*
*.metallib
*.pdb
*.exe
*.ilk
*.vs
*.obj
*.lib
*.dll
*.sln
*.wasm
*.app
*.dylib
Debug/*
src/bindgen_core_api.c
src/bindgen_gles_api.c

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "milepost"]
path = milepost
url = ssh://git@git.forkingpaths.dev/martinfouilleul/milepost.git

64
build.sh Executable file
View File

@ -0,0 +1,64 @@
#!/bin/bash
target="$1"
if [ -z $target ] ; then
target='orca'
fi
target=$(echo $target | tr '[:upper:]' '[:lower:]')
if [ ! \( -e bin \) ] ; then
mkdir ./bin
fi
if [ ! \( -e resources \) ] ; then
mkdir ./resources
fi
if [ $target = milepost ] ; then
echo "building milepost"
pushd milepost > /dev/null
./build.sh lib "$2"
popd > /dev/null
elif [ $target = wasm3 ] ; then
echo "building wasm3"
mkdir ./bin/obj
for file in ./ext/wasm3/source/*.c ; do
name=$(basename $file)
name=${name/.c/.o}
clang -c -g -Wno-extern-initializer -Dd_m3VerboseErrorMessages -o ./bin/obj/$name -I./ext/wasm3/source $file
done
ar -rcs ./bin/libwasm3.a ./bin/obj/*.o
rm -rf ./bin/obj
elif [ $target = orca ] ; then
echo "building orca"
# copies libraries
cp milepost/bin/mtl_renderer.metallib bin/
cp milepost/bin/libmilepost.dylib bin/
cp milepost/bin/libGLESv2.dylib bin/
cp milepost/bin/libEGL.dylib bin/
INCLUDES="-Imilepost/src -Imilepost/src/util -Imilepost/src/platform -Iext/wasm3/source -Imilepost/ext/"
LIBS="-Lbin -lmilepost -lwasm3"
FLAGS="-g -DLOG_COMPILE_DEBUG -mmacos-version-min=10.15.4 -maes"
# generate wasm3 api bindings
./scripts/bindgen.py core ./src
./scripts/bindgen.py gles ./src
# compile orca
clang $FLAGS $INCLUDES $LIBS -o bin/orca src/main.c
# fix libs imports
install_name_tool -change "./bin/libmilepost.dylib" "@rpath/libmilepost.dylib" bin/orca
install_name_tool -add_rpath "@executable_path/" bin/orca
else
echo "unknown build target $target"
fi

5
ext/wasm3/.codespellrc Normal file
View File

@ -0,0 +1,5 @@
[codespell]
skip = ./test/wasi/brotli/alice29.txt,./test/.spec-*,./build*
quiet-level = 2
ignore-words-list = gameboy,iif,strng,woh

180
ext/wasm3/.github/workflows/publish.yml vendored Normal file
View File

@ -0,0 +1,180 @@
name: publish
on:
push:
tags:
- "v*.*.*"
env:
draft: true
jobs:
wasm3-windows:
runs-on: windows-latest
name: ${{ matrix.config.target }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
config:
- {target: wasm3-win-x64, platform: "-A x64", toolset: "-T ClangCL" }
- {target: wasm3-win-x86, platform: "-A Win32", toolset: "-T ClangCL" }
- {target: wasm3-strace-win-x64, platform: "-A x64", toolset: "-T ClangCL", cflags: "-DDEBUG -Dd_m3EnableStrace=2 -Dd_m3RecordBacktraces=1" }
env:
LDFLAGS: -s
steps:
- uses: actions/checkout@v2
- name: Configure
env:
CFLAGS: ${{ matrix.config.cflags }}
run: |
mkdir build
cd build
cmake ${{ matrix.config.platform }} ${{ matrix.config.toolset }} ..
- name: Build
run: |
cmake --build build --config Release
cp ./build/Release/wasm3.exe ./${{ matrix.config.target }}.exe
- name: Publish
uses: softprops/action-gh-release@v1
with:
draft: ${{ env.draft }}
files: "*.exe"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
wasm3-linux-x64:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Setup musl
run: |
sudo apt update
sudo apt install musl-tools
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.9
with:
cmake-version: '3.16.x'
- name: Configure
env:
CC: musl-gcc
LDFLAGS: "-static -s"
run: |
mkdir build
cd build
cmake --version
cmake ..
- name: Build
run: |
cmake --build build --verbose
cp ./build/wasm3 ./wasm3-linux-x64.elf
- name: Publish
uses: softprops/action-gh-release@v1
with:
draft: ${{ env.draft }}
files: "*.elf"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
wasm3-cosmopolitan:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Build αcτµαlly pδrταblε εxεcµταblε
run: |
cd platforms/cosmopolitan
./build.sh
cp ./wasm3.com ../../wasm3-cosmopolitan.com
cp ./wasm3.com.dbg ../../wasm3-cosmopolitan.com.dbg
- name: Publish
uses: softprops/action-gh-release@v1
with:
draft: ${{ env.draft }}
files: |
*.com
*.com.dbg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
wasm3-wasi:
runs-on: ubuntu-latest
timeout-minutes: 10
env:
LDFLAGS: -s
steps:
- uses: actions/checkout@v2
- name: Install Python dependencies
run: |
python3 -m pip install pip==20.1.1
python3 -m pip install --upgrade setuptools wheel
pip3 --version
- name: Install Wasienv
env:
WASMER_RELEASE_TAG: "1.0.2"
run: curl https://raw.githubusercontent.com/wasienv/wasienv/master/install.sh | sh
- name: Configure
run: |
source $HOME/.wasienv/wasienv.sh
mkdir build
cd build
wasimake cmake ..
- name: Build
run: |
source $HOME/.wasienv/wasienv.sh
cmake --build build
cp ./build/wasm3.wasm ./wasm3-wasi.wasm
- name: Configure, Build wasm3-strace
env:
CFLAGS: -DDEBUG -Dd_m3EnableStrace=2 -Dd_m3RecordBacktraces=1
run: |
source $HOME/.wasienv/wasienv.sh
mkdir build-strace
cd build-strace
wasimake cmake ..
cmake --build .
cp ./wasm3.wasm ../wasm3-strace.wasm
- name: Publish
uses: softprops/action-gh-release@v1
with:
draft: ${{ env.draft }}
files: "*.wasm"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
wasm3-android-coremark:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- uses: seanmiddleditch/gha-setup-ninja@master
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Install NDK
run: |
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.1.7171670"
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --uninstall "cmake;3.18.1"
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "cmake;3.10.2.4988404"
- name: Build
run: |
cd platforms/android
./gradlew build
- name: Copy
run: |
cp ./platforms/android/app/build/outputs/apk/debug/app-debug.apk ./wasm3-android-coremark.apk
- name: Publish
uses: softprops/action-gh-release@v1
with:
draft: ${{ env.draft }}
files: "*.apk"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

619
ext/wasm3/.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,619 @@
name: tests
on:
push:
paths-ignore: ['**.md', '**.svg', '**.png']
pull_request:
paths-ignore: ['**.md', '**.svg', '**.png']
jobs:
linux:
runs-on: ubuntu-latest
name: linux-${{ matrix.config.target }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
config:
- {target: clang, cc: clang, }
- {target: clang-x86, cc: clang, flags: -DCMAKE_C_FLAGS="-m32", install: "gcc-multilib" }
- {target: gcc, cc: gcc, }
# Builds without uvwasi
- {target: gcc-no-uvwasi, cc: gcc, flags: -DBUILD_WASI=simple }
- {target: clang-no-uvwasi, cc: clang, flags: -DBUILD_WASI=simple }
# Debug builds
- {target: gcc-debug, cc: gcc, flags: -DCMAKE_BUILD_TYPE=Debug }
- {target: clang-no-uvwasi-debug, cc: clang, flags: -DCMAKE_BUILD_TYPE=Debug -DBUILD_WASI=simple }
# TODO: fails on numeric operations
#- {target: gcc-x86, cc: gcc, flags: "-m32", install: "gcc-multilib" }
steps:
- uses: actions/checkout@v2
- name: Install ${{ matrix.config.install }}
if: ${{ matrix.config.install }}
run: |
sudo apt update
sudo apt install ${{ matrix.config.install }}
- name: Configure
env:
CC: ${{ matrix.config.cc }}
CFLAGS: ${{ matrix.config.cflags }}
run: |
mkdir build
cd build
cmake ${{ matrix.config.flags }} ..
- name: Build
run: |
cmake --build build
- name: Test WebAssembly spec
run: cd test && python3 run-spec-test.py
- name: Test previous WebAssembly specs
run: |
cd test
python3 run-spec-test.py --spec=v1.1
- name: Test WASI apps
run: cd test && python3 run-wasi-test.py
linux-alpine:
runs-on: ubuntu-latest
container: alpine:3.10
steps:
- uses: actions/checkout@v2
- name: Prepare
run: apk add build-base cmake python3 git --update-cache
- name: Configure
run: |
mkdir build
cd build
cmake ..
- name: Build
run: cmake --build build
- name: Test WebAssembly spec
run: cd test && python3 run-spec-test.py
- name: Test WASI apps
run: cd test && python3 run-wasi-test.py
macos:
runs-on: macos-latest
name: macos-${{ matrix.config.target }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
config:
- {target: uvwasi, }
- {target: no-uvwasi, flags: -DBUILD_WASI=simple }
steps:
- uses: actions/checkout@v2
- name: Configure
run: |
mkdir build
cd build
cmake ${{ matrix.config.flags }} ..
- name: Build
run: |
cmake --build build
- name: Test WebAssembly spec
run: cd test && python3 run-spec-test.py
- name: Test previous WebAssembly specs
run: |
cd test
python3 run-spec-test.py --spec=v1.1
- name: Test WASI apps
run: cd test && python3 run-wasi-test.py
windows:
runs-on: windows-latest
name: windows-${{ matrix.config.target }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
config:
- {target: clang-x64, platform: "-A x64", toolset: "-T ClangCL" }
- {target: msvc-x64, platform: "-A x64", toolset: "" }
- {target: clang-x86, platform: "-A Win32", toolset: "-T ClangCL" }
- {target: msvc-x86, platform: "-A Win32", toolset: "" }
# Builds without uvwasi
- {target: clang-x64-no-uvwasi, platform: "-A x64", toolset: "-T ClangCL", flags: "-DBUILD_WASI=simple" }
- {target: msvc-x64-no-uvwasi, platform: "-A x64", toolset: "", flags: "-DBUILD_WASI=simple" }
- {target: clang-x86-no-uvwasi, platform: "-A Win32", toolset: "-T ClangCL", flags: "-DBUILD_WASI=simple" }
- {target: msvc-x86-no-uvwasi, platform: "-A Win32", toolset: "", flags: "-DBUILD_WASI=simple" }
defaults:
run:
shell: cmd
steps:
- uses: actions/checkout@v2
- name: Configure
run: |
mkdir build
cd build
cmake ${{ matrix.config.platform }} ${{ matrix.config.toolset }} ${{ matrix.config.flags }} ..
- name: Build
run: |
cmake --build build --config Release
cp ./build/Release/wasm3.exe ./build/
- name: Test WebAssembly spec
run: |
cd test
python run-spec-test.py
- name: Test previous WebAssembly specs
run: |
cd test
python run-spec-test.py --spec=v1.1
- name: Test WASI apps
run: |
cd test
python run-wasi-test.py
cygwin-build:
runs-on: windows-latest
steps:
- uses: actions/checkout@main
- name: Set up Cygwin
uses: egor-tensin/setup-cygwin@master
with:
platform: x64
packages: make gcc-g++ cmake
- run: cd $GITHUB_WORKSPACE && cmake -DBUILD_WASI=simple . && make
shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}'
wasi:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Install Python dependencies
run: |
python3 -m pip install pip==20.1.1
python3 -m pip install --upgrade setuptools wheel
pip3 --version
- name: Install Wasienv
env:
WASMER_RELEASE_TAG: "1.0.2"
run: curl https://raw.githubusercontent.com/wasienv/wasienv/master/install.sh | sh
- name: Configure
run: |
source $HOME/.wasienv/wasienv.sh
mkdir build-wasi
cd build-wasi
wasimake cmake ..
- name: Build
run: |
source $HOME/.wasienv/wasienv.sh
cmake --build build-wasi
- name: Test WebAssembly spec (in Wasmer)
run: |
source $HOME/.wasmer/wasmer.sh
cd test
python3 run-spec-test.py --exec "wasmer run --mapdir=/:. ../build-wasi/wasm3.wasm -- --repl"
- name: Test WASI apps (in Wasmer)
run: |
source $HOME/.wasmer/wasmer.sh
cd test
python3 run-wasi-test.py --exec "wasmer run --mapdir=/:. ../build-wasi/wasm3.wasm --" --fast
- name: Configure (native)
run: |
mkdir build
cd build
cmake ..
- name: Build (native)
run: |
cmake --build build
- name: Test WebAssembly spec (in Wasm3, self-hosting)
run: |
cd test
cp ../build-wasi/wasm3.wasm ./
python3 run-spec-test.py --exec "../build/wasm3 --stack-size 2097152 ../build-wasi/wasm3.wasm --repl"
- name: Test WASI apps (in Wasm3, self-hosting)
run: |
cd test
python3 run-wasi-test.py --fast --exec "../build/wasm3 --stack-size 2097152 ../build-wasi/wasm3.wasm"
ios:
runs-on: macos-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: List Xcode versions
run: ls /Applications | grep Xcode
- name: Select Xcode 12
run: sudo xcode-select -switch /Applications/Xcode_12.4.app
- name: Build (iPhone 11)
run: |
cd platforms/ios
xcodebuild build -scheme wasm3 -project wasm3.xcodeproj -configuration Release -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.4'
android:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- uses: seanmiddleditch/gha-setup-ninja@master
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Install NDK
run: |
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.1.7171670"
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --uninstall "cmake;3.18.1"
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "cmake;3.10.2.4988404"
- name: Build
run: |
cd platforms/android
./gradlew build
python:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v2
with:
path: wasm3
- name: Checkout pywasm3
uses: actions/checkout@v2
with:
repository: wasm3/pywasm3
path: pywasm3
- name: Set up Python
uses: actions/setup-python@v2
- name: Update and Build Python module
run: |
rm -rf ./pywasm3/wasm3
cp -r wasm3/source ./pywasm3/wasm3
pip install ./pywasm3
- name: Install WABT
run: |
sudo apt update
sudo apt install wabt
- name: Test
run: |
pip install pytest
cd ./pywasm3
pytest
cosmopolitan:
runs-on: ubuntu-20.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Build αcτµαlly pδrταblε εxεcµταblε
run: |
cd platforms/cosmopolitan
gcc -v
./build.sh
- name: Prepare tests
run: |
cd test
cp ../platforms/cosmopolitan/wasm3.com ./wasm3-lin.com
cp ../platforms/cosmopolitan/wasm3.com ./wasm3-win.com
sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register"
- name: Test WebAssembly spec
run: |
cd test
python3 run-spec-test.py --exec "./wasm3-lin.com --repl"
- name: Test WASI apps
run: |
cd test
python3 run-wasi-test.py --fast --exec "./wasm3-lin.com"
- name: Install Wine64
run: |
sudo apt update
sudo apt install wine64
wine --version
- name: Test WebAssembly spec (in Wine)
run: |
cd test
python3 run-spec-test.py --exec "wine ./wasm3-win.com --repl"
- name: Test WASI apps (in Wine)
run: |
cd test
python3 run-wasi-test.py --fast --exec "wine ./wasm3-win.com"
cross-qemu:
runs-on: ubuntu-20.04
name: cross-qemu-${{ matrix.config.target }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
config:
#- {target: i386, toolchain: gcc-multilib, cc: clang -m32, qemu: qemu-i386-static }
- {target: arm, toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static }
- {target: armhf, toolchain: gcc-arm-linux-gnueabihf, cc: arm-linux-gnueabihf-gcc, qemu: qemu-arm-static }
- {target: aarch64, toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static }
- {target: riscv64, toolchain: gcc-riscv64-linux-gnu, cc: riscv64-linux-gnu-gcc, qemu: qemu-riscv64-static }
- {target: ppc, toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static }
- {target: ppc64, toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static }
#- {target: ppc64le, toolchain: gcc-powerpc64le-linux-gnu, cc: powerpc64le-linux-gnu-gcc, qemu: qemu-ppc64le-static }
- {target: s390x, toolchain: gcc-s390x-linux-gnu, cc: s390x-linux-gnu-gcc, qemu: qemu-s390x-static }
- {target: mips, toolchain: gcc-mips-linux-gnu, cc: mips-linux-gnu-gcc, qemu: qemu-mips-static }
- {target: mips64, toolchain: gcc-mips64-linux-gnuabi64, cc: mips64-linux-gnuabi64-gcc, qemu: qemu-mips64-static }
- {target: mipsel, toolchain: gcc-mipsel-linux-gnu, cc: mipsel-linux-gnu-gcc, qemu: qemu-mipsel-static }
- {target: mips64el,toolchain: gcc-mips64el-linux-gnuabi64, cc: mips64el-linux-gnuabi64-gcc,qemu: qemu-mips64el-static }
- {target: alpha, toolchain: gcc-alpha-linux-gnu, cc: alpha-linux-gnu-gcc, qemu: qemu-alpha-static }
- {target: sparc64, toolchain: gcc-sparc64-linux-gnu, cc: sparc64-linux-gnu-gcc, qemu: qemu-sparc64-static, skip_wasi: true }
#- {target: i386 (u64 slots), toolchain: gcc-multilib, cc: clang -m32, qemu: qemu-i386-static, cflags: -Dd_m3Use32BitSlots=0 }
- {target: arm (u64 slots), toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static, cflags: -Dd_m3Use32BitSlots=0 }
- {target: aarch64 (u64 slots), toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static, cflags: -Dd_m3Use32BitSlots=0 }
- {target: ppc (u64 slots), toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static, cflags: -Dd_m3Use32BitSlots=0 }
- {target: ppc64 (u64 slots), toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static, cflags: -Dd_m3Use32BitSlots=0 }
steps:
- uses: actions/checkout@v2
- name: Install QEMU
run: |
sudo apt update
sudo apt install qemu-user-static
- name: Install ${{ matrix.config.toolchain }}
run: |
sudo apt install ${{ matrix.config.toolchain }}
- name: Build
run: |
mkdir build
cd build
${{ matrix.config.cc }} -DASSERTS -Dd_m3HasWASI ${{ matrix.config.cflags }} \
-I../source ../source/*.c ../platforms/app/main.c \
-O3 -g0 -flto -lm -static \
-o wasm3
- name: Test WebAssembly spec
run: |
cd test
python3 run-spec-test.py --exec "${{ matrix.config.qemu }} ../build/wasm3 --repl"
- name: Test WASI apps
if: ${{ !matrix.config.skip_wasi }}
run: |
cd test
python3 run-wasi-test.py --fast --exec "${{ matrix.config.qemu }} ../build/wasm3"
platformio:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install -U platformio
- name: Build AVR ATmega1284
run: |
cd platforms/embedded/arduino
pio run -e mega1284
! nm .pio/build/mega1284/firmware.elf | grep printf
- name: Build ESP8266
run: |
cd platforms/embedded/esp8266
pio run
# TODO:
#- name: Build ESP32
# run: |
# cd platforms/embedded/esp32-pio
# pio run
platformio-arm:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install -U platformio
- name: Build Arduino MKR1000
run: |
cd platforms/embedded/arduino
pio run -e mkr1000
- name: Build Blue Pill (JeeH)
run: |
cd platforms/embedded/bluepill
pio run
- name: Build TinyBLE
run: |
cd platforms/embedded/arduino
pio run -e tinyBLE
- name: Build MXChip AZ3166
run: |
cd platforms/embedded/arduino
pio run -e az3166
platformio-riscv:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install -U platformio
- name: Build HiFive1
run: |
cd platforms/embedded/hifive1
pio run
- name: Build Sipeed MAIX
run: |
cd platforms/embedded/arduino
pio run -e maix
particle:
runs-on: ubuntu-latest
timeout-minutes: 10
if: "github.event_name == 'push'"
steps:
- uses: actions/checkout@v2
- name: Set up Particle CLI
run: sudo npm install -g particle-cli
- name: Log in
env:
PARTICLE_TOKEN: ${{ secrets.PARTICLE_TOKEN }}
run: particle login --token $PARTICLE_TOKEN
- name: Build Photon
run: |
cd platforms/embedded/particle
particle compile --followSymlinks photon
particle compile --followSymlinks argon
esp32-idf:
runs-on: ubuntu-latest
container: igrr/idf-qemu:release-v4.0-esp-develop-20191228
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Build for ESP32 (IDF v4.0)
run: |
. $IDF_PATH/export.sh
cd platforms/embedded/esp32-idf
export EXTRA_CFLAGS="-Werror"
idf.py build
shell: bash
- name: Test for ESP32 in QEMU
run: |
cd platforms/embedded/esp32-idf
make-flash-img.sh wasm3 flash_img.bin
qemu-system-xtensa -machine esp32 -nographic -drive file=flash_img.bin,if=mtd,format=raw -no-reboot | tee out.txt
grep "Result: 46368" out.txt
grep "Elapsed: " out.txt
grep "Restarting..." out.txt
test $(($(grep "ets Jun 8 2016" out.txt | wc -l))) -eq 1
- name: Check that IDF and PIO examples are in sync
run: |
diff -q platforms/embedded/esp32-idf/main/main.cpp platforms/embedded/esp32-pio/src/main.cpp
# TODO: also check that the build flags are in sync
cpp:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Configure
run: |
cd platforms/cpp
mkdir build
cd build
cmake ..
- name: Build
run: |
cd platforms/cpp
cmake --build build
- name: Run
run: |
cd platforms/cpp/build
./wasm3_cpp_example
as-cpp:
name: maintenance (build as C++)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Build
run: |
mkdir build
cd build
clang -xc++ -Dd_m3HasWASI \
-I../source ../source/*.c ../platforms/app/main.c \
-O3 -g0 -lm \
-o wasm3
- name: Test
run: ./build/wasm3 ./test/wasi/simple/test.wasm
with-logs:
name: maintenance (debug logs)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Build
run: |
mkdir build
cd build
clang -xc++ -Dd_m3HasWASI -DDEBUG \
-Dd_m3EnableOpTracing=1 \
-Dd_m3EnableStrace=1 \
-Dd_m3LogParse=1 \
-Dd_m3LogModule=1 \
-Dd_m3LogCompile=1 \
-Dd_m3LogWasmStack=1 \
-Dd_m3LogEmit=1 \
-Dd_m3LogCodePages=1 \
-Dd_m3LogRuntime=1 \
-Dd_m3LogNativeStack=1 \
-I../source ../source/*.c ../platforms/app/main.c \
-O3 -g0 -lm \
-o wasm3
- name: Test
run: ./build/wasm3 ./test/wasi/simple/test.wasm > /dev/null
preprocessed-ops:
name: maintenance (preprocess ops)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Install sponge
run: |
sudo apt update
sudo apt install moreutils
- name: Build
run: |
make -f extra/utils.mk preprocess
mkdir build
cd build
gcc -Dd_m3HasWASI \
-I../source ../source/*.c ../platforms/app/main.c \
-O3 -g0 -lm \
-o wasm3
- name: Test
run: ./build/wasm3 ./test/wasi/simple/test.wasm
spellcheck:
runs-on: ubuntu-latest
steps:
- name: Set up Python
uses: actions/setup-python@v2
- name: Install codespell
run: |
pip install codespell
- name: Spellcheck
run: |
codespell

13
ext/wasm3/.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
/build*
/source-*
/.toolchains
test/.spec-*
test/*.log
test/tailcall/*.S
node_modules/
__pycache__/
.project
.cproject
.settings
.vscode
.DS_Store

225
ext/wasm3/CMakeLists.txt Executable file
View File

@ -0,0 +1,225 @@
cmake_minimum_required(VERSION 3.11)
# Detect WasiEnv
if(DEFINED ENV{WASI_CC})
set(WASIENV 1)
endif()
# Detect MinGW
if(WIN32 AND CMAKE_C_COMPILER_ID MATCHES "GNU")
set(MINGW 1)
endif()
# Set options
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "set build type to Release")
endif()
if(WASIENV)
set(BUILD_WASI "metawasi" CACHE STRING "WASI implementation")
elseif(EMSCRIPTEN OR EMSCRIPTEN_LIB)
set(BUILD_WASI "none" CACHE STRING "WASI implementation")
else()
set(BUILD_WASI "uvwasi" CACHE STRING "WASI implementation")
endif()
set_property(CACHE BUILD_WASI PROPERTY STRINGS none simple uvwasi metawasi)
option(BUILD_NATIVE "Build with machine-specific optimisations" ON)
set(OUT_FILE "wasm3")
if(NOT APP_DIR)
set(APP_DIR "platforms/app")
endif()
# Configure the toolchain
if(CLANG OR CLANG_SUFFIX)
set(CMAKE_C_COMPILER "clang${CLANG_SUFFIX}")
set(CMAKE_CXX_COMPILER "clang++${CLANG_SUFFIX}")
if(BUILD_FUZZ)
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
set(OUT_FILE "wasm3-fuzzer")
set(APP_DIR "platforms/app_fuzz")
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "set build type to Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=fuzzer,address")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=fuzzer,address")
endif()
endif()
if(CLANG_CL)
set(CMAKE_C_COMPILER "clang-cl")
set(CMAKE_CXX_COMPILER "clang-cl")
set(CMAKE_LINKER "lld-link")
endif()
if(EMSCRIPTEN OR EMSCRIPTEN_LIB)
set(CMAKE_C_COMPILER "emcc")
set(CMAKE_CXX_COMPILER "em++")
if (EMSCRIPTEN_LIB)
set(APP_DIR "platforms/emscripten_lib")
set(OUT_FILE "wasm3.wasm")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s STANDALONE_WASM")
else()
set(APP_DIR "platforms/emscripten")
set(OUT_FILE "wasm3.html")
endif()
endif()
if(BUILD_32BIT)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
endif()
project(wasm3)
message("----")
message("Generator: ${CMAKE_GENERATOR}")
message("Compiler: ${CMAKE_C_COMPILER_ID}")
message("Build Type: ${CMAKE_BUILD_TYPE}")
include(CheckIPOSupported)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED YES)
set(CMAKE_C_EXTENSIONS NO)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)
file(GLOB app_srcs "${APP_DIR}/*.c")
add_executable(${OUT_FILE} ${app_srcs})
#-fno-optimize-sibling-calls
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG=1")
if(EMSCRIPTEN OR EMSCRIPTEN_LIB)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s GLOBAL_BASE=1024 -s TOTAL_STACK=2MB -s INITIAL_MEMORY=4MB -s ALLOW_MEMORY_GROWTH")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s EXPORTED_FUNCTIONS=\"[\\\"_malloc\\\",\\\"_free\\\",\\\"_main\\\"]\"")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -flto -Wfatal-errors -s ASSERTIONS=0")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} --strip-all --gc-sections")
if(WASM_EXT)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mbulk-memory -mnontrapping-fptoint -msign-ext -mtail-call")
endif()
elseif(WASIENV)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasTracer")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wfatal-errors -fomit-frame-pointer -fno-stack-check -fno-stack-protector")
if(WASM_EXT)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mbulk-memory -mnontrapping-fptoint -msign-ext -mtail-call")
endif()
# TODO: LTO breaks wasm imports currently:
# https://www.mail-archive.com/llvm-bugs@lists.llvm.org/msg36273.html
#-flto -Wl,--lto-O3
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,stack-size=8388608")
elseif(WIN32 AND NOT MINGW)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasTracer -D_CRT_SECURE_NO_WARNINGS /WX- /diagnostics:column")
string(REGEX REPLACE "/W[0-4]" "/W0" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
if (CMAKE_C_COMPILER_ID MATCHES "MSVC")
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /d2noftol3")
endif()
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Oxs /Oy /GS- /Zi /Zo /arch:AVX2")
# Uncomment this if you want to disassemble the release build,
# for example: dumpbin /DISASM wasm3.exe /out:wasm3.S
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG:FULL")
else()
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Oxs /Oy /GS- /Qvec -Clang -O3")
endif()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8388608") # stack size
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasTracer") #-Dd_m3FixedHeap=1048576
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wparentheses -Wundef -Wpointer-arith -Wstrict-aliasing=2")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=implicit-function-declaration") # -Werror=cast-align
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers")
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
# TODO: Place clang-specific options here
elseif(CMAKE_C_COMPILER_ID MATCHES "Intel")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fp-model precise")
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wjump-misses-init")
endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -save-temps")
set(CMAKE_C_FLAGS_RELEASE "-O3 -Wfatal-errors -fomit-frame-pointer -fno-stack-check -fno-stack-protector") #-fno-inline
if(BUILD_NATIVE)
if(APPLE AND CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -mcpu=native")
else()
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -march=native")
endif()
endif()
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-O0")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-O3")
target_link_libraries(${OUT_FILE} m)
endif()
target_link_libraries(${OUT_FILE} m3)
if(BUILD_WASI MATCHES "simple")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasWASI")
elseif(BUILD_WASI MATCHES "metawasi")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasMetaWASI")
elseif(BUILD_WASI MATCHES "uvwasi")
include(FetchContent)
FetchContent_Declare(
uvwasi
GIT_REPOSITORY https://github.com/nodejs/uvwasi.git
GIT_TAG b599542f7ce001e04cdff9db82b05fee96bb3332
)
FetchContent_GetProperties(uvwasi)
if(NOT uvwasi_POPULATED)
FetchContent_Populate(uvwasi)
include_directories("${uvwasi_SOURCE_DIR}/include")
add_subdirectory(${uvwasi_SOURCE_DIR} ${uvwasi_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasUVWASI")
target_link_libraries(${OUT_FILE} uvwasi_a uv_a)
endif()
check_ipo_supported(RESULT result)
if(result AND NOT WASIENV) # TODO: LTO breaks wasm imports
set_property(TARGET ${OUT_FILE} PROPERTY INTERPROCEDURAL_OPTIMIZATION True)
message("LTO: ON")
else()
message("LTO: OFF")
endif()
add_subdirectory(source)
message("Flags: ${CMAKE_C_FLAGS}")
message("Debug flags: ${CMAKE_C_FLAGS_DEBUG}")
message("Release flags: ${CMAKE_C_FLAGS_RELEASE}")
message("----")

21
ext/wasm3/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Steven Massey, Volodymyr Shymanskyy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

118
ext/wasm3/README.md Normal file
View File

@ -0,0 +1,118 @@
[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine)
<img align="right" width="30%" src="/extra/screenshot-ios.png">
# <img src="/extra/wasm-symbol.svg" width="32" height="32" /> Wasm3
[![WAPM](https://wapm.io/package/vshymanskyy/wasm3/badge.svg)](https://wapm.io/package/vshymanskyy/wasm3)
[![GitHub issues](https://img.shields.io/github/issues-raw/wasm3/wasm3?style=flat-square&label=issues&color=success)](https://github.com/wasm3/wasm3/issues)
[![Tests status](https://img.shields.io/github/workflow/status/wasm3/wasm3/tests/main?style=flat-square&logo=github&label=tests)](https://github.com/wasm3/wasm3/actions)
[![Fuzzing Status](https://img.shields.io/badge/oss--fuzz-fuzzing-success?style=flat-square)](https://bugs.chromium.org/p/oss-fuzz/issues/list?can=1&q=proj:wasm3)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/wasm3/wasm3)
The fastest WebAssembly interpreter, and the most universal runtime.
<sub>Based on [**CoreMark 1.0**](./docs/Performance.md) and [**independent**](https://00f.net/2021/02/22/webassembly-runtimes-benchmarks) benchmarks. Your mileage may vary.</sub>
[![Twitter](https://img.shields.io/twitter/follow/wasm3_engine?style=flat-square&color=1da1f2&label=twitter&logo=twitter)](https://twitter.com/wasm3_engine)
[![Discord](https://img.shields.io/discord/671415645073702925?style=flat-square&logo=discord&color=7289da&label=discord)](https://discord.gg/qmZjgnd)
[![Telegram](https://img.shields.io/badge/telegram-chat-0088cc?style=flat-square&logo=telegram)](https://t.me/joinchat/DD8s3xVG8Vj_LxRDm52eTQ)
## Getting Started
Here's a small [getting started guide](https://wapm.io/package/vshymanskyy/wasm3). Click here to start:
[![LIVE DEMO](extra/button.png)](https://webassembly.sh/?run-command=wasm3)
## Installation
**Please follow the [installation instructions](./docs/Installation.md).**
Wasm3 can also be used as a library for:
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/python.svg" width="18" height="18" /> Python3](https://github.com/wasm3/pywasm3) │
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/rust.svg" width="18" height="18" /> Rust](https://github.com/Veykril/wasm3-rs) │
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/cplusplus.svg" width="18" height="18" /> C/C++](https://github.com/wasm3/wasm3) │
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/go.svg" width="18" height="18" /> GoLang](https://github.com/matiasinsaurralde/go-wasm3) │
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/zig.svg" width="18" height="18" /> Zig](https://github.com/alichay/zig-wasm3) │
[<img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/perl.svg" width="18" height="18" /> Perl](https://metacpan.org/pod/Wasm::Wasm3)
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/swift.svg" width="18" height="18" /> Swift](https://github.com/shareup/wasm-interpreter-apple) │
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/dotnet.svg" width="18" height="18" /> .Net](https://github.com/tana/Wasm3DotNet) │
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/arduino.svg" width="18" height="18" /> Arduino, PlatformIO, Particle](https://github.com/wasm3/wasm3-arduino) │ [QuickJS](https://github.com/saghul/txiki.js)
## Status
`wasm3` passes the [WebAssembly spec testsuite](https://github.com/WebAssembly/spec/tree/master/test/core) and is able to run many `WASI` apps.
Minimum useful system requirements: **~64Kb** for code and **~10Kb** RAM
`wasm3` runs on a wide range of architectures (`x86`, `x86_64`, `ARM`, `RISC-V`, `PowerPC`, `MIPS`, `Xtensa`, `ARC32`, ...) and [platforms](/platforms):
- <img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/linux.svg" width="18" height="18" /> Linux,
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/windows.svg" width="18" height="18" /> Windows,
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/apple.svg" width="18" height="18" /> OS X,
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/freebsd.svg" width="18" height="18" /> FreeBSD,
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/android.svg" width="18" height="18" /> Android,
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/apple.svg" width="18" height="18" /> iOS
- <img src="https://cdn.rawgit.com/feathericons/feather/master/icons/wifi.svg" width="18" height="18" /> OpenWrt, Yocto, Buildroot (routers, modems, etc.)
- <img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/raspberrypi.svg" width="18" height="18" /> Raspberry Pi, Orange Pi and other SBCs
- <img src="https://cdn.rawgit.com/feathericons/feather/master/icons/cpu.svg" width="18" height="18" /> MCUs: Arduino, ESP8266, ESP32, Particle, ... [see full list](./docs/Hardware.md)
- <img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/firefoxbrowser.svg" width="18" height="18" /> Browsers. Yes, using WebAssembly itself!
- <img src="extra/wasm-symbol.svg" width="18" height="18" /> `wasm3` can execute `wasm3` (self-hosting)
## Features
| Webassembly [Core Proposals][WasmProps] | Extra |
| --- | --- |
| ☑ Import/Export of Mutable Globals | ☑ Structured execution tracing |
| ☑ Non-trapping float-to-int conversions | ☑ Big-Endian systems support |
| ☑ Sign-extension operators | ☑ Wasm and WASI self-hosting |
| ☑ Multi-value | ☑ Gas metering |
| ☑ Bulk memory operations (partial support) | ☑ Linear memory limit (< 64KiB) |
| ☐ Multiple memories |
| ☐ Reference types |
| ☐ Tail call optimization |
| ☐ Fixed-width SIMD |
| ☐ Exception handling |
## Motivation
**Why use a "slow interpreter" versus a "fast JIT"?**
In many situations, speed is not the main concern. Runtime executable size, memory usage, startup latency can be improved with the interpreter approach. Portability and security are much easier to achieve and maintain. Additionally, development impedance is much lower. A simple library like Wasm3 is easy to compile and integrate into an existing project. (Wasm3 builds in a just few seconds). Finally, on some platforms (i.e. iOS and WebAssembly itself) you can't generate executable code pages in runtime, so JIT is unavailable.
**Why would you want to run WASM on embedded devices?**
Wasm3 started as a research project and remains so by many means. Evaluating the engine in different environments is part of the research. Given that we have `Lua`, `JS`, `Python`, `Lisp`, `...` running on MCUs, `WebAssembly` is actually a promising alternative. It provides toolchain decoupling as well as a completely sandboxed, well-defined, predictable environment. Among practical use cases we can list `edge computing`, `scripting`, `plugin systems`, running `IoT rules`, `smart contracts`, etc.
## Used by
[<img src="https://wasmcloud.dev/images/logo.png" height="32" />](https://wasmcloud.dev/) 
[<img src="/extra/wowcube.png" height="32" />](https://wowcube.com/) 
[<img src="/extra/scailable.png" height="32" />](https://scailable.net/) 
[<img src="/extra/blynk.png" height="32" />](https://blynk.io/) 
[<img src="/extra/iden3.svg" height="32" />](https://www.iden3.io/) 
[<img src="https://user-images.githubusercontent.com/1506708/114701856-069ce700-9d2c-11eb-9b72-9ce2dfd9f0fb.png" height="32" />](https://github.com/kateinoigakukun/wasmic-ios) 
[<img src="https://www.balena.io/avatar.png" height="32" />](https://github.com/balena-io-playground/balena-wasm3) 
[<img src="https://krustlet.dev/images/horizontal.svg" height="32" />](https://github.com/deislabs/krustlet-wasm3) 
[<img src="/extra/shareup_app.svg" height="24" />](https://shareup.app/blog/introducing-shareup/)
## Further Resources
[Demos](./docs/Demos.md)
[Installation instructions](./docs/Installation.md)
[Cookbook](./docs/Cookbook.md)
[Troubleshooting](./docs/Troubleshooting.md)
[Build and Development instructions](./docs/Development.md)
[Supported Hardware](./docs/Hardware.md)
[Testing & Fuzzing](./docs/Testing.md)
[Performance](./docs/Performance.md)
[Interpreter Architecture](./docs/Interpreter.md)
[Logging](./docs/Diagnostics.md)
[Awesome WebAssembly Tools](https://github.com/vshymanskyy/awesome-wasm-tools/blob/main/README.md)
### License
This project is released under The MIT License (MIT)
[WasmProps]: https://github.com/WebAssembly/proposals/blob/master/finished-proposals.md "WebAssembly Finished Proposals"

462
ext/wasm3/docs/Cookbook.md Normal file
View File

@ -0,0 +1,462 @@
# WASM module examples
### Rust WASI app
Create a new project:
```sh
$ cargo new --bin hello
$ cd hello
$ rustup target add wasm32-wasi
```
Build and run:
```sh
$ cargo build --release --target wasm32-wasi
$ wasm3 ./target/wasm32-wasi/release/hello.wasm
Hello, world!
```
### AssemblyScript WASI app
Create `hello.ts`:
```ts
import "wasi"
import {Console} from "as-wasi"
Console.log('Hello World!');
```
Create `package.json`:
```json
{
"scripts": {
"asbuild:debug": "asc hello.ts -b hello.wasm --debug",
"asbuild:optimized": "asc hello.ts -b hello.wasm -O3s --noAssert",
"asbuild:tiny": "asc hello.ts -b hello.wasm -O3z --noAssert --runtime stub --use abort=",
"build": "npm run asbuild:optimized"
},
"devDependencies": {
"assemblyscript": "*",
"as-wasi": "*"
}
}
```
Build and run:
```sh
$ npm install
$ npm run build
$ wasm3 hello.wasm
Hello World!
```
### TinyGo WASI app
Create `hello.go`:
```go
package main
import "fmt"
func main() {
fmt.Printf("Hello, %s!\n", "world")
}
```
Build and run:
```sh
$ tinygo build -o hello.wasm -target wasi -no-debug hello.go
$ wasm3 hello.wasm
Hello, world!
```
### Go WASI app
Go currently does not provide the WASI interface.
For reference see [this issue](https://github.com/golang/go/issues/31105).
### Zig WASI app
Create `hello.zig`:
```zig
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("Hello, {s}!\n", .{"world"});
}
```
Build and run:
```sh
$ zig build-exe -O ReleaseSmall -target wasm32-wasi hello.zig
$ wasm3 hello.wasm
Hello, world!
```
### Zig C-code WASI app
Create `hello.c`:
```c
#include <stdio.h>
int main() {
printf("Hello, %s!\n", "world");
return 0;
}
```
Build and run:
```sh
$ zig build-exe -O ReleaseSmall -target wasm32-wasi hello.c -lc
$ wasm3 hello.wasm
Hello, world!
```
## Zig library
Create `add.zig`:
```zig
export fn add(a: i32, b: i32) i64 {
return a + b;
}
```
Build and run:
```sh
$ zig build-lib add.zig -O ReleaseSmall -target wasm32-freestanding
$ wasm3 --repl add.wasm
wasm3> add 1 2
Result: 3
```
### C/C++ WASI app
The easiest way to start is to use [Wasienv](https://github.com/wasienv/wasienv).
Create `hello.cpp`:
```cpp
#include <iostream>
int main() {
std::cout << "Hello, world!" << std::endl;
return 0;
}
```
Or `hello.c`:
```c
#include <stdio.h>
int main() {
printf("Hello, %s!\n", "world");
return 0;
}
```
Build and run:
```sh
$ wasic++ -O3 hello.cpp -o hello.wasm
$ wasicc -O3 hello.c -o hello.wasm
$ wasm3 hello.wasm
Hello World!
```
Useful `clang` options:
- **-nostdlib** Do not use the standard system startup files or libraries when linking
- **-Wl,--no-entry** Do not output any entry point
- **-Wl,--export=\<value\>** Force a symbol to be exported, e.g. **-Wl,--export=foo** to export foo function
- **-Wl,--export-all** Export all symbols (normally combined with --no-gc-sections)
- **-Wl,--initial-memory=\<value\>** Initial size of the linear memory, which must be a multiple of 65536
- **-Wl,--max-memory=\<value\>** Maximum size of the linear memory, which must be a multiple of 65536
- **-z stack-size=\<vlaue\>** The auxiliary stack size, which is an area of linear memory, and must be smaller than initial memory size.
- **-Wl,--strip-all** Strip all symbols
- **-Wl,--shared-memory** Use shared linear memory
- **-Wl,--allow-undefined** Allow undefined symbols in linked binary
- **-Wl,--allow-undefined-file=\<value\>** Allow symbols listed in \<file\> to be undefined in linked binary
- **-pthread** Support POSIX threads in generated code
Limitations:
- `setjmp/longjmp` and `C++ exceptions` are not available
- no support for `threads` and `atomics`
- no support for `dynamic libraries`
### Grain WASI app
Create `hello.gr`:
```
print("Hello, world!")
```
Build and run:
```sh
$ grain compile hello.gr -o hello.wasm
$ wasm3 hello.wasm
Hello, world!
```
### WAT WASI app
Create `hello.wat`:
```wat
(module
;; wasi_snapshot_preview1!fd_write(file_descriptor, *iovs, iovs_len, nwritten) -> status_code
(import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))
(memory 1)
(export "memory" (memory 0))
;; Put a message to linear memory at offset 32
(data (i32.const 32) "Hello, world!\n")
(func $main (export "_start")
;; Create a new io vector at address 0x4
(i32.store (i32.const 4) (i32.const 32)) ;; iov.iov_base - pointer to the start of the message
(i32.store (i32.const 8) (i32.const 14)) ;; iov.iov_len - length of the message
(call $fd_write
(i32.const 1) ;; file_descriptor - 1 for stdout
(i32.const 4) ;; *iovs - pointer to the io vector
(i32.const 1) ;; iovs_len - count of items in io vector
(i32.const 20) ;; nwritten - where to store the number of bytes written
)
drop ;; discard the WASI status code
)
)
```
Build and run:
```sh
$ wat2wasm hello.wat -o hello.wasm
$ wasm3 hello.wasm
Hello, world!
```
### WAT library
Create `swap.wat`:
```wat
(module
(func (export "swap") (param i32 i32) (result i32 i32)
(get_local 1)
(get_local 0)
)
)
```
Build and run:
```sh
$ wat2wasm swap.wat -o swap.wasm
$ wasm3 --repl swap.wasm
wasm3> :invoke swap 123 456
Result: 456:i32, 123:i32
```
# Tracing
Drag'n'drop any of the WASI apps to [`WebAssembly.sh`](https://webassembly.sh/) and run:
```sh
$ wasm3-strace /tmp/hello.wasm
```
The execution trace will be produced:
```js
_start () {
__wasilibc_init_preopen () {
malloc (i32: 16) {
dlmalloc (i32: 16) {
sbrk (i32: 0) {
} = 131072
sbrk (i32: 65536) {
```
<details>
<summary>Click to expand!</summary>
```js
} = 131072
} = 131088
} = 131088
calloc (i32: 24, i32: 0) {
dlmalloc (i32: 96) {
} = 131120
memset (i32: 131120, i32: 65504, i32: 0) {
} = 131120
} = 131120
po_map_assertvalid (i32: 131088) {
}
po_map_assertvalid (i32: 131088) {
}
}
wasi_unstable!fd_prestat_get(3, 65528) { <native> } = 0
malloc (i32: 2) {
dlmalloc (i32: 2) {
} = 131232
} = 131232
wasi_unstable!fd_prestat_dir_name(3, 131232, 1) { <native> } = 0
__wasilibc_register_preopened_fd (i32: 3, i32: 131120) {
po_map_assertvalid (i32: 131088) {
}
po_map_assertvalid (i32: 131088) {
}
strdup (i32: 131232) {
strlen (i32: 131232) {
} = 1
malloc (i32: 2) {
dlmalloc (i32: 2) {
} = 131248
} = 131248
memcpy (i32: 131248, i32: 131233, i32: 131232) {
} = 131248
} = 131248
wasi_unstable!fd_fdstat_get(3, 65496) { <native> } = 0
po_map_assertvalid (i32: 131088) {
}
po_map_assertvalid (i32: 131088) {
}
} = 0
free (i32: 131232) {
dlfree (i32: 131232) {
}
}
wasi_unstable!fd_prestat_get(4, 65528) { <native> } = 0
malloc (i32: 2) {
dlmalloc (i32: 2) {
} = 131232
} = 131232
wasi_unstable!fd_prestat_dir_name(4, 131232, 1) { <native> } = 0
__wasilibc_register_preopened_fd (i32: 4, i32: 131120) {
po_map_assertvalid (i32: 131088) {
}
po_map_assertvalid (i32: 131088) {
}
strdup (i32: 131232) {
strlen (i32: 131232) {
} = 1
malloc (i32: 2) {
dlmalloc (i32: 2) {
} = 131264
} = 131264
memcpy (i32: 131264, i32: 131233, i32: 131232) {
} = 131264
} = 131264
wasi_unstable!fd_fdstat_get(4, 65496) { <native> } = 0
po_map_assertvalid (i32: 131088) {
}
po_map_assertvalid (i32: 131088) {
}
} = 0
free (i32: 131232) {
dlfree (i32: 131232) {
}
}
wasi_unstable!fd_prestat_get(5, 65528) { <native> } = 8
__wasm_call_ctors () {
}
__original_main () {
printf (i32: 65536, i32: 0) {
vfprintf (i32: 69512, i32: 0, i32: 65536) {
printf_core (i32: 0, i32: -1, i32: 65536, i32: -16, i32: 65480) {
} = 0
__towrite (i32: 69512) {
} = 0
printf_core (i32: 69512, i32: 8, i32: 65536, i32: -1, i32: 65480) {
__fwritex (i32: 65536, i32: 0, i32: 7) {
memcpy (i32: 68472, i32: 0, i32: 65536) {
} = 68472
} = 7
__fwritex (i32: 65543, i32: 0, i32: 0) {
memcpy (i32: 68479, i32: 0, i32: 65543) {
} = 68479
} = 0
pop_arg (i32: 64456, i32: 0, i32: 9) {
}
strnlen (i32: 65548, i32: 0) {
memchr (i32: 65548, i32: 4, i32: 0) {
} = 65553
} = 5
__fwritex (i32: 67222, i32: 65553, i32: 0) {
memcpy (i32: 68479, i32: 0, i32: 67222) {
} = 68479
} = 0
__fwritex (i32: 65548, i32: 65553, i32: 5) {
memcpy (i32: 68479, i32: 0, i32: 65548) {
} = 68479
} = 5
__fwritex (i32: 65545, i32: 0, i32: 2) {
__stdout_write (i32: 69512, i32: 0, i32: 65545) {
__isatty (i32: 1) {
wasi_unstable!fd_fdstat_get(1, 64376) { <native> } = 0
} = 1
__stdio_write (i32: 69512, i32: 64368, i32: 65545) {
writev (i32: 1, i32: -16, i32: 64384) {
Hello, world!
wasi_unstable!fd_write(1, 64384, 2, 64380) { <native> } = 0
} = 14
} = 2
} = 2
memcpy (i32: 68472, i32: -1, i32: 65547) {
} = 68472
} = 2
} = 14
} = 14
} = 14
} = 0
__prepare_for_exit () {
dummy () {
}
__stdio_exit () {
__ofl_lock () {
} = 69504
}
}
}
```
</details>
# Gas Metering
```sh
$ npm install -g wasm-metering
$ wasm-meter hello.wasm hello-metered.wasm
$ wasm3 hello-metered.wasm
Warning: Gas is limited to 500000000.0000
Hello, world!
Gas used: 20.8950
$ wasm3 --gas-limit 10 hello-metered.wasm
Warning: Gas is limited to 10.0000
Gas used: 10.0309
Error: [trap] Out of gas
```
# Other resources
- [WebAssembly by examples](https://wasmbyexample.dev/home.en-us.html) by Aaron Turner
- [Writing WebAssembly](https://docs.wasmtime.dev/wasm.html) in Wasmtime docs

17
ext/wasm3/docs/Demos.md Normal file
View File

@ -0,0 +1,17 @@
# Wasm3 demos
- **In-browser Wasm3 (with MetaWASI support) on Webassembly.sh** │ [try it](https://webassembly.sh/?run-command=wasm3)
- **PyGame + pywasm3 examples** | [github](https://github.com/wasm3/pywasm3/tree/main/examples)
- **DOOM compiled to WASI, running on pywasm3** | [video](https://twitter.com/wasm3_engine/status/1393588527863144450), [github](https://github.com/wasm3/pywasm3-doom-demo)
- **WebAssembly On Your Nintendo DS** | [blog](https://softwayre.com/blog/2021/09/13/webassembly-on-your-nintendo-ds), [github](https://github.com/moxon6/snake-assemblyscript-ds)
- **Each pixel on this ESP32 board is controlled by a different WebAssembly app** | [video](https://twitter.com/zubr_kabbi/status/1436833749359017985), [github](https://github.com/kabbi/m5-atom-wasms)
- **Wasm3 self-compilation using `clang.wasm`** | [github](https://github.com/wasm3/wasm3-self-compiling)
- **Dino game**
- on PyBadge/Arduino | [video](https://twitter.com/vshymanskyy/status/1345048053041029121), [github](https://github.com/wasm3/wasm3-arduino/tree/main/examples/Wasm_Dino_PyBadge)
- on Raspberry Pi Pico | [github](https://github.com/vshymanskyy/wasm3_dino_rpi_pico)
- on ESP32 TTGO TDisplay | [github](https://github.com/wasm3/wasm3-arduino/tree/main/examples/Wasm_Dino_ESP32_TDisplay)
- **Basic WiFi/Gpio access, updating `wasm` file over-the-air** │ [video](https://twitter.com/alvaroviebrantz/status/1221618910803513344), [github](https://github.com/alvarowolfx/wasm-arduino-wifi) [ESP32 / Web]
- **RGB lamp animation using WebAssembly** │ [video](https://twitter.com/wasm3_engine/status/1222835097289752576), [github](https://github.com/vshymanskyy/Wasm3_RGB_Lamp) [nRF51 / nRF52 / ESP8266 / ESP32]
- **LCD display rendering with AssemblyScript** │ [video](https://twitter.com/h1romas4/status/1228581467850100736), [github](https://github.com/h1romas4/m5stack-wasm3-testing) [M5Stack / ESP32]
- **Conway's Game Of Life with AssemblyScript** │ [video](https://www.youtube.com/watch?v=Hc2sbhGMrig), [github](https://github.com/h1romas4/maixduino-wasm3-testing) [Maixduino / K210]

View File

@ -0,0 +1,181 @@
# Wasm3 development notes
This project uses CMake.
General build steps look like:
```sh
mkdir -p build
cd build
cmake ..
make -j8
```
Wasm3 is continuously tested with Clang, GCC, MSVC compilers, and on multiple platforms.
It can be easily integrated into any build system, as shown in `platforms`.
## Build on Linux, OS X
### Clang
```sh
mkdir build
cd build
cmake -GNinja -DCLANG_SUFFIX="-12" ..
ninja
```
### GCC
```sh
mkdir build
cd build
cmake -GNinja ..
ninja
```
## Build on Windows
Prerequisites:
- [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019)
- [CMake](https://cmake.org/download/)
- [Ninja](https://github.com/ninja-build/ninja/releases)
- [Clang 9](https://releases.llvm.org/download.html#9.0.0)
Recommended tools:
- [Cmder](https://cmder.net/)
- [Python3](https://www.python.org/downloads/)
- [ptime](http://www.pc-tools.net/win32/ptime/)
```sh
# Prepare environment (if needed):
"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
set PATH=C:\Python36-32\;C:\Python36-32\Scripts\;%PATH%
```
### Build with MSBuild
```sh
# Create a build directory as usual, then build a specific configuration
mkdir build
cd build
# Configure Clang, x64
cmake -G"Visual Studio 16 2019" -A x64 -T ClangCL ..
# Configure Clang, x86
cmake -G"Visual Studio 16 2019" -A Win32 -T ClangCL ..
# Configure MSVC, x64
cmake -G"Visual Studio 16 2019" -A x64 ..
# Configure MSVC, x86
cmake -G"Visual Studio 16 2019" -A Win32 ..
# Build
cmake --build . --config Release
cp ./Release/wasm3.exe ./
```
### Build with Ninja
```sh
set PATH=C:\Program Files\CMake\bin;%PATH%
set PATH=C:\Program Files\LLVM\bin;%PATH%
# Clang
cmake -GNinja -DCLANG_CL=1 ..
ninja
# MSVC
cmake -GNinja ..
ninja
```
## Build using compiler directly
This can be useful for cross-compilation, quick builds or when a build system (CMake, Ninja, etc.) is not available.
### gcc/clang
```sh
gcc -O3 -g0 -s -Isource -Dd_m3HasWASI source/*.c platforms/app/main.c -lm -o wasm3
```
### msvc/clang-cl
```sh
cl source/*.c platforms/app/main.c /Isource /MD /Ox /Oy /Gw /GS- /W0 /Dd_m3HasWASI /Fewasm3.exe /link advapi32.lib
```
### mingw-w64
```sh
x86_64-w64-mingw32-gcc -O3 -g0 -s -Isource -Dd_m3HasWASI source/*.c platforms/app/main.c -lm -lpthread -static -o wasm3.exe
```
## Build for microcontrollers
In `./platforms/` folder you can find projects for different targets. Some of them are using Platformio, so you can follow the regular pio build process. Others have custom instructions in respective `README.md` files.
## Build with WasiEnv
```sh
wasimake cmake -GNinja ..
ninja
```
If you want to enable experimental WASM features during the build:
```sh
export CFLAGS="-Xclang -target-feature -Xclang +tail-call"
wasimake cmake -GNinja ..
ninja
```
Here's how some options can be used with different tools:
```log
Clang target-feature option WABT option Chromium --js-flags option
----------------------------------------------------------------------------------------------------------------
+multivalue --enable-multi-value --experimental-wasm-mv
+tail-call --enable-tail-call --experimental-wasm-return-call
+bulk-memory --enable-bulk-memory --experimental-wasm-bulk-memory
+nontrapping-fptoint --enable-saturating-float-to-int --experimental-wasm-sat-f2i-conversions
+sign-ext --enable-sign-extension --experimental-wasm-se
+simd128, +unimplemented-simd128 --enable-simd --experimental-wasm-simd
```
```sh
# List clang options:
llc -march=wasm32 -mattr=help
# List Chromium options:
chromium-browser --single-process --js-flags="--help" 2>&1 | grep wasm
```
## Build with Zig
Grab the latest nightly Zig toolchain from [ziglang.org/download], and then simply run:
[ziglang.org/download]: https://ziglang.org/download/
```sh
zig build
```
This will install `wasm3` compiled for your target architecture in `zig-out/bin/wasm3`.
In order to build `wasm3` in a mode different than Debug, e.g., in ReleaseFast, pass in
an appropriate flag like so:
```sh
zig build -Drelease-fast
```
If you want to cross-compile to some specific target, pass in the target with a flag like so:
```sh
zig build -Dtarget=wasm32-wasi
```
Or if targeting Apple Silicon (this works from *any* host with Zig):
```sh
zig build -Dtarget=aarch64-macos
```

View File

@ -0,0 +1,57 @@
## Logs
To enable various logs, modify the defines in `m3_config.h`. These are only enabled when compiled in debug mode.
```C
# define d_m3LogParse 0 // .wasm binary decoding info
# define d_m3LogModule 0 // Wasm module info
# define d_m3LogCompile 0 // wasm -> metacode generation phase
# define d_m3LogWasmStack 0 // dump the wasm stack when pushed or popped
# define d_m3LogEmit 0 // metacode-generation info
# define d_m3LogCodePages 0 // dump metacode pages when released
# define d_m3LogRuntime 0 // higher-level runtime information
# define d_m3LogNativeStack 0 // track the memory usage of the C-stack
```
## Operation Profiling
To profile the interpreter's operations enable `d_m3EnableOpProfiling` in `m3_config.h`. This profiling option works in either release or debug builds.
When a runtime is released or `m3_PrintProfilerInfo ()` is called, a table of the executed operations and
their instance counts will be printed to stderr.
```
23199904 op_SetSlot_i32
12203917 op_i32_Add_ss
6682992 op_u32_GreaterThan_sr
2021555 op_u32_ShiftLeft_sr
1136577 op_u32_ShiftLeft_ss
1019725 op_CopySlot_32
775431 op_i32_Subtract_ss
703307 op_i32_Store_i32_rs
337656 op_i32_Multiply_rs
146383 op_u32_Or_rs
99168 op_u64_Or_rs
50311 op_u32_ShiftRight_rs
43319 op_u32_ShiftLeft_rs
21104 op_i64_Load_i64_s
17450 op_i32_LessThan_sr
7129 op_If_s
5574 op_i32_Wrap_i64_r
1630 op_f64_Load_f64_r
1116 op_u32_Divide_sr
903 op_i32_GreaterThan_ss
390 op_u64_And_rs
108 op_Select_f64_rss
77 op_u64_ShiftLeft_sr
64 op_Select_i32_ssr
11 op_f64_Store_f64_ss
10 op_MemGrow
8 op_f64_GreaterThan_sr
4 op_u64_LessThan_rs
1 op_u32_Xor_ss
1 op_i64_Subtract_ss
```

View File

@ -0,0 +1,60 @@
# Wasm3 hardware support
## Recommended devices (ideal for beginners)
- ESP32-based
- **LilyGO TTGO T7 V1.4** │ [AliExpress](https://www.aliexpress.com/item/32977375539.html)
- **M5Stack Fire** │ [AliExpress](https://www.aliexpress.com/item/32847906756.html)
- nRF52840-based
- **Adafruit CLUE** │ [Adafruit](https://www.adafruit.com/clue)
- **Arduino Nano 33 BLE (or BLE Sense)** │ [Arduino](https://store.arduino.cc/arduino-nano-33-ble)
- **Particle Argon** │ [Particle](https://store.particle.io/collections/bluetooth/products/argon)
- **Adafruit Feather nRF52840** | [Adafruit](https://www.adafruit.com/product/4062)
- Other
- **Raspberry Pi Pico** | [Raspberry Pi](https://www.raspberrypi.org/products/raspberry-pi-pico)
- **Adafruit PyGamer/PyBadge/PyBadge LC** │ [Adafruit](https://www.adafruit.com/product/4242)
- **SparkFun Artemis** | [SparkFun](https://www.sparkfun.com/search/results?term=Artemis)
- **Teensy 4.0** │ [PJRC](https://www.pjrc.com/store/teensy40.html)
- **Wemos W600 PICO** │ [AliExpress](https://www.aliexpress.com/item/4000314757449.html)
## Compatibility table
Device | Chipset | Architecture | Clock | Flash | RAM
--- |:---: | --- | -----:| --- | ---
Espressif ESP32 | | Xtensa LX6 <sup>⚠️</sup> | 240MHz | 4 MB | 520KB
Particle Argon, Boron, Xenon | nRF52840 | Cortex-M4F | 64MHz | 1 MB | 256KB
Particle Photon, Electron | STM32F205 | Cortex-M3 | 120Mhz | 1 MB | 128KB
Sparkfun Photon RedBoard | STM32F205 | Cortex-M3 | 120Mhz | 1 MB | 128KB
Air602 | WM W600 | Cortex-M3 | 80MHz | 1 MB | 160KB+128KB
Adafruit PyBadge | ATSAMD51J19 | Cortex-M4F | 120MHz | 512KB | 192KB
Realtek RTL8711 | | Cortex-M3 | 166MHz | 2 MB | 2 MB+512KB
Nordic nRF52840 | | Cortex-M4F | 64MHz | 1 MB | 256KB
Nordic nRF52833 | | Cortex-M4F | 64MHz | 512KB | 128KB
P-Nucleo WB55RG | STM32WB55RG | Cortex-M4F | 64MHz | 1 MB | 256KB
Teensy 4.0 | NXP iMXRT1062 | Cortex-M7 | 600MHz | 2 MB | 1 MB
Teensy 3.5 | MK64FX512 | Cortex-M4F | 120MHz | 512KB | 192KB
MXChip AZ3166 | EMW3166 | Cortex-M4 | 100MHz | 1 MB+2 MB | 256KB
Arduino Due | AT91SAM3X8E | Cortex-M3 | 84MHz | 512KB | 96KB
Sipeed MAIX | Kendryte K210 | RV64IMAFDC | 400MHz | 16 MB | 8 MB
Fomu (soft CPU) | Lattice ICE40UP5K | RV32I | 12MHz | 2 MB | 128KB
## Limited support
The following devices can run Wasm3, however they cannot afford to allocate even a single Linear Memory page (64KB).
This means `memoryLimit` should be set to the actual amount of RAM available, and that in turn usually breaks the allocator of the hosted Wasm application (which still assumes the page is 64KB and performs OOB access).
Device | Chipset | Architecture | Clock | Flash | RAM
--- |:---: | --- | -----:| --- | ---
Espressif ESP8266 | | Xtensa L106 <sup>⚠️</sup> | 160MHz | 4 MB | ~50KB (available)
Teensy 3.1/3.2 | NXP MK20DX256 | Cortex-M4 | 72MHz | 288KB | 64KB
Blue Pill | STM32F103 | Cortex-M3 | 72MHz | 64KB | 20KB
Arduino MKR* | SAMD21 | Cortex-M0+ <sup>⚠️</sup> | 48MHz | 256KB | 32KB
Arduino 101 | Intel Curie | ARC32 | 32MHz | 196KB | 24KB
SiFive HiFive1 | Freedom E310 | RV32IMAC | 320MHz | 16 MB | 16KB
Nordic nRF52832 | | Cortex-M4F | 64MHz | 256/512KB | 32/64KB
Nordic nRF51822 | | Cortex-M0 <sup>⚠️</sup> | 16MHz | 128/256KB | 16/32KB
Wicked Device WildFire | ATmega1284 | 8-bit AVR <sup>⚠️</sup> | 20MHz | 128KB | 16KB
### Legend:
⚠️ This architecture/compiler currently fails to perform TCO (Tail Call Optimization/Elimination), which leads to sub-optimal interpreter behaviour (intense native stack usage, lower performance).
There are plans to improve this in future 🦄.

View File

@ -0,0 +1,26 @@
# Wasm3 installation
## Windows
Download `wasm3-win-x64.exe` or `wasm3-win-x86.exe` from the [latest release page](https://github.com/wasm3/wasm3/releases/latest).
## macOS
Use [Homebrew](https://brew.sh) to build and install automatically:
```sh
brew install wasm3
```
## Cosmopolitan / Actually Portable Executable
[APE](https://justine.lol/cosmopolitan/index.html) is a polyglot format that runs natively on Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS.
Download `wasm3-cosmopolitan.com` from the [latest release page](https://github.com/wasm3/wasm3/releases/latest)
## OpenWRT
Follow instructions [here](https://github.com/wasm3/wasm3-openwrt-packages).
## Arduino / PlatformIO / Particle.io library
Find `Wasm3` in the `Library Manager` and install it.
## Python module
```sh
pip3 install pywasm3
```

View File

@ -0,0 +1,166 @@
# M3
This is a WebAssembly interpreter written in C using a novel, high performance interpreter topology. The interpreter strategy (named M3) was developed prior to this particular Wasm project and is described some below.
## Purpose
I don't know. I just woke up one day and started hacking this out after realizing my interpreter was well suited for the Wasm bytecode structure. Some ideas:
* It could be useful for embedded systems.
* It might be a good start-up, pre-JIT interpreter in a more complex Wasm compiler system.
* It could serve as a Wasm validation library.
* The interpreter topology might be inspiring to others.
## Benchmarks
It's at least fleshed out enough to run some simple benchmarks.
Tested on a 4 GHz Intel Core i7 iMac (Retina 5K, 27-inch, Late 2014). M3 was compiled with Clang -Os. C benchmarks were compiled with gcc with -O3
### Mandelbrot
C code lifted from: https://github.com/ColinEberhardt/wasm-mandelbrot
|Interpreter|Execution Time|Relative to GCC| |
|-----------|--------------|---------------|----|
|Life |547 s |133 x | https://github.com/perlin-network/life
|Lua |122 s |30 x | This isn't Lua running some weird Wasm transcoding; a manual Lua conversion of the C benchmark as an additional reference point.
|M3 |17.9 s |4.4 x | (3.7 x on my 2016 MacBook Pro)
|GCC |4.1 s | |
### CRC32
|Interpreter|Execution Time|Relative to GCC|
|-----------|--------------|---------------|
|Life |153 s |254 x |
|M3 |5.1 s |8.5 x |
|GCC |600 ms | |
In general, the M3 strategy seems capable of executing code around 4-15X slower than compiled code on a modern x86 processor. (Older CPUs don't fare as well. I suspect branch predictor differences.) I have yet to test on anything ARM.
## M3: Massey Meta Machine
Over the years, I've mucked around with creating my own personal programming language. It's called Gestalt. The yet unreleased repository will be here: https://github.com/soundandform/gestalt
Early on I decided I needed an efficient interpreter to achieve the instant-feedback, live-coding environment I desire. Deep traditional compilation is too slow and totally unnecessary during development. And, most importantly, compilation latency destroys creative flow.
I briefly considered retooling something extant. The Lua virtual machine, one of the faster interpreters, is too Lua centric. And everything else is too slow.
I've also always felt that the "spin in a loop around a giant switch statement" thing most interpreters do was clumsy and boring. My intuition said there was something more elegant to be found.
The structure that emerged I named a "meta machine" since it mirrors the execution of compiled code much more closely than the switch-based virtual machine.
I set out with a goal of approximating Lua's performance. But this interpreter appears to beat Lua by 3X and more on basic benchmarks -- whatever they're worth!
### How it works
This rough information might not be immediately intelligible without referencing the source code.
#### Reduce bytecode decoding overhead
* Bytecode/opcodes are translated into more efficient "operations" during a compilation pass, generating pages of meta-machine code
* M3 trades some space for time. Opcodes map to up to 3 different operations depending on the number of source operands and commutative-ness.
* Commonly occurring sequences of operations can can also be optimized into a "fused" operation. This *sometimes* results in improved performance.
* the modern CPU pipeline is a mysterious beast
* In M3/Wasm, the stack machine model is translated into a more direct and efficient "register file" approach.
#### Tightly Chained Operations
* M3 operations are C functions with a single, fixed function signature.
```C
void * Operation_Whatever (pc_t pc, u64 * sp, u8 * mem, reg_t r0, f64 fp0);
```
* The arguments of the operation are the M3's virtual machine registers
* program counter, stack pointer, etc.
* The return argument is a trap/exception and program flow control signal
* The M3 program code is traversed by each operation calling the next. The operations themselves drive execution forward. There is no outer control structure.
* Because operations end with a call to the next function, the C compiler will tail-call optimize most operations.
* Finally, note that x86/ARM calling conventions pass initial arguments through registers, and the indirect jumps between operations are branch predicted.
#### The End Result
Since operations all have a standardized signature and arguments are tail-call passed through to the next, the M3 "virtual" machine registers end up mapping directly to real CPU registers. It's not virtual! Instead, it's a meta machine with very low execution impedance.
|M3 Register |x86 Register|
|-----------------------------|------------|
|program counter (pc) |rdi |
|stack pointer (sp) |rsi |
|linear memory (mem) |rdx |
|integer register (r0) |rcx |
|floating-point register (fp0)|xmm0 |
For example, here's a bitwise-or operation in the M3 compiled on x86.
```assembly
m3`op_u64_Or_sr:
0x1000062c0 <+0>: movslq (%rdi), %rax ; load operand stack offset
0x1000062c3 <+3>: orq (%rsi,%rax,8), %rcx ; or r0 with stack operand
0x1000062c7 <+7>: movq 0x8(%rdi), %rax ; fetch next operation
0x1000062cb <+11>: addq $0x10, %rdi ; increment program counter
0x1000062cf <+15>: jmpq *%rax ; jump to next operation
```
#### Registers and Operational Complexity
* The conventional Windows calling convention isn't compatible with M3, as-is, since it only passes 4 arguments through registers. Applying the vectorcall calling convention (https://docs.microsoft.com/en-us/cpp/cpp/vectorcall) resolves this problem.
* It's possible to use more CPU registers. For example, adding an additional floating-point register to the meta-machine did marginally increase performance in prior tests. However, the operation space increases exponentially. With one register, there are up to 3 operations per opcode (e.g. a non-commutative math operation). Adding another register increases the operation count to 10. However, as you can see above, operations tend to be tiny.
#### Stack Usage
* Looking at the above assembly code, you'll notice that once an M3 operation is optimized, it doesn't need the regular stack (no mucking with the ebp/esp registers). This is the case for 90% of the opcodes. Branching and call operations do require stack variables. Therefore, execution can't march forward indefinitely; the stack would eventually overflow.
Therefore, loops unwind the stack. When a loop is continued, the Continue operation returns, unwinding the stack. Its return value is a pointer to the loop opcode it wants to unwind to. The Loop operations checks for its pointer and responds appropriately, either calling back into the loop code or returning the loop pointer back down the call stack.
* Traps/Exceptions work similarly. A trap pointer is returned from the trap operation which has the effect of unwinding the entire stack.
* Returning from a Wasm function also unwinds the stack, back to the point of the Call operation.
* But, because M3 execution leans heavily on the native stack, this does create one runtime usage issue.
A conventional interpreter can save its state, break out of its processing loop and return program control to the client code. This is not the case in M3 since the C stack might be wound up in a loop for long periods of time.
With Gestalt, I resolved this problem with fibers (built with Boost Context). M3 execution occurs in a fiber so that control can effortlessly switch to the "main" fiber. No explicit saving of state is necessary since that's the whole purpose of a fiber.
More simplistically, the interpreter runtime can also periodically call back to the client (in the either the Loop or LoopContinue operation). This is necessary, regardless, to detect hangs and break out of infinite loops.
## The M3 strategy for other languages (is rad)
The Gestalt M3 interpreter works slightly differently than this Wasm version. With Gestalt, blocks of all kind (if/else/try), not just loops, unwind the native stack. (This somewhat degrades raw x86 performance.)
But, this adds a really beautiful property to the interpreter. The lexical scoping of a block in the language source code maps directly into the interpreter. All opcodes/operations end up having an optional prologue/epilogue structure. This made things like reference-counting objects in Gestalt effortless. Without this property, the compiler itself would have to track scope and insert dererence opcodes intentionally. Instead, the "CreateObject" operation is also the "DestroyObject" operation on the exit/return pathway.
Here's some pseudocode to make this more concrete:
```
return_t Operation_NewObject (registers...)
{
Object o = runtime->CreateObject (registers...);
* stack [index] = o;
return_t r = CallNextOperation (registers...); // executes to the end of the scope/block/curly-brace & returns
if (o->ReferenceCount () == 0)
runtime->DestroyObject (registers..., o); // calls o's destructor and frees memory
return r;
}
```
Likewise, a "defer" function (like in Go) becomes absolutely effortless to implement. Exceptions (try/catch) as well.
## Prior Art
After the Wasm3 project was posted to Hacker News (https://news.ycombinator.com/item?id=22024758), I finally discovered precedent for this tail-call interpreter design. It has previously been called "threaded code". See the "Continuation-passing style" section: http://www.complang.tuwien.ac.at/forth/threaded-code.html).
If this style of interpreter was discussed back in the 70's, why hasn't it been more popular? I suspect because there was no benefit until more recently. Older calling conventions only used the stack to pass arguments, older CPUs didn't have branch prediction and compiler tail-call optimization maybe weren't ubiqutous.

View File

@ -0,0 +1,91 @@
# Performance
## CoreMark 1.0 results
```log
# Intel i5-8250U x64 (1.6-3.4GHz)
Node v13.0.1 (interpreter) 28.544923 57.0x
wac (wax) 101.864113 16.0x
wasm-micro-runtime 201.612903 8.1x ▲ slower
wasm3 1627.869119 1.0
Wasmer 0.13.1 singlepass 4065.591544 2.5x ▼ faster
wasmtime 0.8.0 (--optimize) 6453.505427 4.0x
Wasmer 0.13.1 cranelift 6467.164442 4.0x
Webassembly.sh (Chromium 78) 6914.325225 4.2x
Webassembly.sh (Firefox 70) 7251.153593 4.5x
wasmer-js (Node v13.0.1) 10043.827611 6.2x
Wasmer 0.13.1 llvm 12450.199203 7.6x
WAVM 14946.566026 9.2x
Native (GCC 7.4.0, 32-bit) 18070.112035 11.1x
Native (GCC 7.4.0, 64-bit) 19144.862795 11.8x
```
**Note:** Here is more info on [how to run CoreMark benchmark](https://github.com/wasm3/wasm-coremark).
## Simple recursive Fibonacci calculation test
```log
fib(40)
-----------------------------------------------------------------------------------------
# Intel i5-8250U x64 (1.6-3.4GHz)
Native C implementation 0.23s
Linux 3.83s
Win 10 5.35s
wasm3 on V8 (Emscripten 1.38, node v13.0.1) 17.98s
# Raspberry Pi 4 BCM2711B0 armv7l (A72 @ 1.5GHz)
Native C implementation 1.11s
Linux 22.97s
# Orange Pi Zero Plus2 H5 aarch64 (A53 @ 1GHz)
Native C implementation 2.55s
Linux 50.00s
# VoCore2 mips32r2 (MT7628AN @ 580MHz)
Native C implementation 6.21s
OpenWRT 2m 38s
# Xiaomi Mi Router 3G mips32r2 (MT7621AT @ 880MHz)
Native C implementation 8.83s
OpenWRT 3m 20s
```
## Wasm3 on MCUs
```log
fib(24) comments
-----------------------------------------------------------------------------------------
Maix (K210) rv64imafc @ 400MHz 77ms
ESP8266 LX106 @ 160MHz 308ms TCO failed, stack used: 9024
ESP32 LX6 @ 240MHz 297ms TCO failed, stack used: 10600
ESP32-s2 (beta) LX6 @ 240MHz 297ms TCO failed
Particle Photon Arm M3 @ 120MHz 536ms
MXChip AZ3166 Arm M4 @ 100MHz ms
WM W600 Arm M3 @ 80MHz 698ms TCO enabled, stack used: 1325
WM W600 Arm M3 @ 80MHz 826ms TCO disabled, stack used: 8109
Arduino DUE (SAM3X8E) Arm M3 @ 84MHz 754ms
BleNano2 (nRF52) Arm M4 @ 64MHz 1.19s
Arduino MKR1000 Arm M0+ @ 48MHz 1.93s TCO failed
TinyBLE (nRF51) Arm M0 @ 16MHz 5.69s TCO failed
BluePill Arm M3 @ 72MHz 7.62s
HiFive1 (FE310) rv32imac @ 320MHz 9.10s <- something wrong here?
ATmega1284 avr @ 16MHz 12.99s TCO failed
Fomu rv32i @ 12MHz 25.20s
```
## Wasm3 vs other languages
```log
fib(40)
-----------------------------------------------------------------------------------------
LuaJIT jit 1.15s
Node v10.15 jit 2.97s ▲ faster
Wasm3 interp 3.83s
Lua 5.1 interp 16.65s ▼ slower
Python 2.7 interp 34.08s
Python 3.4 interp 35.67s
Micropython v1.11 interp 85,00s
Espruino 2v04 interp >20m
```

61
ext/wasm3/docs/Testing.md Normal file
View File

@ -0,0 +1,61 @@
# Wasm3 tests
## Running WebAssembly spec tests
To run spec tests, you need `python3`
```sh
# In test directory:
python3 ./run-spec-test.py
```
It will automatically download, extract, run the WebAssembly core test suite.
## Running WASI test
Wasm3 comes with a set of benchmarks and test programs (prebuilt as `WASI` apps) including `CoreMark`, `C-Ray`, `Brotli`, `mandelbrot`, `smallpt` and `wasm3` itself.
This test will run all of them and verify the output:
```sh
# In test directory:
python3 ./run-wasi-test.py
```
It can be run against other engines as well:
```sh
./run-wasi-test.py --exec wasmtime # [PASS]
./run-wasi-test.py --exec "wavm run" # [PASS]
./run-wasi-test.py --exec "wasmer run" # [PASS]
./run-wasi-test.py --exec "wasmer-js run" # [PASS]
./run-wasi-test.py --exec $WAMR/iwasm --timeout=300 # [PASS, but very slow]
./run-wasi-test.py --exec $WAC/wax --timeout=300 # [FAIL, crashes on most tests]
```
## Running coverage-guided fuzz testing with libFuzzer
You need to produce a fuzzer build first (use your version of Clang):
```sh
# In wasm3 root:
mkdir build-fuzzer
cd build-fuzzer
cmake -GNinja -DCLANG_SUFFIX="-9" ..
cmake -DBUILD_FUZZ:BOOL=TRUE ..
ninja
```
```sh
# In test directory:
../build-fuzzer/wasm3-fuzzer -detect_leaks=0 ./fuzz/corpus
```
Read [more on libFuzzer](https://llvm.org/docs/LibFuzzer.html) and it's options.
Note: to catch fuzzer errors in debugger, you need to define:
```sh
export ASAN_OPTIONS=abort_on_error=1
export UBSAN_OPTIONS=abort_on_error=1
```

View File

@ -0,0 +1,26 @@
# Wasm3 troubleshooting
### `Error: [trap] stack overflow`
Increase the stack size:
```sh
wasm3 --stack-size 1000000 file.wasm
```
### `Error: missing imported function`
This means that the runtime doesn't provide a specific function, needed for your module execution.
You need to implement the required functions, and re-build Wasm3.
Alternatively, you can use Python to define your environment. Check out [`pywasm3`](https://pypi.org/project/pywasm3/) module.
**Note:** If this happens with `WASI` functions like `wasi_unstable.*` or `wasi_snapshot_preview1.*`, please report as a bug.
### `Error: compiling function overran its stack height limit`
Try increasing `d_m3MaxFunctionStackHeight` in `m3_config.h` and rebuilding Wasm3.
### `Error: [Fatal] repl_load: unallocated linear memory`
Your module requires some `Memory`, but doesn't define/export it by itself.
This happens if module is built by `Emscripten`, or it's a library that is intended to be linked to some other modules.
Wasm3 currently doesn't support running such modules directly, but you can remove this limitation when embedding Wasm3 into your own app.

BIN
ext/wasm3/extra/blynk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
ext/wasm3/extra/button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

17
ext/wasm3/extra/disasm-func.sh Executable file
View File

@ -0,0 +1,17 @@
#
# Utility to disassemble a specific function.
# Usage:
# ./disasm-func.sh ../build/wasm3 i32_Divide_sr
#
FILE="$1"
FUNC="$2"
# get starting address and size of function fact from nm
ADDR=$(nm --print-size --radix=d $FILE | grep $FUNC | cut -d ' ' -f 1,2)
# strip leading 0's to avoid being interpreted by objdump as octal addresses
STARTADDR=$(echo $ADDR | cut -d ' ' -f 1 | sed 's/^0*\(.\)/\1/')
SIZE=$(echo $ADDR | cut -d ' ' -f 2 | sed 's/^0*//')
STOPADDR=$(( $STARTADDR + $SIZE ))
objdump --disassemble $FILE --start-address=$STARTADDR --stop-address=$STOPADDR -M suffix

202
ext/wasm3/extra/iden3.svg Normal file
View File

@ -0,0 +1,202 @@
<svg version="1.1" id="grid" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 214 77" style="enable-background:new 0 0 214 77;" xml:space="preserve">
<style type="text/css">
.st0{fill:#674BDE;}
.st1{fill:#4D38A5;}
.st2{fill:#0799AC;}
.st3{fill:#187C80;}
.st4{fill:#09CDE7;}
.st5{filter:url(#Adobe_OpacityMaskFilter);}
.st6{fill:url(#SVGID_2_);}
.st7{opacity:0.23;mask:url(#SVGID_1_);fill:url(#SVGID_3_);}
.st8{fill:#C9F1F6;}
.st9{fill:#DCD6F4;}
.st10{clip-path:url(#SVGID_7_);fill:url(#SVGID_8_);}
.st11{filter:url(#Adobe_OpacityMaskFilter_1_);}
.st12{clip-path:url(#SVGID_7_);fill:url(#SVGID_10_);stroke:#1D1D1B;stroke-width:0.9757;stroke-miterlimit:10;}
.st13{clip-path:url(#SVGID_7_);mask:url(#SVGID_9_);fill:url(#SVGID_11_);}
.st14{opacity:0.5;fill:#7F6A55;}
.st15{fill:#1D1D1B;}
.st16{fill:#7D6852;}
.st17{fill:#E03D3D;}
.st18{clip-path:url(#SVGID_13_);}
.st19{fill:none;}
.st20{fill:none;stroke:#09CDE7;stroke-width:20.4194;stroke-linecap:round;stroke-miterlimit:10;}
.st21{fill:none;stroke:#674BDE;stroke-width:20.4194;stroke-linecap:round;stroke-miterlimit:10;}
.st22{fill:#98E8F2;}
.st23{fill:#FFFFFF;}
.st24{fill:#3D3196;}
.st25{opacity:0.1;fill:#1D1D1B;}
.st26{opacity:0.1;}
.st27{fill:#373735;}
.st28{fill:none;stroke:#674BDE;stroke-width:1.0781;stroke-linecap:round;stroke-miterlimit:10;}
.st29{fill:none;stroke:#674BDE;stroke-width:1.2938;stroke-linecap:round;stroke-miterlimit:10;}
.st30{fill:none;stroke:#674BDE;stroke-width:1.0707;stroke-linecap:round;stroke-miterlimit:10;}
.st31{opacity:0.2;fill:#1D1D1B;}
.st32{fill:#806FE4;}
.st33{fill:none;stroke:#FFFFFF;stroke-width:6.4688;stroke-linecap:round;stroke-miterlimit:10;}
.st34{opacity:0.2;}
.st35{fill:#057780;}
.st36{fill:none;stroke:#9F8EE9;stroke-width:2.3621;stroke-linecap:round;stroke-miterlimit:10;}
.st37{fill:none;stroke:#9F8EE9;stroke-width:2.3621;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:7.0935,11.8225;}
.st38{fill:#342672;}
.st39{fill:#1A1339;}
.st40{fill:#6BDFEF;}
.st41{fill:none;stroke:#674BDE;stroke-width:2.3883;stroke-linecap:round;stroke-miterlimit:10;}
.st42{fill:#3A2A80;}
.st43{fill:url(#SVGID_14_);}
.st44{fill:url(#SVGID_15_);}
.st45{fill:url(#SVGID_16_);}
.st46{fill:#F0F0F0;}
.st47{opacity:0.5;}
.st48{opacity:0.25;fill:#1A1339;}
.st49{clip-path:url(#SVGID_18_);}
.st50{fill:none;stroke:#FFFFFF;stroke-width:1.1495;stroke-linecap:round;stroke-miterlimit:10;}
.st51{fill:#056976;}
.st52{fill:#B1A4ED;}
.st53{fill:#3AD6EB;}
.st54{fill:none;stroke:#9F8EE9;stroke-width:1.7304;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:5.1912,8.6519;}
.st55{fill:url(#SVGID_19_);}
.st56{opacity:0.5;fill:#FFFFFF;}
.st57{fill:url(#SVGID_22_);}
.st58{fill:url(#SVGID_23_);}
.st59{fill:none;stroke:#674BDE;stroke-miterlimit:10;}
.st60{fill:url(#SVGID_24_);}
.st61{fill:url(#SVGID_29_);}
.st62{fill:url(#SVGID_30_);}
.st63{fill:url(#SVGID_33_);}
.st64{fill:url(#SVGID_34_);}
.st65{fill:url(#SVGID_35_);}
.st66{fill:url(#SVGID_42_);}
.st67{filter:url(#Adobe_OpacityMaskFilter_2_);}
.st68{fill:url(#SVGID_44_);stroke:#1D1D1B;stroke-width:0.9757;stroke-miterlimit:10;}
.st69{mask:url(#SVGID_43_);fill:url(#SVGID_45_);}
.st70{fill:url(#SVGID_46_);}
.st71{fill:url(#SVGID_47_);}
.st72{clip-path:url(#SVGID_49_);}
.st73{fill:none;stroke:#674BDE;stroke-width:3;stroke-miterlimit:10;}
.st74{fill:none;stroke:#674BDE;stroke-width:0.8089;stroke-linecap:round;stroke-miterlimit:10;}
.st75{fill:none;stroke:#674BDE;stroke-width:0.9707;stroke-linecap:round;stroke-miterlimit:10;}
.st76{fill:none;stroke:#674BDE;stroke-width:0.8033;stroke-linecap:round;stroke-miterlimit:10;}
.st77{fill:none;stroke:#FFFFFF;stroke-width:4.8536;stroke-linecap:round;stroke-miterlimit:10;}
.st78{fill:none;stroke:#9F8EE9;stroke-width:1.7184;stroke-linecap:round;stroke-miterlimit:10;}
.st79{fill:none;stroke:#9F8EE9;stroke-width:1.7184;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:5.1601,8.6002;}
.st80{fill:none;stroke:#674BDE;stroke-width:1.7374;stroke-linecap:round;stroke-miterlimit:10;}
.st81{fill:none;stroke:#BDB2EF;stroke-miterlimit:10;}
.st82{fill:none;stroke:#9F8EE9;stroke-width:1.2056;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:3.6169,6.0282;}
.st83{fill:url(#SVGID_50_);}
.st84{fill:url(#SVGID_51_);}
.st85{clip-path:url(#SVGID_53_);}
.st86{fill:none;stroke:#FFFFFF;stroke-width:3;stroke-miterlimit:10;}
.st87{fill:#E3DEF6;}
.st88{fill:url(#SVGID_54_);}
.st89{opacity:0.56;fill:url(#SVGID_55_);}
.st90{opacity:0.56;fill:url(#SVGID_56_);}
.st91{opacity:0.56;fill:url(#SVGID_57_);}
.st92{opacity:0.56;fill:url(#SVGID_64_);}
.st93{opacity:0.56;fill:url(#SVGID_65_);}
.st94{opacity:0.56;fill:url(#SVGID_66_);}
.st95{fill:#E7E7E7;}
.st96{fill:url(#SVGID_67_);}
.st97{fill:url(#SVGID_68_);}
.st98{fill:none;stroke:#FFFFFF;stroke-miterlimit:10;}
.st99{fill:none;stroke:#C1C1C0;stroke-miterlimit:10;}
.st100{fill:none;stroke:#09CDE7;stroke-miterlimit:10;}
.st101{fill:url(#SVGID_85_);}
.st102{clip-path:url(#SVGID_101_);}
.st103{fill:url(#SVGID_102_);}
.st104{filter:url(#Adobe_OpacityMaskFilter_3_);}
.st105{fill:url(#SVGID_104_);stroke:#1D1D1B;stroke-width:0.5657;stroke-miterlimit:10;}
.st106{mask:url(#SVGID_103_);fill:url(#SVGID_105_);}
.st107{fill:url(#SVGID_106_);}
.st108{fill:url(#SVGID_107_);}
.st109{clip-path:url(#SVGID_109_);}
.st110{fill:none;stroke:#FFFFFF;stroke-width:0.6665;stroke-linecap:round;stroke-miterlimit:10;}
.st111{fill:url(#SVGID_110_);}
.st112{clip-path:url(#SVGID_126_);}
.st113{fill:none;stroke:#674BDE;stroke-width:0.469;stroke-linecap:round;stroke-miterlimit:10;}
.st114{fill:none;stroke:#674BDE;stroke-width:0.5628;stroke-linecap:round;stroke-miterlimit:10;}
.st115{fill:none;stroke:#674BDE;stroke-width:0.4658;stroke-linecap:round;stroke-miterlimit:10;}
.st116{fill:none;stroke:#FFFFFF;stroke-width:2.8141;stroke-linecap:round;stroke-miterlimit:10;}
.st117{fill:none;stroke:#BDB2EF;stroke-width:0.5798;stroke-miterlimit:10;}
.st118{fill:none;stroke:#9F8EE9;stroke-width:0.699;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:2.0971,3.4951;}
.st119{clip-path:url(#SVGID_142_);}
.st120{fill:url(#SVGID_143_);}
.st121{fill:url(#SVGID_144_);}
.st122{clip-path:url(#SVGID_146_);}
.st123{fill:none;stroke:#FFFFFF;stroke-width:1.7394;stroke-miterlimit:10;}
.st124{fill:#D5D6D9;}
.st125{fill:#F7F7F7;}
.st126{fill:url(#SVGID_147_);}
.st127{fill:#BCBCBC;}
.st128{fill:url(#SVGID_148_);}
.st129{fill:#3C3E42;}
.st130{filter:url(#Adobe_OpacityMaskFilter_4_);}
.st131{clip-path:url(#SVGID_150_);fill:url(#SVGID_152_);stroke:#1D1D1B;stroke-width:0.4331;stroke-miterlimit:10;}
.st132{clip-path:url(#SVGID_150_);mask:url(#SVGID_151_);fill:url(#SVGID_153_);}
.st133{fill:url(#SVGID_154_);}
.st134{fill:url(#SVGID_155_);}
.st135{fill:url(#SVGID_156_);}
.st136{clip-path:url(#SVGID_158_);}
.st137{fill:none;stroke:#FFFFFF;stroke-width:0.3836;stroke-linecap:round;stroke-miterlimit:10;}
.st138{fill:url(#SVGID_159_);}
.st139{fill:none;stroke:#674BDE;stroke-width:0.3711;stroke-linecap:round;stroke-miterlimit:10;}
.st140{fill:none;stroke:#674BDE;stroke-width:0.4454;stroke-linecap:round;stroke-miterlimit:10;}
.st141{fill:none;stroke:#674BDE;stroke-width:0.3686;stroke-linecap:round;stroke-miterlimit:10;}
.st142{fill:none;stroke:#FFFFFF;stroke-width:2.2268;stroke-linecap:round;stroke-miterlimit:10;}
.st143{fill:none;stroke:#BDB2EF;stroke-width:0.4715;stroke-miterlimit:10;}
.st144{clip-path:url(#SVGID_161_);}
.st145{fill:none;stroke:#9F8EE9;stroke-width:0.5276;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:1.5829,2.6381;}
.st146{fill:url(#SVGID_162_);}
.st147{fill:none;stroke:#FFFFFF;stroke-width:1.6174;stroke-miterlimit:10;}
.st148{clip-path:url(#SVGID_164_);}
.st149{fill:#382A87;}
.st150{fill:#0A5454;}
.st151{fill:url(#SVGID_165_);}
</style>
<g>
<g>
<g>
<g>
<path class="st15" d="M14.9,18.3c-0.5-0.5-0.8-1.1-0.8-1.8c0-0.7,0.3-1.2,0.8-1.7c0.5-0.5,1.2-0.7,1.9-0.7
c0.8,0,1.4,0.2,1.9,0.7c0.5,0.5,0.8,1,0.8,1.7c0,0.7-0.3,1.3-0.8,1.8c-0.5,0.5-1.1,0.8-1.9,0.8C16.1,19.1,15.5,18.8,14.9,18.3z
M15.7,65.2V28.4c0-0.4,0.2-0.6,0.6-0.6h1.1c0.4,0,0.6,0.2,0.6,0.6v36.8c0,0.4-0.2,0.6-0.6,0.6h-1.1
C15.9,65.8,15.7,65.6,15.7,65.2z"></path>
</g>
<g>
<path class="st15" d="M57.3,12.7h1.1c0.4,0,0.6,0.2,0.6,0.6v51.9c0,0.4-0.2,0.6-0.6,0.6h-1.1c-0.4,0-0.6-0.2-0.6-0.6v-4.9
c0-0.1,0-0.2-0.1-0.2c-0.1,0-0.1,0-0.2,0.1c-1.1,1.9-2.6,3.4-4.5,4.5c-1.9,1.1-4.1,1.6-6.6,1.6c-3.4,0-6.2-0.9-8.6-2.7
c-2.3-1.8-3.9-4.2-4.6-7.3c-0.3-1.1-0.4-2.3-0.5-3.6c-0.1-1.3-0.1-3.3-0.1-5.8c0-2.6,0-4.6,0.1-6c0.1-1.4,0.2-2.6,0.5-3.7
c0.7-3,2.2-5.4,4.6-7.2c2.4-1.8,5.3-2.8,8.6-2.8c2.5,0,4.7,0.5,6.6,1.6c1.9,1.1,3.4,2.6,4.5,4.6c0,0.1,0.1,0.1,0.2,0.1
c0,0,0.1-0.1,0.1-0.2v-20C56.7,12.9,56.9,12.7,57.3,12.7z M56.6,52.8c0.1-1,0.1-3,0.1-6c0-2.9,0-4.9-0.1-6c-0.1-1-0.2-2-0.4-2.9
c-0.5-2.5-1.6-4.5-3.5-6.2c-1.9-1.6-4.2-2.5-7.1-2.5c-2.9,0-5.3,0.8-7.4,2.5c-2,1.6-3.3,3.7-3.8,6.2c-0.2,0.9-0.3,1.8-0.4,2.8
c-0.1,1-0.1,3-0.1,6c0,2.9,0,4.8,0.1,5.9c0.1,1,0.2,2,0.3,3c0.4,2.5,1.6,4.6,3.7,6.2c2.1,1.6,4.6,2.4,7.5,2.4
c2.9,0,5.2-0.8,7.1-2.5c1.8-1.6,3-3.7,3.5-6.2C56.4,54.8,56.5,53.8,56.6,52.8z"></path>
</g>
<g>
<path class="st15" d="M93.4,62.4c2-1.3,3.4-3,4.2-5.1l0.2-0.3c0.1-0.2,0.2-0.2,0.4-0.2h0.4l0.8,0.2c0.4,0.1,0.5,0.4,0.4,0.8
c-1,2.6-2.6,4.7-5,6.3c-2.4,1.6-5.1,2.4-8.4,2.4c-3.4,0-6.3-0.9-8.7-2.7c-2.4-1.8-3.9-4.2-4.7-7.2c-0.3-1.2-0.5-2.4-0.6-3.8
c-0.1-1.4-0.1-3.4-0.1-6c0-2.4,0-4.3,0.1-5.7c0.1-1.4,0.2-2.6,0.5-3.8c0.7-3,2.3-5.5,4.7-7.3c2.4-1.8,5.3-2.8,8.8-2.8
c3.6,0,6.7,0.9,9.1,2.8c2.4,1.9,4,4.4,4.6,7.6c0.2,0.9,0.3,2.1,0.4,3.5c0,1.4,0.1,3.2,0.1,5.6c0,0.4-0.2,0.6-0.6,0.6H74.7
c-0.2,0-0.2,0.1-0.2,0.2c0,1.8,0,3.3,0.1,4.7c0,1.4,0.2,2.4,0.3,3.1c0.5,2.7,1.8,4.8,3.8,6.5c2,1.6,4.6,2.5,7.7,2.5
C89.1,64.3,91.4,63.7,93.4,62.4z M78.6,31.8c-2,1.6-3.3,3.8-3.8,6.4c-0.3,1.2-0.4,3.5-0.4,6.9c0,0.2,0.1,0.2,0.2,0.2h23.3
c0.2,0,0.2-0.1,0.2-0.2c0-3.4-0.1-5.7-0.4-6.8c-0.6-2.7-1.9-4.8-3.9-6.5c-2-1.6-4.6-2.5-7.6-2.5C83.2,29.3,80.6,30.2,78.6,31.8z
"></path>
</g>
<g>
<path class="st15" d="M136.5,30.8c2.4,2.4,3.6,5.4,3.6,9.2v25.2c0,0.4-0.2,0.6-0.6,0.6h-1.1c-0.4,0-0.6-0.2-0.6-0.6V40.4
c0-3.2-1-5.9-3-7.9c-2-2-4.6-3.1-7.8-3.1c-3.4,0-6.1,1-8.2,3c-2.1,2-3.1,4.6-3.1,7.8v25c0,0.4-0.2,0.6-0.6,0.6H114
c-0.4,0-0.6-0.2-0.6-0.6V28.4c0-0.4,0.2-0.6,0.6-0.6h1.1c0.4,0,0.6,0.2,0.6,0.6V33c0,0.1,0,0.2,0.1,0.2c0,0.1,0.1,0,0.2-0.1
c1.1-1.9,2.6-3.3,4.6-4.3c1.9-1,4.2-1.5,6.8-1.5C131.1,27.3,134.2,28.5,136.5,30.8z"></path>
</g>
</g>
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="194.2343" y1="58.916" x2="157.4035" y2="11.5682">
<stop offset="0" style="stop-color:#09CDE7"></stop>
<stop offset="1" style="stop-color:#674BDE"></stop>
</linearGradient>
<path style="fill:url(#SVGID_1_);" d="M177,69.5c-12.6,0-22.8-10.2-22.8-22.8c0-3.2,2.6-5.8,5.8-5.8c3.2,0,5.8,2.6,5.8,5.8
c0,6.2,5.1,11.3,11.3,11.3s11.3-5,11.3-11.3c0-4.6-2.8-8.7-7-10.4c-3-1.2-4.4-4.6-3.2-7.5c1.2-3,4.6-4.4,7.5-3.2
c8.6,3.5,14.2,11.8,14.2,21.1C199.9,59.3,189.6,69.5,177,69.5z M193.8,13.3c0-3.2-2.6-5.8-5.8-5.8h-28c-3.2,0-5.8,2.6-5.8,5.8
s2.6,5.8,5.8,5.8h28C191.2,19,193.8,16.4,193.8,13.3z"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

BIN
ext/wasm3/extra/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

57
ext/wasm3/extra/testutils.py Executable file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env python3
# Author: Volodymyr Shymanskyy
import os
import re, fnmatch
import pathlib
class ansi:
ENDC = '\033[0m'
HEADER = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class dotdict(dict):
def __init__(self, *args, **kwargs):
super(dotdict, self).__init__(*args, **kwargs)
for arg in args:
if isinstance(arg, dict):
for k, v in arg.items():
self[k] = v
if kwargs:
for k, v in kwargs.items():
self[k] = v
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
class Blacklist():
def __init__(self, patterns):
self._patterns = list(map(fnmatch.translate, patterns))
self.update()
def add(self, patterns):
self._patterns += list(map(fnmatch.translate, patterns))
self.update()
def update(self):
self._regex = re.compile('|'.join(self._patterns))
def __contains__(self, item):
return self._regex.match(item) is not None
def filename(p):
_, fn = os.path.split(p)
return fn
def pathname(p):
pn, _ = os.path.split(p)
return pn
def ensure_path(p):
pathlib.Path(p).mkdir(parents=True, exist_ok=True)

39
ext/wasm3/extra/utils.mk Normal file
View File

@ -0,0 +1,39 @@
.PHONY: format
all:
format:
# Convert tabs to spaces
find ./source -type f -iname '*.[cpp\|c\|h]' -exec bash -c 'expand -t 4 "$$0" | sponge "$$0"' {} \;
# Remove trailing whitespaces
find ./source -type f -iname '*.[cpp\|c\|h]' -exec sed -i 's/ *$$//' {} \;
preprocess: preprocess_restore
cp ./source/m3_exec.h ./source/m3_exec.h.bak
cp ./source/m3_compile.c ./source/m3_compile.c.bak
awk 'BEGIN {RS=""}{gsub(/\\\n/,"\\\n__NL__ ")}1' ./source/m3_exec.h | sponge ./source/m3_exec.h
cpp -I./source -P ./source/m3_compile.c | sponge ./source/m3_compile.c
awk '{gsub(/__NL__/,"\n")}1' ./source/m3_compile.c | sponge ./source/m3_compile.c
mv ./source/m3_exec.h.bak ./source/m3_exec.h
preprocess_restore:
-mv source/m3_compile.c.bak source/m3_compile.c
touch source/m3_compile.c
test_rv64:
riscv64-linux-gnu-gcc -DDEBUG -Dd_m3HasWASI \
-I./source ./source/*.c ./platforms/app/main.c \
-O3 -g0 -flto -lm -static \
-o wasm3-rv64
cd test; python3 run-wasi-test.py --fast --exec "qemu-riscv64-static ../wasm3-rv64"
rm ./wasm3-rv64
test_cpp:
clang -xc++ -Dd_m3HasWASI \
-I./source ./source/*.c ./platforms/app/main.c \
-O3 -g0 -flto -lm -static \
-o wasm3-cpp
cd test; python3 run-wasi-test.py --fast --exec "../wasm3-cpp"
rm ./wasm3-cpp

View File

@ -0,0 +1,105 @@
# Wasm3
[Wasm3](https://github.com/wasm3/wasm3) is the fastest WebAssembly interpreter, and the most universal runtime.
It's packaged into a `WebAssembly` package, so you can finally run `WebAssembly` on `WebAssembly` 😆
## Running on WebAssembly.sh
Open [**WebAssembly.sh**](https://webassembly.sh)
First you need to fetch a wasm file you'd like to run:
```sh
$ curl https://raw.githubusercontent.com/wasm3/wasm3/main/test/lang/fib32.wasm -o /tmp/fib32.wasm
$ ls -l /tmp/fib32.wasm
---------- 1 somebody somegroup 62 1970-01-19 05:45 /tmp/fib32.wasm
```
Now we can run `wasm3` in interactive mode:
```sh
$ wasm3 --repl /tmp/fib32.wasm
wasm3> fib 20
Result: 6765
wasm3> fib 30
Result: 832040
wasm3> ^C
$
```
Or run a specific function directly:
```sh
$ wasm3 --func fib /tmp/fib32.wasm 30
Result: 832040
```
`wasm3` also supports `WASI`, so you can run:
```sh
$ curl https://raw.githubusercontent.com/wasm3/wasm3/main/test/wasi/simple/test.wasm -o /tmp/test.wasm
$ wasm3 /tmp/test.wasm
```
or even... run wasm3 inside wasm3:
```sh
$ curl https://registry-cdn.wapm.io/contents/vshymanskyy/wasm3/0.5.0/build/wasm3-wasi.wasm -o /tmp/wasm3.wasm
$ wasm3 --stack-size 100000 /tmp/wasm3.wasm /tmp/test.wasm
```
## Tracing
You can also get structured traces of arbitrary WASM file execution (and this requires no specific support from the runtime):
```sh
$ wasm3-strace --repl /tmp/fib32.wasm
wasm3> fib 3
fib (i32: 3) {
fib (i32: 1) {
} = 1
fib (i32: 2) {
fib (i32: 0) {
} = 0
fib (i32: 1) {
} = 1
} = 1
} = 2
Result: 2
```
WASI apps tracing is also supported:
```sh
$ wasm3-strace /tmp/test.wasm trap
_start () {
__wasm_call_ctors () {
__wasilibc_populate_preopens () {
wasi_snapshot_preview1!fd_prestat_get(3, 65528) { <native> } = 0
malloc (i32: 2) {
dlmalloc (i32: 2) {
sbrk (i32: 0) {
} = 131072
} = 70016
} = 70016
...
strcmp (i32: 70127, i32: 32) {
} = 0
test_trap () {
a () {
b () {
c () {
} !trap = [trap] unreachable executed
...
==== wasm backtrace:
0: 0x000c59 - .unnamed!c
1: 0x000c5e - .unnamed!b
2: 0x000c68 - .unnamed!a
3: 0x000c72 - .unnamed!test_trap
4: 0x000d2c - .unnamed!main
5: 0x0037c9 - .unnamed!__main_void
6: 0x000edb - .unnamed!__original_main
7: 0x0002f3 - .unnamed!_start
Error: [trap] unreachable executed
```

View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -0,0 +1,25 @@
[package]
name = "vshymanskyy/wasm3"
version = "0.5.0"
description = "🚀 The fastest WebAssembly interpreter. You can finally run WebAssembly on WebAssembly 😆"
readme = "README.md"
repository = "https://github.com/wasm3/wasm3"
homepage = "https://github.com/wasm3/wasm3"
[[module]]
name = "wasm3"
source = "build/wasm3-wasi.wasm"
abi = "wasi"
[[module]]
name = "wasm3-strace"
source = "build/wasm3-strace.wasm"
abi = "wasi"
[[command]]
name = "wasm3"
module = "wasm3"
[[command]]
name = "wasm3-strace"
module = "wasm3-strace"

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created by AtomCrusher for the English Wikipedia -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="612"
height="612"
id="svg6"
sodipodi:docname="wasm-symbol.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<path
d="m376 0c0 1.08 0 2.16 0 3.3 0 38.76-31.42 70.17-70.17 70.17-38.76 0-70.17-31.42-70.17-70.17l0 0c0-1.14 0-2.22 0-3.3L0 0l0 612 612 0 0-612z"
id="path2"
fill="#654ff0"
style="fill:#000000" />
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1876"
inkscape:window-height="1043"
id="namedview8"
showgrid="false"
inkscape:zoom="0.54535033"
inkscape:cx="194.41042"
inkscape:cy="320.43749"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="flowRoot816" />
<!-- Block -->
<!-- Letters -->
<g
aria-label="3"
transform="matrix(1.141234,0,0,1.141234,-389.85377,-108.04318)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:1.25;font-family:'padmaa-Bold.1.1';-inkscape-font-specification:'padmaa-Bold.1.1 Bold';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
id="flowRoot816">
<path
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:333.33334351px;font-family:Keraleeyam;-inkscape-font-specification:'Keraleeyam Bold';fill:#ffffff;stroke-width:1.14123404"
d="m 195.96484,329.72656 46.52149,216.68946 h 41 l 31.91015,-147.46875 h 0.7793 l 29.81055,147.46875 h 40.25 l 43.84765,-183.8125 h 74.51954 l -42.72266,57.02539 v 24.88867 c 6.06776,-1.11449 11.63969,-1.67188 16.7168,-1.67188 30.46262,0 45.69531,9.90761 45.69531,29.72071 0,26.1285 -16.2838,39.19336 -48.85156,39.19336 -13.993,0 -31.02128,-3.09552 -51.08203,-9.28711 v 35.47656 c 16.59346,5.82009 34.30255,8.73047 53.125,8.73047 24.64252,0 44.4558,-6.625 59.43945,-19.875 7.18224,-6.31542 12.69246,-13.93177 16.53125,-22.84766 3.83878,-8.91589 5.75781,-17.8302 5.75781,-26.74609 0,-35.04441 -17.64543,-55.29164 -52.9375,-60.74024 l 42.16406,-54.42382 v -32.32032 c -49.28217,0 -103.6131,0 -150.29297,0 l -31.56054,149.2793 h -0.58985 l -30.06054,-149.2793 h -37.93946 l -33.28125,147.47071 h -0.5 L 236.52539,329.72656 Z"
transform="matrix(0.87624449,0,0,0.87624449,341.60722,94.672241)"
id="path834"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccsssccscsscccccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
ext/wasm3/extra/wowcube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,9 @@
.gradle
# Build output directies
*/.externalNativeBuild
/target
*/target
/build
*/build
*/.cxx

View File

@ -0,0 +1,26 @@
## Build for Android
<p align="center"><img width="50%" src="https://github.com/wasm3/wasm3/raw/main/extra/screenshot-android.png"></p>
Install OpenJDK 15, [Android Command Line Tools](https://developer.android.com/studio#cmdline-tools), then:
```sh
export ANDROID_HOME=/opt/android-sdk/
export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH
```
Install NDK:
```sh
sdkmanager --install ndk-bundle platform-tools
```
Build:
```sh
./gradlew build
```
Install on device:
```
adb install -r ./app/build/outputs/apk/debug/app-debug.apk
```

View File

@ -0,0 +1,33 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
ndkVersion '22.1.7171670'
defaultConfig {
applicationId 'com.example.wasm3'
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
version '3.10.2+'
path "src/main/cpp/CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}

View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/gfan/dev/sdk_current/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wasm3">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity" android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.4.1)
add_definitions(-DANDROID -Wno-format-security -O3
#-fno-optimize-sibling-calls
-flto -fomit-frame-pointer -fno-stack-check -fno-stack-protector)
file(GLOB M3_SRC FOLLOW_SYMLINKS "m3/*.c" "*.c")
add_library(wasm3-jni SHARED ${M3_SRC})
set_target_properties(wasm3-jni PROPERTIES LINK_FLAGS "-flto -O3")
# Include libraries needed for wasm3-jni lib
target_link_libraries(wasm3-jni
android
log)

View File

@ -0,0 +1,96 @@
//
// Wasm3 - high performance WebAssembly interpreter written in C.
//
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
// All rights reserved.
//
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <pthread.h>
#include <unistd.h>
#include <jni.h>
extern int main();
/*
* JNI init
*/
JavaVM* javaVM;
JNIEnv* jniEnv;
jclass activityClz;
jobject activityObj;
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* reserved)
{
if ((*vm)->GetEnv(vm, (void**)&jniEnv, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR; // JNI version not supported
}
javaVM = vm;
return JNI_VERSION_1_6;
}
static int pfd[2];
static pthread_t pumpThread;
static pthread_t mainThread;
static void* runOutputPump(void* ctx)
{
int readSize;
char buff[128];
JNIEnv* env;
(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
jmethodID outputTextId = (*env)->GetMethodID(env, activityClz,
"outputText",
"(Ljava/lang/String;)V");
while ((readSize = read(pfd[0], buff, sizeof(buff) - 1)) > 0)
{
buff[readSize] = '\0';
jstring javaMsg = (*env)->NewStringUTF(env, buff);
(*env)->CallVoidMethod(env, activityObj, outputTextId, javaMsg);
(*env)->DeleteLocalRef(env, javaMsg);
}
return 0;
}
static void* runMain(void* ctx)
{
(*javaVM)->AttachCurrentThread(javaVM, &jniEnv, NULL);
main();
return NULL;
}
JNIEXPORT void JNICALL
Java_com_example_wasm3_MainActivity_runMain(JNIEnv* env, jobject instance)
{
setvbuf(stdout, 0, _IOLBF, 0); // stdout: line-buffered
setvbuf(stderr, 0, _IONBF, 0); // stderr: unbuffered
// create the pipe and redirect stdout and stderr
pipe(pfd);
dup2(pfd[1], 1);
dup2(pfd[1], 2);
jclass clz = (*env)->GetObjectClass(env, instance);
activityClz = (*env)->NewGlobalRef(env, clz);
activityObj = (*env)->NewGlobalRef(env, instance);
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
pthread_create( &pumpThread, &threadAttr, runOutputPump, NULL);
pthread_create( &mainThread, &threadAttr, runMain, NULL);
pthread_attr_destroy(&threadAttr);
}

View File

@ -0,0 +1 @@
../../../../../../source

View File

@ -0,0 +1,70 @@
//
// Wasm3 - high performance WebAssembly interpreter written in C.
//
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
// All rights reserved.
//
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "m3/wasm3.h"
#include "m3/m3_api_libc.h"
#include "m3/extra/coremark_minimal.wasm.h"
#define FATAL(msg, ...) { printf("Fatal: " msg "\n", ##__VA_ARGS__); return; }
void run_wasm()
{
M3Result result = m3Err_none;
uint8_t* wasm = (uint8_t*)coremark_minimal_wasm;
size_t fsize = coremark_minimal_wasm_len;
printf("Loading WebAssembly...\n");
IM3Environment env = m3_NewEnvironment ();
if (!env) FATAL("m3_NewEnvironment failed");
IM3Runtime runtime = m3_NewRuntime (env, 8192, NULL);
if (!runtime) FATAL("m3_NewRuntime failed");
IM3Module module;
result = m3_ParseModule (env, &module, wasm, fsize);
if (result) FATAL("m3_ParseModule: %s", result);
result = m3_LoadModule (runtime, module);
if (result) FATAL("m3_LoadModule: %s", result);
result = m3_LinkLibC (module);
if (result) FATAL("m3_LinkLibC: %s", result);
IM3Function f;
result = m3_FindFunction (&f, runtime, "run");
if (result) FATAL("m3_FindFunction: %s", result);
printf("Running CoreMark 1.0...\n");
result = m3_CallV (f);
if (result) FATAL("m3_Call: %s", result);
float value = 0;
result = m3_GetResultsV (f, &value);
if (result) FATAL("m3_GetResults: %s", result);
printf("Result: %0.3f\n", value);
}
int main()
{
printf("Wasm3 v" M3_VERSION " on Android (" M3_ARCH ")\n");
printf("Build " __DATE__ " " __TIME__ "\n");
clock_t start = clock();
run_wasm();
clock_t end = clock();
printf("Elapsed: %ld ms\n", (end - start)*1000 / CLOCKS_PER_SEC);
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.wasm3;
import androidx.annotation.Keep;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.console);
runMain();
}
@Keep
private void outputText(final String s) {
runOnUiThread(new Runnable() {
@Override
public void run() {
MainActivity.this.tv.append(s);
}
});
}
static {
System.loadLibrary("wasm3-jni");
}
public native void runMain();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
tools:context="com.example.wasm3.MainActivity">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
<ScrollView
android:id="@+id/scroll_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:scrollbars="none">
<TextView
android:id="@+id/console"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:typeface = "monospace"
android:textColor="@android:color/white"
/>
</ScrollView>
</HorizontalScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/black" />
<foreground android:drawable="@drawable/ic_launcher" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#654ff0</color>
<color name="colorPrimaryDark">#4b3bb3</color>
<color name="colorAccent">#bfd9cc</color>
</resources>

View File

@ -0,0 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">Wasm3</string>
</resources>

View File

@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@ -0,0 +1,25 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.enableJetifier=true
android.useAndroidX=true

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

183
ext/wasm3/platforms/android/gradlew vendored Executable file
View File

@ -0,0 +1,183 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

103
ext/wasm3/platforms/android/gradlew.bat vendored Normal file
View File

@ -0,0 +1,103 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,43 @@
/*
* Override printf, puts, putchar
*/
JNIEnv* jniEnv;
void callOutputText(const char* text) {
JNIEnv *env = jniEnv;
jstring javaMsg = (*env)->NewStringUTF(env, text);
(*env)->CallVoidMethod(env, activityObj, outputTextId, javaMsg);
(*env)->DeleteLocalRef(env, javaMsg);
}
int printf(const char * format, ... )
{
char buff[256] = {};
va_list args;
va_start (args, format);
const int result = vsnprintf(buff, sizeof(buff), format, args);
va_end (args);
if (result > 0) {
callOutputText(buff);
}
return result;
}
int puts(const char *s)
{
callOutputText(s);
callOutputText("\n");
return strlen(s);
}
int putchar(int c)
{
char buff[2] = { c, '\0' };
callOutputText(buff);
return c;
}

View File

@ -0,0 +1 @@
include ':app'

View File

@ -0,0 +1 @@
This is the main file for Linux, Windows, Mac OS, OpenWRT builds

View File

@ -0,0 +1,723 @@
//
// Wasm3 - high performance WebAssembly interpreter written in C.
//
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
// All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include "wasm3.h"
#include "m3_api_libc.h"
#if defined(d_m3HasWASI) || defined(d_m3HasMetaWASI) || defined(d_m3HasUVWASI)
#include "m3_api_wasi.h"
#define LINK_WASI
#endif
#if defined(d_m3HasTracer)
#include "m3_api_tracer.h"
#endif
// TODO: remove
#include "m3_env.h"
/*
* NOTE: Gas metering/limit only applies to pre-instrumented modules.
* You can generate a metered version from any wasm file automatically, using
* https://github.com/ewasm/wasm-metering
*/
#define GAS_LIMIT 500000000
#define GAS_FACTOR 10000LL
#define MAX_MODULES 16
#define FATAL(msg, ...) { fprintf(stderr, "Error: [Fatal] " msg "\n", ##__VA_ARGS__); goto _onfatal; }
static IM3Environment env;
static IM3Runtime runtime;
static u8* wasm_bins[MAX_MODULES];
static int wasm_bins_qty = 0;
#if defined(GAS_LIMIT)
static int64_t initial_gas = GAS_FACTOR * GAS_LIMIT;
static int64_t current_gas = GAS_FACTOR * GAS_LIMIT;
static bool is_gas_metered = false;
m3ApiRawFunction(metering_usegas)
{
m3ApiGetArg (int32_t, gas)
current_gas -= gas;
if (M3_UNLIKELY(current_gas < 0)) {
m3ApiTrap("[trap] Out of gas");
}
m3ApiSuccess();
}
#endif // GAS_LIMIT
M3Result link_all (IM3Module module)
{
M3Result res;
res = m3_LinkSpecTest (module);
if (res) return res;
res = m3_LinkLibC (module);
if (res) return res;
#if defined(LINK_WASI)
res = m3_LinkWASI (module);
if (res) return res;
#endif
#if defined(d_m3HasTracer)
res = m3_LinkTracer (module);
if (res) return res;
#endif
#if defined(GAS_LIMIT)
res = m3_LinkRawFunction (module, "metering", "usegas", "v(i)", &metering_usegas);
if (!res) {
fprintf(stderr, "Warning: Gas is limited to %0.4f\n", (double)(current_gas) / GAS_FACTOR);
is_gas_metered = true;
}
if (res == m3Err_functionLookupFailed) { res = NULL; }
#endif
return res;
}
const char* modname_from_fn(const char* fn)
{
const char* sep = "/\\:*?";
char c;
while ((c = *sep++)) {
const char* off = strrchr(fn, c) + 1;
fn = (fn < off) ? off : fn;
}
return fn;
}
M3Result repl_load (const char* fn)
{
M3Result result = m3Err_none;
IM3Module module = NULL;
u8* wasm = NULL;
u32 fsize = 0;
FILE* f = fopen (fn, "rb");
if (!f) {
return "cannot open file";
}
fseek (f, 0, SEEK_END);
fsize = ftell(f);
fseek (f, 0, SEEK_SET);
if (fsize < 8) {
result = "file is too small";
goto on_error;
} else if (fsize > 64*1024*1024) {
result = "file is too big";
goto on_error;
}
wasm = (u8*) malloc(fsize);
if (!wasm) {
result = "cannot allocate memory for wasm binary";
goto on_error;
}
if (fread (wasm, 1, fsize, f) != fsize) {
result = "cannot read file";
goto on_error;
}
fclose (f);
f = NULL;
result = m3_ParseModule (env, &module, wasm, fsize);
if (result) goto on_error;
result = m3_LoadModule (runtime, module);
if (result) goto on_error;
m3_SetModuleName(module, modname_from_fn(fn));
result = link_all (module);
if (result) goto on_error;
if (wasm_bins_qty < MAX_MODULES) {
wasm_bins[wasm_bins_qty++] = wasm;
}
return result;
on_error:
m3_FreeModule(module);
if (wasm) free(wasm);
if (f) fclose(f);
return result;
}
M3Result repl_load_hex (u32 fsize)
{
M3Result result = m3Err_none;
if (fsize < 8) {
return "file is too small";
} else if (fsize > 10*1024*1024) {
return "file too big";
}
u8* wasm = (u8*) malloc(fsize);
if (!wasm) {
return "cannot allocate memory for wasm binary";
}
{ // Load hex data from stdin
u32 wasm_idx = 0;
char hex[3] = { 0, };
int hex_idx = 0;
while (wasm_idx < fsize) {
int c = fgetc(stdin);
if (!isxdigit(c)) continue; // Skip non-hex chars
hex[hex_idx++] = c;
if (hex_idx == 2) {
int val = strtol(hex, NULL, 16);
wasm[wasm_idx++] = val;
hex_idx = 0;
}
}
if (!fgets(hex, 3, stdin)) // Consume a newline
return "cannot read EOL";
}
IM3Module module;
result = m3_ParseModule (env, &module, wasm, fsize);
if (result) return result;
result = m3_LoadModule (runtime, module);
if (result) return result;
result = link_all (module);
return result;
}
void print_gas_used()
{
#if defined(GAS_LIMIT)
if (is_gas_metered) {
fprintf(stderr, "Gas used: %0.4f\n", (double)(initial_gas - current_gas) / GAS_FACTOR);
}
#endif
}
void print_backtrace()
{
IM3BacktraceInfo info = m3_GetBacktrace(runtime);
if (!info) {
return;
}
fprintf(stderr, "==== wasm backtrace:");
int frameCount = 0;
IM3BacktraceFrame curr = info->frames;
while (curr)
{
fprintf(stderr, "\n %d: 0x%06x - %s!%s",
frameCount, curr->moduleOffset,
m3_GetModuleName (m3_GetFunctionModule(curr->function)),
m3_GetFunctionName (curr->function)
);
curr = curr->next;
frameCount++;
}
if (info->lastFrame == M3_BACKTRACE_TRUNCATED) {
fprintf(stderr, "\n (truncated)");
}
fprintf(stderr, "\n");
}
M3Result repl_call (const char* name, int argc, const char* argv[])
{
IM3Function func;
M3Result result = m3_FindFunction (&func, runtime, name);
if (result) return result;
if (argc && (!strcmp(name, "main") || !strcmp(name, "_main"))) {
return "passing arguments to libc main() not implemented";
}
if (!strcmp(name, "_start")) {
#if defined(LINK_WASI)
// Strip wasm file path
if (argc > 0) {
argv[0] = modname_from_fn(argv[0]);
}
m3_wasi_context_t* wasi_ctx = m3_GetWasiContext();
wasi_ctx->argc = argc;
wasi_ctx->argv = argv;
result = m3_CallArgv(func, 0, NULL);
print_gas_used();
if (result == m3Err_trapExit) {
exit(wasi_ctx->exit_code);
}
return result;
#else
return "WASI not linked";
#endif
}
int arg_count = m3_GetArgCount(func);
int ret_count = m3_GetRetCount(func);
if (argc < arg_count) {
return "not enough arguments";
} else if (argc > arg_count) {
return "too many arguments";
}
result = m3_CallArgv (func, argc, argv);
print_gas_used();
if (result) return result;
static uint64_t valbuff[128];
static const void* valptrs[128];
memset(valbuff, 0, sizeof(valbuff));
for (int i = 0; i < ret_count; i++) {
valptrs[i] = &valbuff[i];
}
result = m3_GetResults (func, ret_count, valptrs);
if (result) return result;
if (ret_count <= 0) {
fprintf (stderr, "Result: <Empty Stack>\n");
}
for (int i = 0; i < ret_count; i++) {
switch (m3_GetRetType(func, i)) {
case c_m3Type_i32: fprintf (stderr, "Result: %" PRIi32 "\n", *(i32*)valptrs[i]); break;
case c_m3Type_i64: fprintf (stderr, "Result: %" PRIi64 "\n", *(i64*)valptrs[i]); break;
# if d_m3HasFloat
case c_m3Type_f32: fprintf (stderr, "Result: %" PRIf32 "\n", *(f32*)valptrs[i]); break;
case c_m3Type_f64: fprintf (stderr, "Result: %" PRIf64 "\n", *(f64*)valptrs[i]); break;
# endif
default: return "unknown return type";
}
}
return result;
}
// :invoke is used by spec tests, so it treats floats as raw data
M3Result repl_invoke (const char* name, int argc, const char* argv[])
{
IM3Function func;
M3Result result = m3_FindFunction (&func, runtime, name);
if (result) return result;
int arg_count = m3_GetArgCount(func);
int ret_count = m3_GetRetCount(func);
if (argc > 128) {
return "arguments limit reached";
} else if (argc < arg_count) {
return "not enough arguments";
} else if (argc > arg_count) {
return "too many arguments";
}
static uint64_t valbuff[128];
static const void* valptrs[128];
memset(valbuff, 0, sizeof(valbuff));
memset(valptrs, 0, sizeof(valptrs));
for (int i = 0; i < argc; i++) {
u64* s = &valbuff[i];
valptrs[i] = s;
switch (m3_GetArgType(func, i)) {
case c_m3Type_i32:
case c_m3Type_f32: *(u32*)(s) = strtoul(argv[i], NULL, 10); break;
case c_m3Type_i64:
case c_m3Type_f64: *(u64*)(s) = strtoull(argv[i], NULL, 10); break;
default: return "unknown argument type";
}
}
result = m3_Call (func, argc, valptrs);
if (result) return result;
// reuse valbuff for return values
memset(valbuff, 0, sizeof(valbuff));
for (int i = 0; i < ret_count; i++) {
valptrs[i] = &valbuff[i];
}
result = m3_GetResults (func, ret_count, valptrs);
if (result) return result;
fprintf (stderr, "Result: ");
if (ret_count <= 0) {
fprintf (stderr, "<Empty Stack>");
}
for (int i = 0; i < ret_count; i++) {
switch (m3_GetRetType(func, i)) {
case c_m3Type_i32: fprintf (stderr, "%" PRIu32 ":i32", *(u32*)valptrs[i]); break;
case c_m3Type_f32: fprintf (stderr, "%" PRIu32 ":f32", *(u32*)valptrs[i]); break;
case c_m3Type_i64: fprintf (stderr, "%" PRIu64 ":i64", *(u64*)valptrs[i]); break;
case c_m3Type_f64: fprintf (stderr, "%" PRIu64 ":f64", *(u64*)valptrs[i]); break;
default: return "unknown return type";
}
if (i != ret_count-1) {
fprintf (stderr, ", ");
}
}
fprintf (stderr, "\n");
return result;
}
M3Result repl_global_get (const char* name)
{
IM3Global g = m3_FindGlobal(runtime->modules, name);
M3TaggedValue tagged;
M3Result err = m3_GetGlobal (g, &tagged);
if (err) return err;
switch (tagged.type) {
case c_m3Type_i32: fprintf (stderr, "%" PRIu32 ":i32\n", tagged.value.i32); break;
case c_m3Type_i64: fprintf (stderr, "%" PRIu64 ":i64\n", tagged.value.i64); break;
case c_m3Type_f32: fprintf (stderr, "%" PRIf32 ":f32\n", tagged.value.f32); break;
case c_m3Type_f64: fprintf (stderr, "%" PRIf64 ":f64\n", tagged.value.f64); break;
default: return m3Err_invalidTypeId;
}
return m3Err_none;
}
M3Result repl_global_set (const char* name, const char* value)
{
IM3Global g = m3_FindGlobal(runtime->modules, name);
M3TaggedValue tagged = {
.type = m3_GetGlobalType(g)
};
switch (tagged.type) {
case c_m3Type_i32: tagged.value.i32 = strtoul(value, NULL, 10); break;
case c_m3Type_i64: tagged.value.i64 = strtoull(value, NULL, 10); break;
case c_m3Type_f32: tagged.value.f32 = strtod(value, NULL); break;
case c_m3Type_f64: tagged.value.f64 = strtod(value, NULL); break;
default: return m3Err_invalidTypeId;
}
return m3_SetGlobal (g, &tagged);
}
M3Result repl_compile ()
{
return m3_CompileModule(runtime->modules);
}
M3Result repl_dump ()
{
uint32_t len;
uint8_t* mem = m3_GetMemory(runtime, &len, 0);
if (mem) {
FILE* f = fopen ("wasm3_dump.bin", "wb");
if (!f) {
return "cannot open file";
}
if (fwrite (mem, 1, len, f) != len) {
return "cannot write file";
}
fclose (f);
}
return m3Err_none;
}
void repl_free ()
{
if (runtime) {
m3_FreeRuntime (runtime);
runtime = NULL;
}
for (int i = 0; i < wasm_bins_qty; i++) {
free (wasm_bins[i]);
wasm_bins[i] = NULL;
}
}
M3Result repl_init (unsigned stack)
{
repl_free();
runtime = m3_NewRuntime (env, stack, NULL);
if (runtime == NULL) {
return "m3_NewRuntime failed";
}
return m3Err_none;
}
static
void unescape(char* buff)
{
char* outp = buff;
while (*buff) {
if (*buff == '\\') {
switch (*(buff+1)) {
case '0': *outp++ = '\0'; break;
case 'b': *outp++ = '\b'; break;
case 'n': *outp++ = '\n'; break;
case 'r': *outp++ = '\r'; break;
case 't': *outp++ = '\t'; break;
case 'x': {
char hex[3] = { *(buff+2), *(buff+3), '\0' };
*outp = strtol(hex, NULL, 16);
buff += 2; outp += 1;
break;
}
// Otherwise just pass the letter
// Also handles '\\'
default: *outp++ = *(buff+1); break;
}
buff += 2;
} else {
*outp++ = *buff++;
}
}
*outp = '\0';
}
static
int split_argv(char *str, char** argv)
{
int result = 0;
char* curr = str;
int len = 0;
for (int i = 0; str[i] != '\0'; i++) {
if (strchr(" \n\r\t", str[i])) {
if (len) { // Found space after non-space
str[i] = '\0';
//unescape(curr); // TODO: breaks windows build?
argv[result++] = curr;
len = 0;
}
} else {
if (!len) { // Found non-space after space
curr = &str[i];
}
len++;
}
}
argv[result] = NULL;
return result;
}
void print_version() {
const char* wasm3_env = getenv("WASM3");
const char* wasm3_arch = getenv("WASM3_ARCH");
printf("Wasm3 v" M3_VERSION "%s on %s\n",
(wasm3_arch || wasm3_env) ? " self-hosting" : "",
wasm3_arch ? wasm3_arch : M3_ARCH);
printf("Build: " __DATE__ " " __TIME__ ", " M3_COMPILER_VER "\n");
}
void print_usage() {
puts("Usage:");
puts(" wasm3 [options] <file> [args...]");
puts(" wasm3 --repl [file]");
puts("Options:");
puts(" --func <function> function to run default: _start");
puts(" --stack-size <size> stack size in bytes default: 64KB");
puts(" --compile disable lazy compilation");
puts(" --dump-on-trap dump wasm memory");
puts(" --gas-limit set gas limit");
}
#define ARGV_SHIFT() { i_argc--; i_argv++; }
#define ARGV_SET(x) { if (i_argc > 0) { x = i_argv[0]; ARGV_SHIFT(); } }
int main (int i_argc, const char* i_argv[])
{
M3Result result = m3Err_none;
env = m3_NewEnvironment ();
runtime = NULL;
bool argRepl = false;
bool argDumpOnTrap = false;
bool argCompile = false;
const char* argFile = NULL;
const char* argFunc = "_start";
unsigned argStackSize = 64*1024;
// m3_PrintM3Info ();
ARGV_SHIFT(); // Skip executable name
while (i_argc > 0)
{
const char* arg = i_argv[0];
if (arg[0] != '-') break;
ARGV_SHIFT();
if (!strcmp("--help", arg) or !strcmp("-h", arg)) {
print_usage();
return 0;
} else if (!strcmp("--version", arg)) {
print_version();
return 0;
} else if (!strcmp("--repl", arg)) {
argRepl = true;
} else if (!strcmp("--dump-on-trap", arg)) {
argDumpOnTrap = true;
} else if (!strcmp("--compile", arg)) {
argCompile = true;
} else if (!strcmp("--stack-size", arg)) {
const char* tmp = "65536";
ARGV_SET(tmp);
argStackSize = atol(tmp);
} else if (!strcmp("--gas-limit", arg)) {
const char* tmp = "0";
ARGV_SET(tmp);
initial_gas = current_gas = GAS_FACTOR * atol(tmp);
} else if (!strcmp("--dir", arg)) {
const char* argDir;
ARGV_SET(argDir);
(void)argDir;
} else if (!strcmp("--func", arg) or !strcmp("-f", arg)) {
ARGV_SET(argFunc);
}
}
if ((argRepl and (i_argc > 1)) or // repl supports 0 or 1 args
(not argRepl and (i_argc < 1)) // normal expects at least 1
) {
print_usage();
return 1;
}
ARGV_SET(argFile);
result = repl_init(argStackSize);
if (result) FATAL("repl_init: %s", result);
if (argFile) {
result = repl_load(argFile);
if (result) FATAL("repl_load: %s", result);
if (argCompile) {
repl_compile();
}
if (argFunc and not argRepl) {
if (!strcmp(argFunc, "_start")) {
// When passing args to WASI, include wasm filename as argv[0]
result = repl_call(argFunc, i_argc+1, i_argv-1);
} else {
result = repl_call(argFunc, i_argc, i_argv);
}
if (result) {
if (argDumpOnTrap) {
repl_dump();
}
print_backtrace();
goto _onfatal;
}
}
}
while (argRepl)
{
char cmd_buff[2048] = { 0, };
char* argv[32] = { 0, };
fprintf(stdout, "wasm3> ");
fflush(stdout);
if (!fgets(cmd_buff, sizeof(cmd_buff), stdin)) {
return 0;
}
int argc = split_argv(cmd_buff, argv);
if (argc <= 0) {
continue;
}
result = m3Err_none;
if (!strcmp(":init", argv[0])) {
result = repl_init(argStackSize);
} else if (!strcmp(":version", argv[0])) {
print_version();
} else if (!strcmp(":exit", argv[0])) {
repl_free();
return 0;
} else if (!strcmp(":load", argv[0])) { // :load <filename>
result = repl_load(argv[1]);
} else if (!strcmp(":load-hex", argv[0])) { // :load-hex <size>\n <hex-encoded-binary>
result = repl_load_hex(atol(argv[1]));
} else if (!strcmp(":get-global", argv[0])) {
result = repl_global_get(argv[1]);
} else if (!strcmp(":set-global", argv[0])) {
result = repl_global_set(argv[1], argv[2]);
} else if (!strcmp(":dump", argv[0])) {
result = repl_dump();
} else if (!strcmp(":compile", argv[0])) {
result = repl_compile();
} else if (!strcmp(":invoke", argv[0])) {
unescape(argv[1]);
result = repl_invoke(argv[1], argc-2, (const char**)(argv+2));
} else if (argv[0][0] == ':') {
result = "no such command";
} else {
unescape(argv[0]);
result = repl_call(argv[0], argc-1, (const char**)(argv+1));
if (result) {
print_backtrace();
}
}
if (result == m3Err_trapExit) {
//TODO: fprintf(stderr, M3_ARCH "-wasi: exit(%d)\n", runtime->exit_code);
} else if (result) {
fprintf (stderr, "Error: %s", result);
M3ErrorInfo info;
m3_GetErrorInfo (runtime, &info);
fprintf (stderr, " (%s)\n", info.message);
}
}
_onfatal:
if (result) {
fprintf (stderr, "Error: %s", result);
if (runtime)
{
M3ErrorInfo info;
m3_GetErrorInfo (runtime, &info);
if (strlen(info.message)) {
fprintf (stderr, " (%s)", info.message);
}
}
fprintf (stderr, "\n");
}
m3_FreeRuntime (runtime);
m3_FreeEnvironment (env);
return result ? 1 : 0;
}

View File

@ -0,0 +1,49 @@
//
// Wasm3 - high performance WebAssembly interpreter written in C.
//
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
// All rights reserved.
//
#include <stdint.h>
#include <stddef.h>
#include "wasm3.h"
#define FATAL(...) __builtin_trap()
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
M3Result result = m3Err_none;
if (size < 8 || size > 256*1024) {
return 0;
}
IM3Environment env = m3_NewEnvironment ();
if (env) {
IM3Runtime runtime = m3_NewRuntime (env, 128, NULL);
if (runtime) {
IM3Module module = NULL;
result = m3_ParseModule (env, &module, data, size);
if (module) {
result = m3_LoadModule (runtime, module);
if (result == 0) {
IM3Function f = NULL;
result = m3_FindFunction (&f, runtime, "fib");
/* TODO:
if (f) {
m3_CallV (f, 10);
}*/
} else {
m3_FreeModule (module);
}
}
m3_FreeRuntime(runtime);
}
m3_FreeEnvironment(env);
}
return 0;
}

View File

@ -0,0 +1,4 @@
cosmopolitan/
wasm3.com
wasm3.com.dbg

View File

@ -0,0 +1,41 @@
COSMOPOLITAN_VERSION=1.0
COSMOPOLITAN_URL=https://github.com/jart/cosmopolitan/releases/download/$COSMOPOLITAN_VERSION/cosmopolitan-amalgamation-$COSMOPOLITAN_VERSION.zip
SOURCE_DIR=../../source
EXTRA_FLAGS="-Dd_m3PreferStaticAlloc -Dd_m3HasWASI"
STD=./cosmopolitan/std
if [ ! -d "./cosmopolitan" ]; then
echo "Downloading Cosmopolitan..."
curl -L -o cosmopolitan.zip $COSMOPOLITAN_URL
unzip cosmopolitan.zip -d cosmopolitan
rm cosmopolitan.zip
fi
if [ ! -d "$STD/sys" ]; then
# Generate header stubs
mkdir -p $STD/sys
touch $STD/assert.h $STD/limits.h $STD/ctype.h $STD/time.h $STD/errno.h \
$STD/inttypes.h $STD/fcntl.h $STD/math.h $STD/stdarg.h $STD/stdbool.h \
$STD/stdint.h $STD/stdio.h $STD/stdlib.h $STD/string.h $STD/stddef.h \
$STD/sys/types.h $STD/sys/stat.h $STD/unistd.h $STD/sys/uio.h \
$STD/sys/random.h
fi
echo "Building Wasm3..."
# TODO: remove -fno-strict-aliasing
gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \
-Wl,--gc-sections -Wl,-z,max-page-size=0x1000 -fuse-ld=bfd \
-Wl,-T,cosmopolitan/ape.lds -include cosmopolitan/cosmopolitan.h \
-Wno-format-security -Wfatal-errors -fno-strict-aliasing $EXTRA_FLAGS \
-fomit-frame-pointer -fno-stack-check -fno-stack-protector \
-o wasm3.com.dbg -DAPE -I$STD -I$SOURCE_DIR $SOURCE_DIR/*.c ../app/main.c \
cosmopolitan/crt.o cosmopolitan/ape.o cosmopolitan/cosmopolitan.a
objcopy -SO binary wasm3.com.dbg wasm3.com

2
ext/wasm3/platforms/cpp/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build
cmake-build-debug

View File

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.9)
project(wasm3_cpp_example)
set(target ${CMAKE_PROJECT_NAME})
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../source ${CMAKE_BINARY_DIR}/m3)
add_executable(${target} main.cpp)
target_link_libraries(${target} PRIVATE m3)
add_subdirectory(wasm3_cpp)
target_link_libraries(${target} PRIVATE wasm3_cpp)
target_compile_options(${target} PUBLIC -g)
target_compile_options(m3 PUBLIC -g)
# Copy the 'wasm' directory into the build directory, so that
# wasm/test_prog.wasm is found even if wasm3_cpp_example is executed
# from the build directory.
add_custom_target(copy_wasm ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_LIST_DIR}/wasm
${CMAKE_BINARY_DIR}/wasm
)
add_dependencies(${CMAKE_PROJECT_NAME} copy_wasm)

View File

@ -0,0 +1,98 @@
## C++ wrapper
This example shows how to embed WASM3 into a C++ application. It uses a header-only library, `wasm3_cpp.h`, provided in `wasm3_cpp` subdirectory. Like WASM3 itself, this library can be included into CMake projects using `add_subdirectory` function.
The main code of the example in `main.cpp` initializes WASM3, loads a WebAssembly module, links two external functions to the module, and executes two functions defined in WebAssembly.
The WebAssembly module source code is inside `wasm` subdirectory.
### `wasm3_cpp.h` reference
All the classes are located in `wasm3` namespace.
#### Class `environment`
`environment::environment()` — create a new WASM3 environment. Runtimes, modules are owned by an environment.
`runtime environment::new_runtime(size_t stack_size_bytes)` — create new runtime inside the environment.
`module environment::parse_module(std::istream &in)` or `module environment::parse_module(const uint8_t *data, size_t size)` — parse a WASM binary module.
#### Class `runtime`
`runtime` objects are created using `environment::new_runtime` method, see above.
`void runtime::load(module &m)` — load a parsed module into the runtime.
`function runtime::find_function(const char *name)` — find a function defined in one of the loaded modules, by name. Raises a `wasm3::error` exception if the function is not found.
#### Class `module`
`module` objects are created by `environment::parse_module`. Parsed modules can be loaded into a `runtime` object. One module can only be loaded into one runtime.
Before loading a module, you may need to link some external functions to it:
`template <Func> void module::link(const char *mod, const char *function_name, Func *function)` — link a function `function` to module named `mod` under the name `function_name`. To link to any module, use `mod="*"`.
`function` has to be either a non-member function or a static member function.
Currently, the following types of arguments can be passed to functions linked this way:
* int32_t
* int64_t
* float
* double
* const/non-const pointers
Automatic conversion of other integral types may be implemented in the future.
If the module doesn't reference an imported function named `func`, an exception is thrown. To link a function "optionally", i.e. without throwing an exception if the function is not imported, use `module::link_optional` instead.
#### Class `function`
`function` object can be obtained from a `runtime`, looking up the function by name. Function objects are used to call WebAssembly functions.
`template <typename Ret = void, typename ...Args> Ret function::call(Args...)` — calls a WebAssembly function with or without arguments and a return value.<br>
The return value of the function, if not `void`, is automatically converted to the type `Ret`.<br>
Note that you always need to specify the matching return type when using this template with a non-void function.<br>
Examples:
```cpp
// WASM signature: [] → []
func.call();
// WASM signature: [i32, i32] → []
func.call(42, 43); // implicit argument types
// WASM signature: [i32, i64] → []
func.call<void, int32_t, int64_t>(42, 43); // explicit argument types require the return type
// WASM signature: [] → [i32]
auto result = func.call<int32_t>();
// WASM signature: [i32, i32] → [i64]
auto result = func.call<int64_t>(42, 43); // implicit argument types
// WASM signature: [i32, i64] → [i64]
auto result = func.call<int64_t, int32_t, int64_t>(42, 43); // explicit argument types
```
`template <typename Ret, typename ...Args> Ret function::call_argv(Args...)` — same as above, except that this function takes arguments as C strings (`const char*`).
### Building and running
This directory is a CMake project, and can be built as follows:
```bash
mkdir build
cd build
cmake ..
cmake --build .
```
Then run the example:
```bash
./wasm3_cpp_example
```

View File

@ -0,0 +1,89 @@
#include <cstdio>
#include <cstring>
#include <fstream>
#include "wasm3_cpp.h"
#include "wasm/test_prog.wasm.h"
int sum(int a, int b)
{
return a + b;
}
void * ext_memcpy (void* dst, const void* arg, int32_t size)
{
return memcpy(dst, arg, (size_t) size);
}
int main(void)
{
std::cout << "Loading WebAssembly..." << std::endl;
/* Wasm module can be loaded from a file */
try {
wasm3::environment env;
wasm3::runtime runtime = env.new_runtime(1024);
const char* file_name = "wasm/test_prog.wasm";
std::ifstream wasm_file(file_name, std::ios::binary | std::ios::in);
if (!wasm_file.is_open()) {
throw std::runtime_error("Failed to open wasm file");
}
wasm3::module mod = env.parse_module(wasm_file);
runtime.load(mod);
}
catch(std::runtime_error &e) {
std::cerr << "WASM3 error: " << e.what() << std::endl;
return 1;
}
/* Wasm module can also be loaded from an array */
try {
wasm3::environment env;
wasm3::runtime runtime = env.new_runtime(1024);
wasm3::module mod = env.parse_module(test_prog_wasm, test_prog_wasm_len);
runtime.load(mod);
mod.link("*", "sum", sum);
mod.link("*", "ext_memcpy", ext_memcpy);
{
wasm3::function test_fn = runtime.find_function("test");
auto res = test_fn.call<int>(20, 10);
std::cout << "result: " << res << std::endl;
}
{
wasm3::function memcpy_test_fn = runtime.find_function("test_memcpy");
auto res = memcpy_test_fn.call<int64_t>();
std::cout << "result: 0x" << std::hex << res << std::dec << std::endl;
}
/**
* Calling functions that modify an internal state, with mixed argument / return types
*/
{
wasm3::function counter_get_fn = runtime.find_function("test_counter_get");
wasm3::function counter_inc_fn = runtime.find_function("test_counter_inc");
wasm3::function counter_add_fn = runtime.find_function("test_counter_add");
// call with no arguments and a return value
auto value = counter_get_fn.call<int32_t>();
std::cout << "counter: " << value << std::endl;
// call with no arguments and no return value
counter_inc_fn.call();
value = counter_get_fn.call<int32_t>();
std::cout << "counter after increment: " << value << std::endl;
// call with one argument and no return value
counter_add_fn.call(42);
value = counter_get_fn.call<int32_t>();
std::cout << "counter after adding value: " << value << std::endl;
}
}
catch(wasm3::error &e) {
std::cerr << "WASM3 error: " << e.what() << std::endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,18 @@
NAME=test_prog
SRC=$(NAME).c
WASM=$(NAME).wasm
HEADER=$(NAME).wasm.h
EMCC_FLAGS=-s STANDALONE_WASM -s ERROR_ON_UNDEFINED_SYMBOLS=0 -O3
all: $(HEADER)
clean:
rm -f $(HEADER) $(WASM)
.PHONY: all clean
$(WASM): $(SRC)
emcc $< -o $@ $(EMCC_FLAGS)
$(HEADER): $(WASM)
xxd -i $< >$@

View File

@ -0,0 +1,41 @@
#include <stddef.h>
#include <stdint.h>
extern int sum(int, int);
extern int ext_memcpy(void*, const void*, size_t);
int32_t counter = 0;
#define WASM_EXPORT __attribute__((used)) __attribute__((visibility ("default")))
int WASM_EXPORT test(int32_t arg1, int32_t arg2)
{
int x = arg1 + arg2;
int y = arg1 - arg2;
return sum(x, y) / 2;
}
int64_t WASM_EXPORT test_memcpy(void)
{
int64_t x = 0;
int32_t low = 0x01234567;
int32_t high = 0x89abcdef;
ext_memcpy(&x, &low, 4);
ext_memcpy(((uint8_t*)&x) + 4, &high, 4);
return x;
}
int32_t WASM_EXPORT test_counter_get()
{
return counter;
}
void WASM_EXPORT test_counter_inc()
{
++counter;
}
void WASM_EXPORT test_counter_add(int32_t inc_value)
{
counter += inc_value;
}

View File

@ -0,0 +1,68 @@
unsigned char test_prog_wasm[] = {
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x22, 0x07, 0x60,
0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x00, 0x00,
0x60, 0x00, 0x01, 0x7f, 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60,
0x00, 0x01, 0x7e, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x94, 0x01, 0x06,
0x03, 0x65, 0x6e, 0x76, 0x03, 0x73, 0x75, 0x6d, 0x00, 0x00, 0x03, 0x65,
0x6e, 0x76, 0x0a, 0x65, 0x78, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x63, 0x70,
0x79, 0x00, 0x04, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61,
0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65,
0x77, 0x31, 0x0e, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65,
0x73, 0x5f, 0x67, 0x65, 0x74, 0x00, 0x00, 0x16, 0x77, 0x61, 0x73, 0x69,
0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72,
0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x08, 0x61, 0x72, 0x67, 0x73, 0x5f,
0x67, 0x65, 0x74, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x04, 0x6d, 0x61,
0x69, 0x6e, 0x00, 0x00, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e,
0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69,
0x65, 0x77, 0x31, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x65, 0x78, 0x69,
0x74, 0x00, 0x01, 0x03, 0x0c, 0x0b, 0x02, 0x00, 0x05, 0x03, 0x02, 0x01,
0x02, 0x03, 0x01, 0x06, 0x03, 0x04, 0x05, 0x01, 0x70, 0x01, 0x02, 0x02,
0x05, 0x06, 0x01, 0x01, 0x80, 0x02, 0x80, 0x02, 0x06, 0x09, 0x01, 0x7f,
0x01, 0x41, 0x90, 0x88, 0xc0, 0x02, 0x0b, 0x07, 0xb8, 0x01, 0x0c, 0x06,
0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x00, 0x07, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x6d,
0x63, 0x70, 0x79, 0x00, 0x08, 0x10, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x67, 0x65, 0x74, 0x00, 0x09,
0x10, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
0x72, 0x5f, 0x69, 0x6e, 0x63, 0x00, 0x0a, 0x10, 0x74, 0x65, 0x73, 0x74,
0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64,
0x00, 0x0b, 0x19, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x72, 0x65, 0x63,
0x74, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
0x61, 0x62, 0x6c, 0x65, 0x01, 0x00, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72,
0x74, 0x00, 0x0c, 0x10, 0x5f, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x5f,
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x10, 0x09, 0x73,
0x74, 0x61, 0x63, 0x6b, 0x53, 0x61, 0x76, 0x65, 0x00, 0x0d, 0x0c, 0x73,
0x74, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x00,
0x0e, 0x0a, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x6c, 0x6f, 0x63,
0x00, 0x0f, 0x09, 0x07, 0x01, 0x00, 0x41, 0x01, 0x0b, 0x01, 0x06, 0x0a,
0xd1, 0x02, 0x0b, 0x03, 0x00, 0x01, 0x0b, 0x11, 0x00, 0x20, 0x00, 0x20,
0x01, 0x6a, 0x20, 0x00, 0x20, 0x01, 0x6b, 0x10, 0x00, 0x41, 0x02, 0x6d,
0x0b, 0x58, 0x02, 0x02, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x10, 0x6b,
0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, 0x08, 0x20,
0x00, 0x41, 0xe7, 0x8a, 0x8d, 0x09, 0x36, 0x02, 0x04, 0x20, 0x00, 0x41,
0xef, 0x9b, 0xaf, 0xcd, 0x78, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08,
0x6a, 0x22, 0x01, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x41, 0x04, 0x10, 0x01,
0x1a, 0x20, 0x01, 0x41, 0x04, 0x72, 0x20, 0x00, 0x41, 0x04, 0x10, 0x01,
0x1a, 0x20, 0x00, 0x29, 0x03, 0x08, 0x21, 0x02, 0x20, 0x00, 0x41, 0x10,
0x6a, 0x24, 0x00, 0x20, 0x02, 0x0b, 0x08, 0x00, 0x41, 0x80, 0x08, 0x28,
0x02, 0x00, 0x0b, 0x11, 0x00, 0x41, 0x80, 0x08, 0x41, 0x80, 0x08, 0x28,
0x02, 0x00, 0x41, 0x01, 0x6a, 0x36, 0x02, 0x00, 0x0b, 0x11, 0x00, 0x41,
0x80, 0x08, 0x41, 0x80, 0x08, 0x28, 0x02, 0x00, 0x20, 0x00, 0x6a, 0x36,
0x02, 0x00, 0x0b, 0x8f, 0x01, 0x01, 0x04, 0x7f, 0x02, 0x7f, 0x23, 0x00,
0x41, 0x10, 0x6b, 0x22, 0x00, 0x24, 0x00, 0x02, 0x40, 0x20, 0x00, 0x22,
0x01, 0x41, 0x0c, 0x6a, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x10, 0x02, 0x45,
0x04, 0x40, 0x20, 0x01, 0x28, 0x02, 0x0c, 0x22, 0x02, 0x04, 0x7f, 0x20,
0x00, 0x20, 0x02, 0x41, 0x02, 0x74, 0x22, 0x03, 0x41, 0x13, 0x6a, 0x41,
0x70, 0x71, 0x6b, 0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x20, 0x01, 0x28,
0x02, 0x08, 0x41, 0x0f, 0x6a, 0x41, 0x70, 0x71, 0x6b, 0x22, 0x02, 0x24,
0x00, 0x20, 0x00, 0x20, 0x03, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20,
0x00, 0x20, 0x02, 0x10, 0x03, 0x0d, 0x02, 0x20, 0x01, 0x28, 0x02, 0x0c,
0x05, 0x41, 0x00, 0x0b, 0x20, 0x00, 0x10, 0x04, 0x21, 0x00, 0x20, 0x01,
0x41, 0x10, 0x6a, 0x24, 0x00, 0x20, 0x00, 0x0c, 0x02, 0x0b, 0x41, 0xc7,
0x00, 0x10, 0x05, 0x00, 0x0b, 0x41, 0xc7, 0x00, 0x10, 0x05, 0x00, 0x0b,
0x10, 0x05, 0x00, 0x0b, 0x04, 0x00, 0x23, 0x00, 0x0b, 0x06, 0x00, 0x20,
0x00, 0x24, 0x00, 0x0b, 0x10, 0x00, 0x23, 0x00, 0x20, 0x00, 0x6b, 0x41,
0x70, 0x71, 0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x0b, 0x05, 0x00, 0x41,
0x84, 0x08, 0x0b
};
unsigned int test_prog_wasm_len = 771;

View File

@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.5)
add_library(wasm3_cpp INTERFACE)
target_include_directories(wasm3_cpp INTERFACE include)
target_compile_features(wasm3_cpp INTERFACE cxx_std_17)

View File

@ -0,0 +1,412 @@
#pragma once
#include <tuple>
#include <algorithm>
#include <type_traits>
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <iterator>
#include <cassert>
#include "wasm3.h"
namespace wasm3 {
/** @cond */
namespace detail {
typedef uint64_t *stack_type;
typedef void *mem_type;
template<typename T, typename...> struct first_type { typedef T type; };
typedef const void *(*m3_api_raw_fn)(IM3Runtime, uint64_t *, void *);
template<typename T>
void arg_from_stack(T &dest, stack_type &_sp, mem_type mem) {
m3ApiGetArg(T, tmp);
dest = tmp;
}
template<typename T>
void arg_from_stack(T* &dest, stack_type &_sp, mem_type _mem) {
m3ApiGetArgMem(T*, tmp);
dest = tmp;
};
template<typename T>
void arg_from_stack(const T* &dest, stack_type &_sp, mem_type _mem) {
m3ApiGetArgMem(const T*, tmp);
dest = tmp;
};
template<char c>
struct m3_sig {
static const char value = c;
};
template<typename T> struct m3_type_to_sig;
template<> struct m3_type_to_sig<int32_t> : m3_sig<'i'> {};
template<> struct m3_type_to_sig<int64_t> : m3_sig<'I'> {};
template<> struct m3_type_to_sig<float> : m3_sig<'f'> {};
template<> struct m3_type_to_sig<double> : m3_sig<'F'> {};
template<> struct m3_type_to_sig<void> : m3_sig<'v'> {};
template<> struct m3_type_to_sig<void *> : m3_sig<'*'> {};
template<> struct m3_type_to_sig<const void *> : m3_sig<'*'> {};
template<typename Ret, typename ... Args>
struct m3_signature {
constexpr static size_t n_args = sizeof...(Args);
constexpr static const char value[n_args + 4] = {
m3_type_to_sig<Ret>::value,
'(',
m3_type_to_sig<Args>::value...,
')',
0
};
};
template <typename ...Args>
static void get_args_from_stack(stack_type &sp, mem_type mem, std::tuple<Args...> &tuple) {
std::apply([&](auto &... item) {
(arg_from_stack(item, sp, mem), ...);
}, tuple);
}
template<typename Func>
struct wrap_helper;
template <typename Ret, typename ...Args>
struct wrap_helper<Ret(Args...)> {
using Func = Ret(Args...);
static const void *wrap_fn(IM3Runtime rt, IM3ImportContext _ctx, stack_type _sp, mem_type mem) {
std::tuple<Args...> args;
// The order here matters: m3ApiReturnType should go before calling get_args_from_stack,
// since both modify `_sp`, and the return value on the stack is reserved before the arguments.
m3ApiReturnType(Ret);
get_args_from_stack(_sp, mem, args);
Func* function = reinterpret_cast<Func*>(_ctx->userdata);
Ret r = std::apply(function, args);
m3ApiReturn(r);
}
};
template <typename ...Args>
struct wrap_helper<void(Args...)> {
using Func = void(Args...);
static const void *wrap_fn(IM3Runtime rt, IM3ImportContext _ctx, stack_type sp, mem_type mem) {
std::tuple<Args...> args;
get_args_from_stack(sp, mem, args);
Func* function = reinterpret_cast<Func*>(_ctx->userdata);
std::apply(function, args);
m3ApiSuccess();
}
};
template<typename Func>
class m3_wrapper;
template<typename Ret, typename ... Args>
class m3_wrapper<Ret(Args...)> {
public:
static M3Result link(IM3Module io_module,
const char *const i_moduleName,
const char *const i_functionName,
Ret (*function)(Args...)) {
return m3_LinkRawFunctionEx(io_module, i_moduleName, i_functionName,
m3_signature<Ret, Args...>::value,
&wrap_helper<Ret(Args...)>::wrap_fn,
reinterpret_cast<void*>(function));
}
};
} // namespace detail
/** @endcond */
class module;
class runtime;
class function;
/**
* Exception thrown for wasm3 errors.
*
* Use error:what() to get the reason as a string.
*/
class error : public std::runtime_error {
public:
explicit error(M3Result err) : std::runtime_error(err) {}
};
/** @cond */
namespace detail {
static inline void check_error(M3Result err) {
if (err != m3Err_none) {
throw error(err);
}
}
} // namespace detail
/** @endcond */
/**
* Wrapper for WASM3 environment.
*
* Runtimes, modules are owned by an environment.
*/
class environment {
public:
environment() {
m_env.reset(m3_NewEnvironment(), m3_FreeEnvironment);
if (m_env == nullptr) {
throw std::bad_alloc();
}
}
/**
* Create new runtime
*
* @param stack_size_bytes size of the WASM stack for this runtime
* @return runtime object
*/
runtime new_runtime(size_t stack_size_bytes);
/**
* Parse a WASM module from file
*
* The parsed module is not loaded into any runtime. Use runtime::load to
* load the module after parsing it.
*
* @param in file (WASM binary)
* @return module object
*/
module parse_module(std::istream &in);
/**
* Parse a WASM module from binary data
*
* @param data pointer to the start of the binary
* @param size size of the binary
* @return module object
*/
module parse_module(const uint8_t *data, size_t size);
protected:
std::shared_ptr<struct M3Environment> m_env;
};
/**
* Wrapper for the runtime, where modules are loaded and executed.
*/
class runtime {
public:
/**
* Load the module into runtime
* @param mod module parsed by environment::parse_module
*/
void load(module &mod);
/**
* Get a function handle by name
*
* If the function is not found, throws an exception.
* @param name name of a function, c-string
* @return function object
*/
function find_function(const char *name);
protected:
friend class environment;
runtime(const std::shared_ptr<M3Environment> &env, size_t stack_size_bytes)
: m_env(env) {
m_runtime.reset(m3_NewRuntime(env.get(), stack_size_bytes, nullptr), &m3_FreeRuntime);
if (m_runtime == nullptr) {
throw std::bad_alloc();
}
}
/* runtime extends the lifetime of the environment */
std::shared_ptr<M3Environment> m_env;
std::shared_ptr<M3Runtime> m_runtime;
};
/**
* Module object holds a webassembly module
*
* It can be constructed by parsing a WASM binary using environment::parse_module.
* Functions can be linked to the loaded module.
* Once constructed, modules can be loaded into the runtime.
*/
class module {
public:
/**
* Link an external function.
*
* Throws an exception if the module doesn't reference a function with the given name.
*
* @tparam Func Function type (signature)
* @param module Name of the module to link the function to, or "*" to link to any module
* @param function_name Name of the function (as referenced by the module)
* @param function Function to link (a function pointer)
*/
template<typename Func>
void link(const char *module, const char *function_name, Func *function);
/**
* Same as module::link, but doesn't throw an exception if the function is not referenced.
*/
template<typename Func>
void link_optional(const char *module, const char *function_name, Func *function);
protected:
friend class environment;
friend class runtime;
module(const std::shared_ptr<M3Environment> &env, std::istream &in_wasm) {
in_wasm.unsetf(std::ios::skipws);
std::copy(std::istream_iterator<uint8_t>(in_wasm),
std::istream_iterator<uint8_t>(),
std::back_inserter(m_moduleRawData));
parse(env.get(), m_moduleRawData.data(), m_moduleRawData.size());
}
module(const std::shared_ptr<M3Environment> &env, const uint8_t *data, size_t size) : m_env(env) {
m_moduleRawData = std::vector<uint8_t>{data, data + size};
parse(env.get(), m_moduleRawData.data(), m_moduleRawData.size());
}
void parse(IM3Environment env, const uint8_t *data, size_t size) {
IM3Module p;
M3Result err = m3_ParseModule(env, &p, data, size);
detail::check_error(err);
m_module.reset(p, [this](IM3Module module) {
if (!m_loaded) {
m3_FreeModule(module);
}
});
}
void load_into(IM3Runtime runtime) {
M3Result err = m3_LoadModule(runtime, m_module.get());
detail::check_error(err);
m_loaded = true;
}
std::shared_ptr<M3Environment> m_env;
std::shared_ptr<M3Module> m_module;
bool m_loaded = false;
std::vector<uint8_t> m_moduleRawData {};
};
/**
* Handle of a function. Can be obtained from runtime::find_function method by name.
*/
class function {
public:
/**
* Call the function with the provided arguments, expressed as strings.
*
* Arguments are passed as strings. WASM3 automatically converts them into the types expected
* by the function being called.
*
* Note that the type of the return value must be explicitly specified as a template argument.
*
* @return the return value of the function.
*/
template<typename Ret, typename ... Args>
typename detail::first_type<Ret,
typename std::enable_if<std::is_convertible<Args, const char*>::value>::type...>::type
call_argv(Args... args) {
/* std::enable_if above checks that all argument types are convertible const char* */
const char* argv[] = {args...};
M3Result res = m3_CallArgv(m_func, sizeof...(args), argv);
detail::check_error(res);
Ret ret;
res = m3_GetResults(m_func, 1, &ret);
detail::check_error(res);
return ret;
}
template<typename ... Args>
typename detail::first_type<void,
typename std::enable_if<std::is_convertible<Args, const char*>::value>::type...>::type
call_argv(Args... args) {
/* std::enable_if above checks that all argument types are convertible const char* */
const char* argv[] = {args...};
M3Result res = m3_CallArgv(m_func, sizeof...(args), argv);
detail::check_error(res);
}
/**
* Call the function with the provided arguments (int/float types).
*
* Note that the type of the return value must be explicitly specified as a template argument.
*
* @return the return value of the function or void.
*/
template<typename Ret = void, typename ... Args>
Ret call(Args... args) {
const void *arg_ptrs[] = { reinterpret_cast<const void*>(&args)... };
M3Result res = m3_Call(m_func, sizeof...(args), arg_ptrs);
detail::check_error(res);
if constexpr (!std::is_void<Ret>::value) {
Ret ret;
const void* ret_ptrs[] = { &ret };
res = m3_GetResults(m_func, 1, ret_ptrs);
detail::check_error(res);
return ret;
}
}
protected:
friend class runtime;
function(const std::shared_ptr<M3Runtime> &runtime, const char *name) : m_runtime(runtime) {
M3Result err = m3_FindFunction(&m_func, runtime.get(), name);
detail::check_error(err);
assert(m_func != nullptr);
}
std::shared_ptr<M3Runtime> m_runtime;
M3Function *m_func = nullptr;
};
inline runtime environment::new_runtime(size_t stack_size_bytes) {
return runtime(m_env, stack_size_bytes);
}
inline module environment::parse_module(std::istream &in) {
return module(m_env, in);
}
inline module environment::parse_module(const uint8_t *data, size_t size) {
return module(m_env, data, size);
}
inline void runtime::load(module &mod) {
mod.load_into(m_runtime.get());
}
inline function runtime::find_function(const char *name) {
return function(m_runtime, name);
}
template<typename Func>
void module::link(const char *module, const char *function_name, Func *function) {
M3Result ret = detail::m3_wrapper<Func>::link(m_module.get(), module, function_name, function);
detail::check_error(ret);
}
template<typename Func>
void module::link_optional(const char *module, const char *function_name, Func *function) {
M3Result ret = detail::m3_wrapper<Func>::link(m_module.get(), module, function_name, function);
if (ret == m3Err_functionLookupFailed) {
return;
}
detail::check_error(ret);
}
} // namespace wasm3

View File

@ -0,0 +1 @@
.pio

View File

@ -0,0 +1 @@
../../../../source

View File

@ -0,0 +1,137 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs =
mkr1000
due
mega1284
tinyBLE
blenano
blenano2
teensy31
bluepill-mapple
bluepill
az3166
maix
#titiva TODO: undefined reference to `_exit'
[env]
framework = arduino
monitor_speed = 115200
src_filter =
+<*>
-<m3/extensions/*>
[env:mkr1000]
platform = atmelsam
board = mkr1000USB
src_build_flags =
-O3 -Wfatal-errors
-flto
#-fPIC -fomit-frame-pointer -foptimize-sibling-calls
[env:due]
platform = atmelsam
board = due
src_build_flags =
-O3 -Wfatal-errors
-flto
[env:mega1284]
platform = atmelavr
board = wildfirev3
src_build_flags =
-Dd_m3CodePageAlignSize=512
-Os -Wfatal-errors
-flto
[env:tinyBLE]
platform = nordicnrf51
board = seeedTinyBLE
src_build_flags =
-O3 -Wfatal-errors
-flto
[env:blenano]
platform = nordicnrf51
board = redBearLabBLENano
src_build_flags =
-O3 -Wfatal-errors
-flto
[env:blenano2]
platform = nordicnrf52
board = redbear_blenano2
src_build_flags =
-O3 -Wfatal-errors
-flto
[env:teensy31]
platform = teensy
board = teensy31
upload_protocol = teensy-cli
src_build_flags =
-O3 -Wfatal-errors
-flto
[env:bluepill-mapple]
platform = ststm32
board = bluepill_f103c8_128k
board_build.core = maple
upload_protocol = stlink
#upload_protocol = dfu
src_build_flags =
-Dd_m3FixedHeap=8192
-Os -Wfatal-errors
-flto
[env:bluepill]
platform = ststm32
board = bluepill_f103c8
upload_protocol = stlink
src_build_flags =
-Dd_m3FixedHeap=8192
-Os -Wfatal-errors
-flto
[env:az3166]
platform = ststm32
board = mxchip_az3166
src_build_flags =
-O3 -Wfatal-errors
-flto
[env:maix]
platform = kendryte210
board = sipeed-maix-one-dock
src_build_flags =
-O3 -Wfatal-errors
-flto
[env:titiva]
platform = titiva
board = lptm4c1294ncpdt
src_build_flags =
-DLED_BUILTIN=13
-O3 -Wfatal-errors

View File

@ -0,0 +1,82 @@
//
// Wasm3 - high performance WebAssembly interpreter written in C.
//
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
// All rights reserved.
//
#include "Arduino.h"
#include "wasm3.h"
#include "extra/fib32.wasm.h"
#define FATAL(func, msg) { \
Serial.print("Fatal: " func ": "); \
Serial.println(msg); return; }
void run_wasm()
{
M3Result result = m3Err_none;
uint8_t* wasm = (uint8_t*)fib32_wasm;
size_t fsize = fib32_wasm_len;
Serial.println("Loading WebAssembly...");
IM3Environment env = m3_NewEnvironment ();
if (!env) FATAL("m3_NewEnvironment", "failed");
IM3Runtime runtime = m3_NewRuntime (env, 1024, NULL);
if (!runtime) FATAL("m3_NewRuntime", "failed");
IM3Module module;
result = m3_ParseModule (env, &module, wasm, fsize);
if (result) FATAL("m3_ParseModule", result);
result = m3_LoadModule (runtime, module);
if (result) FATAL("m3_LoadModule", result);
IM3Function f;
result = m3_FindFunction (&f, runtime, "fib");
if (result) FATAL("m3_FindFunction", result);
Serial.println("Running...");
result = m3_CallV(f, 24);
if (result) FATAL("m3_Call", result);
uint32_t value = 0;
result = m3_GetResultsV (f, &value);
if (result) FATAL("m3_GetResults: %s", result);
Serial.print("Result: ");
Serial.println(value);
}
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
delay(10);
while (!Serial) {}
Serial.println();
Serial.println("Wasm3 v" M3_VERSION " on Arduino (" M3_ARCH "), build " __DATE__ " " __TIME__);
digitalWrite(LED_BUILTIN, HIGH);
uint32_t start = millis();
run_wasm();
uint32_t end = millis();
digitalWrite(LED_BUILTIN, LOW);
Serial.print("Elapsed: ");
Serial.print(end - start);
Serial.println(" ms");
}
void loop()
{
delay(100);
}

View File

@ -0,0 +1 @@
.pio

View File

@ -0,0 +1,7 @@
{
"build" : {
"flags": "-Os -fomit-frame-pointer -fno-stack-check -fno-stack-protector -Wfatal-errors -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers",
"srcFilter": ["+<*>", "-<extensions/*>"],
"libArchive": false
}
}

View File

@ -0,0 +1 @@
../../../../../source

View File

@ -0,0 +1,10 @@
[env:bluepill]
platform = ststm32
board = bluepill_f103c8
framework = stm32cube
upload_protocol = stlink
lib_deps = jeeh
build_flags =
-Dd_m3FixedHeap=8192
-Os -Wfatal-errors

View File

@ -0,0 +1,75 @@
//
// Wasm3 - high performance WebAssembly interpreter written in C.
//
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
// All rights reserved.
//
#include "wasm3.h"
#include "extra/fib32.wasm.h"
#include <stdio.h>
#include <jee.h>
#define FATAL(func, msg) { \
puts("Fatal: " func ": "); \
puts(msg); return; }
void run_wasm()
{
M3Result result = m3Err_none;
uint8_t* wasm = (uint8_t*)fib32_wasm;
size_t fsize = fib32_wasm_len;
puts("Loading WebAssembly...");
IM3Environment env = m3_NewEnvironment ();
if (!env) FATAL("m3_NewEnvironment", "failed");
IM3Runtime runtime = m3_NewRuntime (env, 1024, NULL);
if (!runtime) FATAL("m3_NewRuntime", "failed");
IM3Module module;
result = m3_ParseModule (env, &module, wasm, fsize);
if (result) FATAL("m3_ParseModule", result);
result = m3_LoadModule (runtime, module);
if (result) FATAL("m3_LoadModule", result);
IM3Function f;
result = m3_FindFunction (&f, runtime, "fib");
if (result) FATAL("m3_FindFunction", result);
puts("Running...");
result = m3_CallV (f, 24);
if (result) FATAL("m3_Call", result);
uint32_t value = 0;
result = m3_GetResultsV (f, &value);
if (result) FATAL("m3_GetResults", result);
char buff[32];
itoa(value, buff, 10);
puts("Result: ");
puts(buff);
puts("\n");
}
PinC<13> led;
int main()
{
enableSysTick();
led.mode(Pinmode::out);
puts("Wasm3 v" M3_VERSION " on BluePill, build " __DATE__ " " __TIME__ "\n");
led = 0;
run_wasm();
led = 1;
}

View File

@ -0,0 +1,3 @@
build
sdkconfig
sdkconfig.old

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := wasm3
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(wasm3)

View File

@ -0,0 +1,24 @@
## Build for ESP-IDF, with minimal WASI support
**Note:** Currently, to run this example you need a PSRAM-enabled ESP32 module (this might be improved in future).
Download and install ESP-IDF v4.0
```sh
export IDF_PATH=/opt/esp32/esp-idf
# Set tools path if needed:
#export IDF_TOOLS_PATH=/opt/esp32
source $IDF_PATH/export.sh
idf.py menuconfig
# Select target:
idf.py set-target esp32 # or: esp32s2, esp32c3, esp32s3, linux
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor
```

View File

@ -0,0 +1,11 @@
file(GLOB_RECURSE M3_SOURCES "wasm3/*.c")
set(APP_SOURCES "main.cpp" "m3_api_esp_wasi.c")
idf_component_register(SRCS ${APP_SOURCES} ${M3_SOURCES}
INCLUDE_DIRS "wasm3")
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error -O3 -DESP32 -Dd_m3MaxFunctionStackHeight=256)
# Disable harmless warnings
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers)

View File

@ -0,0 +1,616 @@
//
// m3_api_esp_wasi.c
//
// Created by Volodymyr Shymanskyy on 01/07/20.
// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved.
//
#define _POSIX_C_SOURCE 200809L
#include "m3_api_esp_wasi.h"
#include "m3_env.h"
#include "m3_exception.h"
#if defined(ESP32)
typedef uint32_t __wasi_size_t;
#include "extra/wasi_core.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
static m3_wasi_context_t* wasi_context;
typedef struct wasi_iovec_t
{
__wasi_size_t buf;
__wasi_size_t buf_len;
} wasi_iovec_t;
#define PREOPEN_CNT 3
typedef struct Preopen {
int fd;
char* path;
} Preopen;
Preopen preopen[PREOPEN_CNT] = {
{ 0, "<stdin>" },
{ 1, "<stdout>" },
{ 2, "<stderr>" },
};
# define APE_SWITCH_BEG switch (errnum) {
# define APE_SWITCH_END }
# define APE_CASE_RET(e1,e2) case e1: return e2; break;
static
__wasi_errno_t errno_to_wasi(int errnum) {
APE_SWITCH_BEG
APE_CASE_RET( EPERM , __WASI_ERRNO_PERM )
APE_CASE_RET( ENOENT , __WASI_ERRNO_NOENT )
APE_CASE_RET( ESRCH , __WASI_ERRNO_SRCH )
APE_CASE_RET( EINTR , __WASI_ERRNO_INTR )
APE_CASE_RET( EIO , __WASI_ERRNO_IO )
APE_CASE_RET( ENXIO , __WASI_ERRNO_NXIO )
APE_CASE_RET( E2BIG , __WASI_ERRNO_2BIG )
APE_CASE_RET( ENOEXEC , __WASI_ERRNO_NOEXEC )
APE_CASE_RET( EBADF , __WASI_ERRNO_BADF )
APE_CASE_RET( ECHILD , __WASI_ERRNO_CHILD )
APE_CASE_RET( EAGAIN , __WASI_ERRNO_AGAIN )
APE_CASE_RET( ENOMEM , __WASI_ERRNO_NOMEM )
APE_CASE_RET( EACCES , __WASI_ERRNO_ACCES )
APE_CASE_RET( EFAULT , __WASI_ERRNO_FAULT )
APE_CASE_RET( EBUSY , __WASI_ERRNO_BUSY )
APE_CASE_RET( EEXIST , __WASI_ERRNO_EXIST )
APE_CASE_RET( EXDEV , __WASI_ERRNO_XDEV )
APE_CASE_RET( ENODEV , __WASI_ERRNO_NODEV )
APE_CASE_RET( ENOTDIR , __WASI_ERRNO_NOTDIR )
APE_CASE_RET( EISDIR , __WASI_ERRNO_ISDIR )
APE_CASE_RET( EINVAL , __WASI_ERRNO_INVAL )
APE_CASE_RET( ENFILE , __WASI_ERRNO_NFILE )
APE_CASE_RET( EMFILE , __WASI_ERRNO_MFILE )
APE_CASE_RET( ENOTTY , __WASI_ERRNO_NOTTY )
APE_CASE_RET( ETXTBSY , __WASI_ERRNO_TXTBSY )
APE_CASE_RET( EFBIG , __WASI_ERRNO_FBIG )
APE_CASE_RET( ENOSPC , __WASI_ERRNO_NOSPC )
APE_CASE_RET( ESPIPE , __WASI_ERRNO_SPIPE )
APE_CASE_RET( EROFS , __WASI_ERRNO_ROFS )
APE_CASE_RET( EMLINK , __WASI_ERRNO_MLINK )
APE_CASE_RET( EPIPE , __WASI_ERRNO_PIPE )
APE_CASE_RET( EDOM , __WASI_ERRNO_DOM )
APE_CASE_RET( ERANGE , __WASI_ERRNO_RANGE )
APE_SWITCH_END
return __WASI_ERRNO_INVAL;
}
static inline
int convert_clockid(__wasi_clockid_t in) {
switch (in) {
case __WASI_CLOCKID_MONOTONIC: return CLOCK_MONOTONIC;
//case __WASI_CLOCKID_PROCESS_CPUTIME_ID: return CLOCK_PROCESS_CPUTIME_ID;
case __WASI_CLOCKID_REALTIME: return CLOCK_REALTIME;
//case __WASI_CLOCKID_THREAD_CPUTIME_ID: return CLOCK_THREAD_CPUTIME_ID;
default: return -1;
}
}
static inline
__wasi_timestamp_t convert_timespec(const struct timespec *ts) {
if (ts->tv_sec < 0)
return 0;
if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000)
return UINT64_MAX;
return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + ts->tv_nsec;
}
/*
* WASI API implementation
*/
m3ApiRawFunction(m3_wasi_generic_args_get)
{
m3ApiReturnType (uint32_t)
m3ApiGetArgMem (uint32_t * , argv)
m3ApiGetArgMem (char * , argv_buf)
m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata);
if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); }
m3ApiCheckMem(argv, context->argc * sizeof(uint32_t));
for (u32 i = 0; i < context->argc; ++i)
{
m3ApiWriteMem32(&argv[i], m3ApiPtrToOffset(argv_buf));
size_t len = strlen (context->argv[i]);
m3ApiCheckMem(argv_buf, len);
memcpy (argv_buf, context->argv[i], len);
argv_buf += len;
* argv_buf++ = 0;
}
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_args_sizes_get)
{
m3ApiReturnType (uint32_t)
m3ApiGetArgMem (__wasi_size_t * , argc)
m3ApiGetArgMem (__wasi_size_t * , argv_buf_size)
m3ApiCheckMem(argc, sizeof(__wasi_size_t));
m3ApiCheckMem(argv_buf_size, sizeof(__wasi_size_t));
m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata);
if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); }
__wasi_size_t buf_len = 0;
for (u32 i = 0; i < context->argc; ++i)
{
buf_len += strlen (context->argv[i]) + 1;
}
m3ApiWriteMem32(argc, context->argc);
m3ApiWriteMem32(argv_buf_size, buf_len);
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_environ_get)
{
m3ApiReturnType (uint32_t)
m3ApiGetArgMem (uint32_t * , env)
m3ApiGetArgMem (char * , env_buf)
// TODO
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_environ_sizes_get)
{
m3ApiReturnType (uint32_t)
m3ApiGetArgMem (__wasi_size_t * , env_count)
m3ApiGetArgMem (__wasi_size_t * , env_buf_size)
m3ApiCheckMem(env_count, sizeof(__wasi_size_t));
m3ApiCheckMem(env_buf_size, sizeof(__wasi_size_t));
// TODO
m3ApiWriteMem32(env_count, 0);
m3ApiWriteMem32(env_buf_size, 0);
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t , fd)
m3ApiGetArgMem (char * , path)
m3ApiGetArg (__wasi_size_t , path_len)
m3ApiCheckMem(path, path_len);
if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); }
size_t slen = strlen(preopen[fd].path) + 1;
memcpy(path, preopen[fd].path, M3_MIN(slen, path_len));
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_fd_prestat_get)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t , fd)
m3ApiGetArgMem (uint8_t * , buf)
m3ApiCheckMem(buf, 8);
if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); }
m3ApiWriteMem32(buf+0, __WASI_PREOPENTYPE_DIR);
m3ApiWriteMem32(buf+4, strlen(preopen[fd].path) + 1);
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t , fd)
m3ApiGetArgMem (__wasi_fdstat_t * , fdstat)
m3ApiCheckMem(fdstat, sizeof(__wasi_fdstat_t));
struct stat fd_stat;
int fl = fcntl(fd, F_GETFL);
if (fl < 0) { m3ApiReturn(errno_to_wasi(errno)); }
fstat(fd, &fd_stat);
int mode = fd_stat.st_mode;
fdstat->fs_filetype = (S_ISBLK(mode) ? __WASI_FILETYPE_BLOCK_DEVICE : 0) |
(S_ISCHR(mode) ? __WASI_FILETYPE_CHARACTER_DEVICE : 0) |
(S_ISDIR(mode) ? __WASI_FILETYPE_DIRECTORY : 0) |
(S_ISREG(mode) ? __WASI_FILETYPE_REGULAR_FILE : 0) |
//(S_ISSOCK(mode) ? __WASI_FILETYPE_SOCKET_STREAM : 0) |
(S_ISLNK(mode) ? __WASI_FILETYPE_SYMBOLIC_LINK : 0);
m3ApiWriteMem16(&fdstat->fs_flags,
((fl & O_APPEND) ? __WASI_FDFLAGS_APPEND : 0) |
//((fl & O_DSYNC) ? __WASI_FDFLAGS_DSYNC : 0) |
((fl & O_NONBLOCK) ? __WASI_FDFLAGS_NONBLOCK : 0) |
//((fl & O_RSYNC) ? __WASI_FDFLAGS_RSYNC : 0) |
((fl & O_SYNC) ? __WASI_FDFLAGS_SYNC : 0));
fdstat->fs_rights_base = (uint64_t)-1; // all rights
// Make descriptors 0,1,2 look like a TTY
if (fd <= 2) {
fdstat->fs_rights_base &= ~(__WASI_RIGHTS_FD_SEEK | __WASI_RIGHTS_FD_TELL);
}
fdstat->fs_rights_inheriting = (uint64_t)-1; // all rights
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t , fd)
m3ApiGetArg (__wasi_fdflags_t , flags)
// TODO
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_unstable_fd_seek)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t , fd)
m3ApiGetArg (__wasi_filedelta_t , offset)
m3ApiGetArg (uint32_t , wasi_whence)
m3ApiGetArgMem (__wasi_filesize_t * , result)
m3ApiCheckMem(result, sizeof(__wasi_filesize_t));
int whence;
switch (wasi_whence) {
case 0: whence = SEEK_CUR; break;
case 1: whence = SEEK_END; break;
case 2: whence = SEEK_SET; break;
default: m3ApiReturn(__WASI_ERRNO_INVAL);
}
int64_t ret;
ret = lseek(fd, offset, whence);
if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); }
m3ApiWriteMem64(result, ret);
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t , fd)
m3ApiGetArg (__wasi_filedelta_t , offset)
m3ApiGetArg (uint32_t , wasi_whence)
m3ApiGetArgMem (__wasi_filesize_t * , result)
m3ApiCheckMem(result, sizeof(__wasi_filesize_t));
int whence;
switch (wasi_whence) {
case 0: whence = SEEK_SET; break;
case 1: whence = SEEK_CUR; break;
case 2: whence = SEEK_END; break;
default: m3ApiReturn(__WASI_ERRNO_INVAL);
}
int64_t ret;
ret = lseek(fd, offset, whence);
if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); }
m3ApiWriteMem64(result, ret);
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_path_open)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t , dirfd)
m3ApiGetArg (__wasi_lookupflags_t , dirflags)
m3ApiGetArgMem (const char * , path)
m3ApiGetArg (__wasi_size_t , path_len)
m3ApiGetArg (__wasi_oflags_t , oflags)
m3ApiGetArg (__wasi_rights_t , fs_rights_base)
m3ApiGetArg (__wasi_rights_t , fs_rights_inheriting)
m3ApiGetArg (__wasi_fdflags_t , fs_flags)
m3ApiGetArgMem (__wasi_fd_t * , fd)
m3ApiCheckMem(path, path_len);
m3ApiCheckMem(fd, sizeof(__wasi_fd_t));
if (path_len >= 512)
m3ApiReturn(__WASI_ERRNO_INVAL);
// copy path so we can ensure it is NULL terminated
char host_path[path_len+1];
memcpy (host_path, path, path_len);
host_path[path_len] = '\0'; // NULL terminator
// TODO
m3ApiReturn(__WASI_ERRNO_NOSYS);
}
m3ApiRawFunction(m3_wasi_generic_fd_read)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t , fd)
m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs)
m3ApiGetArg (__wasi_size_t , iovs_len)
m3ApiGetArgMem (__wasi_size_t * , nread)
m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t));
m3ApiCheckMem(nread, sizeof(__wasi_size_t));
ssize_t res = 0;
for (__wasi_size_t i = 0; i < iovs_len; i++) {
void* addr = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf));
size_t len = m3ApiReadMem32(&wasi_iovs[i].buf_len);
if (len == 0) continue;
int ret = read (fd, addr, len);
if (ret < 0) m3ApiReturn(errno_to_wasi(errno));
res += ret;
if ((size_t)ret < len) break;
}
m3ApiWriteMem32(nread, res);
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_fd_write)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t , fd)
m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs)
m3ApiGetArg (__wasi_size_t , iovs_len)
m3ApiGetArgMem (__wasi_size_t * , nwritten)
m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t));
m3ApiCheckMem(nwritten, sizeof(__wasi_size_t));
ssize_t res = 0;
for (__wasi_size_t i = 0; i < iovs_len; i++) {
void* addr = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf));
size_t len = m3ApiReadMem32(&wasi_iovs[i].buf_len);
if (len == 0) continue;
int ret = write (fd, addr, len);
if (ret < 0) m3ApiReturn(errno_to_wasi(errno));
res += ret;
if ((size_t)ret < len) break;
}
m3ApiWriteMem32(nwritten, res);
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_fd_close)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t, fd)
int ret = close(fd);
m3ApiReturn(ret == 0 ? __WASI_ERRNO_SUCCESS : ret);
}
m3ApiRawFunction(m3_wasi_generic_fd_datasync)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_fd_t, fd)
// TODO
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_random_get)
{
m3ApiReturnType (uint32_t)
m3ApiGetArgMem (uint8_t * , buf)
m3ApiGetArg (__wasi_size_t , buf_len)
m3ApiCheckMem(buf, buf_len);
while (1) {
ssize_t retlen = 0;
#if defined(__wasi__) || defined(__APPLE__) || defined(__ANDROID_API__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
size_t reqlen = M3_MIN (buf_len, 256);
# if defined(__APPLE__) && (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)
retlen = SecRandomCopyBytes(kSecRandomDefault, reqlen, buf) < 0 ? -1 : reqlen;
# else
retlen = getentropy(buf, reqlen) < 0 ? -1 : reqlen;
# endif
#elif defined(__FreeBSD__) || defined(__linux__)
retlen = getrandom(buf, buf_len, 0);
#elif defined(_WIN32)
if (RtlGenRandom(buf, buf_len) == TRUE) {
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
#else
m3ApiReturn(__WASI_ERRNO_NOSYS);
#endif
if (retlen < 0) {
if (errno == EINTR || errno == EAGAIN) {
continue;
}
m3ApiReturn(errno_to_wasi(errno));
} else if (retlen == buf_len) {
m3ApiReturn(__WASI_ERRNO_SUCCESS);
} else {
buf += retlen;
buf_len -= retlen;
}
}
}
m3ApiRawFunction(m3_wasi_generic_clock_res_get)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_clockid_t , wasi_clk_id)
m3ApiGetArgMem (__wasi_timestamp_t * , resolution)
m3ApiCheckMem(resolution, sizeof(__wasi_timestamp_t));
int clk = convert_clockid(wasi_clk_id);
if (clk < 0) m3ApiReturn(__WASI_ERRNO_INVAL);
struct timespec tp;
if (clock_getres(clk, &tp) != 0) {
m3ApiWriteMem64(resolution, 1000000);
} else {
m3ApiWriteMem64(resolution, convert_timespec(&tp));
}
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_clock_time_get)
{
m3ApiReturnType (uint32_t)
m3ApiGetArg (__wasi_clockid_t , wasi_clk_id)
m3ApiGetArg (__wasi_timestamp_t , precision)
m3ApiGetArgMem (__wasi_timestamp_t * , time)
m3ApiCheckMem(time, sizeof(__wasi_timestamp_t));
int clk = convert_clockid(wasi_clk_id);
if (clk < 0) m3ApiReturn(__WASI_ERRNO_INVAL);
struct timespec tp;
if (clock_gettime(clk, &tp) != 0) {
m3ApiReturn(errno_to_wasi(errno));
}
m3ApiWriteMem64(time, convert_timespec(&tp));
m3ApiReturn(__WASI_ERRNO_SUCCESS);
}
m3ApiRawFunction(m3_wasi_generic_proc_exit)
{
m3ApiGetArg (uint32_t, code)
m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata);
if (context) {
context->exit_code = code;
}
m3ApiTrap(m3Err_trapExit);
}
static
M3Result SuppressLookupFailure(M3Result i_result)
{
if (i_result == m3Err_functionLookupFailed)
return m3Err_none;
else
return i_result;
}
m3_wasi_context_t* m3_GetWasiContext()
{
return wasi_context;
}
M3Result m3_LinkEspWASI (IM3Module module)
{
M3Result result = m3Err_none;
// TODO: Preopen dirs
if (!wasi_context) {
wasi_context = (m3_wasi_context_t*)malloc(sizeof(m3_wasi_context_t));
wasi_context->exit_code = 0;
wasi_context->argc = 0;
wasi_context->argv = 0;
}
static const char* namespaces[2] = { "wasi_unstable", "wasi_snapshot_preview1" };
// fd_seek is incompatible
_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek", "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek)));
for (int i=0; i<2; i++)
{
const char* wasi = namespaces[i];
_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_generic_args_get, wasi_context)));
_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_generic_args_sizes_get, wasi_context)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_generic_clock_res_get)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_generic_clock_time_get)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_generic_environ_get)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_generic_environ_sizes_get)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_advise", "i(iIIi)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_allocate", "i(iII)", )));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_generic_fd_close)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_generic_fd_datasync)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_generic_fd_fdstat_get)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_generic_fd_fdstat_set_flags)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_rights", "i(iII)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_get", "i(i*)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_size", "i(iI)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_times","i(iIIi)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_generic_fd_prestat_get)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_generic_fd_prestat_dir_name)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pwrite", "i(i*iI*)",)));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_generic_fd_read)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_renumber", "i(ii)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_sync", "i(i)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_tell", "i(i*)", )));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_generic_fd_write)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_get", "i(ii*i*)", &m3_wasi_generic_path_filestat_get)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_set_times", "i(ii*iIIi)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_link", "i(ii*ii*i)", )));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_generic_path_open)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink", "i(*ii*i)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_generic_poll_oneoff)));
_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_generic_proc_exit, wasi_context)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "proc_raise", "i(i)", )));
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_generic_random_get)));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sched_yield", "i()", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_recv", "i(i*ii**)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_send", "i(i*ii*)", )));
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_shutdown", "i(ii)", )));
}
_catch:
return result;
}
#endif // ESP32

View File

@ -0,0 +1,28 @@
//
// m3_api_esp_wasi.h
//
// Created by Volodymyr Shymanskyy on 01/07/20.
// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved.
//
#ifndef m3_api_esp_wasi_h
#define m3_api_esp_wasi_h
#include "m3_core.h"
d_m3BeginExternC
typedef struct m3_wasi_context_t
{
i32 exit_code;
u32 argc;
ccstr_t * argv;
} m3_wasi_context_t;
M3Result m3_LinkEspWASI (IM3Module io_module);
m3_wasi_context_t* m3_GetWasiContext();
d_m3EndExternC
#endif /* m3_api_esp_wasi_h */

View File

@ -0,0 +1,76 @@
//
// Wasm3 - high performance WebAssembly interpreter written in C.
//
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
// All rights reserved.
//
#include "esp_system.h"
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include "wasm3.h"
#include "m3_env.h"
#include "m3_api_esp_wasi.h"
#include "wasi_test.wasm.h"
#define FATAL(msg, ...) { printf("Fatal: " msg "\n", ##__VA_ARGS__); return; }
static void run_wasm(void)
{
M3Result result = m3Err_none;
uint8_t* wasm = (uint8_t*)wasi_test_wasm;
uint32_t fsize = wasi_test_wasm_len;
printf("Loading WebAssembly...\n");
IM3Environment env = m3_NewEnvironment ();
if (!env) FATAL("m3_NewEnvironment failed");
IM3Runtime runtime = m3_NewRuntime (env, 8*1024, NULL);
if (!runtime) FATAL("m3_NewRuntime failed");
IM3Module module;
result = m3_ParseModule (env, &module, wasm, fsize);
if (result) FATAL("m3_ParseModule: %s", result);
result = m3_LoadModule (runtime, module);
if (result) FATAL("m3_LoadModule: %s", result);
result = m3_LinkEspWASI (runtime->modules);
if (result) FATAL("m3_LinkEspWASI: %s", result);
IM3Function f;
result = m3_FindFunction (&f, runtime, "_start");
if (result) FATAL("m3_FindFunction: %s", result);
printf("Running...\n");
const char* i_argv[2] = { "test.wasm", NULL };
m3_wasi_context_t* wasi_ctx = m3_GetWasiContext();
wasi_ctx->argc = 1;
wasi_ctx->argv = i_argv;
result = m3_CallV (f);
if (result) FATAL("m3_Call: %s", result);
}
extern "C" void app_main(void)
{
printf("\nWasm3 v" M3_VERSION " on " CONFIG_IDF_TARGET ", build " __DATE__ " " __TIME__ "\n");
clock_t start = clock();
run_wasm();
clock_t end = clock();
printf("Elapsed: %ld ms\n", (end - start)*1000 / CLOCKS_PER_SEC);
sleep(3);
printf("Restarting...\n\n\n");
esp_restart();
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More