Coverage for wasmtime/_types.py: 89%

306 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-12-01 19:40 +0000

1import ctypes 

2from ctypes import POINTER, byref 

3from typing import Any, List, Optional, Union 

4 

5from wasmtime import Managed, WasmtimeError 

6 

7from . import _ffi as ffi 

8 

9 

10class ValType(Managed["ctypes._Pointer[ffi.wasm_valtype_t]"]): 

11 _owner: Optional[Any] 

12 

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

14 # If this is owned by another object we don't free it since that object 

15 # is responsible for freeing the backing memory. 

16 if self._owner is None: 

17 ffi.wasm_valtype_delete(ptr) 

18 

19 @classmethod 

20 def i32(cls) -> "ValType": 

21 ptr = ffi.wasm_valtype_new(ffi.WASM_I32) 

22 return ValType._from_ptr(ptr, None) 

23 

24 @classmethod 

25 def i64(cls) -> "ValType": 

26 ptr = ffi.wasm_valtype_new(ffi.WASM_I64) 

27 return ValType._from_ptr(ptr, None) 

28 

29 @classmethod 

30 def f32(cls) -> "ValType": 

31 ptr = ffi.wasm_valtype_new(ffi.WASM_F32) 

32 return ValType._from_ptr(ptr, None) 

33 

34 @classmethod 

35 def f64(cls) -> "ValType": 

36 ptr = ffi.wasm_valtype_new(ffi.WASM_F64) 

37 return ValType._from_ptr(ptr, None) 

38 

39 @classmethod 

40 def externref(cls) -> "ValType": 

41 ptr = ffi.wasm_valtype_new(ffi.WASM_ANYREF) 

42 return ValType._from_ptr(ptr, None) 

43 

44 @classmethod 

45 def funcref(cls) -> "ValType": 

46 ptr = ffi.wasm_valtype_new(ffi.WASM_FUNCREF) 

47 return ValType._from_ptr(ptr, None) 

48 

49 def __init__(self) -> None: 

50 raise WasmtimeError("cannot construct directly") 

51 

52 @classmethod 

53 def _from_ptr(cls, ptr: "ctypes._Pointer[ffi.wasm_valtype_t]", owner: Optional[Any]) -> "ValType": 

54 if not isinstance(ptr, POINTER(ffi.wasm_valtype_t)): 

55 raise TypeError("wrong pointer type") 

56 ty: "ValType" = cls.__new__(cls) 

57 ty._set_ptr(ptr) 

58 ty._owner = owner 

59 return ty 

60 

61 def __eq__(self, other: object) -> bool: 

62 if not isinstance(other, ValType): 

63 return False 

64 kind1 = ffi.wasm_valtype_kind(self.ptr()) 

65 kind2 = ffi.wasm_valtype_kind(other.ptr()) 

66 return kind1 == kind2 

67 

68 def __ne__(self, other: object) -> bool: 

69 return not self.__eq__(other) 

70 

71 def __repr__(self) -> str: 

72 return str(self) 

73 

74 def __str__(self) -> str: 

75 kind = ffi.wasm_valtype_kind(self.ptr()) 

76 if kind == ffi.WASM_I32.value: 

77 return 'i32' 

78 if kind == ffi.WASM_I64.value: 

79 return 'i64' 

80 if kind == ffi.WASM_F32.value: 

81 return 'f32' 

82 if kind == ffi.WASM_F64.value: 

83 return 'f64' 

84 if kind == ffi.WASM_ANYREF.value: 

85 return 'anyref' 

86 if kind == ffi.WASM_FUNCREF.value: 

87 return 'funcref' 

88 return 'ValType(%d)' % kind.value 

89 

90 @classmethod 

91 def _from_list(cls, items: "ctypes._Pointer[ffi.wasm_valtype_vec_t]", owner: Optional[Any]) -> List["ValType"]: 

92 types = [] 

93 for i in range(0, items.contents.size): 

94 types.append(ValType._from_ptr(items.contents.data[i], owner)) 

95 return types 

96 

97 

98def take_owned_valtype(ty: ValType) -> "ctypes._Pointer[ffi.wasm_valtype_t]": 

99 if not isinstance(ty, ValType): 

100 raise TypeError("expected valtype") 

101 

102 # Need to allocate a new type because we need to take ownership. 

103 # 

104 # Trying to expose this as an implementation detail by sneaking out 

105 # types and having some be "taken" feels pretty weird 

106 return ffi.wasm_valtype_new(ffi.wasm_valtype_kind(ty.ptr())) 

107 

108 

109class FuncType(Managed["ctypes._Pointer[ffi.wasm_functype_t]"]): 

