Coverage for wasmtime/_linker.py: 92%

90 statements  

« 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 

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 if not isinstance(allow, bool): 

36 raise TypeError("expected a boolean") 

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

38 

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

40 """ 

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

42 

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. 

45 

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) 

65 

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. 

69 

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) 

94 

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

96 """ 

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

98 

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. 

102 

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) 

117 

118 def define_wasi(self) -> None: 

119 """ 

120 Defines a WASI instance in this linker. 

121 

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. 

125 

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) 

132 

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

134 """ 

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

136 

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

138 instantiations which respect WASI Commands and Reactors. 

139 

140 For more information see the Rust documentation at 

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

142 

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) 

153 

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

155 """ 

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

157 

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. 

161 

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) 

173 

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

175 """ 

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

177 

178 For more information on this see the Rust documentation at 

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

180 

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) 

191 

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

193 """ 

194 Gets a singular item defined in this linker. 

195 

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