Coverage for tests/codegen/__init__.py: 85%
20 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-20 16:25 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-20 16:25 +0000
1# The `codegen` directory here is intended to test the bindings generator for
2# components in Python. Each file represents a distinct test where an input
3# wasm file is bound and then the generated bindings are imported dynamically.
4#
5# This structure is done so a general `pytest` will execute everything,
6# generating bindings during test collection and otherwise setting up everything
7# to be naturally checked with mypy and other tests configured.
9from wasmtime import wat2wasm
10from wasmtime.bindgen import generate
11from pathlib import Path
12import os
15# Helper function to generate bindings for the `wat` specified into the
16# `generated` sub-folder. After calling this method the bindings can be
17# imported with:
18#
19# from .generated.name import Name
20#
21# and then used to type-check everything.
22def bindgen(name: str, wat: str) -> None:
23 files = generate(name, wat2wasm(wat))
24 root = Path(__file__).parent.joinpath('generated')
25 dst = root.joinpath(name)
26 for name, contents in files.items():
27 # If the file already has the desired contents then skip writing. This
28 # is an attempt to fix CI issues on windows.
29 file = dst.joinpath(name)
30 if file.exists():
31 with open(file, 'rb') as f:
32 if f.read() == contents:
33 continue
35 # Write the contents to a temporary file and then attempt to atomically
36 # replace the previous file, if any, with the new contents. This
37 # is done to hopefully fix an apparent issue in `pytest` where it seems
38 # that there are multiple threads of the python interpreter, perhaps for
39 # pytest itself, mypy, and flake8, and overwriting files in-place causes
40 # issues are partial files may be seen.
41 tmp_file = file.with_suffix('.tmp')
42 if not file.parent.exists():
43 file.parent.mkdir(parents=True)
44 tmp_file.write_bytes(contents)
45 os.replace(tmp_file, file)
48REALLOC = """
49 (global $last (mut i32) (i32.const 1000))
50 (func $realloc (export "realloc")
51 (param $old_ptr i32)
52 (param $old_size i32)
53 (param $align i32)
54 (param $new_size i32)
55 (result i32)
57 (local $ret i32)
59 ;; Test if the old pointer is non-null
60 local.get $old_ptr
61 if
62 ;; If the old size is bigger than the new size then
63 ;; this is a shrink and transparently allow it
64 local.get $old_size
65 local.get $new_size
66 i32.gt_u
67 if
68 local.get $old_ptr
69 return
70 end
72 ;; otherwise fall through to allocate a new chunk which will later
73 ;; copy data over
74 end
76 ;; align up `$last`
77 (global.set $last
78 (i32.and
79 (i32.add
80 (global.get $last)
81 (i32.add
82 (local.get $align)
83 (i32.const -1)))
84 (i32.xor
85 (i32.add
86 (local.get $align)
87 (i32.const -1))
88 (i32.const -1))))
90 ;; save the current value of `$last` as the return value
91 global.get $last
92 local.set $ret
94 ;; bump our pointer
95 (global.set $last
96 (i32.add
97 (global.get $last)
98 (local.get $new_size)))
100 ;; while `memory.size` is less than `$last`, grow memory
101 ;; by one page
102 (loop $loop
103 (if
104 (i32.lt_u
105 (i32.mul (memory.size) (i32.const 65536))
106 (global.get $last))
107 (then
108 i32.const 1
109 memory.grow
110 ;; test to make sure growth succeeded
111 i32.const -1
112 i32.eq
113 if unreachable end
115 br $loop)))
118 ;; ensure anything necessary is set to valid data by spraying a bit
119 ;; pattern that is invalid
120 local.get $ret
121 i32.const 0xde
122 local.get $new_size
123 memory.fill
125 ;; If the old pointer is present then that means this was a reallocation
126 ;; of an existing chunk which means the existing data must be copied.
127 local.get $old_ptr
128 if
129 local.get $ret ;; destination
130 local.get $old_ptr ;; source
131 local.get $old_size ;; size
132 memory.copy
133 end
135 local.get $ret
136 )
137"""