Coverage for wasmtime/_module.py: 91%
90 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 . import _ffi as ffi
2from ._wat2wasm import _to_wasm
3import ctypes
4from wasmtime import Engine, wat2wasm, ImportType, ExportType, WasmtimeError, Managed
5import typing
6from os import PathLike
9class Module(Managed["ctypes._Pointer[ffi.wasmtime_module_t]"]):
11 @classmethod
12 def from_file(cls, engine: Engine, path: typing.Union[str, bytes, PathLike]) -> "Module":
13 """
14 Compiles and creates a new `Module` by reading the file at `path` and
15 then delegating to the `Module` constructor.
16 """
18 with open(path, "rb") as f:
19 contents = f.read()
20 return cls(engine, contents)
22 def __init__(self, engine: Engine, wasm: typing.Union[str, bytes]):
23 if not isinstance(engine, Engine):
24 raise TypeError("expected an Engine")
26 wasm = _to_wasm(wasm)
28 # TODO: can the copy be avoided here? I can't for the life of me
29 # figure this out.
30 binary = (ctypes.c_uint8 * len(wasm)).from_buffer_copy(wasm)
31 ptr = ctypes.POINTER(ffi.wasmtime_module_t)()
32 error = ffi.wasmtime_module_new(engine.ptr(), binary, len(wasm), ctypes.byref(ptr))
33 if error:
34 raise WasmtimeError._from_ptr(error)
35 self._set_ptr(ptr)
37 def _delete(self, ptr: "ctypes._Pointer[ffi.wasmtime_module_t]") -> None:
38 ffi.wasmtime_module_delete(ptr)
40 @classmethod
41 def _from_ptr(cls, ptr: "ctypes._Pointer[ffi.wasmtime_module_t]") -> "Module":
42 if not isinstance(ptr, ctypes.POINTER(ffi.wasmtime_module_t)):
43 raise TypeError("wrong pointer type")
44 ty: "Module" = cls.__new__(cls)
45 ty._set_ptr(ptr)
46 return ty
48 @classmethod
49 def deserialize(cls, engine: Engine, encoded: typing.Union[bytes, bytearray]) -> 'Module':
50 """
51 Deserializes bytes previously created by `Module.serialize`.
53 This constructor for `Module` will deserialize bytes previously created
54 by a serialized module. This will only succeed if the bytes were
55 previously created by the same version of `wasmtime` as well as the
56 same configuration within `Engine`.
57 """
59 if not isinstance(encoded, (bytes, bytearray)):
60 raise TypeError("expected bytes")
62 ptr = ctypes.POINTER(ffi.wasmtime_module_t)()
64 # TODO: can the copy be avoided here? I can't for the life of me
65 # figure this out.
66 error = ffi.wasmtime_module_deserialize(
67 engine.ptr(),
68 (ctypes.c_uint8 * len(encoded)).from_buffer_copy(encoded),
69 len(encoded),
70 ctypes.byref(ptr))
71 if error:
72 raise WasmtimeError._from_ptr(error)
73 return cls._from_ptr(ptr)
75 @classmethod
76 def deserialize_file(cls, engine: Engine, path: str) -> 'Module':
77 """
78 Deserializes bytes previously created by `Module.serialize` that are
79 stored in a file on the filesystem.
81 Otherwise this function is the same as `Module.deserialize`.
82 """
84 ptr = ctypes.POINTER(ffi.wasmtime_module_t)()
85 path_bytes = path.encode('utf-8')
86 error = ffi.wasmtime_module_deserialize_file(
87 engine.ptr(),
88 path_bytes,
89 ctypes.byref(ptr))
90 if error:
91 raise WasmtimeError._from_ptr(error)
92 return cls._from_ptr(ptr)
94 @classmethod
95 def validate(cls, engine: Engine, wasm: typing.Union[bytes, bytearray]) -> None:
96 """
97 Validates whether the list of bytes `wasm` provided is a valid
98 WebAssembly binary given the configuration in `store`
100 Raises a `WasmtimeError` if the wasm isn't valid.
101 """
103 if not isinstance(wasm, (bytes, bytearray)):
104 raise TypeError("expected wasm bytes")
106 # TODO: can the copy be avoided here? I can't for the life of me
107 # figure this out.
108 buf = (ctypes.c_uint8 * len(wasm)).from_buffer_copy(wasm)
109 error = ffi.wasmtime_module_validate(engine.ptr(), buf, len(wasm))
111 if error:
112 raise WasmtimeError._from_ptr(error)
114 @property
115 def imports(self) -> typing.List[ImportType]:
116 """
117 Returns the types of imports that this module has
118 """
120 imports = ImportTypeList()
121 ffi.wasmtime_module_imports(self.ptr(), ctypes.byref(imports.vec))
122 ret = []
123 for i in range(0, imports.vec.size):
124 ret.append(ImportType._from_ptr(imports.vec.data[i], imports))
125 return ret
127 @property
128 def exports(self) -> typing.List[ExportType]:
129 """
130 Returns the types of the exports that this module has
131 """
133 exports = ExportTypeList()
134 ffi.wasmtime_module_exports(self.ptr(), ctypes.byref(exports.vec))
135 ret = []
136 for i in range(0, exports.vec.size):
137 ret.append(ExportType._from_ptr(exports.vec.data[i], exports))
138 return ret
140 def serialize(self) -> bytearray:
141 """
142 Serializes this module to a binary representation.
144 This method will serialize this module to an in-memory byte array which
145 can be cached and later passed to `Module.deserialize` to recreate this
146 module.
147 """
148 raw = ffi.wasm_byte_vec_t()
149 err = ffi.wasmtime_module_serialize(self.ptr(), ctypes.byref(raw))
150 if err:
151 raise WasmtimeError._from_ptr(err)
152 ret = ffi.to_bytes(raw)
153 ffi.wasm_byte_vec_delete(ctypes.byref(raw))
154 return ret
157class ImportTypeList:
158 def __init__(self) -> None:
159 self.vec = ffi.wasm_importtype_vec_t(0, None)
161 def __del__(self) -> None:
162 ffi.wasm_importtype_vec_delete(ctypes.byref(self.vec))
165class ExportTypeList:
166 def __init__(self) -> None:
167 self.vec = ffi.wasm_exporttype_vec_t(0, None)
169 def __del__(self) -> None:
170 ffi.wasm_exporttype_vec_delete(ctypes.byref(self.vec))