Coverage for wasmtime/_trap.py: 96%

134 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2026-07-20 18:48 +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 = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_STACK_OVERFLOW.value 

12 # An out-of-bounds memory access. 

13 MEMORY_OUT_OF_BOUNDS = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_MEMORY_OUT_OF_BOUNDS.value 

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

15 HEAP_MISALIGNED = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_HEAP_MISALIGNED.value 

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

17 TABLE_OUT_OF_BOUNDS = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_TABLE_OUT_OF_BOUNDS.value 

18 # Indirect call to a null table entry. 

19 INDIRECT_CALL_TO_NULL = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_INDIRECT_CALL_TO_NULL.value 

20 # Signature mismatch on indirect call. 

21 BAD_SIGNATURE = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_BAD_SIGNATURE.value 

22 # An integer arithmetic operation caused an overflow. 

23 INTEGER_OVERFLOW = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_INTEGER_OVERFLOW.value 

24 # An integer division by zero. 

25 INTEGER_DIVISION_BY_ZERO = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_INTEGER_DIVISION_BY_ZERO.value 

26 # Failed float-to-int conversion. 

27 BAD_CONVERSION_TO_INTEGER = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_BAD_CONVERSION_TO_INTEGER.value 

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

29 UNREACHABLE = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_UNREACHABLE_CODE_REACHED.value 

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

31 INTERRUPT = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_INTERRUPT.value 

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

33 OUT_OF_FUEL = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_OUT_OF_FUEL.value 

34 # Atomic wait on non-shared memory. 

35 ATOMIC_WAIT_NON_SHARED_MEMORY = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_ATOMIC_WAIT_NON_SHARED_MEMORY.value 

36 # Call to a null reference. 

37 NULL_REFERENCE = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_NULL_REFERENCE.value 

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

39 ARRAY_OUT_OF_BOUNDS = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_ARRAY_OUT_OF_BOUNDS.value 

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

41 ALLOCATION_TOO_LARGE = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_ALLOCATION_TOO_LARGE.value 

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

43 CAST_FAILURE = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_CAST_FAILURE.value 

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

45 CANNOT_ENTER_COMPONENT = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_CANNOT_ENTER_COMPONENT.value 

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

47 NO_ASYNC_RESULT = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_NO_ASYNC_RESULT.value 

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

49 UNHANDLED_TAG = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_UNHANDLED_TAG.value 

50 # Attempt to resume a continuation twice. 

51 CONTINUATION_ALREADY_CONSUMED = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_CONTINUATION_ALREADY_CONSUMED.value 

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

53 DISABLED_OPCODE = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_DISABLED_OPCODE.value 

54 # Async event loop deadlocked. 

55 ASYNC_DEADLOCK = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_ASYNC_DEADLOCK.value 

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

57 CANNOT_LEAVE_COMPONENT = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_CANNOT_LEAVE_COMPONENT.value 

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

59 CANNOT_BLOCK_SYNC_TASK = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_CANNOT_BLOCK_SYNC_TASK.value 

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

61 INVALID_CHAR = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_INVALID_CHAR.value 

62 # Debug assertion: string encoding not finished. 

63 DEBUG_ASSERT_STRING_ENCODING_FINISHED = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_DEBUG_ASSERT_STRING_ENCODING_FINISHED.value 

64 # Debug assertion: equal code units. 

65 DEBUG_ASSERT_EQUAL_CODE_UNITS = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_DEBUG_ASSERT_EQUAL_CODE_UNITS.value 

66 # Debug assertion: pointer aligned. 

67 DEBUG_ASSERT_POINTER_ALIGNED = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_DEBUG_ASSERT_POINTER_ALIGNED.value 

68 # Debug assertion: upper bits unset. 

69 DEBUG_ASSERT_UPPER_BITS_UNSET = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_DEBUG_ASSERT_UPPER_BITS_UNSET.value 

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

71 STRING_OUT_OF_BOUNDS = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_STRING_OUT_OF_BOUNDS.value 

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

73 LIST_OUT_OF_BOUNDS = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_LIST_OUT_OF_BOUNDS.value 

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

75 INVALID_DISCRIMINANT = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_INVALID_DISCRIMINANT.value 

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

77 UNALIGNED_POINTER = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_UNALIGNED_POINTER.value 

78 # task.cancel invoked in an invalid way. 

79 TASK_CANCEL_NOT_CANCELLED = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_TASK_CANCEL_NOT_CANCELLED.value 

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

81 TASK_CANCEL_OR_RETURN_TWICE = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_TASK_CANCEL_OR_RETURN_TWICE.value 

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

