Coverage for wasmtime/_linker.py: 91%

107 statements  

« 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 

13 

14 

15class Linker(Managed["ctypes._Pointer[ffi.wasmtime_linker_t]"]): 

16 engine: Engine 

17 

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 

25 

26 def _delete(self, ptr: "ctypes._Pointer[ffi.wasmtime_linker_t]") -> None: 

27 ffi.wasmtime_linker_delete(ptr) 

28 

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) 

36 

37 def define(self, store: Storelike, module: str, name: str, item: AsExtern) -> None: 

38 """ 

39 Defines a new item, by name, in this linker. 

40 

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. 

43 

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) 

63 

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. 

67 

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) 

92 

93 def define_instance(self, store: Storelike, name: str, instance: Instance) -> None: 

94 """ 

95 Convenience wrapper to define an entire instance in this linker. 

96 

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. 

100 

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) 

115 

116 def define_wasi(self) -> None: 

117 """ 

118 Defines a WASI instance in this linker. 

119 

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. 

123 

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) 

130 

131 def define_module(self, store: Storelike, name: str, module: Module) -> None: 

132 """ 

133 Defines automatic instantiations of the provided module in this linker. 

134 

135 The `module` provided is defined under `name` with automatic 

136 instantiations which respect WASI Commands and Reactors. 

137 

138 For more information see the Rust documentation at 

139 https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.module. 

140 

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) 

151 

152 def instantiate(self, store: Storelike, module: Module) -> Instance: 

153 """ 

154 Instantiates a module using this linker's defined set of names. 

155 

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. 

159 

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) 

171 

172 def get_default(self, store: Storelike, name: str) -> Func: 

173 """ 

174 Gets the default export for the named module in this linker. 

175 

176 For more information on this see the Rust documentation at 

177 https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.get_default. 

178 

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) 

189 

190 def get(self, store: Storelike, module: str, name: str) -> AsExtern: 

191 """ 

192 Gets a singular item defined in this linker. 

193 

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") 

209 

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. 

214 

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) 

225 

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.). 

230 

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) 

241 

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. 

246 

247 Returns an `InstancePre` that can be used to cheaply 

248 create multiple instances of the same module without repeating import 

249 resolution. 

250 

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)