Coverage for wasmtime/_ffi.py: 91%
136 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-12-01 19:40 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2025-12-01 19:40 +0000
1from ctypes import POINTER, Structure, Union, addressof, byref, cdll, string_at
2from ctypes import c_double, c_float, c_int32, c_int64, c_uint8
3from pathlib import Path
4import ctypes
5import sys
6import platform
7import typing
9if sys.maxsize <= 2**32:
10 raise RuntimeError("wasmtime only works on 64-bit platforms right now")
12sys_platform = sys.platform
14# For Python versions <=3.12. 3.13+ supports PEP 738 and uses sys.platform
15if hasattr(sys, 'getandroidapilevel'):
16 sys_platform = 'android'
18if sys_platform == 'linux' or sys_platform == 'android':
19 libname = '_libwasmtime.so'
20elif sys_platform == 'win32':
21 libname = '_wasmtime.dll'
22elif sys_platform == 'darwin':
23 libname = '_libwasmtime.dylib'
24else:
25 raise RuntimeError("unsupported platform `{}` for wasmtime".format(sys_platform))
28machine = platform.machine()
29if machine == 'AMD64':
30 machine = 'x86_64'
31if machine == 'arm64' or machine == 'ARM64':
32 machine = 'aarch64'
33if machine != 'x86_64' and machine != 'aarch64':
34 raise RuntimeError("unsupported architecture for wasmtime: {}".format(machine))
36filename = Path(__file__).parent / (sys_platform + '-' + machine) / libname
38dll = cdll.LoadLibrary(str(filename))
40WASM_I32 = c_uint8(0)
41WASM_I64 = c_uint8(1)
42WASM_F32 = c_uint8(2)
43WASM_F64 = c_uint8(3)
44WASM_ANYREF = c_uint8(128)
45WASM_FUNCREF = c_uint8(129)
46# WASM_V128 = c_uint8(4)
48WASMTIME_I32 = c_uint8(0)
49WASMTIME_I64 = c_uint8(1)
50WASMTIME_F32 = c_uint8(2)
51WASMTIME_F64 = c_uint8(3)
52WASMTIME_V128 = c_uint8(4)
53WASMTIME_FUNCREF = c_uint8(5)
54WASMTIME_EXTERNREF = c_uint8(6)
56WASM_CONST = c_uint8(0)
57WASM_VAR = c_uint8(1)
59WASMTIME_EXTERN_FUNC = c_uint8(0)
60WASMTIME_EXTERN_GLOBAL = c_uint8(1)
61WASMTIME_EXTERN_TABLE = c_uint8(2)
62WASMTIME_EXTERN_MEMORY = c_uint8(3)
63WASMTIME_EXTERN_SHAREDMEMORY = c_uint8(4)
65WASMTIME_FUNCREF_NULL = (1 << 64) - 1
67WASMTIME_COMPONENT_ITEM_COMPONENT = c_uint8(0)
68WASMTIME_COMPONENT_ITEM_COMPONENT_INSTANCE = c_uint8(1)
69WASMTIME_COMPONENT_ITEM_MODULE = c_uint8(2)
70WASMTIME_COMPONENT_ITEM_COMPONENT_FUNC = c_uint8(3)
71WASMTIME_COMPONENT_ITEM_RESOURCE = c_uint8(4)
72WASMTIME_COMPONENT_ITEM_CORE_FUNC = c_uint8(5)
73WASMTIME_COMPONENT_ITEM_TYPE = c_uint8(6)
75WASMTIME_COMPONENT_VALTYPE_BOOL = c_uint8(0)
76WASMTIME_COMPONENT_VALTYPE_S8 = c_uint8(1)
77WASMTIME_COMPONENT_VALTYPE_S16 = c_uint8(2)
78WASMTIME_COMPONENT_VALTYPE_S32 = c_uint8(3)
79WASMTIME_COMPONENT_VALTYPE_S64 = c_uint8(4)
80WASMTIME_COMPONENT_VALTYPE_U8 = c_uint8(5)
81WASMTIME_COMPONENT_VALTYPE_U16 = c_uint8(6)
82WASMTIME_COMPONENT_VALTYPE_U32 = c_uint8(7)
83WASMTIME_COMPONENT_VALTYPE_U64 = c_uint8(8)
84WASMTIME_COMPONENT_VALTYPE_F32 = c_uint8(9)
85WASMTIME_COMPONENT_VALTYPE_F64 = c_uint8(10)
86WASMTIME_COMPONENT_VALTYPE_CHAR = c_uint8(11)
87WASMTIME_COMPONENT_VALTYPE_STRING = c_uint8(12)
88WASMTIME_COMPONENT_VALTYPE_LIST = c_uint8(13)
89WASMTIME_COMPONENT_VALTYPE_RECORD = c_uint8(14)
90WASMTIME_COMPONENT_VALTYPE_TUPLE = c_uint8(15)
91WASMTIME_COMPONENT_VALTYPE_VARIANT = c_uint8(16)
92WASMTIME_COMPONENT_VALTYPE_ENUM = c_uint8(17)
93WASMTIME_COMPONENT_VALTYPE_OPTION = c_uint8(18)
94WASMTIME_COMPONENT_VALTYPE_RESULT = c_uint8(19)
95WASMTIME_COMPONENT_VALTYPE_FLAGS = c_uint8(20)
96WASMTIME_COMPONENT_VALTYPE_OWN = c_uint8(21)
97WASMTIME_COMPONENT_VALTYPE_BORROW = c_uint8(22)
98WASMTIME_COMPONENT_VALTYPE_FUTURE = c_uint8(23)
99WASMTIME_COMPONENT_VALTYPE_STREAM = c_uint8(24)
100WASMTIME_COMPONENT_VALTYPE_ERROR_CONTEXT = c_uint8(25)
102WASMTIME_COMPONENT_BOOL = c_uint8(0)
103WASMTIME_COMPONENT_S8 = c_uint8(1)
104WASMTIME_COMPONENT_U8 = c_uint8(2)
105WASMTIME_COMPONENT_S16 = c_uint8(3)
106WASMTIME_COMPONENT_U16 = c_uint8(4)
107WASMTIME_COMPONENT_S32 = c_uint8(5)
108WASMTIME_COMPONENT_U32 = c_uint8(6)
109WASMTIME_COMPONENT_S64 = c_uint8(7)
110WASMTIME_COMPONENT_U64 = c_uint8(8)
111WASMTIME_COMPONENT_F32 = c_uint8(9)
112WASMTIME_COMPONENT_F64 = c_uint8(10)
113WASMTIME_COMPONENT_CHAR = c_uint8(11)
114WASMTIME_COMPONENT_STRING = c_uint8(12)
115WASMTIME_COMPONENT_LIST = c_uint8(13)
116WASMTIME_COMPONENT_RECORD = c_uint8(14)
117WASMTIME_COMPONENT_TUPLE = c_uint8(15)
118WASMTIME_COMPONENT_VARIANT = c_uint8(16)
119WASMTIME_COMPONENT_ENUM = c_uint8(17)
120WASMTIME_COMPONENT_OPTION = c_uint8(18)
121WASMTIME_COMPONENT_RESULT = c_uint8(19)
122WASMTIME_COMPONENT_FLAGS = c_uint8(20)
123WASMTIME_COMPONENT_RESOURCE = c_uint8(21)
125class wasm_ref_t(Structure):
126 pass
129class wasm_val_union(Union):
130 _fields_ = [
131 ("i32", c_int32),
132 ("i64", c_int64),
133 ("f32", c_float),
134 ("f64", c_double),
135 ("ref", POINTER(wasm_ref_t)),
136 ]
138 i32: int
139 i64: int
140 f32: float
141 f64: float
142 ref: "typing.Union[ctypes._Pointer[wasm_ref_t], None]"
145class wasm_val_t(Structure):
146 _fields_ = [("kind", c_uint8), ("of", wasm_val_union)]
148 kind: int
149 of: wasm_val_union
152from ._bindings import * # noqa
155def to_bytes(vec: wasm_byte_vec_t) -> bytearray:
156 ty = c_uint8 * vec.size
157 return bytearray(ty.from_address(addressof(vec.data.contents)))
159def to_str(vec: wasm_byte_vec_t) -> str:
160 return to_bytes(vec).decode("utf-8")
163def to_str_raw(ptr: "ctypes._Pointer", size: int) -> str:
164 return string_at(ptr, size).decode("utf-8")
167def str_to_capi(s: str) -> wasm_byte_vec_t:
168 if not isinstance(s, str):
169 raise TypeError("expected a string")
170 return bytes_to_capi(s.encode('utf8'))
172def bytes_to_capi(s: typing.Union[bytes, bytearray]) -> wasm_byte_vec_t:
173 if not isinstance(s, (bytes, bytearray)):
174 raise TypeError("expected bytes or bytearray")
175 vec = wasm_byte_vec_t()
176 wasm_byte_vec_new_uninitialized(byref(vec), len(s))
177 buf = (c_uint8 * len(s)).from_buffer_copy(s)
178 ctypes.memmove(vec.data, buf, len(s))
179 return vec
181def take_pointer(structure: ctypes._Pointer, field_name: str) -> ctypes._Pointer:
182 """
183 Moral equivalent of `mem::replace(&mut structure.field_name, NULL)`
185 Ctypes explicitly documents "Surprises" which includes, for example:
187 import ctypes
189 class A(ctypes.Structure):
190 _fields_ = [("x", ctypes.POINTER(ctypes.c_int))]
192 x_p = ctypes.pointer(ctypes.c_int(3))
193 a = A(x_p)
194 x = a.x
196 print(x.contents)
197 a.x = None
198 print(x.contents)
200 This program will segfault on the second access. It turns out that `x = a.x`
201 is still actually a pointer into the original structure, and `a.x`
202 overwrites that field so accessing `x` later accesses null memory. This
203 method is an attempt to work around this surprising behavior and actually
204 read the field from a structure and replace it with null.
206 I'll be honest I just sat through a 3 hour flight, a 5 hour layover, a 9
207 hour flight, 1 hour train ride, and I'm sitting in a hotel lobby for
208 another 5 hours. That's my state of mind writing this up, so please
209 draw conclusions about this method as appropriate.
210 """
211 field = getattr(structure, field_name)
212 assert(isinstance(field, ctypes._Pointer))
213 ret = ctypes.cast(ctypes.addressof(field.contents), ctypes.POINTER(field._type_))
214 setattr(structure, field_name, None)
215 return ret