Coverage for wasmtime/_value.py: 91%

180 statements  

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

1from ._error import WasmtimeError 

2from ._types import ValType 

3import ctypes 

4import typing 

5from ._slab import Slab 

6 

7from . import _ffi as ffi 

8 

9if typing.TYPE_CHECKING: 

10 from ._store import Storelike 

11 

12 

13SLAB: Slab[typing.Any] = Slab() 

14 

15 

16# Note that 0 as a `c_void_p` is reserved for null-lookalike externrefs so 

17# indices from the slabs are off-by-one. 

18@ctypes.CFUNCTYPE(None, ctypes.c_void_p) 

19def _externref_finalizer(idx: int) -> None: 

20 SLAB.deallocate(idx - 1) 

21 

22 

23def _intern(obj: typing.Any) -> int: 

24 return SLAB.allocate(obj) + 1 

25 

26 

27def _unintern(idx: int) -> typing.Any: 

28 return SLAB.get(idx - 1) 

29 

30 

31class Val: 

32 _kind: ffi.wasmtime_valkind_t 

33 _val: typing.Any 

34 

35 @classmethod 

36 def i32(cls, val: int) -> "Val": 

37 """ 

38 Create a new 32-bit integer value 

39 """ 

40 if not isinstance(val, int): 

41 raise TypeError("expected an integer") 

42 val = ffi.wasmtime_valunion_t(i32=val).i32 

43 return Val(ffi.WASMTIME_I32, val) 

44 

45 @classmethod 

46 def i64(cls, val: int) -> "Val": 

47 """ 

48 Create a new 64-bit integer value 

49 """ 

50 if not isinstance(val, int): 

51 raise TypeError("expected an integer") 

52 val = ffi.wasmtime_valunion_t(i64=val).i64 

53 return Val(ffi.WASMTIME_I64, val) 

54 

55 @classmethod 

56 def f32(cls, val: float) -> "Val": 

57 """ 

58 Create a new 32-bit float value 

59 """ 

60 if not isinstance(val, float): 

61 raise TypeError("expected a float") 

62 val = ffi.wasmtime_valunion_t(f32=val).f32 

63 return Val(ffi.WASMTIME_F32, val) 

64 

65 @classmethod 

66 def f64(cls, val: float) -> "Val": 

67 """ 

68 Create a new 64-bit float value 

69 """ 

70 if not isinstance(val, float): 

71 raise TypeError("expected a float") 

72 val = ffi.wasmtime_valunion_t(f64=val).f64 

73 return Val(ffi.WASMTIME_F64, val) 

74 

75 @classmethod 

76 def externref(cls, extern: typing.Optional[typing.Any]) -> "Val": 

77 return Val(ffi.WASMTIME_EXTERNREF, extern) 

78 

79 @classmethod 

80 def funcref(cls, f: "typing.Optional[wasmtime.Func]") -> "Val": 

81 return Val(ffi.WASMTIME_FUNCREF, f) 

82 

83 @classmethod 

84 def ref_null(cls, ty: ValType) -> "Val": 

85 """ 

86 Create a null reference value of the given type. 

87 

88 Raise an exception if `ty` is not a reference type. 

89 """ 

90 if ty == ValType.externref(): 

91 return Val.externref(None) 

92 if ty == ValType.funcref(): 

93 return Val.funcref(None) 

94 raise WasmtimeError("Invalid reference type for `ref_null`: %s" % ty) 

95 

96 def __init__(self, kind: ffi.wasmtime_valkind_t, val: typing.Any): 

97 self._kind = kind 

98 self._val = val 

99 

100 def __eq__(self, rhs: typing.Any) -> typing.Any: 

101 if isinstance(rhs, Val): 

102 return self._kind == rhs._kind and self._val == rhs._val 

103 return self._val == rhs 

104 

105 @classmethod 

106 def _convert_to_raw(cls, store: 'Storelike', ty: ValType, val: typing.Any) -> ffi.wasmtime_val_t: 

107 if isinstance(val, Val): 

108 if ty != val.type: 

109 raise TypeError("wrong type of `Val` provided") 

110 return val._new_raw(store) 

111 if ty == ValType.externref(): 

112 return Val.externref(val)._new_raw(store) 

113 elif isinstance(val, int): 

114 if ty == ValType.i32(): 

115 return Val.i32(val)._new_raw(store) 

116 if ty == ValType.i64(): 

117 return Val.i64(val)._new_raw(store) 

118 elif isinstance(val, float): 

119 if ty == ValType.f32(): 

120 return Val.f32(val)._new_raw(store) 

121 if ty == ValType.f64(): 

122 return Val.f64(val)._new_raw(store) 

123 elif isinstance(val, wasmtime.Func): 

124 return Val.funcref(val)._new_raw(store) 

125 elif val is None: 

126 if ty == ValType.externref(): 

127 return Val.externref(None)._new_raw(store) 

128 if ty == ValType.funcref(): 

129 return Val.funcref(None)._new_raw(store) 

130 raise TypeError("don't know how to convert %r to %s" % (val, ty)) 

131 

132 def _new_raw(self, store: 'Storelike') -> ffi.wasmtime_val_t: 

133 ret = ffi.wasmtime_val_t(kind = self._kind) 

134 if ret.kind == ffi.WASMTIME_I32.value: 

135 ret.of.i32 = self._val 

136 elif ret.kind == ffi.WASMTIME_I64.value: 

137 ret.of.i64 = self._val 

138 elif ret.kind == ffi.WASMTIME_F32.value: 

139 ret.of.f32 = self._val 