83 SUBTASK_CANCEL_AFTER_TERMINAL = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_SUBTASK_CANCEL_AFTER_TERMINAL.value 

84 # task.return invoked with an invalid type. 

85 TASK_RETURN_INVALID = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_TASK_RETURN_INVALID.value 

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

87 WAITABLE_SET_DROP_HAS_WAITERS = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_WAITABLE_SET_DROP_HAS_WAITERS.value 

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

89 SUBTASK_DROP_NOT_RESOLVED = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_SUBTASK_DROP_NOT_RESOLVED.value 

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

91 THREAD_NEW_INDIRECT_INVALID_TYPE = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_THREAD_NEW_INDIRECT_INVALID_TYPE.value 

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

93 THREAD_NEW_INDIRECT_UNINITIALIZED = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_THREAD_NEW_INDIRECT_UNINITIALIZED.value 

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

95 BACKPRESSURE_OVERFLOW = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_BACKPRESSURE_OVERFLOW.value 

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

97 UNSUPPORTED_CALLBACK_CODE = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_UNSUPPORTED_CALLBACK_CODE.value 

98 # Cannot resume a thread which is not suspended. 

99 CANNOT_RESUME_THREAD = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_CANNOT_RESUME_THREAD.value 

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

101 CONCURRENT_FUTURE_STREAM_OP = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_CONCURRENT_FUTURE_STREAM_OP.value 

102 # A reference count overflowed the built-in counter. 

103 REFERENCE_COUNT_OVERFLOW = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_REFERENCE_COUNT_OVERFLOW.value 

104 # A stream operation was performed with a buffer that was too large. 

105 STREAM_OP_TOO_BIG = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_STREAM_OP_TOO_BIG.value 

106 # A waitable was used both synchronously and asynchronously. 

107 WAITABLE_SYNC_AND_ASYNC = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_WAITABLE_SYNC_AND_ASYNC.value 

108 # An exception was thrown and never caught. 

109 UNCAUGHT_EXCEPTION = ffi.wasmtime_trap_code_enum.WASMTIME_TRAP_CODE_UNCAUGHT_EXCEPTION.value 

110 

111 

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

113 

114 def __init__(self, message: str): 

115 """ 

116 Creates a new trap with the given `message` 

117 """ 

118 

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

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

121 

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

123 ffi.wasm_trap_delete(ptr) 

124 

125 @classmethod 

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

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

128 raise TypeError("wrong pointer type") 

129 trap: Trap = cls.__new__(cls) 

130 trap._set_ptr(ptr) 

131 return trap 

132 

133 @property 

134 def message(self) -> str: 

135 """ 

136 Returns the message for this trap 

137 """ 

138 

139 message = ffi.wasm_byte_vec_t() 

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

141 # subtract one to chop off the trailing nul byte 

142 message.size -= 1 

143 ret = ffi.to_str(message) 

144 message.size += 1 

145 ffi.wasm_byte_vec_delete(byref(message)) 

146 return ret 

147 

148 @property 

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

150 frames = FrameList(self) 

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

152 ret = [] 

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

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

155 return ret 

156 

157 @property 

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

159 """ 

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

161 happened. 

162 

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

164 not have an associated code with them. 

165 """ 

166 code = ffi.wasmtime_trap_code_t() 

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

168 return TrapCode(code.value) 

169 return None 

170 

171 def __str__(self) -> str: 

172 return self.message 

173 

174 

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

176 _owner: Optional[Any] 

177 

178 @classmethod 

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

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

181 raise TypeError("wrong pointer type") 

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

183 ty._set_ptr(ptr) 

184 ty._owner = owner 

185 return ty 

186 

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

188 if self._owner is None: 

189 ffi.wasm_frame_delete(ptr) 

190 

191 @property 

192 def func_index(self) -> int: 

193 """ 

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

195 """ 

196 

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

198 

199 @property 

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

201 """ 

202 Returns the name of the function this frame corresponds to 

203 

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

205 """ 

206 

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

208 if ptr: 

209 return ffi.to_str(ptr.contents) 

210 else: 

211 return None 

212 

213 @property 

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

215 """ 

216 Returns the name of the module this frame corresponds to 

217 

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

219 """ 

220 

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

222 if ptr: 

223 return ffi.to_str(ptr.contents) 

224 else: 

225 return None 

226 

227 @property 

228 def module_offset(self) -> int: 

229 """ 

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

231 wasm source module. 

232 """ 

233 

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

235 

236 @property 

237 def func_offset(self) -> int: 

238 """ 

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

240 wasm function. 

241 """ 

242 

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

244 

245 

246class FrameList: 

247 owner: Any 

248 

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

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

251 self.owner = owner 

252 

253 def __del__(self) -> None: 

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