110 def __init__(self, params: List[ValType], results: List[ValType]): 

111 for param in params: 

112 if not isinstance(param, ValType): 

113 raise TypeError("expected ValType") 

114 for result in results: 

115 if not isinstance(result, ValType): 

116 raise TypeError("expected ValType") 

117 

118 params_ffi = ffi.wasm_valtype_vec_t() 

119 ffi.wasm_valtype_vec_new_uninitialized(byref(params_ffi), len(params)) 

120 

121 results_ffi = ffi.wasm_valtype_vec_t() 

122 for i, param in enumerate(params): 

123 params_ffi.data[i] = take_owned_valtype(param) 

124 

125 ffi.wasm_valtype_vec_new_uninitialized( 

126 byref(results_ffi), len(results)) 

127 for i, result in enumerate(results): 

128 results_ffi.data[i] = take_owned_valtype(result) 

129 ptr = ffi.wasm_functype_new(byref(params_ffi), byref(results_ffi)) 

130 if not ptr: 

131 raise WasmtimeError("failed to allocate FuncType") 

132 self._set_ptr(ptr) 

133 self._owner = None 

134 

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

136 if self._owner is None: 

137 ffi.wasm_functype_delete(ptr) 

138 

139 @classmethod 

140 def _from_ptr(cls, ptr: "ctypes._Pointer[ffi.wasm_functype_t]", owner: Optional[Any]) -> "FuncType": 

141 if not isinstance(ptr, POINTER(ffi.wasm_functype_t)): 

142 raise TypeError("wrong pointer type") 

143 ty: "FuncType" = cls.__new__(cls) 

144 ty._set_ptr(ptr) 

145 ty._owner = owner 

146 return ty 

147 

148 @property 

149 def params(self) -> List["ValType"]: 

150 """ 

151 Returns the list of parameter types for this function type 

152 """ 

153 

154 ptr = ffi.wasm_functype_params(self.ptr()) 

155 return ValType._from_list(ptr, self) 

156 

157 @property 

158 def results(self) -> List["ValType"]: 

159 """ 

160 Returns the list of result types for this function type 

161 """ 

162 

163 ptr = ffi.wasm_functype_results(self.ptr()) 

164 return ValType._from_list(ptr, self) 

165 

166 def _as_extern(self) -> "ctypes._Pointer[ffi.wasm_externtype_t]": 

167 return ffi.wasm_functype_as_externtype_const(self.ptr()) 

168 

169 

170class GlobalType(Managed["ctypes._Pointer[ffi.wasm_globaltype_t]"]): 

171 def __init__(self, valtype: ValType, mutable: bool): 

172 if mutable: 

173 mutability = ffi.WASM_VAR 

174 else: 

175 mutability = ffi.WASM_CONST 

176 type_ptr = take_owned_valtype(valtype) 

177 ptr = ffi.wasm_globaltype_new(type_ptr, mutability) 

178 if ptr == 0: 

179 raise WasmtimeError("failed to allocate GlobalType") 

180 self._set_ptr(ptr) 

181 self._owner = None 

182 

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

184 if self._owner is None: 

185 ffi.wasm_globaltype_delete(ptr) 

186 

187 @classmethod 

188 def _from_ptr(cls, ptr: "ctypes._Pointer[ffi.wasm_globaltype_t]", owner: Optional[Any]) -> "GlobalType": 

189 if not isinstance(ptr, POINTER(ffi.wasm_globaltype_t)): 

190 raise TypeError("wrong pointer type") 

191 ty: "GlobalType" = cls.__new__(cls) 

192 ty._set_ptr(ptr) 

193 ty._owner = owner 

194 return ty 

195 

196 @property 

197 def content(self) -> ValType: 

198 """ 

199 Returns the type this global contains 

200 """ 

201 

202 ptr = ffi.wasm_globaltype_content(self.ptr()) 

203 return ValType._from_ptr(ptr, self) 

204 

205 @property 

206 def mutable(self) -> bool: 

207 """ 

208 Returns whether this global is mutable or not 

209 """ 

210 val = ffi.wasm_globaltype_mutability(self.ptr()) 

211 return val == ffi.WASM_VAR.value 

212 

213 def _as_extern(self) -> "ctypes._Pointer[ffi.wasm_externtype_t]": 

214 return ffi.wasm_globaltype_as_externtype_const(self.ptr()) 

215 

216 

217class Limits: 

218 def __init__(self, min: int, max: Optional[int]): 

219 self.min = min 

220 self.max = max 

221 

222 def __ffi__(self) -> ffi.wasm_limits_t: 

223 max = self.max 

