Coverage for wasmtime/_ffi.py: 91%

136 statements  

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

1from ctypes import POINTER, Structure, Union, addressof, byref, cdll, string_at 

2from ctypes import c_double, c_float, c_int32, c_int64, c_uint8 

3from pathlib import Path 

4import ctypes 

5import sys 

6import platform 

7import typing 

8 

9if sys.maxsize <= 2**32: 

10 raise RuntimeError("wasmtime only works on 64-bit platforms right now") 

11 

12sys_platform = sys.platform 

13 

14# For Python versions <=3.12. 3.13+ supports PEP 738 and uses sys.platform 

15if hasattr(sys, 'getandroidapilevel'): 

16 sys_platform = 'android' 

17 

18if sys_platform == 'linux' or sys_platform == 'android': 

19 libname = '_libwasmtime.so' 

20elif sys_platform == 'win32': 

21 libname = '_wasmtime.dll' 

22elif sys_platform == 'darwin': 

23 libname = '_libwasmtime.dylib' 

24else: 

25 raise RuntimeError("unsupported platform `{}` for wasmtime".format(sys_platform)) 

26 

27 

28machine = platform.machine() 

29if machine == 'AMD64': 

30 machine = 'x86_64' 

31if machine == 'arm64' or machine == 'ARM64': 

32 machine = 'aarch64' 

33if machine != 'x86_64' and machine != 'aarch64': 

34 raise RuntimeError("unsupported architecture for wasmtime: {}".format(machine)) 

35 

36filename = Path(__file__).parent / (sys_platform + '-' + machine) / libname 

37 

38dll = cdll.LoadLibrary(str(filename)) 

39 

40WASM_I32 = c_uint8(0) 

41WASM_I64 = c_uint8(1) 

42WASM_F32 = c_uint8(2) 

43WASM_F64 = c_uint8(3) 

44WASM_ANYREF = c_uint8(128) 

45WASM_FUNCREF = c_uint8(129) 

46# WASM_V128 = c_uint8(4) 

47 

48WASMTIME_I32 = c_uint8(0) 

49WASMTIME_I64 = c_uint8(1) 

50WASMTIME_F32 = c_uint8(2) 

51WASMTIME_F64 = c_uint8(3) 

52WASMTIME_V128 = c_uint8(4) 

53WASMTIME_FUNCREF = c_uint8(5) 

54WASMTIME_EXTERNREF = c_uint8(6) 

55 

56WASM_CONST = c_uint8(0) 

57WASM_VAR = c_uint8(1) 

58 

59WASMTIME_EXTERN_FUNC = c_uint8(0) 

60WASMTIME_EXTERN_GLOBAL = c_uint8(1) 

61WASMTIME_EXTERN_TABLE = c_uint8(2) 

62WASMTIME_EXTERN_MEMORY = c_uint8(3) 

63WASMTIME_EXTERN_SHAREDMEMORY = c_uint8(4) 

64 

65WASMTIME_FUNCREF_NULL = (1 << 64) - 1 

66 

67WASMTIME_COMPONENT_ITEM_COMPONENT = c_uint8(0) 

68WASMTIME_COMPONENT_ITEM_COMPONENT_INSTANCE = c_uint8(1) 

69WASMTIME_COMPONENT_ITEM_MODULE = c_uint8(2) 

70WASMTIME_COMPONENT_ITEM_COMPONENT_FUNC = c_uint8(3) 

71WASMTIME_COMPONENT_ITEM_RESOURCE = c_uint8(4) 

72WASMTIME_COMPONENT_ITEM_CORE_FUNC = c_uint8(5) 

73WASMTIME_COMPONENT_ITEM_TYPE = c_uint8(6) 

74 

75WASMTIME_COMPONENT_VALTYPE_BOOL = c_uint8(0) 

76WASMTIME_COMPONENT_VALTYPE_S8 = c_uint8(1) 

77WASMTIME_COMPONENT_VALTYPE_S16 = c_uint8(2) 

78WASMTIME_COMPONENT_VALTYPE_S32 = c_uint8(3) 

79WASMTIME_COMPONENT_VALTYPE_S64 = c_uint8(4) 

80WASMTIME_COMPONENT_VALTYPE_U8 = c_uint8(5) 

81WASMTIME_COMPONENT_VALTYPE_U16 = c_uint8(6) 

82WASMTIME_COMPONENT_VALTYPE_U32 = c_uint8(7) 

83WASMTIME_COMPONENT_VALTYPE_U64 = c_uint8(8) 

84WASMTIME_COMPONENT_VALTYPE_F32 = c_uint8(9) 

85WASMTIME_COMPONENT_VALTYPE_F64 = c_uint8(10) 

86WASMTIME_COMPONENT_VALTYPE_CHAR = c_uint8(11) 

87WASMTIME_COMPONENT_VALTYPE_STRING = c_uint8(12) 

88WASMTIME_COMPONENT_VALTYPE_LIST = c_uint8(13) 

89WASMTIME_COMPONENT_VALTYPE_RECORD = c_uint8(14) 

90WASMTIME_COMPONENT_VALTYPE_TUPLE = c_uint8(15) 

91WASMTIME_COMPONENT_VALTYPE_VARIANT = c_uint8(16) 

92WASMTIME_COMPONENT_VALTYPE_ENUM = c_uint8(17) 

93WASMTIME_COMPONENT_VALTYPE_OPTION = c_uint8(18) 

94WASMTIME_COMPONENT_VALTYPE_RESULT = c_uint8(19) 

95WASMTIME_COMPONENT_VALTYPE_FLAGS = c_uint8(20) 

96WASMTIME_COMPONENT_VALTYPE_OWN = c_uint8(21) 