140 elif ret.kind == ffi.WASMTIME_F64.value: 

141 ret.of.f64 = self._val 

142 elif ret.kind == ffi.WASMTIME_EXTERNREF.value: 

143 if self._val is not None: 

144 extern_id = _intern(self._val) 

145 if not ffi.wasmtime_externref_new(store._context(), extern_id, _externref_finalizer, 

146 ctypes.byref(ret.of.externref)): 

147 raise WasmtimeError("failed to create an externref value") 

148 else: 

149 

150 ret.of.externref.store_id = 0 

151 elif ret.kind == ffi.WASMTIME_FUNCREF.value: 

152 if self._val is not None: 

153 ret.of.funcref = self._val._func 

154 else: 

155 raise RuntimeError("unknown kind") 

156 return ret 

157 

158 @classmethod 

159 def _from_raw(cls, store: 'Storelike', raw: ffi.wasmtime_val_t, owned: bool = True) -> 'Val': 

160 val: typing.Any = None 

161 if raw.kind == ffi.WASMTIME_I32.value: 

162 val = raw.of.i32 

163 elif raw.kind == ffi.WASMTIME_I64.value: 

164 val = raw.of.i64 

165 elif raw.kind == ffi.WASMTIME_F32.value: 

166 val = raw.of.f32 

167 elif raw.kind == ffi.WASMTIME_F64.value: 

168 val = raw.of.f64 

169 elif raw.kind == ffi.WASMTIME_EXTERNREF.value: 

170 if raw.of.externref: 

171 extern_id = ffi.wasmtime_externref_data(store._context(), raw.of.externref) 

172 # FIXME https://github.com/bytecodealliance/wasmtime-py/issues/303 

173 if extern_id: 

174 val = _unintern(extern_id) # type: ignore 

175 elif raw.kind == ffi.WASMTIME_FUNCREF.value: 

176 if raw.of.funcref.store_id != 0: 

177 val = wasmtime.Func._from_raw(raw.of.funcref) 

178 else: 

179 raise WasmtimeError("Unkown `ffi.wasmtime_valkind_t`: {}".format(raw.kind)) 

180 

181 if owned: 

182 ffi.wasmtime_val_unroot(ctypes.byref(raw)) 

183 

184 return Val(raw.kind, val) 

185 

186 @property 

187 def value(self) -> typing.Any: 

188 """ 

189 Get the the underlying value as a python value 

190 """ 

191 return self._val 

192 

193 def as_i32(self) -> typing.Optional[int]: 

194 """ 

195 Get the 32-bit integer value of this value, or `None` if it's not an i32 

196 """ 

197 if self._kind == ffi.WASMTIME_I32: 

198 assert(isinstance(self._val, int)) 

199 return self._val 

200 else: 

201 return None 

202 

203 def as_i64(self) -> typing.Optional[int]: 

204 """ 

205 Get the 64-bit integer value of this value, or `None` if it's not an i64 

206 """ 

207 if self._kind == ffi.WASMTIME_I64: 

208 assert(isinstance(self._val, int)) 

209 return self._val 

210 else: 

211 return None 

212 

213 def as_f32(self) -> typing.Optional[float]: 

214 """ 

215 Get the 32-bit float value of this value, or `None` if it's not an f32 

216 """ 

217 if self._kind == ffi.WASMTIME_F32: 

218 assert(isinstance(self._val, float)) 

219 return self._val 

220 else: 

221 return None 

222 

223 def as_f64(self) -> typing.Optional[float]: 

224 """ 

225 Get the 64-bit float value of this value, or `None` if it's not an f64 

226 """ 

227 if self._kind == ffi.WASMTIME_F64: 

228 assert(isinstance(self._val, float)) 

229 return self._val 

230 else: 

231 return None 

232 

233 def as_externref(self) -> typing.Optional[typing.Any]: 

234 """ 

235 Get the extern data referenced by this `externref` value, or `None` if 

236 it's not an `externref`. 

237 """ 

238 if self._kind == ffi.WASMTIME_EXTERNREF: 

239 return self._val 

240 else: 

241 return None 

242 

243 def as_funcref(self) -> typing.Optional["wasmtime.Func"]: 

244 """ 

245 Get the function that this `funcref` value is referencing, or `None` if 

246 this is not a `funcref` value, or is a null reference. 

247 """ 

248 if self._kind == ffi.WASMTIME_FUNCREF: 

249 assert(isinstance(self._val, wasmtime.Func)) 

250 return self._val 

251 else: 

252 return None 

253 

254 @property 

255 def type(self) -> ValType: 

256 """ 

257 Returns the `ValType` corresponding to this `Val` 

258 """ 

259 kind = self._kind 

260 if kind == ffi.WASMTIME_I32: 

261 return ValType.i32() 

262 elif kind == ffi.WASMTIME_I64: 

263 return ValType.i64() 

264 elif kind == ffi.WASMTIME_F32: 

265 return ValType.f32() 

266 elif kind == ffi.WASMTIME_F64: 

267 return ValType.f64() 

268 elif kind == ffi.WASMTIME_V128: 

269 raise Exception("unimplemented v128 type") 

270 elif kind == ffi.WASMTIME_EXTERNREF: 

271 return ValType.externref() 

272 elif kind == ffi.WASMTIME_FUNCREF: 

273 return ValType.funcref() 

274 else: 

275 raise Exception("unknown kind %d" % kind.value) 

276 

277 

278# This needs to be imported so that mypy understands `wasmtime.Func` in typings, 

279# but it also has to come after `Val`'s definition to deal with circular module 

280# dependency issues. 

281import wasmtime # noqa: E402