Coverage for wasmtime/_linker.py: 91%
107 statements
« prev ^ index » next coverage.py v7.11.3, created at 2026-05-07 14:30 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2026-05-07 14:30 +0000
1import ctypes
2from typing import Any
3from wasmtime import Instance, Engine, FuncType
4from wasmtime import Module, WasmtimeError, Func, Managed
5from . import _ffi as ffi
6from ._extern import get_extern_ptr, wrap_extern
7from ._config import setter_property
8from ._exportable import AsExtern
9from ._store import Storelike
10from ._func import enter_wasm, trampoline, FUNCTIONS, finalize
11from typing import Callable
12from ._instance_pre import InstancePre
15class Linker(Managed["ctypes._Pointer[ffi.wasmtime_linker_t]"]):
16 engine: Engine
18 def __init__(self, engine: Engine):
19 """
20 Creates a new linker ready to instantiate modules within the store
21 provided.
22 """
23 self._set_ptr(ffi.wasmtime_linker_new(engine.ptr()))
24 self.engine = engine
26 def _delete(self, ptr: "ctypes._Pointer[ffi.wasmtime_linker_t]") -> None:
27 ffi.wasmtime_linker_delete(ptr)
29 @setter_property
30 def allow_shadowing(self, allow: bool) -> None:
31 """
32 Configures whether definitions are allowed to shadow one another within
33 this linker
34 """
35 ffi.wasmtime_linker_allow_shadowing(self.ptr(), allow)
37 def define(self, store: Storelike, module: str, name: str, item: AsExtern) -> None:
38 """
39 Defines a new item, by name, in this linker.
41 This method will add a new definition to this linker. The `module` nad
42 `name` provided are what to name the `item` within the linker.
44 This function will raise an error if `item` comes from the wrong store
45 or if shadowing is disallowed and the module/name pair has already been
46 defined.
47 """
48 raw_item = get_extern_ptr(item)
49 module_bytes = module.encode('utf-8')
50 module_buf = ctypes.create_string_buffer(module_bytes)
51 name_bytes = name.encode('utf-8')
52 name_buf = ctypes.create_string_buffer(name_bytes)
53 error = ffi.wasmtime_linker_define(
54 self.ptr(),
55 store._context(),
56 module_buf,
57 len(module_bytes),
58 name_buf,
59 len(name_bytes),
60 ctypes.byref(raw_item))
61 if error:
62 raise WasmtimeError._from_ptr(error)
64 def define_func(self, module: str, name: str, ty: FuncType, func: Callable[..., Any], access_caller: bool = False) -> None:
65 """
66 Defines a new function, by name, in this linker.
68 This method is similar to `define` except that you can directly define a
69 function without creating a `Func` itself. This enables
70 `Store`-independent functions to be inserted into this linker, meaning
71 the linker can be used to instantiate modules in multiple stores.
72 """
73 module_bytes = module.encode('utf-8')
74 module_buf = ctypes.create_string_buffer(module_bytes)
75 name_bytes = name.encode('utf-8')
76 name_buf = ctypes.create_string_buffer(name_bytes)
77 if not isinstance(ty, FuncType):
78 raise TypeError("expected a FuncType")
79 idx = FUNCTIONS.allocate((func, ty.results, access_caller))
80 error = ffi.wasmtime_linker_define_func(
81 self.ptr(),
82 module_buf,
83 len(module_bytes),
84 name_buf,
85 len(name_bytes),
86 ty.ptr(),
87 trampoline,
88 idx,
89 finalize)
90 if error:
91 raise WasmtimeError._from_ptr(error)
93 def define_instance(self, store: Storelike, name: str, instance: Instance) -> None:
94 """
95 Convenience wrapper to define an entire instance in this linker.
97 This function will `define` eaech of the exports on the instance into
98 this linker, using the name provided as the module name and the export's
99 own name as the field name.
101 This function will raise an error if `instance` comes from the wrong
102 store or if shadowing is disallowed and a name was previously defined.
103 """
104 if not isinstance(instance, Instance):
105 raise TypeError("expected an `Instance`")
106 name_bytes = name.encode('utf8')
107 name_buf = ctypes.create_string_buffer(name_bytes)
108 error = ffi.wasmtime_linker_define_instance(self.ptr(),
109 store._context(),
110 name_buf,
111 len(name_bytes),
112 ctypes.byref(instance._instance))
113 if error:
114 raise WasmtimeError._from_ptr(error)
116 def define_wasi(self) -> None:
117 """
118 Defines a WASI instance in this linker.
120 The instance provided has been previously constructed and this method
121 will define all the appropriate imports and their names into this linker
122 to assist with instantiating modules that use WASI.
124 This function will raise an error if shadowing is disallowed and a name
125 was previously defined.
126 """
127 error = ffi.wasmtime_linker_define_wasi(self.ptr())
128 if error:
129 raise WasmtimeError._from_ptr(error)
131 def define_module(self, store: Storelike, name: str, module: Module) -> None:
132 """
133 Defines automatic instantiations of the provided module in this linker.
135 The `module` provided is defined under `name` with automatic
136 instantiations which respect WASI Commands and Reactors.
138 For more information see the Rust documentation at
139 https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.module.
141 This method will throw an error if shadowing is disallowed and an item
142 has previously been defined.
143 """
144 if not isinstance(module, Module):
145 raise TypeError("expected a `Module`")
146 name_bytes = name.encode('utf-8')
147 name_buf = ctypes.create_string_buffer(name_bytes)
148 error = ffi.wasmtime_linker_module(self.ptr(), store._context(), name_buf, len(name_bytes), module.ptr())
149 if error:
150 raise WasmtimeError._from_ptr(error)
152 def instantiate(self, store: Storelike, module: Module) -> Instance:
153 """
154 Instantiates a module using this linker's defined set of names.
156 This method will attempt to satisfy all the imports of the `module`
157 provided with the names defined within this linker. If all names are
158 defined then the module is instantiated.
160 Raises an error if an import of `module` hasn't been defined in this
161 linker or if a trap happens while instantiating the instance.
162 """
163 trap = ctypes.POINTER(ffi.wasm_trap_t)()
164 instance = ffi.wasmtime_instance_t()
165 with enter_wasm(store) as trap:
166 error = ffi.wasmtime_linker_instantiate(
167 self.ptr(), store._context(), module.ptr(), ctypes.byref(instance), trap)
168 if error:
169 raise WasmtimeError._from_ptr(error)
170 return Instance._from_raw(instance)
172 def get_default(self, store: Storelike, name: str) -> Func:
173 """
174 Gets the default export for the named module in this linker.
176 For more information on this see the Rust documentation at
177 https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.get_default.
179 Raises an error if the default export wasn't present.
180 """
181 name_bytes = name.encode('utf-8')
182 name_buf = ctypes.create_string_buffer(name_bytes)
183 func = ffi.wasmtime_func_t()
184 error = ffi.wasmtime_linker_get_default(self.ptr(), store._context(),
185 name_buf, len(name_bytes), ctypes.byref(func))
186 if error:
187 raise WasmtimeError._from_ptr(error)
188 return Func._from_raw(func)
190 def get(self, store: Storelike, module: str, name: str) -> AsExtern:
191 """
192 Gets a singular item defined in this linker.
194 Raises an error if this item hasn't been defined or if the item has been
195 defined twice with different types.
196 """
197 module_bytes = module.encode('utf-8')
198 module_buf = ctypes.create_string_buffer(module_bytes)
199 name_bytes = name.encode('utf-8')
200 name_buf = ctypes.create_string_buffer(name_bytes)
201 item = ffi.wasmtime_extern_t()
202 ok = ffi.wasmtime_linker_get(self.ptr(), store._context(),
203 module_buf, len(module_bytes),
204 name_buf, len(name_bytes),
205 ctypes.byref(item))
206 if ok:
207 return wrap_extern(item)
208 raise WasmtimeError("item not defined in linker")
210 def define_unknown_imports_as_traps(self, module: Module) -> None:
211 """
212 Defines all currently-unknown imports of `module` as functions that
213 unconditionally trap when called.
215 This is useful when you want to instantiate a module whose imports
216 aren't fully known yet and are willing to trap on any call to an
217 unresolved import.
218 """
219 if not isinstance(module, Module):
220 raise TypeError("expected a `Module`")
221 error = ffi.wasmtime_linker_define_unknown_imports_as_traps(
222 self.ptr(), module.ptr())
223 if error:
224 raise WasmtimeError._from_ptr(error)
226 def define_unknown_imports_as_default_values(self, store: Storelike, module: Module) -> None:
227 """
228 Defines all currently-unknown imports of `module` as default values
229 (zero for numerics, null for references, etc.).
231 This is similar to `define_unknown_imports_as_traps` but instead
232 of trapping the imported functions return appropriate zero/default
233 values.
234 """
235 if not isinstance(module, Module):
236 raise TypeError("expected a `Module`")
237 error = ffi.wasmtime_linker_define_unknown_imports_as_default_values(
238 self.ptr(), store._context(), module.ptr())
239 if error:
240 raise WasmtimeError._from_ptr(error)
242 def instantiate_pre(self, module: Module) -> InstancePre:
243 """
244 Pre-instantiates `module` by resolving all of its imports from the
245 definitions in this linker.
247 Returns an `InstancePre` that can be used to cheaply
248 create multiple instances of the same module without repeating import
249 resolution.
251 Raises `WasmtimeError` if any imports are unresolvable.
252 """
253 if not isinstance(module, Module):
254 raise TypeError("expected a `Module`")
255 ptr = ctypes.POINTER(ffi.wasmtime_instance_pre_t)()
256 error = ffi.wasmtime_linker_instantiate_pre(
257 self.ptr(), module.ptr(), ctypes.byref(ptr))
258 if error:
259 raise WasmtimeError._from_ptr(error)
260 return InstancePre._from_ptr(ptr)