Coverage for wasmtime/_linker.py: 92%
90 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
1import ctypes
2from ctypes import *
3from typing import Any
4from wasmtime import Instance, Engine, FuncType
5from wasmtime import Module, WasmtimeError, Func, Managed
6from . import _ffi as ffi
7from ._extern import get_extern_ptr, wrap_extern
8from ._config import setter_property
9from ._exportable import AsExtern
10from ._store import Storelike
11from ._func import enter_wasm, trampoline, FUNCTIONS, finalize
12from typing import Callable
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 if not isinstance(allow, bool):
36 raise TypeError("expected a boolean")
37 ffi.wasmtime_linker_allow_shadowing(self.ptr(), allow)
39 def define(self, store: Storelike, module: str, name: str, item: AsExtern) -> None:
40 """
41 Defines a new item, by name, in this linker.
43 This method will add a new definition to this linker. The `module` nad
44 `name` provided are what to name the `item` within the linker.
46 This function will raise an error if `item` comes from the wrong store
47 or if shadowing is disallowed and the module/name pair has already been
48 defined.
49 """
50 raw_item = get_extern_ptr(item)
51 module_bytes = module.encode('utf-8')
52 module_buf = create_string_buffer(module_bytes)
53 name_bytes = name.encode('utf-8')
54 name_buf = create_string_buffer(name_bytes)
55 error = ffi.wasmtime_linker_define(
56 self.ptr(),
57 store._context(),
58 module_buf,
59 len(module_bytes),
60 name_buf,
61 len(name_bytes),
62 byref(raw_item))
63 if error:
64 raise WasmtimeError._from_ptr(error)
66 def define_func(self, module: str, name: str, ty: FuncType, func: Callable[..., Any], access_caller: bool = False) -> None:
67 """
68 Defines a new function, by name, in this linker.
70 This method is similar to `define` except that you can directly define a
71 function without creating a `Func` itself. This enables
72 `Store`-independent functions to be inserted into this linker, meaning
73 the linker can be used to instantiate modules in multiple stores.
74 """
75 module_bytes = module.encode('utf-8')
76 module_buf = create_string_buffer(module_bytes)
77 name_bytes = name.encode('utf-8')
78 name_buf = create_string_buffer(name_bytes)
79 if not isinstance(ty, FuncType):
80 raise TypeError("expected a FuncType")
81 idx = FUNCTIONS.allocate((func, ty.results, access_caller))
82 error = ffi.wasmtime_linker_define_func(
83 self.ptr(),
84 module_buf,
85 len(module_bytes),
86 name_buf,
87 len(name_bytes),
88 ty.ptr(),
89 trampoline,
90 idx,
91 finalize)
92 if error:
93 raise WasmtimeError._from_ptr(error)
95 def define_instance(self, store: Storelike, name: str, instance: Instance) -> None:
96 """
97 Convenience wrapper to define an entire instance in this linker.
99 This function will `define` eaech of the exports on the instance into
100 this linker, using the name provided as the module name and the export's
101 own name as the field name.
103 This function will raise an error if `instance` comes from the wrong
104 store or if shadowing is disallowed and a name was previously defined.
105 """
106 if not isinstance(instance, Instance):
107 raise TypeError("expected an `Instance`")
108 name_bytes = name.encode('utf8')
109 name_buf = create_string_buffer(name_bytes)
110 error = ffi.wasmtime_linker_define_instance(self.ptr(),
111 store._context(),
112 name_buf,
113 len(name_bytes),
114 byref(instance._instance))
115 if error:
116 raise WasmtimeError._from_ptr(error)
118 def define_wasi(self) -> None:
119 """
120 Defines a WASI instance in this linker.
122 The instance provided has been previously constructed and this method
123 will define all the appropriate imports and their names into this linker
124 to assist with instantiating modules that use WASI.
126 This function will raise an error if shadowing is disallowed and a name
127 was previously defined.
128 """
129 error = ffi.wasmtime_linker_define_wasi(self.ptr())
130 if error:
131 raise WasmtimeError._from_ptr(error)
133 def define_module(self, store: Storelike, name: str, module: Module) -> None:
134 """
135 Defines automatic instantiations of the provided module in this linker.
137 The `module` provided is defined under `name` with automatic
138 instantiations which respect WASI Commands and Reactors.
140 For more information see the Rust documentation at
141 https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.module.
143 This method will throw an error if shadowing is disallowed and an item
144 has previously been defined.
145 """
146 if not isinstance(module, Module):
147 raise TypeError("expected a `Module`")
148 name_bytes = name.encode('utf-8')
149 name_buf = create_string_buffer(name_bytes)
150 error = ffi.wasmtime_linker_module(self.ptr(), store._context(), name_buf, len(name_bytes), module.ptr())
151 if error:
152 raise WasmtimeError._from_ptr(error)
154 def instantiate(self, store: Storelike, module: Module) -> Instance:
155 """
156 Instantiates a module using this linker's defined set of names.
158 This method will attempt to satisfy all the imports of the `module`
159 provided with the names defined within this linker. If all names are
160 defined then the module is instantiated.
162 Raises an error if an import of `module` hasn't been defined in this
163 linker or if a trap happens while instantiating the instance.
164 """
165 trap = POINTER(ffi.wasm_trap_t)()
166 instance = ffi.wasmtime_instance_t()
167 with enter_wasm(store) as trap:
168 error = ffi.wasmtime_linker_instantiate(
169 self.ptr(), store._context(), module.ptr(), byref(instance), trap)
170 if error:
171 raise WasmtimeError._from_ptr(error)
172 return Instance._from_raw(instance)
174 def get_default(self, store: Storelike, name: str) -> Func:
175 """
176 Gets the default export for the named module in this linker.
178 For more information on this see the Rust documentation at
179 https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.get_default.
181 Raises an error if the default export wasn't present.
182 """
183 name_bytes = name.encode('utf-8')
184 name_buf = create_string_buffer(name_bytes)
185 func = ffi.wasmtime_func_t()
186 error = ffi.wasmtime_linker_get_default(self.ptr(), store._context(),
187 name_buf, len(name_bytes), byref(func))
188 if error:
189 raise WasmtimeError._from_ptr(error)
190 return Func._from_raw(func)
192 def get(self, store: Storelike, module: str, name: str) -> AsExtern:
193 """
194 Gets a singular item defined in this linker.
196 Raises an error if this item hasn't been defined or if the item has been
197 defined twice with different types.
198 """
199 module_bytes = module.encode('utf-8')
200 module_buf = create_string_buffer(module_bytes)
201 name_bytes = name.encode('utf-8')
202 name_buf = create_string_buffer(name_bytes)
203 item = ffi.wasmtime_extern_t()
204 ok = ffi.wasmtime_linker_get(self.ptr(), store._context(),
205 module_buf, len(module_bytes),
206 name_buf, len(name_bytes),
207 byref(item))
208 if ok:
209 return wrap_extern(item)
210 raise WasmtimeError("item not defined in linker")