Coverage for wasmtime/_value.py: 91%
180 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-12-01 19:40 +0000
« 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
7from . import _ffi as ffi
9if typing.TYPE_CHECKING:
10 from ._store import Storelike
13SLAB: Slab[typing.Any] = Slab()
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)
23def _intern(obj: typing.Any) -> int:
24 return SLAB.allocate(obj) + 1
27def _unintern(idx: int) -> typing.Any:
28 return SLAB.get(idx - 1)
31class Val:
32 _kind: ffi.wasmtime_valkind_t
33 _val: typing.Any
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)
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)
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)
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)
75 @classmethod
76 def externref(cls, extern: typing.Optional[typing.Any]) -> "Val":
77 return Val(ffi.WASMTIME_EXTERNREF, extern)
79 @classmethod
80 def funcref(cls, f: "typing.Optional[wasmtime.Func]") -> "Val":
81 return Val(ffi.WASMTIME_FUNCREF, f)
83 @classmethod
84 def ref_null(cls, ty: ValType) -> "Val":
85 """
86 Create a null reference value of the given type.
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)
96 def __init__(self, kind: ffi.wasmtime_valkind_t, val: typing.Any):
97 self._kind = kind
98 self._val = val
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
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))
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:
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
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))
181 if owned:
182 ffi.wasmtime_val_unroot(ctypes.byref(raw))
184 return Val(raw.kind, val)
186 @property
187 def value(self) -> typing.Any:
188 """
189 Get the the underlying value as a python value
190 """
191 return self._val
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
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
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
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
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
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
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)
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