MVP #1

Merged
jbwdevries merged 73 commits from idea_crc32 into master 2022-08-21 12:59:21 +00:00
6 changed files with 101 additions and 13 deletions
Showing only changes of commit 58424cf2a0 - Show all commits

14
stubs/pywasm/__init__.pyi Normal file
View File

@ -0,0 +1,14 @@
from typing import Any, Dict, List, Optional, Union
from . import binary
from . import option
from . import execution
class Runtime:
store: execution.Store
def __init__(self, module: binary.Module, imps: Optional[Dict[str, Any]] = None, opts: Optional[option.Option] = None):
...
def exec(self, name: str, args: List[Union[int, float]]) -> Any:
...

6
stubs/pywasm/binary.pyi Normal file
View File

@ -0,0 +1,6 @@
from typing import BinaryIO
class Module:
@classmethod
def from_reader(cls, reader: BinaryIO) -> 'Module':
...

View File

@ -0,0 +1,10 @@
from typing import List
class Result:
...
class MemoryInstance:
data: bytearray
class Store:
memory_list: List[MemoryInstance]

2
stubs/pywasm/option.pyi Normal file
View File

@ -0,0 +1,2 @@
class Option:
...

View File

@ -1,20 +1,21 @@
"""
Runners to help run WebAssembly code on various interpreters
"""
from typing import Any, TextIO
from typing import Any, Iterable, TextIO
import io
import os
import subprocess
import sys
import tempfile
import pywasm.binary
import wasm3
from phasm.compiler import phasm_compile
from phasm.parser import phasm_parse
from phasm import ourlang
from phasm import wasm
import wasm3
def wat2wasm(code_wat: str) -> bytes:
"""
Converts the given WebAssembly Assembly code into WebAssembly Binary
@ -101,6 +102,18 @@ class RunnerBase:
"""
raise NotImplementedError
def interpreter_write_memory(self, offset: int, data: Iterable[int]) -> None:
"""
Writes into the interpreters memory
"""
raise NotImplementedError
def interpreter_read_memory(self, offset: int, length: int) -> bytes:
"""
Reads from the interpreters memory
"""
raise NotImplementedError
def interpreter_dump_memory(self, textio: TextIO) -> None:
"""
Dumps the interpreters memory for debugging
@ -113,6 +126,37 @@ class RunnerBase:
"""
raise NotImplementedError
class RunnerPywasm(RunnerBase):
"""
Implements a runner for pywasm
See https://pypi.org/project/pywasm/
"""
module: pywasm.binary.Module
runtime: pywasm.Runtime
def interpreter_setup(self) -> None:
# Nothing to set up
pass
def interpreter_load(self) -> None:
bytesio = io.BytesIO(self.wasm_bin)
self.module = pywasm.binary.Module.from_reader(bytesio)
self.runtime = pywasm.Runtime(self.module, {}, None)
def interpreter_write_memory(self, offset: int, data: Iterable[int]) -> None:
for idx, byt in enumerate(data):
self.runtime.store.memory_list[0].data[offset + idx] = byt
def interpreter_read_memory(self, offset: int, length: int) -> bytes:
return self.runtime.store.memory_list[0].data[offset:length]
def interpreter_dump_memory(self, textio: TextIO) -> None:
_dump_memory(textio, self.runtime.store.memory_list[0].data)
def call(self, function: str, *args: Any) -> Any:
return self.runtime.exec(function, [*args])
class RunnerPywasm3(RunnerBase):
"""
Implements a runner for pywasm3
@ -131,6 +175,16 @@ class RunnerPywasm3(RunnerBase):
self.mod = self.env.parse_module(self.wasm_bin)
self.rtime.load(self.mod)
def interpreter_write_memory(self, offset: int, data: Iterable[int]) -> None:
memory = self.rtime.get_memory(0)
for idx, byt in enumerate(data):
memory[offset + idx] = byt # type: ignore
def interpreter_read_memory(self, offset: int, length: int) -> bytes:
memory = self.rtime.get_memory(0)
return memory[offset:length].tobytes()
def interpreter_dump_memory(self, textio: TextIO) -> None:
_dump_memory(textio, self.rtime.get_memory(0))

View File

@ -31,11 +31,9 @@ def testEntry() -> u8:
"""
runner = setup_interpreter(code_py)
memory = runner.rtime.get_memory(0)
# Garbage in the memory so we can test for it
for idx in range(128):
memory[idx] = idx
runner.interpreter_write_memory(0, range(128))
sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n')
runner.interpreter_dump_memory(sys.stderr)
@ -45,15 +43,13 @@ def testEntry() -> u8:
sys.stderr.write(f'{DASHES} Memory (post run) {DASHES}\n')
runner.interpreter_dump_memory(sys.stderr)
memory = memory.tobytes()
assert (
b'\xC0\xA1\x00\x00'
b'\x00\x00\x00\x00'
b'\x00\x00\x00\x00'
b'\x10\x00\x00\x00'
b'\x10\x11\x12\x13' # Untouched because unused
) == memory[0:20]
) == runner.interpreter_read_memory(0, 20)
@pytest.mark.integration_test
def test___alloc___no_init():
@ -68,7 +64,7 @@ def testEntry() -> u8:
sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n')
runner.interpreter_dump_memory(sys.stderr)
with pytest.raises(RuntimeError, match='unreachable executed'):
with pytest.raises(Exception, match='unreachable'):
runner.call('stdlib.alloc.__alloc__', 32)
@pytest.mark.integration_test
@ -80,7 +76,6 @@ def testEntry() -> u8:
"""
runner = setup_interpreter(code_py)
memory = runner.rtime.get_memory(0)
sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n')
runner.interpreter_dump_memory(sys.stderr)
@ -93,7 +88,14 @@ def testEntry() -> u8:
sys.stderr.write(f'{DASHES} Memory (post run) {DASHES}\n')
runner.interpreter_dump_memory(sys.stderr)
assert b'\xC0\xA1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' == memory[0:12]
assert (
b'\xC0\xA1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7C\x00\x00\x00'
b'\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00'
) == runner.interpreter_read_memory(0, 0x60)
assert 0x14 == offset0
assert 0x38 == offset1