Coverage for wasmtime/_trap.py: 95%

130 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2026-05-07 14:30 +0000

1from . import _ffi as ffi 

2from enum import Enum 

3from ctypes import byref, POINTER 

4import ctypes 

5from typing import Optional, Any, List 

6from wasmtime import Managed 

7 

8 

9class TrapCode(Enum): 

10 # The current stack space was exhausted. 

11 STACK_OVERFLOW = 0 

12 # An out-of-bounds memory access. 

13 MEMORY_OUT_OF_BOUNDS = 1 

14 # A wasm atomic operation was presented with a not-naturally-aligned linear-memory address. 

15 HEAP_MISALIGNED = 2 

16 # An out-of-bounds access to a table. 

17 TABLE_OUT_OF_BOUNDS = 3 

18 # Indirect call to a null table entry. 

19 INDIRECT_CALL_TO_NULL = 4 

20 # Signature mismatch on indirect call. 

21 BAD_SIGNATURE = 5 

22 # An integer arithmetic operation caused an overflow. 

23 INTEGER_OVERFLOW = 6 

24 # An integer division by zero. 

25 INTEGER_DIVISION_BY_ZERO = 7 

26 # Failed float-to-int conversion. 

27 BAD_CONVERSION_TO_INTEGER = 8 

28 # Code that was supposed to have been unreachable was reached. 

29 UNREACHABLE = 9 

30 # Execution has potentially run too long and may be interrupted. 

31 INTERRUPT = 10 

32 # Execution has run out of the configured fuel amount. 

33 OUT_OF_FUEL = 11 

34 # Atomic wait on non-shared memory. 

35 ATOMIC_WAIT_NON_SHARED_MEMORY = 12 

36 # Call to a null reference. 

37 NULL_REFERENCE = 13 

38 # Attempt to access beyond the bounds of an array. 

39 ARRAY_OUT_OF_BOUNDS = 14 

40 # Attempted an allocation that was too large to succeed. 

41 ALLOCATION_TOO_LARGE = 15 

42 # Attempted to cast a reference to a type that it is not an instance of. 

43 CAST_FAILURE = 16 

44 # A component tried to call another component in violation of the reentrance rules. 

45 CANNOT_ENTER_COMPONENT = 17 

46 # Async-lifted export failed to produce a result before returning STATUS_DONE. 

47 NO_ASYNC_RESULT = 18 

48 # Suspending to a tag for which there is no active handler. 

49 UNHANDLED_TAG = 19 

50 # Attempt to resume a continuation twice. 

51 CONTINUATION_ALREADY_CONSUMED = 20 

52 # A Pulley opcode was executed that was disabled at compile time. 

53 DISABLED_OPCODE = 21 

54 # Async event loop deadlocked. 

55 ASYNC_DEADLOCK = 22 

56 # A component tried to call an import when it was not allowed to. 

57 CANNOT_LEAVE_COMPONENT = 23 

58 # A synchronous task attempted a potentially blocking call before returning. 

59 CANNOT_BLOCK_SYNC_TASK = 24 

60 # A component tried to lift a char with an invalid bit pattern. 

61 INVALID_CHAR = 25 

62 # Debug assertion: string encoding not finished. 

63 DEBUG_ASSERT_STRING_ENCODING_FINISHED = 26 

64 # Debug assertion: equal code units. 

65 DEBUG_ASSERT_EQUAL_CODE_UNITS = 27 

66 # Debug assertion: pointer aligned. 

67 DEBUG_ASSERT_POINTER_ALIGNED = 28 

68 # Debug assertion: upper bits unset. 

69 DEBUG_ASSERT_UPPER_BITS_UNSET = 29 

70 # A component tried to lift or lower a string past the end of its memory. 

71 STRING_OUT_OF_BOUNDS = 30 

72 # A component tried to lift or lower a list past the end of its memory. 

73 LIST_OUT_OF_BOUNDS = 31 

74 # A component used an invalid discriminant when lowering a variant value. 

75 INVALID_DISCRIMINANT = 32 

76 # A component passed an unaligned pointer when lifting or lowering a value. 

77 UNALIGNED_POINTER = 33 

78 # task.cancel invoked in an invalid way. 

79 TASK_CANCEL_NOT_CANCELLED = 34 

80 # task.cancel or task.return called too many times. 

81 TASK_CANCEL_OR_RETURN_TWICE = 35 

82 # subtask.cancel invoked after the subtask already finished. 

83 SUBTASK_CANCEL_AFTER_TERMINAL = 36 

84 # task.return invoked with an invalid type. 

85 TASK_RETURN_INVALID = 37 

86 # waitable-set.drop invoked on a waitable set with waiters. 

87 WAITABLE_SET_DROP_HAS_WAITERS = 38 

88 # subtask.drop invoked on a subtask that hasn't resolved yet. 

89 SUBTASK_DROP_NOT_RESOLVED = 39 

90 # thread.new-indirect invoked with a function that has an invalid type. 

91 THREAD_NEW_INDIRECT_INVALID_TYPE = 40 

92 # thread.new-indirect invoked with an uninitialized function reference. 

93 THREAD_NEW_INDIRECT_UNINITIALIZED = 41 

94 # Backpressure-related intrinsics overflowed the built-in counter. 

95 BACKPRESSURE_OVERFLOW = 42 

96 # Invalid code returned from the callback of an async-lifted function. 