224 if max is None: 

225 max = 0xffffffff 

226 return ffi.wasm_limits_t(self.min, max) 

227 

228 def __eq__(self, other: object) -> bool: 

229 if not isinstance(other, Limits): 

230 return False 

231 return self.min == other.min and self.max == other.max 

232 

233 @classmethod 

234 def _from_ffi(cls, val: 'ctypes._Pointer[ffi.wasm_limits_t]') -> "Limits": 

235 min = val.contents.min 

236 max = val.contents.max 

237 if max == 0xffffffff: 

238 return Limits(min, None) 

239 return Limits(min, max) 

240 

241 

242class TableType(Managed["ctypes._Pointer[ffi.wasm_tabletype_t]"]): 

243 def __init__(self, valtype: ValType, limits: Limits): 

244 if not isinstance(limits, Limits): 

245 raise TypeError("expected Limits") 

246 type_ptr = take_owned_valtype(valtype) 

247 ptr = ffi.wasm_tabletype_new(type_ptr, byref(limits.__ffi__())) 

248 if not ptr: 

249 raise WasmtimeError("failed to allocate TableType") 

250 self._set_ptr(ptr) 

251 self._owner = None 

252 

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

254 if self._owner is None: 

255 ffi.wasm_tabletype_delete(ptr) 

256 

257 @classmethod 

258 def _from_ptr(cls, ptr: 'ctypes._Pointer[ffi.wasm_tabletype_t]', owner: Optional[Any]) -> "TableType": 

259 ty: "TableType" = cls.__new__(cls) 

260 if not isinstance(ptr, POINTER(ffi.wasm_tabletype_t)): 

261 raise TypeError("wrong pointer type") 

262 ty._set_ptr(ptr) 

263 ty._owner = owner 

264 return ty 

265 

266 @property 

267 def element(self) -> ValType: 

268 """ 

269 Returns the type of this table's elements 

270 """ 

271 ptr = ffi.wasm_tabletype_element(self.ptr()) 

272 return ValType._from_ptr(ptr, self) 

273 

274 @property 

275 def limits(self) -> Limits: 

276 """ 

277 Returns the limits on the size of thi stable 

278 """ 

279 val = ffi.wasm_tabletype_limits(self.ptr()) 

280 return Limits._from_ffi(val) 

281 

282 def _as_extern(self) -> "ctypes._Pointer[ffi.wasm_externtype_t]": 

283 return ffi.wasm_tabletype_as_externtype_const(self.ptr()) 

284 

285 

286class MemoryType(Managed["ctypes._Pointer[ffi.wasm_memorytype_t]"]): 

287 def __init__(self, limits: Limits, is_64: bool = False, shared: bool = False, 

288 page_size_log2: int = 16): 

289 if not isinstance(limits, Limits): 

290 raise TypeError("expected Limits") 

291 if is_64: 

292 maximum = 0x10000000000000000 

293 else: 

294 maximum = 0x100000000 

295 if limits.min >= maximum: 

296 raise WasmtimeError("minimum size too large") 

297 if limits.max and limits.max >= maximum: 

298 raise WasmtimeError("maximum size too large") 

299 ptr = POINTER(ffi.wasm_memorytype_t)() 

300 err = ffi.wasmtime_memorytype_new(limits.min, 

301 limits.max is not None, 

302 limits.max if limits.max else 0, 

303 is_64, 

304 shared, 

305 page_size_log2, 

306 byref(ptr)) 

307 if err: 

308 raise WasmtimeError._from_ptr(err) 

309 self._set_ptr(ptr) 

310 self._owner = None 

311 

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

313 if self._owner is None: 

314 ffi.wasm_memorytype_delete(ptr) 

315 

316 @classmethod 

317 def _from_ptr(cls, ptr: "ctypes._Pointer[ffi.wasm_memorytype_t]", owner: Optional[Any]) -> "MemoryType": 

318 if not isinstance(ptr, POINTER(ffi.wasm_memorytype_t)): 

319 raise TypeError("wrong pointer type") 

320 ty: "MemoryType" = cls.__new__(cls) 

321 ty._set_ptr(ptr) 

322 ty._owner = owner 

323 return ty 

324 

325 @property 

326 def limits(self) -> Limits: 

327 """ 

328 Returns the limits on the size of this table 

329 """ 

330 minimum = ffi.wasmtime_memorytype_minimum(self.ptr()) 

331 maximum = ctypes.c_uint64(0) 

332 has_max = ffi.wasmtime_memorytype_maximum(self.ptr(), byref(maximum)) 

333 return Limits(minimum, maximum.value if has_max else None) 

334 

335 @property 

