Coverage for wasmtime/_linker.py: 92%

86 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-12-01 19:40 +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 

12 

13 

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

15 engine: Engine 

16 

17 def __init__(self, engine: Engine): 

18 """ 

19 Creates a new linker ready to instantiate modules within the store 

20 provided. 

21 """ 

22 self._set_ptr(ffi.wasmtime_linker_new(engine.ptr())) 

23 self.engine = engine 

24 

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

26 ffi.wasmtime_linker_delete(ptr) 

27 

28 @setter_property 

29 def allow_shadowing(self, allow: bool) -> None: 

30 """ 

31 Configures whether definitions are allowed to shadow one another within 

32 this linker 

33 """ 

34 ffi.wasmtime_linker_allow_shadowing(self.ptr(), allow) 

35 

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

37 """ 

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

39 

40 This method will add a new definition to this linker. The `module` nad 

41 `name` provided are what to name the `item` within the linker. 

42 

43 This function will raise an error if `item` comes from the wrong store 

44 or if shadowing is disallowed and the module/name pair has already been 

45 defined. 

46 """ 

47 raw_item = get_extern_ptr(item) 

48 module_bytes = module.encode('utf-8') 

49 module_buf = ctypes.create_string_buffer(module_bytes) 

50 name_bytes = name.encode('utf-8') 

51 name_buf = ctypes.create_string_buffer(name_bytes) 

52 error = ffi.wasmtime_linker_define( 

53 self.ptr(), 

54 store._context(), 

55 module_buf, 

56 len(module_bytes), 

57 name_buf, 

58 len(name_bytes), 

59 ctypes.byref(raw_item)) 

60 if error: 

61 raise WasmtimeError._from_ptr(error) 

62 

63 def define_func(self, module: str, name: str, ty: FuncType, func: Callable[..., Any], access_caller: bool = False) -> None: 

64 """ 

65 Defines a new function, by name, in this linker. 

66 

67 This method is similar to `define` except that you can directly define a 

68 function without creating a `Func` itself. This enables 

69 `Store`-independent functions to be inserted into this linker, meaning 

70 the linker can be used to instantiate modules in multiple stores. 

71 """ 

72 module_bytes = module.encode('utf-8') 

73 module_buf = ctypes.create_string_buffer(module_bytes) 

74 name_bytes = name.encode('utf-8') 

75 name_buf = ctypes.create_string_buffer(name_bytes) 

76 if not isinstance(ty, FuncType): 

77 raise TypeError("expected a FuncType") 

78 idx = FUNCTIONS.allocate((func, ty.results, access_caller)) 

79 error = ffi.wasmtime_linker_define_func( 

80 self.ptr(), 

81 module_buf, 

82 len(module_bytes), 

83 name_buf, 

84 len(name_bytes), 

85 ty.ptr(), 

86 trampoline, 

87 idx, 

88 finalize) 

89 if error: 

90 raise WasmtimeError._from_ptr(error) 

91 

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

93 """ 

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

95 

96 This function will `define` eaech of the exports on the instance into 

97 this linker, using the name provided as the module name and the export's 

98 own name as the field name. 

99 

100 This function will raise an error if `instance` comes from the wrong 

101 store or if shadowing is disallowed and a name was previously defined. 

102 """ 

103 if not isinstance(instance, Instance): 

104 raise TypeError("expected an `Instance`") 

105 name_bytes = name.encode('utf8') 

106 name_buf = ctypes.create_string_buffer(name_bytes) 

107 error = ffi.wasmtime_linker_define_instance(self.ptr(), 

108 store._context(), 

109 name_buf, 

110 len(name_bytes), 

111 ctypes.byref(instance._instance)) 

112 if error: 

113 raise WasmtimeError._from_ptr(error) 

114 

115 def define_wasi(self) -> None: 

116 """ 

117 Defines a WASI instance in this linker. 

118 

119 The instance provided has been previously constructed and this method 

120 will define all the appropriate imports and their names into this linker 

121 to assist with instantiating modules that use WASI. 

122 

123 This function will raise an error if shadowing is disallowed and a name 

124 was previously defined. 

125 """ 

126 error = ffi.wasmtime_linker_define_wasi(self.ptr()) 

127 if error: 

128 raise WasmtimeError._from_ptr(error) 

129 

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

131 """ 

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

133 

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

135 instantiations which respect WASI Commands and Reactors. 

136 

137 For more information see the Rust documentation at 

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

139 

140 This method will throw an error if shadowing is disallowed and an item 

141 has previously been defined. 

142 """ 

143 if not isinstance(module, Module): 

144 raise TypeError("expected a `Module`") 

145 name_bytes = name.encode('utf-8') 

146 name_buf = ctypes.create_string_buffer(name_bytes) 

147 error = ffi.wasmtime_linker_module(self.ptr(), store._context(), name_buf, len(name_bytes), module.ptr()) 

148 if error: 

149 raise WasmtimeError._from_ptr(error) 

150 

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

152 """ 

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

154 

155 This method will attempt to satisfy all the imports of the `module` 

156 provided with the names defined within this linker. If all names are 

157 defined then the module is instantiated. 

158 

159 Raises an error if an import of `module` hasn't been defined in this 

160 linker or if a trap happens while instantiating the instance. 

161 """ 

162 trap = ctypes.POINTER(ffi.wasm_trap_t)() 

163 instance = ffi.wasmtime_instance_t() 

164 with enter_wasm(store) as trap: 

165 error = ffi.wasmtime_linker_instantiate( 

166 self.ptr(), store._context(), module.ptr(), ctypes.byref(instance), trap) 

167 if error: 

168 raise WasmtimeError._from_ptr(error) 

169 return Instance._from_raw(instance) 

170 

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

172 """ 

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

174 

175 For more information on this see the Rust documentation at 

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

177 

178 Raises an error if the default export wasn't present. 

179 """ 

180 name_bytes = name.encode('utf-8') 

181 name_buf = ctypes.create_string_buffer(name_bytes) 

182 func = ffi.wasmtime_func_t() 

183 error = ffi.wasmtime_linker_get_default(self.ptr(), store._context(), 

184 name_buf, len(name_bytes), ctypes.byref(func)) 

185 if error: 

186 raise WasmtimeError._from_ptr(error) 

187 return Func._from_raw(func) 

188 

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

190 """ 

191 Gets a singular item defined in this linker. 

192 

193 Raises an error if this item hasn't been defined or if the item has been 

194 defined twice with different types. 

195 """ 

196 module_bytes = module.encode('utf-8') 

197 module_buf = ctypes.create_string_buffer(module_bytes) 

198 name_bytes = name.encode('utf-8') 

199 name_buf = ctypes.create_string_buffer(name_bytes) 

200 item = ffi.wasmtime_extern_t() 

201 ok = ffi.wasmtime_linker_get(self.ptr(), store._context(), 

202 module_buf, len(module_bytes), 

203 name_buf, len(name_bytes), 

204 ctypes.byref(item)) 

205 if ok: 

206 return wrap_extern(item) 

207 raise WasmtimeError("item not defined in linker")