Coverage for wasmtime/_value.py: 91%

191 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-02-20 16:25 +0000

1from ._error import WasmtimeError 

2from ._ffi import * 

3from ._types import ValType 

4import ctypes 

5import typing 

6 

7 

8if typing.TYPE_CHECKING: 

9 from ._store import Storelike 

10 

11 

12@ctypes.CFUNCTYPE(None, c_void_p) 

13def _externref_finalizer(extern_id: int) -> None: 

14 Val._id_to_ref_count[extern_id] -= 1 

15 if Val._id_to_ref_count[extern_id] == 0: 

16 del Val._id_to_ref_count[extern_id] 

17 del Val._id_to_extern[extern_id] 

18 

19 

20def _intern(obj: typing.Any) -> c_void_p: 

21 extern_id = id(obj) 

22 Val._id_to_ref_count.setdefault(extern_id, 0) 

23 Val._id_to_ref_count[extern_id] += 1 

24 Val._id_to_extern[extern_id] = obj 

25 return ctypes.c_void_p(extern_id) 

26 

27 

28def _unintern(val: int) -> typing.Any: 

29 return Val._id_to_extern.get(val) 

30 

31 

32class Val: 

33 # We can't let the extern values we wrap `externref`s around be GC'd, so we 

34 # pin them in `_id_to_extern`. Additionally, we might make multiple 

35 # `externref`s to the same extern value, so we count how many references 

36 # we've created in `_id_to_ref_count`, and only remove a value's entry from 

37 # `_id_to_extern` once the ref count is zero. 

38 _id_to_extern: typing.Dict[int, typing.Any] = {} 

39 _id_to_ref_count: typing.Dict[int, int] = {} 

40 

41 _kind: wasmtime_valkind_t 

42 _val: typing.Any 

43 

44 @classmethod 

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

46 """ 

47 Create a new 32-bit integer value 

48 """ 

49 if not isinstance(val, int): 

50 raise TypeError("expected an integer") 

51 val = wasmtime_valunion_t(i32=val).i32 

52 return Val(WASMTIME_I32, val) 

53 

54 @classmethod 

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

56 """ 

57 Create a new 64-bit integer value 

58 """ 

59 if not isinstance(val, int): 

60 raise TypeError("expected an integer") 

61 val = wasmtime_valunion_t(i64=val).i64 

62 return Val(WASMTIME_I64, val) 

63 

64 @classmethod 

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

66 """ 

67 Create a new 32-bit float value 

68 """ 

69 if not isinstance(val, float): 

70 raise TypeError("expected a float") 

71 val = wasmtime_valunion_t(f32=val).f32 

72 return Val(WASMTIME_F32, val) 

73 

74 @classmethod 

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

76 """ 

77 Create a new 64-bit float value 

78 """ 

79 if not isinstance(val, float): 

80 raise TypeError("expected a float") 

81 val = wasmtime_valunion_t(f64=val).f64 

82 return Val(WASMTIME_F64, val) 

83 

84 @classmethod 

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

86 return Val(WASMTIME_EXTERNREF, extern) 

87 

88 @classmethod 

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

90 return Val(WASMTIME_FUNCREF, f) 

91 

92 @classmethod 

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

94 """ 

95 Create a null reference value of the given type. 

96 

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

98 """ 

99 if ty == ValType.externref(): 

100 return Val.externref(None) 

101 if ty == ValType.funcref(): 

102 return Val.funcref(None) 

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

104 

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

106 self._kind = kind 

107 self._val = val 

108 

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

110 if isinstance(rhs, Val): 

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

112 return self._val == rhs 

113 

114 @classmethod 

115 def _convert_to_raw(cls, store: 'Storelike', ty: ValType, val: Any) -> wasmtime_val_t: 

116 if isinstance(val, Val): 

117 if ty != val.type: 

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

119 return val._new_raw(store) 

120 if ty == ValType.externref(): 

121 print('convert to externref', val); 

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

123 elif isinstance(val, int): 

124 if ty == ValType.i32(): 

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

126 if ty == ValType.i64(): 

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

128 elif isinstance(val, float): 

