Coverage for wasmtime/_trap.py: 94%

97 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-02-20 16:25 +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 

33 

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

35 

36 def __init__(self, message: str): 

37 """ 

38 Creates a new trap with the given `message` 

39 """ 

40 

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

42 self._set_ptr(ffi.wasmtime_trap_new(ffi.create_string_buffer(vec), len(vec))) 

43 

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

45 ffi.wasm_trap_delete(ptr) 

46 

47 @classmethod 

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

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

50 raise TypeError("wrong pointer type") 

51 trap: Trap = cls.__new__(cls) 

52 trap._set_ptr(ptr) 

53 return trap 

54 

55 @property 

56 def message(self) -> str: 

57 """ 

58 Returns the message for this trap 

59 """ 

60 

61 message = ffi.wasm_byte_vec_t() 

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

63 # subtract one to chop off the trailing nul byte 

64 message.size -= 1 

65 ret = ffi.to_str(message) 

66 message.size += 1 

67 ffi.wasm_byte_vec_delete(byref(message)) 

68 return ret 

69 

70 @property 

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

72 frames = FrameList(self) 

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

74 ret = [] 

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

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

77 return ret 

78 

79 @property 

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

81 """ 

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

83 happened. 

84 

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

86 not have an associated code with them. 

87 """ 

88 code = ffi.wasmtime_trap_code_t() 

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

90 return TrapCode(code.value) 

91 return None 

92 

93 def __str__(self) -> str: 

94 return self.message 

95 

96 

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

98 _owner: Optional[Any] 

99 

100 @classmethod 

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

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

103 raise TypeError("wrong pointer type") 

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

105 ty._set_ptr(ptr) 

106 ty._owner = owner 

107 return ty 

108 

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

110 if self._owner is None: 

111 ffi.wasm_frame_delete(ptr) 

112 

113 @property 

114 def func_index(self) -> int: 

115 """ 

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

117 """ 

118 

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

120 

121 @property 

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

123 """ 

124 Returns the name of the function this frame corresponds to 

125 

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

127 """ 

128 

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

130 if ptr: 

131 return ffi.to_str(ptr.contents) 

132 else: 

133 return None 

134 

135 @property 

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

137 """ 

138 Returns the name of the module this frame corresponds to 

139 

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

141 """ 

142 

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

144 if ptr: 

145 return ffi.to_str(ptr.contents) 

146 else: 

147 return None 

148 

149 @property 

150 def module_offset(self) -> int: 

151 """ 

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

153 wasm source module. 

154 """ 

155 

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

157 

158 @property 

159 def func_offset(self) -> int: 

160 """ 

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

162 wasm function. 

163 """ 

164 

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

166 

167 

168class FrameList: 

169 owner: Any 

170 

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

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

173 self.owner = owner 

174 

175 def __del__(self) -> None: 

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