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 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 os
import subprocess import subprocess
import sys
import tempfile import tempfile
import pywasm.binary
import wasm3
from phasm.compiler import phasm_compile from phasm.compiler import phasm_compile
from phasm.parser import phasm_parse from phasm.parser import phasm_parse
from phasm import ourlang from phasm import ourlang
from phasm import wasm from phasm import wasm
import wasm3
def wat2wasm(code_wat: str) -> bytes: def wat2wasm(code_wat: str) -> bytes:
""" """
Converts the given WebAssembly Assembly code into WebAssembly Binary Converts the given WebAssembly Assembly code into WebAssembly Binary
@ -101,6 +102,18 @@ class RunnerBase:
""" """
raise NotImplementedError 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: def interpreter_dump_memory(self, textio: TextIO) -> None:
""" """
Dumps the interpreters memory for debugging Dumps the interpreters memory for debugging
@ -113,6 +126,37 @@ class RunnerBase:
""" """
raise NotImplementedError 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): class RunnerPywasm3(RunnerBase):
""" """
Implements a runner for pywasm3 Implements a runner for pywasm3
@ -131,6 +175,16 @@ class RunnerPywasm3(RunnerBase):
self.mod = self.env.parse_module(self.wasm_bin) self.mod = self.env.parse_module(self.wasm_bin)
self.rtime.load(self.mod) 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: def interpreter_dump_memory(self, textio: TextIO) -> None:
_dump_memory(textio, self.rtime.get_memory(0)) _dump_memory(textio, self.rtime.get_memory(0))

View File

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