97 UNSUPPORTED_CALLBACK_CODE = 43 

98 # Cannot resume a thread which is not suspended. 

99 CANNOT_RESUME_THREAD = 44 

100 # Cannot issue a read/write on a future/stream while there is a pending operation. 

101 CONCURRENT_FUTURE_STREAM_OP = 45 

102 

103 

104class Trap(Exception, Managed["ctypes._Pointer[ffi.wasm_trap_t]"]): 

105 

106 def __init__(self, message: str): 

107 """ 

108 Creates a new trap with the given `message` 

109 """ 

110 

111 vec = message.encode('utf-8') 

112 self._set_ptr(ffi.wasmtime_trap_new(ctypes.create_string_buffer(vec), len(vec))) 

113 

114 def _delete(self, ptr: "ctypes._Pointer[ffi.wasm_trap_t]") -> None: 

115 ffi.wasm_trap_delete(ptr) 

116 

117 @classmethod 

118 def _from_ptr(cls, ptr: "ctypes._Pointer[ffi.wasm_trap_t]") -> "Trap": 

119 if not isinstance(ptr, POINTER(ffi.wasm_trap_t)): 

120 raise TypeError("wrong pointer type") 

121 trap: Trap = cls.__new__(cls) 

122 trap._set_ptr(ptr) 

123 return trap 

124 

125 @property 

126 def message(self) -> str: 

127 """ 

128 Returns the message for this trap 

129 """ 

130 

131 message = ffi.wasm_byte_vec_t() 

132 ffi.wasm_trap_message(self.ptr(), byref(message)) 

133 # subtract one to chop off the trailing nul byte 

134 message.size -= 1 

135 ret = ffi.to_str(message) 

136 message.size += 1 

137 ffi.wasm_byte_vec_delete(byref(message)) 

138 return ret 

139 

140 @property 

141 def frames(self) -> List["Frame"]: 

142 frames = FrameList(self) 

143 ffi.wasm_trap_trace(self.ptr(), byref(frames.vec)) 

144 ret = [] 

145 for i in range(0, frames.vec.size): 

146 ret.append(Frame._from_ptr(frames.vec.data[i], frames)) 

147 return ret 

148 

149 @property 

150 def trap_code(self) -> Optional[TrapCode]: 

151 """ 

152 Returns an optional `TrapCode` that corresponds to why this trap 

153 happened. 

154 

155 Note that `None` may be returned for manually created traps which do 

156 not have an associated code with them. 

157 """ 

158 code = ffi.wasmtime_trap_code_t() 

159 if ffi.wasmtime_trap_code(self.ptr(), byref(code)): 

160 return TrapCode(code.value) 

161 return None 

162 

163 def __str__(self) -> str: 

164 return self.message 

165 

166 

167class Frame(Managed["ctypes._Pointer[ffi.wasm_frame_t]"]): 

168 _owner: Optional[Any] 

169 

170 @classmethod 

171 def _from_ptr(cls, ptr: "ctypes._Pointer[ffi.wasm_frame_t]", owner: Optional[Any]) -> "Frame": 

172 if not isinstance(ptr, POINTER(ffi.wasm_frame_t)): 

173 raise TypeError("wrong pointer type") 

174 ty: "Frame" = cls.__new__(cls) 

175 ty._set_ptr(ptr) 

176 ty._owner = owner 

177 return ty 

178 

179 def _delete(self, ptr: "ctypes._Pointer[ffi.wasm_frame_t]") -> None: 

180 if self._owner is None: 

181 ffi.wasm_frame_delete(ptr) 

182 

183 @property 

184 def func_index(self) -> int: 

185 """ 

186 Returns the function index this frame corresponds to in its wasm module 

187 """ 

188 

189 return ffi.wasm_frame_func_index(self.ptr()) 

190 

191 @property 

192 def func_name(self) -> Optional[str]: 

193 """ 

194 Returns the name of the function this frame corresponds to 

195 

196 May return `None` if no name can be inferred 

197 """ 

198 

199 ptr = ffi.wasmtime_frame_func_name(self.ptr()) 

200 if ptr: 

201 return ffi.to_str(ptr.contents) 

202 else: 

203 return None 

204 

205 @property 

206 def module_name(self) -> Optional[str]: 

207 """ 

208 Returns the name of the module this frame corresponds to 

209 

210 May return `None` if no name can be inferred 

211 """ 

212 

213 ptr = ffi.wasmtime_frame_module_name(self.ptr()) 

214 if ptr: 

215 return ffi.to_str(ptr.contents) 

216 else: 

217 return None 

218 

219 @property 

220 def module_offset(self) -> int: 

221 """ 

222 Returns the offset of this frame's program counter into the original 

223 wasm source module. 

224 """ 

225 

226 return ffi.wasm_frame_module_offset(self.ptr()) 

227 

228 @property 

229 def func_offset(self) -> int: 

230 """ 

231 Returns the offset of this frame's program counter into the original 

232 wasm function. 

233 """ 

234 

235 return ffi.wasm_frame_func_offset(self.ptr()) 

236 

237 

238class FrameList: 

239 owner: Any 

240 

241 def __init__(self, owner: Any) -> None: 

242 self.vec = ffi.wasm_frame_vec_t(0, None) 

243 self.owner = owner 

244 

245 def __del__(self) -> None: 

246 ffi.wasm_frame_vec_delete(byref(self.vec))