Coverage for wasmtime/_value.py: 91%
191 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-20 16:25 +0000
« 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
8if typing.TYPE_CHECKING:
9 from ._store import Storelike
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]
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)
28def _unintern(val: int) -> typing.Any:
29 return Val._id_to_extern.get(val)
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] = {}
41 _kind: wasmtime_valkind_t
42 _val: typing.Any
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)
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)
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)
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)
84 @classmethod
85 def externref(cls, extern: typing.Optional[typing.Any]) -> "Val":
86 return Val(WASMTIME_EXTERNREF, extern)
88 @classmethod
89 def funcref(cls, f: "typing.Optional[wasmtime.Func]") -> "Val":
90 return Val(WASMTIME_FUNCREF, f)
92 @classmethod
93 def ref_null(cls, ty: ValType) -> "Val":
94 """
95 Create a null reference value of the given type.
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)
105 def __init__(self, kind: wasmtime_valkind_t, val: typing.Any):
106 self._kind = kind
107 self._val = val
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
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))
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:
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
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))
189 if owned:
190 wasmtime_val_unroot(store._context(), byref(raw))
192 return Val(raw.kind, val)
194 @property
195 def value(self) -> typing.Any:
196 """
197 Get the the underlying value as a python value
198 """
199 return self._val
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
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
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
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
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
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
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)
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