Compare commits
9 Commits
c3124f4325
...
be28450658
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be28450658 | ||
|
|
4001b086db | ||
|
|
19a29b7327 | ||
|
|
ffd11c4f72 | ||
|
|
5d9ef0e276 | ||
|
|
521171540b | ||
|
|
3e916a242e | ||
|
|
96f52a274c | ||
|
|
5c537f712e |
15
Makefile
15
Makefile
@ -1,7 +1,4 @@
|
||||
WABT_DIR := /home/johan/sources/github.com/WebAssembly/wabt
|
||||
|
||||
WAT2WASM := $(WABT_DIR)/bin/wat2wasm
|
||||
WASM2C := $(WABT_DIR)/bin/wasm2c
|
||||
WAT2WASM := venv/bin/python wat2wasm.py
|
||||
|
||||
%.wat: %.py $(shell find phasm -name '*.py') venv/.done
|
||||
venv/bin/python -m phasm $< $@
|
||||
@ -15,12 +12,6 @@ WASM2C := $(WABT_DIR)/bin/wasm2c
|
||||
%.wasm: %.wat
|
||||
$(WAT2WASM) $^ -o $@
|
||||
|
||||
%.c: %.wasm
|
||||
$(WASM2C) $^ -o $@
|
||||
|
||||
# %.exe: %.c
|
||||
# cc $^ -o $@ -I $(WABT_DIR)/wasm2c
|
||||
|
||||
examples: venv/.done $(subst .py,.wasm,$(wildcard examples/*.py)) $(subst .py,.wat.html,$(wildcard examples/*.py)) $(subst .py,.py.html,$(wildcard examples/*.py))
|
||||
venv/bin/python3 -m http.server --directory examples
|
||||
|
||||
@ -31,10 +22,10 @@ lint: venv/.done
|
||||
venv/bin/ruff check phasm tests
|
||||
|
||||
typecheck: venv/.done
|
||||
venv/bin/mypy --strict phasm tests/integration/helpers.py tests/integration/runners.py
|
||||
venv/bin/mypy --strict phasm wat2wasm.py tests/integration/helpers.py tests/integration/runners.py
|
||||
|
||||
venv/.done: requirements.txt
|
||||
python3.10 -m venv venv
|
||||
python3.12 -m venv venv
|
||||
venv/bin/python3 -m pip install wheel pip --upgrade
|
||||
venv/bin/python3 -m pip install -r $^
|
||||
touch $@
|
||||
|
||||
@ -32,7 +32,7 @@ make lint typecheck
|
||||
|
||||
To compile a Phasm file:
|
||||
```sh
|
||||
python3.10 -m phasm source.py output.wat
|
||||
python3.12 -m phasm source.py output.wat
|
||||
```
|
||||
|
||||
Additional required tools
|
||||
|
||||
17
TODO.md
17
TODO.md
@ -10,3 +10,20 @@
|
||||
- Storing u8 in memory still claims 32 bits (since that's what you need in local variables). However, using load8_u / loadu_s we can optimize this.
|
||||
- Implement a FizzBuzz example
|
||||
- Also, check the codes for FIXME and TODO
|
||||
- Allocation is done using pointers for members, is this desired?
|
||||
|
||||
- Merge in type classes
|
||||
|
||||
- test_bitwise_or_inv_type
|
||||
- test_bytes_index_out_of_bounds vs static trap(?)
|
||||
- test_num.py is probably best as part of the generator?
|
||||
- Find pytest.mark.skip
|
||||
- There's a weird resolve_as reference in calculate_alloc_size
|
||||
- Either there should be more of them or less
|
||||
- At first glance, looks like failure in the typing system
|
||||
- Related to the FIXME in phasm_type3?
|
||||
- WEBASSEMBLY_BUILTIN_FLOAT_OPS and WEBASSEMBLY_BUILTIN_BYTES_OPS are special cased
|
||||
- Should be part of a prelude
|
||||
- Casting is not implemented except u32 which is special cased
|
||||
- Parser is putting stuff in ModuleDataBlock
|
||||
- Compiler should probably do that
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
# CRC-32/ISO-HDLC
|
||||
#
|
||||
# #include <inttypes.h> // uint32_t, uint8_t
|
||||
#
|
||||
# uint32_t CRC32(const uint8_t data[], size_t data_length) {
|
||||
|
||||
@ -497,10 +497,12 @@ class OurVisitor:
|
||||
if 1 != len(node.args):
|
||||
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
||||
|
||||
return UnaryOp(
|
||||
unary_op = UnaryOp(
|
||||
'cast',
|
||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]),
|
||||
)
|
||||
unary_op.type3 = type3types.u32
|
||||
return unary_op
|
||||
elif node.func.id == 'len':
|
||||
if 1 != len(node.args):
|
||||
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
||||
@ -703,7 +705,7 @@ def _not_implemented(check: Any, msg: str) -> None:
|
||||
if not check:
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def _raise_static_error(node: Union[ast.mod, ast.stmt, ast.expr], msg: str) -> NoReturn:
|
||||
def _raise_static_error(node: Union[ast.stmt, ast.expr], msg: str) -> NoReturn:
|
||||
raise StaticError(
|
||||
f'Static error on line {node.lineno}: {msg}'
|
||||
)
|
||||
|
||||
@ -1,10 +1,19 @@
|
||||
mypy==0.991
|
||||
pygments==2.12.0
|
||||
pytest==7.2.0
|
||||
marko==2.1.3
|
||||
mypy==1.15.0
|
||||
pygments==2.19.1
|
||||
pytest==8.3.5
|
||||
pytest-integration==0.2.2
|
||||
pywasm==1.0.7
|
||||
pywasm3==0.5.0
|
||||
ruff==0.1.5
|
||||
wasmer==1.1.0
|
||||
wasmer_compiler_cranelift==1.1.0
|
||||
wasmtime==3.0.0
|
||||
ruff==0.11.4
|
||||
|
||||
|
||||
wasmtime==31.0.0
|
||||
|
||||
# TODO:
|
||||
# extism?
|
||||
# wasmedge
|
||||
|
||||
# Check 2025-04-05
|
||||
# wasm3: minimal maintenance phase
|
||||
# py-wasm: last updated 6 years ago
|
||||
# wasmer-python: Not compatible with python3.12, last updated 2 years ago
|
||||
# WAVM: Last updated 3 years ago
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
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:
|
||||
...
|
||||
@ -1,6 +0,0 @@
|
||||
from typing import BinaryIO
|
||||
|
||||
class Module:
|
||||
@classmethod
|
||||
def from_reader(cls, reader: BinaryIO) -> 'Module':
|
||||
...
|
||||
@ -1,10 +0,0 @@
|
||||
from typing import List
|
||||
|
||||
class Result:
|
||||
...
|
||||
|
||||
class MemoryInstance:
|
||||
data: bytearray
|
||||
|
||||
class Store:
|
||||
memory_list: List[MemoryInstance]
|
||||
@ -1,2 +0,0 @@
|
||||
class Option:
|
||||
...
|
||||
@ -1,23 +0,0 @@
|
||||
from typing import Any, Callable
|
||||
|
||||
class Module:
|
||||
...
|
||||
|
||||
class Runtime:
|
||||
...
|
||||
|
||||
def load(self, wasm_bin: Module) -> None:
|
||||
...
|
||||
|
||||
def get_memory(self, memid: int) -> memoryview:
|
||||
...
|
||||
|
||||
def find_function(self, name: str) -> Callable[[Any], Any]:
|
||||
...
|
||||
|
||||
class Environment:
|
||||
def new_runtime(self, mem_size: int) -> Runtime:
|
||||
...
|
||||
|
||||
def parse_module(self, wasm_bin: bytes) -> Module:
|
||||
...
|
||||
@ -1,39 +0,0 @@
|
||||
from typing import Any, Dict, Callable, Union
|
||||
|
||||
def wat2wasm(inp: str) -> bytes:
|
||||
...
|
||||
|
||||
class Store:
|
||||
...
|
||||
|
||||
class Function:
|
||||
def __init__(self, store: Store, func: Callable[[Any], Any]) -> None:
|
||||
...
|
||||
|
||||
class Module:
|
||||
def __init__(self, store: Store, wasm: bytes) -> None:
|
||||
...
|
||||
|
||||
class Uint8Array:
|
||||
def __getitem__(self, index: Union[int, slice]) -> int:
|
||||
...
|
||||
|
||||
def __setitem__(self, idx: int, value: int) -> None:
|
||||
...
|
||||
|
||||
class Memory:
|
||||
def uint8_view(self, offset: int = 0) -> Uint8Array:
|
||||
...
|
||||
|
||||
class Exports:
|
||||
...
|
||||
|
||||
class ImportObject:
|
||||
def register(self, region: str, values: Dict[str, Function]) -> None:
|
||||
...
|
||||
|
||||
class Instance:
|
||||
exports: Exports
|
||||
|
||||
def __init__(self, module: Module, imports: ImportObject) -> None:
|
||||
...
|
||||
@ -16,10 +16,7 @@ class SuiteResult:
|
||||
self.returned_value = None
|
||||
|
||||
RUNNER_CLASS_MAP = {
|
||||
'pywasm': runners.RunnerPywasm,
|
||||
'pywasm3': runners.RunnerPywasm3,
|
||||
'wasmtime': runners.RunnerWasmtime,
|
||||
'wasmer': runners.RunnerWasmer,
|
||||
}
|
||||
|
||||
class Suite:
|
||||
@ -29,7 +26,7 @@ class Suite:
|
||||
def __init__(self, code_py: str) -> None:
|
||||
self.code_py = code_py
|
||||
|
||||
def run_code(self, *args: Any, runtime: str = 'pywasm3', func_name: str = 'testEntry', imports: runners.Imports = None) -> Any:
|
||||
def run_code(self, *args: Any, runtime: str = 'wasmtime', func_name: str = 'testEntry', imports: runners.Imports = None, do_format_check: bool = True) -> Any:
|
||||
"""
|
||||
Compiles the given python code into wasm and
|
||||
then runs it
|
||||
@ -50,12 +47,12 @@ class Suite:
|
||||
write_header(sys.stderr, 'Assembly')
|
||||
runner.dump_wasm_wat(sys.stderr)
|
||||
|
||||
runner.compile_wasm()
|
||||
runner.interpreter_setup()
|
||||
runner.interpreter_load(imports)
|
||||
|
||||
# Check if code formatting works
|
||||
assert self.code_py == '\n' + phasm_render(runner.phasm_ast) # \n for formatting in tests
|
||||
if do_format_check:
|
||||
assert self.code_py == '\n' + phasm_render(runner.phasm_ast) # \n for formatting in tests
|
||||
|
||||
func_args = [x.type3 for x in runner.phasm_ast.functions[func_name].posonlyargs]
|
||||
if len(func_args) != len(args):
|
||||
|
||||
@ -2,12 +2,8 @@
|
||||
Runners to help run WebAssembly code on various interpreters
|
||||
"""
|
||||
import ctypes
|
||||
import io
|
||||
from typing import Any, Callable, Dict, Iterable, Optional, TextIO
|
||||
|
||||
import pywasm.binary
|
||||
import wasm3
|
||||
import wasmer
|
||||
import wasmtime
|
||||
|
||||
from phasm import ourlang, wasm
|
||||
@ -65,7 +61,7 @@ class RunnerBase:
|
||||
"""
|
||||
Compiles the WebAssembly AST into WebAssembly Binary
|
||||
"""
|
||||
self.wasm_bin = wasmer.wat2wasm(self.wasm_asm)
|
||||
raise NotImplementedError
|
||||
|
||||
def interpreter_setup(self) -> None:
|
||||
"""
|
||||
@ -103,77 +99,6 @@ 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, imports: Optional[Dict[str, Callable[[Any], Any]]] = None) -> None:
|
||||
if imports is not None:
|
||||
raise NotImplementedError
|
||||
|
||||
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
|
||||
|
||||
See https://pypi.org/project/pywasm3/
|
||||
"""
|
||||
env: wasm3.Environment
|
||||
rtime: wasm3.Runtime
|
||||
mod: wasm3.Module
|
||||
|
||||
def interpreter_setup(self) -> None:
|
||||
self.env = wasm3.Environment()
|
||||
self.rtime = self.env.new_runtime(1024 * 1024)
|
||||
|
||||
def interpreter_load(self, imports: Optional[Dict[str, Callable[[Any], Any]]] = None) -> None:
|
||||
if imports is not None:
|
||||
raise NotImplementedError
|
||||
|
||||
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
|
||||
|
||||
def interpreter_read_memory(self, offset: int, length: int) -> bytes:
|
||||
memory = self.rtime.get_memory(0)
|
||||
return memory[offset:offset + length].tobytes()
|
||||
|
||||
def interpreter_dump_memory(self, textio: TextIO) -> None:
|
||||
_dump_memory(textio, self.rtime.get_memory(0))
|
||||
|
||||
def call(self, function: str, *args: Any) -> Any:
|
||||
return self.rtime.find_function(function)(*args)
|
||||
|
||||
class RunnerWasmtime(RunnerBase):
|
||||
"""
|
||||
Implements a runner for wasmtime
|
||||
@ -184,15 +109,44 @@ class RunnerWasmtime(RunnerBase):
|
||||
module: wasmtime.Module
|
||||
instance: wasmtime.Instance
|
||||
|
||||
@classmethod
|
||||
def func2type(cls, func: Callable[[Any], Any]) -> wasmtime.FuncType:
|
||||
params: list[wasmtime.ValType] = []
|
||||
|
||||
code = func.__code__
|
||||
for idx in range(code.co_argcount):
|
||||
varname = code.co_varnames[idx]
|
||||
vartype = func.__annotations__[varname]
|
||||
|
||||
if vartype is int:
|
||||
params.append(wasmtime.ValType.i32())
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
results: list[wasmtime.ValType] = []
|
||||
if func.__annotations__['return'] is None:
|
||||
pass # No return value
|
||||
elif func.__annotations__['return'] is int:
|
||||
results.append(wasmtime.ValType.i32())
|
||||
else:
|
||||
raise NotImplementedError('Return type', func.__annotations__['return'])
|
||||
|
||||
return wasmtime.FuncType(params, results)
|
||||
|
||||
def interpreter_setup(self) -> None:
|
||||
self.store = wasmtime.Store()
|
||||
|
||||
def interpreter_load(self, imports: Optional[Dict[str, Callable[[Any], Any]]] = None) -> None:
|
||||
if imports is not None:
|
||||
raise NotImplementedError
|
||||
functions: list[wasmtime.Func] = []
|
||||
|
||||
self.module = wasmtime.Module(self.store.engine, self.wasm_bin)
|
||||
self.instance = wasmtime.Instance(self.store, self.module, [])
|
||||
if imports is not None:
|
||||
functions = [
|
||||
wasmtime.Func(self.store, self.__class__.func2type(f), f)
|
||||
for f in imports.values()
|
||||
]
|
||||
|
||||
self.module = wasmtime.Module(self.store.engine, self.wasm_asm)
|
||||
self.instance = wasmtime.Instance(self.store, self.module, functions)
|
||||
|
||||
def interpreter_write_memory(self, offset: int, data: Iterable[int]) -> None:
|
||||
exports = self.instance.exports(self.store)
|
||||
@ -217,8 +171,7 @@ class RunnerWasmtime(RunnerBase):
|
||||
data_len = memory.data_len(self.store)
|
||||
|
||||
raw = ctypes.string_at(data_ptr, data_len)
|
||||
|
||||
return raw[offset:length]
|
||||
return raw[offset:offset + length]
|
||||
|
||||
def interpreter_dump_memory(self, textio: TextIO) -> None:
|
||||
exports = self.instance.exports(self.store)
|
||||
@ -237,63 +190,6 @@ class RunnerWasmtime(RunnerBase):
|
||||
|
||||
return func(self.store, *args)
|
||||
|
||||
class RunnerWasmer(RunnerBase):
|
||||
"""
|
||||
Implements a runner for wasmer
|
||||
|
||||
See https://pypi.org/project/wasmer/
|
||||
"""
|
||||
|
||||
# pylint: disable=E1101
|
||||
|
||||
store: wasmer.Store
|
||||
module: wasmer.Module
|
||||
instance: wasmer.Instance
|
||||
|
||||
def interpreter_setup(self) -> None:
|
||||
self.store = wasmer.Store()
|
||||
|
||||
def interpreter_load(self, imports: Optional[Dict[str, Callable[[Any], Any]]] = None) -> None:
|
||||
import_object = wasmer.ImportObject()
|
||||
if imports:
|
||||
import_object.register('imports', {
|
||||
k: wasmer.Function(self.store, v)
|
||||
for k, v in (imports or {}).items()
|
||||
})
|
||||
|
||||
self.module = wasmer.Module(self.store, self.wasm_bin)
|
||||
self.instance = wasmer.Instance(self.module, import_object)
|
||||
|
||||
def interpreter_write_memory(self, offset: int, data: Iterable[int]) -> None:
|
||||
exports = self.instance.exports
|
||||
memory = getattr(exports, 'memory')
|
||||
assert isinstance(memory, wasmer.Memory)
|
||||
view = memory.uint8_view(offset)
|
||||
|
||||
for idx, byt in enumerate(data):
|
||||
view[idx] = byt
|
||||
|
||||
def interpreter_read_memory(self, offset: int, length: int) -> bytes:
|
||||
exports = self.instance.exports
|
||||
memory = getattr(exports, 'memory')
|
||||
assert isinstance(memory, wasmer.Memory)
|
||||
view = memory.uint8_view(offset)
|
||||
return bytes(view[offset:length])
|
||||
|
||||
def interpreter_dump_memory(self, textio: TextIO) -> None:
|
||||
exports = self.instance.exports
|
||||
memory = getattr(exports, 'memory')
|
||||
assert isinstance(memory, wasmer.Memory)
|
||||
view = memory.uint8_view()
|
||||
|
||||
_dump_memory(textio, view) # type: ignore
|
||||
|
||||
def call(self, function: str, *args: Any) -> Any:
|
||||
exports = self.instance.exports
|
||||
func = getattr(exports, function)
|
||||
|
||||
return func(*args)
|
||||
|
||||
def _dump_memory(textio: TextIO, mem: bytes) -> None:
|
||||
line_width = 16
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ def test_index():
|
||||
with open('examples/buffer.py', 'r', encoding='ASCII') as fil:
|
||||
code_py = "\n" + fil.read()
|
||||
|
||||
result = Suite(code_py).run_code(b'Hello, world!', 5, func_name='index', runtime='wasmtime')
|
||||
result = Suite(code_py).run_code(b'Hello, world!', 5, func_name='index')
|
||||
assert 44 == result.returned_value
|
||||
|
||||
@pytest.mark.slow_integration_test
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import binascii
|
||||
|
||||
import pytest
|
||||
|
||||
from ..helpers import Suite
|
||||
@ -7,28 +5,15 @@ from ..helpers import Suite
|
||||
|
||||
@pytest.mark.slow_integration_test
|
||||
def test_crc32():
|
||||
# FIXME: Stub
|
||||
# crc = 0xFFFFFFFF
|
||||
# byt = 0x61
|
||||
# => (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ byt]
|
||||
# (crc >> 8) = 0x00FFFFFF
|
||||
# => 0x00FFFFFF ^ _CRC32_Table[(crc & 0xFF) ^ byt]
|
||||
# (crc & 0xFF) = 0xFF
|
||||
# => 0x00FFFFFF ^ _CRC32_Table[0xFF ^ byt]
|
||||
# 0xFF ^ 0x61 = 0x9E
|
||||
# => 0x00FFFFFF ^ _CRC32_Table[0x9E]
|
||||
# _CRC32_Table[0x9E] = 0x17b7be43
|
||||
# => 0x00FFFFFF ^ 0x17b7be43
|
||||
with open('examples/crc32.py', 'r', encoding='ASCII') as fil:
|
||||
code_py = "\n" + fil.read()
|
||||
|
||||
code_py = """
|
||||
def _crc32_f(crc: u32, byt: u8) -> u32:
|
||||
return 16777215 ^ 397917763
|
||||
# https://reveng.sourceforge.io/crc-catalogue/legend.htm#crc.legend.params
|
||||
in_put = b'123456789'
|
||||
|
||||
def testEntry(data: bytes) -> u32:
|
||||
return 4294967295 ^ _crc32_f(4294967295, data[0])
|
||||
"""
|
||||
exp_result = binascii.crc32(b'a')
|
||||
# https://reveng.sourceforge.io/crc-catalogue/17plus.htm#crc.cat.crc-32-iso-hdlc
|
||||
check = 0xcbf43926
|
||||
|
||||
result = Suite(code_py).run_code(b'a')
|
||||
result = Suite(code_py).run_code(in_put, func_name='crc32', do_format_check=False)
|
||||
|
||||
assert exp_result == result.returned_value
|
||||
assert check == result.returned_value
|
||||
|
||||
@ -102,6 +102,7 @@ def generate_code(markdown, template, settings):
|
||||
print()
|
||||
print('from ..helpers import Suite')
|
||||
print()
|
||||
print()
|
||||
|
||||
for test in get_tests(template):
|
||||
assert len(test) == 4, test
|
||||
@ -121,7 +122,7 @@ def generate_code(markdown, template, settings):
|
||||
print('@pytest.mark.integration_test')
|
||||
print(f'def test_{type_name}_{test_id}():')
|
||||
print(' """')
|
||||
print(' ' + user_story.replace('\n', '\n '))
|
||||
print(' ' + user_story.strip().replace('\n', '\n '))
|
||||
print(' """')
|
||||
print(' code_py = """')
|
||||
if 'CODE_HEADER' in settings:
|
||||
|
||||
@ -3,16 +3,15 @@ import sys
|
||||
import pytest
|
||||
|
||||
from ..helpers import Suite, write_header
|
||||
from ..runners import RunnerPywasm
|
||||
from ..runners import RunnerWasmtime
|
||||
|
||||
|
||||
def setup_interpreter(phash_code: str) -> RunnerPywasm:
|
||||
runner = RunnerPywasm(phash_code)
|
||||
def setup_interpreter(phash_code: str) -> RunnerWasmtime:
|
||||
runner = RunnerWasmtime(phash_code)
|
||||
|
||||
runner.parse()
|
||||
runner.compile_ast()
|
||||
runner.compile_wat()
|
||||
runner.compile_wasm()
|
||||
runner.interpreter_setup()
|
||||
runner.interpreter_load()
|
||||
|
||||
@ -38,16 +37,16 @@ def testEntry(b: bytes) -> u8:
|
||||
result = suite.run_code(b'')
|
||||
assert 128 == result.returned_value
|
||||
|
||||
result = suite.run_code(b'\x80', runtime='pywasm')
|
||||
result = suite.run_code(b'\x80')
|
||||
assert 128 == result.returned_value
|
||||
|
||||
result = suite.run_code(b'\x80\x40', runtime='pywasm')
|
||||
result = suite.run_code(b'\x80\x40')
|
||||
assert 192 == result.returned_value
|
||||
|
||||
result = suite.run_code(b'\x80\x40\x20\x10', runtime='pywasm')
|
||||
result = suite.run_code(b'\x80\x40\x20\x10')
|
||||
assert 240 == result.returned_value
|
||||
|
||||
result = suite.run_code(b'\x80\x40\x20\x10\x08\x04\x02\x01', runtime='pywasm')
|
||||
result = suite.run_code(b'\x80\x40\x20\x10\x08\x04\x02\x01')
|
||||
assert 255 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
|
||||
@ -20,7 +20,7 @@ def testEntry() -> {type_}:
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', TYPE_LIST)
|
||||
def test_division_zero_let_it_crash_float(type_):
|
||||
def test_division_float_follow_ieee_so_inf_pos(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
@ -31,3 +31,17 @@ def testEntry() -> {type_}:
|
||||
# https://www.w3.org/TR/wasm-core-1/#-hrefop-fdivmathrmfdiv_n-z_1-z_2
|
||||
result = Suite(code_py).run_code()
|
||||
assert float('+inf') == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', TYPE_LIST)
|
||||
def test_division_float_follow_ieee_so_inf_neg(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return -10.0 / 0.0
|
||||
"""
|
||||
|
||||
# WebAssembly dictates that float division follows the IEEE rules
|
||||
# https://www.w3.org/TR/wasm-core-1/#-hrefop-fdivmathrmfdiv_n-z_1-z_2
|
||||
result = Suite(code_py).run_code()
|
||||
assert float('-inf') == result.returned_value
|
||||
|
||||
@ -21,7 +21,6 @@ def testEntry() -> i32:
|
||||
return 4238 * mul
|
||||
|
||||
result = Suite(code_py).run_code(
|
||||
runtime='wasmer',
|
||||
imports={
|
||||
'helper': helper,
|
||||
}
|
||||
@ -47,7 +46,6 @@ def testEntry() -> None:
|
||||
prop = mul
|
||||
|
||||
result = Suite(code_py).run_code(
|
||||
runtime='wasmer',
|
||||
imports={
|
||||
'helper': helper,
|
||||
}
|
||||
@ -73,7 +71,6 @@ def testEntry(x: u32) -> u8:
|
||||
|
||||
with pytest.raises(Type3Exception, match=r'u32 must be u8 instead'):
|
||||
Suite(code_py).run_code(
|
||||
runtime='wasmer',
|
||||
imports={
|
||||
'helper': helper,
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ def testEntry() -> {type_}:
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 13 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
assert TYPE_MAP[type_] is type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', FLOAT_TYPES)
|
||||
@ -56,7 +56,7 @@ def testEntry() -> {type_}:
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 32.125 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
assert TYPE_MAP[type_] is type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', INT_TYPES)
|
||||
@ -70,7 +70,7 @@ def testEntry() -> {type_}:
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 7 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
assert TYPE_MAP[type_] is type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', FLOAT_TYPES)
|
||||
@ -84,7 +84,7 @@ def testEntry() -> {type_}:
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 32.125 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
assert TYPE_MAP[type_] is type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_subtraction_negative_result():
|
||||
@ -153,7 +153,7 @@ def helper(left: {type_}, right: {type_}) -> {type_}:
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 22 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
assert TYPE_MAP[type_] is type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', FLOAT_TYPES)
|
||||
@ -170,4 +170,4 @@ def helper(left: {type_}, right: {type_}) -> {type_}:
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 32.125 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
assert TYPE_MAP[type_] is type(result.returned_value)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
import wasmtime
|
||||
|
||||
from phasm.type3.entry import Type3Exception
|
||||
|
||||
@ -64,7 +65,7 @@ def testEntry(x: u32) -> u8:
|
||||
return CONSTANT[x]
|
||||
"""
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
with pytest.raises(wasmtime.Trap):
|
||||
Suite(code_py).run_code(3)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
|
||||
@ -74,11 +74,9 @@ CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
|
||||
@pytest.mark.integration_test
|
||||
def test_tuple_must_use_literal_for_indexing():
|
||||
code_py = """
|
||||
CONSTANT: u32 = 0
|
||||
|
||||
@exported
|
||||
def testEntry(x: (u8, u32, u64)) -> u64:
|
||||
return x[CONSTANT]
|
||||
def testEntry(x: (u8, u32, u64), y: u8) -> u64:
|
||||
return x[y]
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Must index with literal'):
|
||||
|
||||
@ -3,7 +3,7 @@ import sys
|
||||
import pytest
|
||||
|
||||
from ..helpers import write_header
|
||||
from ..runners import RunnerPywasm3 as Runner
|
||||
from ..runners import RunnerWasmtime as Runner
|
||||
|
||||
|
||||
def setup_interpreter(phash_code: str) -> Runner:
|
||||
@ -12,7 +12,6 @@ def setup_interpreter(phash_code: str) -> Runner:
|
||||
runner.parse()
|
||||
runner.compile_ast()
|
||||
runner.compile_wat()
|
||||
runner.compile_wasm()
|
||||
runner.interpreter_setup()
|
||||
runner.interpreter_load()
|
||||
|
||||
|
||||
11
wat2wasm.py
Normal file
11
wat2wasm.py
Normal file
@ -0,0 +1,11 @@
|
||||
import sys
|
||||
|
||||
from wasmtime import wat2wasm
|
||||
|
||||
def main(_prog: str, inp: str, _dash_o: str, out: str) -> None:
|
||||
with open(inp, 'rb') as inp_obj:
|
||||
with open(out, 'wb') as out_obj:
|
||||
out_obj.write(wat2wasm(inp_obj.read()))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(*sys.argv)
|
||||
Loading…
x
Reference in New Issue
Block a user