97WASMTIME_COMPONENT_VALTYPE_BORROW = c_uint8(22) 

98WASMTIME_COMPONENT_VALTYPE_FUTURE = c_uint8(23) 

99WASMTIME_COMPONENT_VALTYPE_STREAM = c_uint8(24) 

100WASMTIME_COMPONENT_VALTYPE_ERROR_CONTEXT = c_uint8(25) 

101 

102WASMTIME_COMPONENT_BOOL = c_uint8(0) 

103WASMTIME_COMPONENT_S8 = c_uint8(1) 

104WASMTIME_COMPONENT_U8 = c_uint8(2) 

105WASMTIME_COMPONENT_S16 = c_uint8(3) 

106WASMTIME_COMPONENT_U16 = c_uint8(4) 

107WASMTIME_COMPONENT_S32 = c_uint8(5) 

108WASMTIME_COMPONENT_U32 = c_uint8(6) 

109WASMTIME_COMPONENT_S64 = c_uint8(7) 

110WASMTIME_COMPONENT_U64 = c_uint8(8) 

111WASMTIME_COMPONENT_F32 = c_uint8(9) 

112WASMTIME_COMPONENT_F64 = c_uint8(10) 

113WASMTIME_COMPONENT_CHAR = c_uint8(11) 

114WASMTIME_COMPONENT_STRING = c_uint8(12) 

115WASMTIME_COMPONENT_LIST = c_uint8(13) 

116WASMTIME_COMPONENT_RECORD = c_uint8(14) 

117WASMTIME_COMPONENT_TUPLE = c_uint8(15) 

118WASMTIME_COMPONENT_VARIANT = c_uint8(16) 

119WASMTIME_COMPONENT_ENUM = c_uint8(17) 

120WASMTIME_COMPONENT_OPTION = c_uint8(18) 

121WASMTIME_COMPONENT_RESULT = c_uint8(19) 

122WASMTIME_COMPONENT_FLAGS = c_uint8(20) 

123WASMTIME_COMPONENT_RESOURCE = c_uint8(21) 

124 

125class wasm_ref_t(Structure): 

126 pass 

127 

128 

129class wasm_val_union(Union): 

130 _fields_ = [ 

131 ("i32", c_int32), 

132 ("i64", c_int64), 

133 ("f32", c_float), 

134 ("f64", c_double), 

135 ("ref", POINTER(wasm_ref_t)), 

136 ] 

137 

138 i32: int 

139 i64: int 

140 f32: float 

141 f64: float 

142 ref: "typing.Union[ctypes._Pointer[wasm_ref_t], None]" 

143 

144 

145class wasm_val_t(Structure): 

146 _fields_ = [("kind", c_uint8), ("of", wasm_val_union)] 

147 

148 kind: int 

149 of: wasm_val_union 

150 

151 

152from ._bindings import * # noqa 

153 

154 

155def to_bytes(vec: wasm_byte_vec_t) -> bytearray: 

156 ty = c_uint8 * vec.size 

157 return bytearray(ty.from_address(addressof(vec.data.contents))) 

158 

159def to_str(vec: wasm_byte_vec_t) -> str: 

160 return to_bytes(vec).decode("utf-8") 

161 

162 

163def to_str_raw(ptr: "ctypes._Pointer", size: int) -> str: 

164 return string_at(ptr, size).decode("utf-8") 

165 

166 

167def str_to_capi(s: str) -> wasm_byte_vec_t: 

168 if not isinstance(s, str): 

169 raise TypeError("expected a string") 

170 return bytes_to_capi(s.encode('utf8')) 

171 

172def bytes_to_capi(s: typing.Union[bytes, bytearray]) -> wasm_byte_vec_t: 

173 if not isinstance(s, (bytes, bytearray)): 

174 raise TypeError("expected bytes or bytearray") 

175 vec = wasm_byte_vec_t() 

176 wasm_byte_vec_new_uninitialized(byref(vec), len(s)) 

177 buf = (c_uint8 * len(s)).from_buffer_copy(s) 

178 ctypes.memmove(vec.data, buf, len(s)) 

179 return vec 

180 

181def take_pointer(structure: ctypes._Pointer, field_name: str) -> ctypes._Pointer: 

182 """ 

183 Moral equivalent of `mem::replace(&mut structure.field_name, NULL)` 

184 

185 Ctypes explicitly documents "Surprises" which includes, for example: 

186 

187 import ctypes 

188 

189 class A(ctypes.Structure): 

190 _fields_ = [("x", ctypes.POINTER(ctypes.c_int))] 

191 

192 x_p = ctypes.pointer(ctypes.c_int(3)) 

193 a = A(x_p) 

194 x = a.x 

195 

196 print(x.contents) 

197 a.x = None 

198 print(x.contents) 

199 

200 This program will segfault on the second access. It turns out that `x = a.x` 

201 is still actually a pointer into the original structure, and `a.x` 

202 overwrites that field so accessing `x` later accesses null memory. This 

203 method is an attempt to work around this surprising behavior and actually 

204 read the field from a structure and replace it with null. 

205 

206 I'll be honest I just sat through a 3 hour flight, a 5 hour layover, a 9 

207 hour flight, 1 hour train ride, and I'm sitting in a hotel lobby for 

208 another 5 hours. That's my state of mind writing this up, so please 

209 draw conclusions about this method as appropriate. 

210 """ 

211 field = getattr(structure, field_name) 

212 assert(isinstance(field, ctypes._Pointer)) 

213 ret = ctypes.cast(ctypes.addressof(field.contents), ctypes.POINTER(field._type_)) 

214 setattr(structure, field_name, None) 

215 return ret