MVP #1
14
stubs/pywasm/__init__.pyi
Normal file
14
stubs/pywasm/__init__.pyi
Normal 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
6
stubs/pywasm/binary.pyi
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from typing import BinaryIO
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
@classmethod
|
||||||
|
def from_reader(cls, reader: BinaryIO) -> 'Module':
|
||||||
|
...
|
||||||
10
stubs/pywasm/execution.pyi
Normal file
10
stubs/pywasm/execution.pyi
Normal 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
2
stubs/pywasm/option.pyi
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
class Option:
|
||||||
|
...
|
||||||
@ -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))
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user