336 def is_64(self) -> bool: 

337 """ 

338 Returns whether or not this is a 64-bit memory 

339 """ 

340 return ffi.wasmtime_memorytype_is64(self.ptr()) 

341 

342 @property 

343 def is_shared(self) -> bool: 

344 """ 

345 Returns whether or not this is a shared memory 

346 """ 

347 return ffi.wasmtime_memorytype_isshared(self.ptr()) 

348 

349 

350 def _as_extern(self) -> "ctypes._Pointer[ffi.wasm_externtype_t]": 

351 return ffi.wasm_memorytype_as_externtype_const(self.ptr()) 

352 

353 

354def wrap_externtype(ptr: "ctypes._Pointer[ffi.wasm_externtype_t]", owner: Optional[Any]) -> "AsExternType": 

355 if not isinstance(ptr, POINTER(ffi.wasm_externtype_t)): 

356 raise TypeError("wrong pointer type") 

357 val = ffi.wasm_externtype_as_functype(ptr) 

358 if val: 

359 return FuncType._from_ptr(val, owner) 

360 val = ffi.wasm_externtype_as_tabletype(ptr) 

361 if val: 

362 return TableType._from_ptr(val, owner) 

363 val = ffi.wasm_externtype_as_globaltype(ptr) 

364 if val: 

365 return GlobalType._from_ptr(val, owner) 

366 val = ffi.wasm_externtype_as_memorytype(ptr) 

367 if val: 

368 return MemoryType._from_ptr(val, owner) 

369 raise WasmtimeError("unknown extern type") 

370 

371 

372class ImportType(Managed["ctypes._Pointer[ffi.wasm_importtype_t]"]): 

373 _owner: Optional[Any] 

374 

375 @classmethod 

376 def _from_ptr(cls, ptr: "ctypes._Pointer[ffi.wasm_importtype_t]", owner: Optional[Any] = None) -> "ImportType": 

377 if not isinstance(ptr, POINTER(ffi.wasm_importtype_t)): 

378 raise TypeError("wrong pointer type") 

379 ty: "ImportType" = cls.__new__(cls) 

380 ty._set_ptr(ptr) 

381 ty._owner = owner 

382 return ty 

383 

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

385 if self._owner is None: 

386 ffi.wasm_importtype_delete(ptr) 

387 

388 @property 

389 def module(self) -> str: 

390 """ 

391 Returns the module this import type refers to 

392 """ 

393 

394 return ffi.to_str(ffi.wasm_importtype_module(self.ptr()).contents) 

395 

396 @property 

397 def name(self) -> Optional[str]: 

398 """ 

399 Returns the name in the module this import type refers to 

400 

401 Note that `None` may be returned for the module linking proposal where 

402 the field name is optional. 

403 """ 

404 ptr = ffi.wasm_importtype_name(self.ptr()) 

405 if ptr: 

406 return ffi.to_str(ptr.contents) 

407 return None 

408 

409 @property 

410 def type(self) -> "AsExternType": 

411 """ 

412 Returns the type that this import refers to 

413 """ 

414 ptr = ffi.wasm_importtype_type(self.ptr()) 

415 return wrap_externtype(ptr, self._owner or self) 

416 

417 

418class ExportType(Managed["ctypes._Pointer[ffi.wasm_exporttype_t]"]): 

419 _ptr: "ctypes._Pointer[ffi.wasm_exporttype_t]" 

420 _owner: Optional[Any] 

421 

422 @classmethod 

423 def _from_ptr(cls, ptr: 'ctypes._Pointer[ffi.wasm_exporttype_t]', owner: Optional[Any] = None) -> "ExportType": 

424 if not isinstance(ptr, POINTER(ffi.wasm_exporttype_t)): 

425 raise TypeError("wrong pointer type") 

426 ty: "ExportType" = cls.__new__(cls) 

427 ty._set_ptr(ptr) 

428 ty._owner = owner 

429 return ty 

430 

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

432 if self._owner is None: 

433 ffi.wasm_exporttype_delete(ptr) 

434 

435 @property 

436 def name(self) -> str: 

437 """ 

438 Returns the name in the modulethis export type refers to 

439 """ 

440 return ffi.to_str(ffi.wasm_exporttype_name(self.ptr()).contents) 

441 

442 @property 

443 def type(self) -> "AsExternType": 

444 """ 

445 Returns the type that this export refers to 

446 """ 

447 ptr = ffi.wasm_exporttype_type(self.ptr()) 

448 return wrap_externtype(ptr, self._owner or self) 

449 

450 

451AsExternType = Union[FuncType, TableType, MemoryType, GlobalType]