129 if ty == ValType.f32(): 

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

131 if ty == ValType.f64(): 

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

133 elif isinstance(val, wasmtime.Func): 

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

135 elif val is None: 

136 if ty == ValType.externref(): 

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

138 if ty == ValType.funcref(): 

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

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

141 

142 def _new_raw(self, store: 'Storelike') -> wasmtime_val_t: 

143 ret = wasmtime_val_t(kind = self._kind) 

144 if ret.kind == WASMTIME_I32.value: 

145 ret.of.i32 = self._val 

146 elif ret.kind == WASMTIME_I64.value: 

147 ret.of.i64 = self._val 

148 elif ret.kind == WASMTIME_F32.value: 

149 ret.of.f32 = self._val 

150 elif ret.kind == WASMTIME_F64.value: 

151 ret.of.f64 = self._val 

152 elif ret.kind == WASMTIME_EXTERNREF.value: 

153 if self._val is not None: 

154 extern_id = _intern(self._val) 

155 if not wasmtime_externref_new(store._context(), extern_id, _externref_finalizer, 

156 byref(ret.of.externref)): 

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

158 else: 

159 

160 ret.of.externref.store_id = 0 

161 elif ret.kind == WASMTIME_FUNCREF.value: 

162 if self._val is not None: 

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

164 else: 

165 raise RuntimeError("unknown kind") 

166 return ret 

167 

168 @classmethod 

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

170 val: typing.Any = None 

171 if raw.kind == WASMTIME_I32.value: 

172 val = raw.of.i32 

173 elif raw.kind == WASMTIME_I64.value: 

174 val = raw.of.i64 

175 elif raw.kind == WASMTIME_F32.value: 

176 val = raw.of.f32 

177 elif raw.kind == WASMTIME_F64.value: 

178 val = raw.of.f64 

179 elif raw.kind == WASMTIME_EXTERNREF.value: 

180 if raw.of.externref: 

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

182 val = _unintern(extern_id) 

183 elif raw.kind == WASMTIME_FUNCREF.value: 

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

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

186 else: 

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

188 

189 if owned: 

190 wasmtime_val_unroot(store._context(), byref(raw)) 

191 

192 return Val(raw.kind, val) 

193 

194 @property 

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

196 """ 

197 Get the the underlying value as a python value 

198 """ 

199 return self._val 

200 

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

202 """ 

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

204 """ 

205 if self._kind == WASMTIME_I32: 

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

207 return self._val 

208 else: 

209 return None 

210 

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

212 """ 

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

214 """ 

215 if self._kind == WASMTIME_I64: 

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

217 return self._val 

218 else: 

219 return None 

220 

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

222 """ 

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

224 """ 

225 if self._kind == WASMTIME_F32: 

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

227 return self._val 

228 else: 

229 return None 

230 

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

232 """ 

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

234 """ 

235 if self._kind == WASMTIME_F64: 

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

237 return self._val 

238 else: 

239 return None 

240 

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

242 """ 

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

244 it's not an `externref`. 

245 """ 

246 if self._kind == WASMTIME_EXTERNREF: 

247 return self._val 

248 else: 

249 return None 

250 

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

252 """ 

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

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

255 """ 

256 if self._kind == WASMTIME_FUNCREF: 

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

258 return self._val 

259 else: 

260 return None 

261 

262 @property 

263 def type(self) -> ValType: 

264 """ 

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

266 """ 

267 kind = self._kind 

268 if kind == WASMTIME_I32: 

269 return ValType.i32() 

270 elif kind == WASMTIME_I64: 

271 return ValType.i64() 

272 elif kind == WASMTIME_F32: 

273 return ValType.f32() 

274 elif kind == WASMTIME_F64: 

275 return ValType.f64() 

276 elif kind == WASMTIME_V128: 

277 raise Exception("unimplemented v128 type") 

278 elif kind == WASMTIME_EXTERNREF: 

279 return ValType.externref() 

280 elif kind == WASMTIME_FUNCREF: 

281 return ValType.funcref() 

282 else: 

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

284 

285 

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

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

288# dependency issues. 

289import wasmtime # noqa: E402