Merge branch 'main' of https://github.com/wasm3/wasm3 into main

extensions
Steven Massey 3 years ago
commit 7a1848cbf6

@ -542,36 +542,6 @@ jobs:
- name: Test
run: ./build/wasm3 ./test/wasi/simple/test.wasm
python:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
python-version: [ "3.6", "3.7", "3.8", "3.9" ]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Build Python module
run: |
cd platforms/python
python setup.py sdist
pip install ./dist/pywasm3-*.tar.gz
- name: Install WABT
run: |
sudo apt install wabt
- name: Lint
run: |
pip install flake8
flake8 . --count --select=E7,E9,F63,F7,F82 --show-source --statistics
- name: Test
run: |
pip install pytest
pytest platforms/python
spellcheck:
runs-on: ubuntu-latest
steps:

@ -28,7 +28,7 @@ Here's an online demo and a small [getting started guide](https://wapm.io/packag
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](./platforms/python) │
[<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/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/rust.svg" width="18" height="18" /> Rust](https://github.com/Veykril/wasm3-rs) │

@ -16,8 +16,13 @@
#include "m3_api_libc.h"
#include "m3_api_tracer.h"
// Gas metering/limit only applies to pre-instrumented modules
#define GAS_LIMIT 2000000000000
/*
* 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
@ -35,7 +40,7 @@ int wasm_bins_qty = 0;
#if defined(GAS_LIMIT)
static int64_t current_gas = GAS_LIMIT;
static int64_t current_gas = GAS_FACTOR * GAS_LIMIT;
static bool is_gas_metered = false;
m3ApiRawFunction(metering_usegas)
@ -75,7 +80,7 @@ M3Result link_all (IM3Module module)
#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)/10000);
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; }
@ -255,7 +260,7 @@ M3Result repl_call (const char* name, int argc, const char* argv[])
#if defined(GAS_LIMIT)
if (is_gas_metered) {
fprintf(stderr, "Gas used: %0.4f\n", (double)(GAS_LIMIT - current_gas)/10000);
fprintf(stderr, "Gas used: %0.4f\n", (double)((GAS_FACTOR * GAS_LIMIT) - current_gas) / GAS_FACTOR);
}
#endif

@ -1,3 +0,0 @@
dist/
pywasm3.egg-info/
.pytest*

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 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.

@ -1,3 +0,0 @@
graft m3
include README.md
include LICENSE

@ -2,34 +2,4 @@
Python binding for Wasm3, the fastest WebAssembly interpreter.
## Install
```sh
pip3 install pywasm3
# Or, if you have a local copy:
python3 setup.py sdist
pip3 install dist/pywasm3-*.tar.gz
```
## Usage example
```py
import wasm3, base64
# WebAssembly binary
WASM = base64.b64decode("AGFzbQEAAAABBgFgAX4"
"BfgMCAQAHBwEDZmliAAAKHwEdACAAQgJUBEAgAA"
"8LIABCAn0QACAAQgF9EAB8Dws=")
env = wasm3.Environment()
rt = env.new_runtime(1024)
mod = env.parse_module(WASM)
rt.load(mod)
func = rt.find_function("fib")
result = func.call_argv("24")
print(result) # 46368
```
### License
This project is released under The MIT License (MIT)
## Moved to https://github.com/wasm3/pywasm3

@ -1,67 +0,0 @@
#!/usr/bin/env python3
import wasm3
import base64, time, timeit
"""
Input module:
(module
(type (;0;) (func (param i64) (result i64)))
(func (;0;) (type 0) (param i64) (result i64)
local.get 0
i64.const 2
i64.lt_u
if ;; label = @1
local.get 0
return
end
local.get 0
i64.const 2
i64.sub
call 0
local.get 0
i64.const 1
i64.sub
call 0
i64.add
return)
(export "fib" (func 0)))
"""
# WebAssembly binary
WASM = base64.b64decode("""
AGFzbQEAAAABBgFgAX4BfgMCAQAHBwEDZmliAAAKHwEdACAAQgJUBEAgAA8LIABCAn0QACAAQgF9
EAB8Dws=
""")
(N, RES, CYCLES) = (24, 46368, 1000)
# Note: this is cold-start
def run_wasm():
env = wasm3.Environment()
rt = env.new_runtime(4096)
mod = env.parse_module(WASM)
rt.load(mod)
wasm_fib = rt.find_function("fib")
assert wasm_fib(N) == RES
def fib(n: int) -> int:
if n < 2:
return n
return fib(n-1) + fib(n-2)
def run_py():
assert fib(N) == RES
t1 = timeit.timeit(run_wasm, number=CYCLES)
print(f"Wasm3: {t1:.4f} seconds")
print("Cooling down... ", end="", flush=True)
time.sleep(10)
print("ok")
t2 = timeit.timeit(run_py, number=CYCLES)
if t2 > t1:
ratio = f"{(t2/t1):.1f}x slower"
else:
retio = f"{(t1/t2):.1f}x faster"
print(f"Python: {t2:.4f} seconds, {ratio}")

@ -1,31 +0,0 @@
#!/usr/bin/env python3
import wasm3
import os, time
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, "./wasm/coremark-minimal.wasm")
print("Initializing Wasm3 engine...")
def clock_ms():
return int(round(time.time() * 1000))
env = wasm3.Environment()
rt = env.new_runtime(4096)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
mod.link_function("env", "clock_ms", "I()", clock_ms)
wasm_run = rt.find_function("run")
print("Running CoreMark 1.0...")
res = wasm_run()
if res > 1:
print(f"Result: {res:.3f}")
else:
print("Error")

@ -1,38 +0,0 @@
#!/usr/bin/env python3
import wasm3
import os, time
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, "./wasm/coremark-metered.wasm")
print("Initializing Wasm3 engine...")
def clock_ms():
return int(round(time.time() * 1000))
env = wasm3.Environment()
rt = env.new_runtime(4096)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
mod.link_function("env", "clock_ms", "I()", clock_ms)
# Gas metering will only apply to metered (pre-instrumented) modules
mod.gasLimit = 500_000_000
wasm_run = rt.find_function("run")
print("Running CoreMark 1.0...")
try:
res = wasm_run()
if res > 1:
print(f"Result: {res:.3f}")
else:
print("Error")
finally:
if mod.gasUsed:
print(f"Gas used: {mod.gasUsed}")

@ -1,115 +0,0 @@
#!/usr/bin/env python3
import wasm3
import base64, struct
import asyncio
"""
This is a straightforward translation of JavaScript example from:
https://kripken.github.io/blog/wasm/2019/07/16/asyncify.html
Input module:
(module
(import "env" "before" (func $before))
(import "env" "sleep" (func $sleep (param i32)))
(import "env" "after" (func $after))
(export "memory" (memory 0))
(export "main" (func $main))
(func $main
(call $before)
(call $sleep (i32.const 2000))
(call $after)
)
(memory 1 1)
)
Asyncify command:
wasm-opt async.wasm --asyncify -O3 -o asyncified.wasm
"""
# WebAssembly binary
WASM = base64.b64decode("""
AGFzbQEAAAABDANgAABgAX8AYAABfwImAwNlbnYGYmVmb3JlAAADZW52BXNsZWVwAAEDZW52BWFm
dGVyAAADBgUAAQABAgUEAQEBAQYLAn8BQQALfwFBAAsHhAEHBm1lbW9yeQIABG1haW4AAxVhc3lu
Y2lmeV9zdGFydF91bndpbmQABBRhc3luY2lmeV9zdG9wX3Vud2luZAAFFWFzeW5jaWZ5X3N0YXJ0
X3Jld2luZAAGFGFzeW5jaWZ5X3N0b3BfcmV3aW5kAAUSYXN5bmNpZnlfZ2V0X3N0YXRlAAcK3gEF
jAEBAX8CfyMAQQJGBEAjASMBKAIAQXxqNgIAIwEoAgAoAgAhAAsgAEVBASMAGwRAEABBACMAQQFG
DQEaCyAAQQFGQQEjABsEQEHQDxABQQEjAEEBRg0BGgsgAEECRkEBIwAbBEAQAkECIwBBAUYNARoL
DwshACMBKAIAIAA2AgAjASMBKAIAQQRqNgIACxkAQQEkACAAJAEjASgCACMBKAIESwRAAAsLFQBB
ACQAIwEoAgAjASgCBEsEQAALCxkAQQIkACAAJAEjASgCACMBKAIESwRAAAsLBAAjAAs=
""")
# Init asyncio
loop = asyncio.get_event_loop()
def set_timeout(ms):
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
loop.call_later(ms/1000, func)
return wrapper
return decorator
# Prepare Wasm3 engine
env = wasm3.Environment()
rt = env.new_runtime(1024)
mod = env.parse_module(WASM)
rt.load(mod)
mem = rt.get_memory(0)
# ------------------------------------------------
def env_before():
print("before!")
@set_timeout(1000)
def callback():
print("(an event that happens during the sleep)")
# ------------------------------------------------
def env_sleep(ms):
global sleeping
if not sleeping:
print(f"sleep...")
DATA_ADDR = 16
mem[DATA_ADDR:DATA_ADDR+8] = struct.pack("<II", DATA_ADDR+8, 1024)
asyncify_start_unwind(DATA_ADDR)
sleeping = True
@set_timeout(ms)
def callback():
print("timeout ended, starting to rewind the stack")
asyncify_start_rewind(DATA_ADDR)
main()
else:
print("...resume")
asyncify_stop_rewind()
sleeping = False
# ------------------------------------------------
def env_after():
print("after!")
loop.stop()
mod.link_function("env", "before", "v()", env_before)
mod.link_function("env", "sleep", "v(i)", env_sleep)
mod.link_function("env", "after", "v()", env_after)
sleeping = False
main = rt.find_function("main")
asyncify_start_unwind = rt.find_function("asyncify_start_unwind")
asyncify_stop_unwind = rt.find_function("asyncify_stop_unwind")
asyncify_start_rewind = rt.find_function("asyncify_start_rewind")
asyncify_stop_rewind = rt.find_function("asyncify_stop_rewind")
main()
print("stack unwound")
asyncify_stop_unwind()
try:
loop.run_forever()
finally:
loop.close()

@ -1,97 +0,0 @@
#!/usr/bin/env python3
import os, struct, time
import multiprocessing as mp
import wasm3
import numpy
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "true"
sample_rate = 22050 # or 44100
def player(q):
import pygame
pygame.mixer.pre_init(frequency=sample_rate, size=-16, channels=2)
pygame.init()
channel = pygame.mixer.Channel(0)
try:
while True:
chunk = pygame.mixer.Sound(buffer=q.get())
indicator = '|' if channel.get_queue() else '.'
print(indicator, end='', flush=True)
while channel.get_queue() is not None:
time.sleep(0.01)
channel.queue(chunk)
except:
pass
finally:
pygame.quit()
if __name__ == '__main__':
print("Hondarribia - intro song for WebAssembly Summit 2020 by Peter Salomonsen")
print("Source: https://petersalomonsen.com/webassemblymusic/livecodev2/?gist=5b795090ead4f192e7f5ee5dcdd17392")
print("Synthesized: https://soundcloud.com/psalomo/hondarribia")
q = mp.Queue()
p = mp.Process(target=player, args=(q,))
p.start()
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, f"./wasm/hondarribia-{sample_rate}.wasm")
# Prepare Wasm3 engine
env = wasm3.Environment()
rt = env.new_runtime(1024)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
print("Pre-buffering...")
buff = b''
buff_sz = 1024
def fd_write(fd, wasi_iovs, iows_len, nwritten):
global buff, buff_sz
mem = rt.get_memory(0)
# get data
(off, size) = struct.unpack("<II", mem[wasi_iovs:wasi_iovs+8])
data = mem[off:off+size]
# decode
arr = numpy.frombuffer(data, dtype=numpy.float32)
data = (arr * 32768).astype(numpy.int16).tobytes()
# buffer
buff += data
if len(buff) > buff_sz*1024:
#print('+', end='', flush=True)
q.put(buff)
buff = b''
buff_sz = 64
return size
for modname in ["wasi_unstable", "wasi_snapshot_preview1"]:
mod.link_function(modname, "fd_write", "i(i*i*)", fd_write)
wasm_start = rt.find_function("_start")
try:
wasm_start()
q.put(buff)
except:
pass
finally:
q.put(None)
p.join()
print()
print("Finished")

@ -1,78 +0,0 @@
#!/usr/bin/env python3
import wasm3
import os, time, random, math, base64
import pygame
print("WebAssembly demo file provided by Ben Smith (binji)")
print("Sources: https://github.com/binji/raw-wasm")
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, "./wasm/chip8.wasm")
# Prepare Wasm3 engine
env = wasm3.Environment()
rt = env.new_runtime(1024)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
mod.link_function("Math", "random", "f()", random.random)
wasm_run = rt.find_function("run")
mem = rt.get_memory(0)
# Load CHIP-8 ROM
ROM = base64.b64decode("""
YwfB/6Js8R7wZUAAEgKEAMUfokz1HvBlyANIABImhkCGAkYAEgKjbIBg8FWEY6Js8R6AQPBVo2yC
EIEygR6BHoEegiaCJoIm0SESAgMGDBgwYMCBBw4cOHDgwYMPHjx48OHDhx8+fPjx48eP////////
////x/////////+D/////////wI/////////BB/4Z88TP/yED/mnjnN/+GgP+aeWcP/4GBP4ZzZw
//AEIfmnAnN/4AJA+CE6cz/AAoH4YTsTP8BDAn//////gP4EP/////+A/wg5ydDD/8A/kHnJ05//
wA/g+cnTn//gB+F5zLDH//ADEnnMs+P/+AMM+E5wh//8AwD4TnCH//wBAP///////gEA////////
AQH5zmEIR/8AA/nMYQnD/4AD+Eyzmcv/gAf4CbOYQ/8Hh/koE5nH/wOH+SnTmEP/AQf56dOYSf8B
B////////wEH////////AQf//////wA=
""")
mem[0x200:0x200+len(ROM)] = ROM
# Map memory region to an RGBA image
img_base = 0x1000
img_size = (64, 32)
(img_w, img_h) = img_size
region = mem[img_base : img_base + (img_w * img_h * 4)]
img = pygame.image.frombuffer(region, img_size, "RGBA")
# Prepare PyGame
scr_size = (img_w*8, img_h*8)
pygame.init()
surface = pygame.display.set_mode(scr_size)
pygame.display.set_caption("Wasm3 CHIP-8")
white = (255, 255, 255)
clock = pygame.time.Clock()
while True:
# Process input
for event in pygame.event.get():
if (event.type == pygame.QUIT or
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)):
pygame.quit()
quit()
# TODO: input support
#mem[10] = 0
# Render next frame
wasm_run(500)
# Image output
img_scaled = pygame.transform.scale(img, scr_size)
surface.fill(white)
surface.blit(img_scaled, (0, 0))
pygame.display.flip()
# Stabilize FPS
clock.tick(60)

@ -1,75 +0,0 @@
#!/usr/bin/env python3
import wasm3
import os, time, random
import pygame
print("WebAssembly demo file provided by Ben Smith (binji)")
print("Sources: https://github.com/binji/raw-wasm")
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, "./wasm/dino.wasm")
# Prepare Wasm3 engine
env = wasm3.Environment()
rt = env.new_runtime(1024)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
mod.link_function("Math", "random", "f()", lambda: random.random())
wasm_run = rt.find_function("run")
mem = rt.get_memory(0)
# Map memory region to an RGBA image
img_base = 0x5000
img_size = (300, 75)
(img_w, img_h) = img_size
region = mem[img_base : img_base + (img_w * img_h * 4)]
img = pygame.image.frombuffer(region, img_size, "RGBA")
# Prepare PyGame
scr_size = (img_w*4, img_h*4)
pygame.init()
surface = pygame.display.set_mode(scr_size)
pygame.display.set_caption("Wasm3 Dino")
white = (255, 255, 255)
k_jump = False
k_duck = False
clock = pygame.time.Clock()
while True:
# Process input
for event in pygame.event.get():
if (event.type == pygame.QUIT or
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)):
pygame.quit()
quit()
elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
k_jump = (event.type == pygame.KEYDOWN)
elif event.key == pygame.K_DOWN:
k_duck = (event.type == pygame.KEYDOWN)
mem[0] = 0
if k_jump:
mem[0] |= 0x1 # Jump flag
if k_duck:
mem[0] |= 0x2 # Duck flag
# Render next frame
wasm_run()
# Image output
img_scaled = pygame.transform.scale(img, scr_size)
surface.fill(white)
surface.blit(img_scaled, (0, 0))
pygame.display.flip()
# Stabilize FPS
clock.tick(60)

@ -1,61 +0,0 @@
#!/usr/bin/env python3
import wasm3
import os, time, random, math
import pygame
print("WebAssembly demo file provided by Ben Smith (binji)")
print("Sources: https://github.com/binji/raw-wasm")
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, "./wasm/fire.wasm")
# Prepare Wasm3 engine
env = wasm3.Environment()
rt = env.new_runtime(1024)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
mod.link_function("", "rand", "F()", random.random)
wasm_run = rt.find_function("run")
mem = rt.get_memory(0)
# Map memory region to an RGBA image
img_base = 53760
img_size = (320, 168)
(img_w, img_h) = img_size
region = mem[img_base : img_base + (img_w * img_h * 4)]
img = pygame.image.frombuffer(region, img_size, "RGBA")
# Prepare PyGame
scr_size = (img_w*2, img_h*2)
pygame.init()
surface = pygame.display.set_mode(scr_size)
pygame.display.set_caption("Wasm3 Doomfire")
background = (255, 255, 255)
clock = pygame.time.Clock()
while True:
# Process input
for event in pygame.event.get():
if (event.type == pygame.QUIT or
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)):
pygame.quit()
quit()
# Render next frame
wasm_run()
# Image output
img_scaled = pygame.transform.scale(img, scr_size)
surface.fill(background)
surface.blit(img_scaled, (0, 0))
pygame.display.flip()
# Stabilize FPS
clock.tick(60)

@ -1,86 +0,0 @@
#!/usr/bin/env python3
import wasm3
import os, time, random, math
import pygame
print("WebAssembly demo file provided by Ben Smith (binji)")
print("Sources: https://github.com/binji/raw-wasm")
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, "./wasm/maze.wasm")
def env_t(start):
pass
# Prepare Wasm3 engine
env = wasm3.Environment()
rt = env.new_runtime(1024)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
mod.link_function("Math", "sin", "f(f)", math.sin)
mod.link_function("Math", "random", "f()", random.random)
mod.link_function("env", "t", "v(i)", env_t)
wasm_run = rt.find_function("run")
mem = rt.get_memory(0)
# Map memory region to an RGBA image
img_base = 0x3000
img_size = (320, 240)
(img_w, img_h) = img_size
region = mem[img_base : img_base + (img_w * img_h * 4)]
img = pygame.image.frombuffer(region, img_size, "RGBA")
# Prepare PyGame
scr_size = (img_w*2, img_h*2)
pygame.init()
surface = pygame.display.set_mode(scr_size)
pygame.display.set_caption("Wasm3 Maze")
white = (255, 255, 255)
k_up = False
k_down = False
k_left = False
k_right = False
clock = pygame.time.Clock()
while True:
# Process input
for event in pygame.event.get():
if (event.type == pygame.QUIT or
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)):
pygame.quit()
quit()
elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP:
is_pressed = (event.type == pygame.KEYDOWN)
if event.key == pygame.K_UP:
k_up = is_pressed
elif event.key == pygame.K_DOWN:
k_down = is_pressed
elif event.key == pygame.K_LEFT:
k_left = is_pressed
elif event.key == pygame.K_RIGHT:
k_right = is_pressed
mem[0] = k_left
mem[1] = k_right
mem[2] = k_up
mem[3] = k_down
# Render next frame
wasm_run()
# Image output
img_scaled = pygame.transform.scale(img, scr_size)
surface.fill(white)
surface.blit(img_scaled, (0, 0))
pygame.display.flip()
# Stabilize FPS
clock.tick(60)

@ -1,63 +0,0 @@
#!/usr/bin/env python3
import wasm3
import os, time, random, math
import pygame
print("WebAssembly demo file provided by Ben Smith (binji)")
print("Sources: https://github.com/binji/raw-wasm")
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, "./wasm/metaball.wasm")
# Prepare Wasm3 engine
env = wasm3.Environment()
rt = env.new_runtime(1024)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
mod.link_function("", "rand", "f()", random.random)
wasm_blobs = rt.find_function("blobs")
wasm_blobs(5)
wasm_run = rt.find_function("run")
mem = rt.get_memory(0)
# Map memory region to an RGBA image
img_base = 1024
img_size = (320, 200)
(img_w, img_h) = img_size
region = mem[img_base : img_base + (img_w * img_h * 4)]
img = pygame.image.frombuffer(region, img_size, "RGBA")
# Prepare PyGame
scr_size = (img_w*2, img_h*2)
pygame.init()
surface = pygame.display.set_mode(scr_size)
pygame.display.set_caption("Wasm3 Metaball")
background = (0xd4, 0x19, 0x5d)
clock = pygame.time.Clock()
while True:
# Process input
for event in pygame.event.get():
if (event.type == pygame.QUIT or
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)):
pygame.quit()
quit()
# Render next frame
wasm_run()
# Image output
img_scaled = pygame.transform.scale(img, scr_size)
surface.fill(background)
surface.blit(img_scaled, (0, 0))
pygame.display.flip()
# Stabilize FPS
clock.tick(30)

@ -1,64 +0,0 @@
#!/usr/bin/env python3
import wasm3
import os, time, random, math
import pygame
print("WebAssembly demo file provided by Ben Smith (binji)")
print("Sources: https://github.com/binji/raw-wasm")
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, "./wasm/ray.wasm")
# Prepare Wasm3 engine
env = wasm3.Environment()
rt = env.new_runtime(1024)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
mod.link_function("Math", "sin", "f(f)", math.sin)
wasm_run = rt.find_function("run")
mem = rt.get_memory(0)
# Map memory region to an RGBA image
img_base = 1024
img_size = (320, 200)
(img_w, img_h) = img_size
region = mem[img_base : img_base + (img_w * img_h * 4)]
img = pygame.image.frombuffer(region, img_size, "RGBA")
# Prepare PyGame
scr_size = (img_w*2, img_h*2)
pygame.init()
surface = pygame.display.set_mode(scr_size)
pygame.display.set_caption("Wasm3 Raytrace")
background = (255, 255, 255)
clock = pygame.time.Clock()
t = 0
while True:
# Process input
for event in pygame.event.get():
if (event.type == pygame.QUIT or
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)):
pygame.quit()
quit()
# Render next frame
wasm_run(t)
t += 50
# Image output
img_scaled = pygame.transform.scale(img, scr_size)
surface.fill(background)
surface.blit(img_scaled, (0, 0))
pygame.display.flip()
# Stabilize FPS
clock.tick(30)

@ -1,74 +0,0 @@
#!/usr/bin/env python3
import wasm3
import os, time, random, math
import pygame
print("WebAssembly demo file provided by Ben Smith (binji)")
print("Sources: https://github.com/binji/raw-wasm")
scriptpath = os.path.dirname(os.path.realpath(__file__))
wasm_fn = os.path.join(scriptpath, "./wasm/snake.wasm")
# Prepare Wasm3 engine
env = wasm3.Environment()
rt = env.new_runtime(1024)
with open(wasm_fn, "rb") as f:
mod = env.parse_module(f.read())
rt.load(mod)
mod.link_function("Math", "sin", "f(f)", math.sin)
mod.link_function("Math", "random", "f()", random.random)
wasm_run = rt.find_function("run")
mem = rt.get_memory(0)
# Map memory region to an RGBA image
img_base = 0x15000
img_size = (240, 320)
(img_w, img_h) = img_size
region = mem[img_base : img_base + (img_w * img_h * 4)]
img = pygame.image.frombuffer(region, img_size, "RGBA")
# Prepare PyGame
scr_size = (img_w*2, img_h*2)
pygame.init()
surface = pygame.display.set_mode(scr_size)
pygame.display.set_caption("Wasm3 Snake")
white = (255, 255, 255)
k_left = False
k_right = False
clock = pygame.time.Clock()
while True:
# Process input
for event in pygame.event.get():
if (event.type == pygame.QUIT or
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)):
pygame.quit()
quit()
elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP:
is_pressed = (event.type == pygame.KEYDOWN)
if event.key == pygame.K_LEFT:
k_left = is_pressed
elif event.key == pygame.K_RIGHT:
k_right = is_pressed
mem[0x2c0] = k_left
mem[0x2c1] = k_right
# Render next frame
wasm_run()
# Image output
img_scaled = pygame.transform.scale(img, scr_size)
surface.fill(white)
surface.blit(img_scaled, (0, 0))
pygame.display.flip()
# Stabilize FPS
clock.tick(60)

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

@ -1,584 +0,0 @@
#include "Python.h"
#include "wasm3.h"
#include "m3_api_defs.h"
#define MAX_ARGS 32
typedef struct {
PyObject_HEAD
IM3Environment e;
} m3_environment;
typedef struct {
PyObject_HEAD
m3_environment *env;
IM3Runtime r;
} m3_runtime;
typedef struct {
PyObject_HEAD
m3_environment *env;
IM3Module m;
//bool is_gas_metered;
int64_t total_gas;
int64_t current_gas;
} m3_module;
typedef struct {
PyObject_HEAD
IM3Function f;
IM3Runtime r;
} m3_function;
static PyObject *M3_Environment_Type;
static PyObject *M3_Runtime_Type;
static PyObject *M3_Module_Type;
static PyObject *M3_Function_Type;
static m3_environment*
newEnvironment(PyObject *arg)
{
m3_environment *self = PyObject_GC_New(m3_environment, (PyTypeObject*)M3_Environment_Type);
if (!self) return NULL;
self->e = m3_NewEnvironment();
return self;
}
static void
delEnvironment(m3_environment *self)
{
m3_FreeEnvironment(self->e);
}
static PyObject *
formatError(PyObject *exception, IM3Runtime runtime, M3Result err)
{
M3ErrorInfo info;
memset(&info, 0, sizeof(info));
m3_GetErrorInfo (runtime, &info);
if (strlen(info.message)) {
PyErr_Format(exception, "%s (%s)", err, info.message);
} else {
PyErr_SetString(exception, err);
}
return NULL;
}
static void
put_arg_on_stack(u64 *s, M3ValueType type, PyObject *arg)
{
switch (type) {
case c_m3Type_i32: *(i32*)(s) = PyLong_AsLong(arg); break;
case c_m3Type_i64: *(i64*)(s) = PyLong_AsLongLong(arg); break;
case c_m3Type_f32: *(f32*)(s) = PyFloat_AsDouble(arg); break;
case c_m3Type_f64: *(f64*)(s) = PyFloat_AsDouble(arg); break;
}
}
static PyObject *
get_arg_from_stack(u64 *s, M3ValueType type)
{
switch (type) {
case c_m3Type_i32: return PyLong_FromLong( *(i32*)s); break;
case c_m3Type_i64: return PyLong_FromLongLong( *(i64*)s); break;
case c_m3Type_f32: return PyFloat_FromDouble( *(f32*)s); break;
case c_m3Type_f64: return PyFloat_FromDouble( *(f64*)s); break;
default:
return PyErr_Format(PyExc_TypeError, "unknown type %d", (int)type);
}
}
static PyObject *
M3_Environment_new_runtime(m3_environment *env, PyObject *stack_size_bytes)
{
size_t n = PyLong_AsSize_t(stack_size_bytes);
m3_runtime *self = PyObject_GC_New(m3_runtime, (PyTypeObject*)M3_Runtime_Type);
if (!self) return NULL;
Py_INCREF(env);
self->env = env;
self->r = m3_NewRuntime(env->e, n, NULL);
return self;
}
static PyObject *
M3_Environment_parse_module(m3_environment *env, PyObject *bytes)
{
Py_ssize_t size;
char *data;
PyBytes_AsStringAndSize(bytes, &data, &size);
IM3Module m;
M3Result err = m3_ParseModule(env->e, &m, data, size);
if (err) {
PyErr_SetString(PyExc_RuntimeError, err);
return NULL;
}
Py_INCREF(bytes);
m3_module *self = PyObject_GC_New(m3_module, (PyTypeObject*)M3_Module_Type);
if (!self) return NULL;
Py_INCREF(env);
self->env = env;
self->m = m;
self->total_gas = self->current_gas = 0;
return self;
}
static PyMethodDef M3_Environment_methods[] = {
{"new_runtime", (PyCFunction)M3_Environment_new_runtime, METH_O,
PyDoc_STR("new_runtime(stack_size_bytes) -> Runtime")},
{"parse_module", (PyCFunction)M3_Environment_parse_module, METH_O,
PyDoc_STR("new_runtime(bytes) -> Module")},
{NULL, NULL} /* sentinel */
};
static PyType_Slot M3_Environment_Type_slots[] = {
{Py_tp_doc, "The wasm3.Environment type"},
{Py_tp_finalize, delEnvironment},
{Py_tp_new, newEnvironment},
{Py_tp_methods, M3_Environment_methods},
{0, 0}
};
static PyObject *
M3_Runtime_load(m3_runtime *runtime, PyObject *arg)
{
m3_module *module = (m3_module *)arg;
M3Result err = m3_LoadModule(runtime->r, module->m);
if (err) {
return formatError(PyExc_RuntimeError, runtime->r, err);
}
Py_RETURN_NONE;
}
static PyObject *
M3_Runtime_find_function(m3_runtime *runtime, PyObject *name)
{
IM3Function func = NULL;
M3Result err = m3_FindFunction(&func, runtime->r, PyUnicode_AsUTF8(name));
if (err) {
return formatError(PyExc_RuntimeError, runtime->r, err);
}
m3_function *self = PyObject_GC_New(m3_function, (PyTypeObject*)M3_Function_Type);
if (!self) return NULL;
self->f = func;
self->r = runtime->r;
return self;
}
static PyObject *
M3_Runtime_get_memory(m3_runtime *runtime, PyObject *index)
{
Py_buffer* pybuff;
uint32_t size = 0;
uint8_t *mem = m3_GetMemory(runtime->r, &size, PyLong_AsLong(index));
if (!mem)
Py_RETURN_NONE;
pybuff = (Py_buffer*) PyMem_Malloc(sizeof(Py_buffer));
PyBuffer_FillInfo(pybuff, (PyObject *)runtime, mem, size, 0, PyBUF_WRITABLE);
return PyMemoryView_FromBuffer(pybuff);
}
static PyMethodDef M3_Runtime_methods[] = {
{"load", (PyCFunction)M3_Runtime_load, METH_O,
PyDoc_STR("load(module) -> None")},
{"find_function", (PyCFunction)M3_Runtime_find_function, METH_O,
PyDoc_STR("find_function(name) -> Function")},
{"get_memory", (PyCFunction)M3_Runtime_get_memory, METH_O,
PyDoc_STR("get_memory(index) -> memoryview")},
{NULL, NULL} /* sentinel */
};
static PyType_Slot M3_Runtime_Type_slots[] = {
{Py_tp_doc, "The wasm3.Runtime type"},
// {Py_tp_finalize, delRuntime},
// {Py_tp_new, newRuntime},
{Py_tp_methods, M3_Runtime_methods},
{0, 0}
};
static PyObject *
Module_name(m3_module *self, void * closure)
{
return PyUnicode_FromString(m3_GetModuleName(self->m));
}
static int
Module_setGasLimit(m3_module *self, PyObject *value, void * closure)
{
self->total_gas = PyFloat_AsDouble(value)*10000.0;
self->current_gas = self->total_gas;
return 0;
}
static PyObject *
Module_getGasLimit(m3_module *self, void * closure)
{
return PyFloat_FromDouble((double)(self->total_gas)/10000.0);
}
static PyObject *
Module_getGasUsed(m3_module *self, void * closure)
{
return PyFloat_FromDouble((double)(self->total_gas - self->current_gas)/10000.0);
}
m3ApiRawFunction(metering_usegas)
{
m3ApiGetArg (int32_t, gas)
m3_module *mod = (m3_module *)(_ctx->userdata);
mod->current_gas -= gas;
if (UNLIKELY(mod->current_gas < 0)) {
m3ApiTrap("[trap] Out of gas");
}
m3ApiSuccess();
}
static const char* trapException = "function raised exception";
m3ApiRawFunction(CallImport)
{
PyObject *pFunc = (PyObject *)(_ctx->userdata);
IM3Function f = _ctx->function;
int nArgs = m3_GetArgCount(f);
int nRets = m3_GetRetCount(f);
PyObject *pArgs = PyTuple_New(nArgs);
if (!pArgs) {
m3ApiTrap("python call: args not allocated");
}
for (Py_ssize_t i = 0; i < nArgs; ++i) {
PyObject *arg = get_arg_from_stack(&_sp[i], m3_GetArgType(f, i));
PyTuple_SET_ITEM(pArgs, i, arg);
}
PyObject * pRets = PyObject_CallObject(pFunc, pArgs);
if (!pRets) m3ApiTrap(trapException);
if (PyTuple_Check(pRets)) {
if (PyTuple_GET_SIZE(pRets) != nRets) {
m3ApiTrap("python call: return tuple length mismatch");
}
for (Py_ssize_t i = 0; i < nRets; ++i) {
PyObject *ret = PyTuple_GET_ITEM(pRets, i);
if (!ret) m3ApiTrap("python call: return type invalid");
put_arg_on_stack(&_sp[i], m3_GetRetType(f, i), ret);
}
} else {
if (nRets == 0) {
if (pRets != Py_None) {
//m3ApiTrap("python call: return value ignored");
}
} else if (nRets == 1) {
if (pRets == Py_None) {
m3ApiTrap("python call: should return a value");
}
put_arg_on_stack(&_sp[0], m3_GetRetType(f, 0), pRets);
} else {
m3ApiTrap("python call: should return a tuple");
}
}
m3ApiSuccess();
}
static PyObject *
M3_Module_link_function(m3_module *self, PyObject *args)
{
if (PyTuple_Size(args) != 4) {
PyErr_SetString(PyExc_TypeError, "link_function takes 4 arguments");
return NULL;
}
PyObject *mod_name = PyTuple_GET_ITEM(args, 0);
PyObject *func_name = PyTuple_GET_ITEM(args, 1);
PyObject *func_sig = PyTuple_GET_ITEM(args, 2);
PyObject *pFunc = PyTuple_GET_ITEM(args, 3);
if (!PyCallable_Check(pFunc)) {
PyErr_SetString(PyExc_TypeError, "function should be a callable object");
return NULL;
}
M3Result err = m3_LinkRawFunctionEx (self->m, PyUnicode_AsUTF8(mod_name), PyUnicode_AsUTF8(func_name),
PyUnicode_AsUTF8(func_sig), CallImport, pFunc);
if (err && err != m3Err_functionLookupFailed) {
return formatError(PyExc_RuntimeError, m3_GetModuleRuntime(self->m), err);
}
err = m3_LinkRawFunctionEx (self->m, "metering", "usegas", "v(i)", &metering_usegas, self);
/*if (!err) {
self->is_gas_metered = true;
}*/
if (err && err != m3Err_functionLookupFailed) {
return formatError(PyExc_RuntimeError, m3_GetModuleRuntime(self->m), err);
}
Py_INCREF(pFunc);
Py_RETURN_NONE;
}
static PyGetSetDef M3_Module_properties[] = {
{"name", (getter) Module_name, NULL, "module name", NULL},
{"gasLimit", (getter) Module_getGasLimit, (setter) Module_setGasLimit, "gas limit for metered modules", NULL},
{"gasUsed", (getter) Module_getGasUsed, NULL, "gas used", NULL},
{0},
};
static PyMethodDef M3_Module_methods[] = {
{"link_function", (PyCFunction)M3_Module_link_function, METH_VARARGS,
PyDoc_STR("link_function(module, name, signature, function)")},
{NULL, NULL} /* sentinel */
};
static PyType_Slot M3_Module_Type_slots[] = {
{Py_tp_doc, "The wasm3.Module type"},
// {Py_tp_finalize, delModule},
// {Py_tp_new, newModule},
{Py_tp_methods, M3_Module_methods},
{Py_tp_getset, M3_Module_properties},
{0, 0}
};
static PyObject *
get_result_from_stack(m3_function *func)
{
int nRets = m3_GetRetCount(func->f);
if (nRets <= 0) {
Py_RETURN_NONE;
}
if (nRets > 1) {
PyErr_SetString(PyExc_NotImplementedError, "multi-value not supported yet");
return NULL;
}
if (nRets > MAX_ARGS) {
PyErr_SetString(PyExc_RuntimeError, "too many rets");
return NULL;
}
static uint64_t valbuff[MAX_ARGS];
static const void* valptrs[MAX_ARGS];
memset(valbuff, 0, sizeof(valbuff));
memset(valptrs, 0, sizeof(valptrs));
for (int i = 0; i < nRets; i++) {
valptrs[i] = &valbuff[i];
}
M3Result err = m3_GetResults (func->f, nRets, valptrs);
if (err) {
return formatError(PyExc_RuntimeError, func->r, err);
}
return get_arg_from_stack(valptrs[0], m3_GetRetType(func->f, 0));
}
static PyObject *
M3_Function_call_argv(m3_function *func, PyObject *args)
{
Py_ssize_t size = PyTuple_GET_SIZE(args);
const char* argv[MAX_ARGS];
for(Py_ssize_t i = 0; i< size;++i) {
argv[i] = PyUnicode_AsUTF8(PyTuple_GET_ITEM(args, i));
}
M3Result err = m3_CallArgv(func->f, size, argv);
if (err == trapException) {
return NULL;
} else if (err) {
return formatError(PyExc_RuntimeError, func->r, err);
}
return get_result_from_stack(func);
}
static PyObject*
M3_Function_call(m3_function *self, PyObject *args, PyObject *kwargs)
{
u32 i;
IM3Function f = self->f;
int nArgs = m3_GetArgCount(f);
if (nArgs > MAX_ARGS) {
PyErr_SetString(PyExc_RuntimeError, "too many args");
return NULL;
}
static uint64_t valbuff[MAX_ARGS];
static const void* valptrs[MAX_ARGS];
memset(valbuff, 0, sizeof(args));
memset(valptrs, 0, sizeof(valptrs));
for (int i = 0; i < nArgs; i++) {
u64* s = &valbuff[i];
valptrs[i] = s;
put_arg_on_stack(s, m3_GetArgType(f, i), PyTuple_GET_ITEM(args, i));
}
M3Result err = m3_Call (f, nArgs, valptrs);
if (err == trapException) {
return NULL;
} else if (err) {
return formatError(PyExc_RuntimeError, self->r, err);
}
return get_result_from_stack(self);
}
static PyObject*
Function_name(m3_function *self, void * closure)
{
return PyUnicode_FromString(m3_GetFunctionName(self->f));
}
static PyObject*
Function_num_args(m3_function *self, void * closure)
{
return PyLong_FromLong(m3_GetArgCount(self->f));
}
static PyObject*
Function_num_rets(m3_function *self, void * closure)
{
return PyLong_FromLong(m3_GetRetCount(self->f));
}
static PyObject*
Function_arg_types(m3_function *self, void * closure)
{
Py_ssize_t nArgs = m3_GetArgCount(self->f);
PyObject *ret = PyTuple_New(nArgs);
if (ret) {
Py_ssize_t i;
for (i = 0; i < nArgs; ++i) {
PyTuple_SET_ITEM(ret, i, PyLong_FromLong(m3_GetArgType(self->f, i)));
}
}
return ret;
}
static PyObject*
Function_ret_types(m3_function *self, void * closure)
{
Py_ssize_t nRets = m3_GetRetCount(self->f);
PyObject *ret = PyTuple_New(nRets);
if (ret) {
Py_ssize_t i;
for (i = 0; i < nRets; ++i) {
PyTuple_SET_ITEM(ret, i, PyLong_FromLong(m3_GetRetType(self->f, i)));
}
}
return ret;
}
static PyGetSetDef M3_Function_properties[] = {
{"name", (getter) Function_name, NULL, "function name", NULL },
{"num_args", (getter) Function_num_args, NULL, "number of args", NULL },
{"num_rets", (getter) Function_num_rets, NULL, "number of rets", NULL },
{"arg_types", (getter) Function_arg_types, NULL, "types of args", NULL },
{"ret_types", (getter) Function_ret_types, NULL, "types of rets", NULL },
{NULL} /* Sentinel */
};
static PyMethodDef M3_Function_methods[] = {
{"call_argv", (PyCFunction)M3_Function_call_argv, METH_VARARGS,
PyDoc_STR("call_argv(args...) -> result")},
{NULL, NULL} /* sentinel */
};
static PyType_Slot M3_Function_Type_slots[] = {
{Py_tp_doc, "The wasm3.Function type"},
// {Py_tp_finalize, delFunction},
// {Py_tp_new, newFunction},
{Py_tp_call, M3_Function_call},
{Py_tp_methods, M3_Function_methods},
{Py_tp_getset, M3_Function_properties},
{0, 0}
};
static PyType_Spec M3_Environment_Type_spec = {
"wasm3.Environment",
sizeof(m3_environment),
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
M3_Environment_Type_slots
};
static PyType_Spec M3_Runtime_Type_spec = {
"wasm3.Runtime",
sizeof(m3_runtime),
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
M3_Runtime_Type_slots
};
static PyType_Spec M3_Module_Type_spec = {
"wasm3.Module",
sizeof(m3_module),
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
M3_Module_Type_slots
};
static PyType_Spec M3_Function_Type_spec = {
"wasm3.Function",
sizeof(m3_function),
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
M3_Function_Type_slots
};
static int
m3_modexec(PyObject *m)
{
M3_Environment_Type = PyType_FromSpec(&M3_Environment_Type_spec);
if (M3_Environment_Type == NULL)
goto fail;
M3_Runtime_Type = PyType_FromSpec(&M3_Runtime_Type_spec);
if (M3_Runtime_Type == NULL)
goto fail;
M3_Module_Type = PyType_FromSpec(&M3_Module_Type_spec);
if (M3_Module_Type == NULL)
goto fail;
M3_Function_Type = PyType_FromSpec(&M3_Function_Type_spec);
if (M3_Function_Type == NULL)
goto fail;
PyModule_AddStringMacro(m, M3_VERSION);
PyModule_AddObject(m, "Environment", M3_Environment_Type);
PyModule_AddObject(m, "Runtime", M3_Runtime_Type);
PyModule_AddObject(m, "Module", M3_Module_Type);
PyModule_AddObject(m, "Function", M3_Function_Type);
return 0;
fail:
Py_XDECREF(m);
return -1;
}
static PyModuleDef_Slot m3_slots[] = {
{Py_mod_exec, m3_modexec},
{0, NULL}
};
PyDoc_STRVAR(m3_doc,
"wasm3 python bindings");
static struct PyModuleDef m3module = {
PyModuleDef_HEAD_INIT,
"wasm3",
m3_doc,
0,
0, // methods
m3_slots,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC
PyInit_wasm3(void)
{
return PyModuleDef_Init(&m3module);
}

@ -1,37 +0,0 @@
#!/usr/bin/env python3
from setuptools import setup
from distutils.core import Extension
from glob import glob
SOURCES = glob('m3/*.c') + ['m3module.c']
setup(
name = "pywasm3",
version = "0.4.9",
description = "The fastest WebAssembly interpreter",
platforms = "any",
url = "https://github.com/wasm3/wasm3",
license = "MIT",
author = "Volodymyr Shymanskyy",
author_email = "vshymanskyi@gmail.com",
long_description = open("README.md").read(),
long_description_content_type = "text/markdown",
ext_modules=[
Extension('wasm3', sources=SOURCES, include_dirs=['m3'],
extra_compile_args=['-g0', '-O3', '-march=native',
'-fomit-frame-pointer', '-fno-stack-check', '-fno-stack-protector',
'-DDEBUG', '-DNASSERTS'])
],
classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
"Development Status :: 4 - Beta",
"License :: OSI Approved :: MIT License",
"Operating System :: POSIX :: Linux",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS :: MacOS X"
]
)

@ -1,24 +0,0 @@
/*
* Build using:
* emcc dyn_callback.c -Os -s WASM=1 -s SIDE_MODULE=1 -o dyn_callback.wasm
*/
typedef int (*fptr_type)(int x, int y);
extern void pass_fptr(fptr_type fptr);
static int callback_add(int x, int y)
{
return x+y;
}
static int callback_mul(int x, int y)
{
return x*y;
}
void run_test()
{
pass_fptr(callback_add);
pass_fptr(callback_mul);
}

@ -1,214 +0,0 @@
import wasm3 as m3
import pytest
import tempfile, subprocess
def wat2wasm(wat):
with tempfile.TemporaryDirectory() as d:
fn_in = d + "/input.wat"
fn_out = d + "/output.wasm"
with open(fn_in, "wb") as f:
f.write(wat.encode('utf8'))
subprocess.run(["wat2wasm", "--enable-all", "-o", fn_out, fn_in], check=True)
with open(fn_out, "rb") as f:
return f.read()
FIB64_WASM = wat2wasm("""
(module
(func $fib2 (param $n i64) (param $a i64) (param $b i64) (result i64)
(if (result i64)
(i64.eqz (get_local $n))
(then (get_local $a))
(else (return_call $fib2 (i64.sub (get_local $n)
(i64.const 1))
(get_local $b)
(i64.add (get_local $a)
(get_local $b))))))
(func $fib (export "fib") (param i64) (result i64)
(return_call $fib2 (get_local 0)
(i64.const 0) ;; seed value $a
(i64.const 1))) ;; seed value $b
)
""")
CALLBACK_WASM = wat2wasm("""
(module
(type (;0;) (func (param i32 i32) (result i32)))
(func $i (import "env" "callback") (type 0))
(func (export "run_callback") (type 0)
local.get 0
local.get 1
call $i)
)
""")
DYN_CALLBACK_WASM = wat2wasm("""
(module
(type $t0 (func (param i32 i32) (result i32)))
(type $t1 (func))
(type $t2 (func (param i32)))
(type $t3 (func (param i32 i32 i32) (result i32)))
(import "env" "pass_fptr" (func $env.pass_fptr (type $t2)))
(import "env" "__table_base" (global $env.__table_base i32))
(func $run_test (export "run_test") (type $t1)
global.get $env.__table_base
call $env.pass_fptr
global.get $env.__table_base
i32.const 1
i32.add
call $env.pass_fptr)
(func $f2 (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
i32.add)
(func $f3 (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
i32.mul)
(func $test (export "call_pass_fptr") (type $t2) (param $p0 i32)
local.get $p0
call $env.pass_fptr
)
(func $dynCall_iii (export "dynCall_iii") (type $t3) (param $p0 i32) (param $p1 i32) (param $p2 i32) (result i32)
local.get $p1
local.get $p2
local.get $p0
call_indirect $table (type $t0))
(table $table (export "table") 2 funcref)
(elem (global.get $env.__table_base) func $f2 $f3))
""")
ADD_WASM = wat2wasm("""
(module
(func (export "add") (param i64 i64) (result i64)
local.get 0
local.get 1
i64.add
return)
)
""")
def test_classes():
assert isinstance(m3.Environment, type)
assert isinstance(m3.Runtime, type)
assert isinstance(m3.Module, type)
assert isinstance(m3.Function, type)
def test_callback():
env = m3.Environment()
rt = env.new_runtime(1024)
mod = env.parse_module(CALLBACK_WASM)
rt.load(mod)
mem = rt.get_memory(0)
def func(x, y):
assert x == 123
assert y == 456
return x*y
mod.link_function("env", "callback", "i(ii)", func)
run_callback = rt.find_function("run_callback")
assert run_callback(123, 456) == 123*456
def test_callback_member():
class WasmRunner:
def __init__(self, wasm):
self.env = m3.Environment()
self.rt = self.env.new_runtime(1024)
self.mod = self.env.parse_module(wasm)
self.rt.load(self.mod)
self.mem = self.rt.get_memory(0)
self.mod.link_function("env", "callback", "i(ii)", self.func)
self.run_callback = self.rt.find_function("run_callback")
def func(self, x, y):
assert x == 987
assert y == 654
return x+y
inst = WasmRunner(CALLBACK_WASM)
assert inst.run_callback(987, 654) == 987+654
def test_dynamic_callback():
env = m3.Environment()
rt = env.new_runtime(1024)
mod = env.parse_module(DYN_CALLBACK_WASM)
rt.load(mod)
dynCall_iii = rt.find_function("dynCall_iii")
def pass_fptr(fptr):
if fptr == 0:
assert dynCall_iii(fptr, 12, 34) == 46
elif fptr == 1:
# TODO: call by table index directly here
assert dynCall_iii(fptr, 12, 34) == 408
else:
raise Exception("Strange function ptr")
mod.link_function("env", "pass_fptr", "v(i)", pass_fptr)
# Indirect calls
assert dynCall_iii(0, 12, 34) == 46
assert dynCall_iii(1, 12, 34) == 408
# Recursive exported function call (single calls)
call_pass_fptr = rt.find_function("call_pass_fptr")
base = 0
call_pass_fptr(base+0)
call_pass_fptr(base+1)
# Recursive exported function call (multiple calls)
rt.find_function("run_test")()
def test_m3(capfd):
env = m3.Environment()
rt = env.new_runtime(1024)
assert isinstance(rt, m3.Runtime)
mod = env.parse_module(FIB64_WASM)
assert isinstance(mod, m3.Module)
assert mod.name == '.unnamed'
rt.load(mod)
assert rt.get_memory(0) is None # XXX
# rt.print_info()
# assert capfd.readouterr().out == """
# -- m3 runtime -------------------------------------------------
# stack-size: 1024
#
# module [0] name: '.unnamed'; funcs: 1
# ----------------------------------------------------------------
# """
with pytest.raises(RuntimeError):
rt.find_function('not_existing')
func = rt.find_function('fib')
assert isinstance(func, m3.Function)
assert func.call_argv('5') == 5
assert func.call_argv('10') == 55
assert func.name == 'fib'
assert func.num_args == 1
assert func.num_rets == 1
assert func.arg_types == (2,)
assert func.ret_types == (2,)
assert func(0) == 0
assert func(1) == 1
rt.load(env.parse_module(ADD_WASM))
add = rt.find_function('add')
assert add(2, 3) == 5
def call_function(wasm, func, *args):
env = m3.Environment()
rt = env.new_runtime(4096)
mod = env.parse_module(wasm)
rt.load(mod)
f = rt.find_function(func)
return f.call_argv(*args)
def test_fib64():
assert call_function(FIB64_WASM, 'fib', '5') == 5
assert call_function(FIB64_WASM, 'fib', '10') == 55
# TODO: Fails on 3.6, 3.7 ?
#assert call_function(FIB64_WASM, 'fib', '90') == 2880067194370816120
Loading…
Cancel
Save