Coverage for tests/test_func.py: 100%

135 statements  

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

1import unittest 

2 

3from wasmtime import * 

4 

5 

6class TestFunc(unittest.TestCase): 

7 def test_smoke(self): 

8 store = Store() 

9 ty = FuncType([], []) 

10 func = Func(store, ty, lambda: None) 

11 func(store) 

12 self.assertTrue(isinstance(func.type(store), FuncType)) 

13 

14 def test_add(self): 

15 store = Store() 

16 ty = FuncType([ValType.i32(), ValType.i32()], [ValType.i32()]) 

17 func = Func(store, ty, lambda a, b: a + b) 

18 self.assertEqual(func(store, 1, 2), 3) 

19 

20 def test_calls(self): 

21 store = Store() 

22 ty = FuncType([ValType.i32()], []) 

23 func = Func(store, ty, lambda a: self.assertEqual(a, 1)) 

24 func(store, 1) 

25 

26 ty = FuncType([ValType.i64()], []) 

27 func = Func(store, ty, lambda a: self.assertEqual(a, 2)) 

28 func(store, Val.i64(2)) 

29 

30 ty = FuncType([ValType.f32()], []) 

31 func = Func(store, ty, lambda a: self.assertEqual(a, 3.0)) 

32 func(store, 3.0) 

33 

34 ty = FuncType([ValType.f64()], []) 

35 func = Func(store, ty, lambda a: self.assertEqual(a, 4.0)) 

36 func(store, 4.0) 

37 

38 def test_multi_return(self): 

39 store = Store() 

40 ty = FuncType([], [ValType.i32(), ValType.i32()]) 

41 func = Func(store, ty, lambda: [1, 2]) 

42 self.assertEqual(func(store), [1, 2]) 

43 

44 def test_errors(self): 

45 store = Store() 

46 ty = FuncType([], []) 

47 with self.assertRaises(TypeError): 

48 Func(1, ty, lambda: None) # type: ignore 

49 with self.assertRaises(TypeError): 

50 Func(store, 1, lambda: None) # type: ignore 

51 func = Func(store, ty, lambda: None) 

52 with self.assertRaises(WasmtimeError): 

53 func(store, 2) 

54 

55 ty = FuncType([ValType.i32()], []) 

56 func = Func(store, ty, lambda: None) 

57 with self.assertRaises(TypeError): 

58 func(store, 3.0) 

59 with self.assertRaises(TypeError): 

60 func(store, Val.i64(3)) 

61 ty = FuncType([ValType.i32()], []) 

62 

63 func = Func(store, ty, lambda x: x) 

64 with self.assertRaises(WasmtimeError, msg="produced results"): 

65 func(store, 1) 

66 

67 def test_produce_wrong(self): 

68 store = Store() 

69 ty = FuncType([], [ValType.i32(), ValType.i32()]) 

70 func = Func(store, ty, lambda: 1) 

71 with self.assertRaises(TypeError, msg="has no len"): 

72 func(store) 

73 func = Func(store, ty, lambda: [1, 2, 3]) 

74 with self.assertRaises(WasmtimeError, msg="wrong number of results"): 

75 func(store) 

76 

77 def test_host_exception(self): 

78 store = Store() 

79 ty = FuncType([], []) 

80 

81 def do_raise(): 

82 raise Exception("hello") 

83 

84 func = Func(store, ty, do_raise) 

85 with self.assertRaises(Exception, msg="hello"): 

86 func(store) 

87 

88 def test_host_base_exception(self): 

89 # Regression test for #336: a BaseException subclass raised from a 

90 # host callback (KeyboardInterrupt, SystemExit, custom BaseException 

91 # subclasses) used to escape the trampoline's `except Exception` 

92 # handler and abort the process inside Rust's array_call_trampoline 

93 # with a libmalloc SIGABRT. It must propagate cleanly to the caller. 

94 store = Store() 

95 ty = FuncType([], []) 

96 

97 def raise_keyboard_interrupt(): 

98 raise KeyboardInterrupt 

99 

100 func = Func(store, ty, raise_keyboard_interrupt) 

101 with self.assertRaises(KeyboardInterrupt): 

102 func(store) 

103 

104 def raise_system_exit(): 

105 raise SystemExit(0) 

106 

107 func = Func(store, ty, raise_system_exit) 

108 with self.assertRaises(SystemExit): 

109 func(store) 

110 

111 class CustomBaseException(BaseException): 

112 pass 

113 

114 def raise_custom(): 

115 raise CustomBaseException("custom") 

116 

117 func = Func(store, ty, raise_custom) 

118 with self.assertRaises(CustomBaseException): 

119 func(store) 

120 

121 def test_type(self): 

122 store = Store() 

123 i32 = ValType.i32() 

124 i64 = ValType.i64() 

125 f32 = ValType.f32() 

126 f64 = ValType.f64() 

127 ty = FuncType([i32, i64, f32, f64], [f64, f32, i64, i32]) 

128 

129 def rev(*args): 

130 ret = list(args) 

131 ret.reverse() 

132 return ret 

133 

134 func = Func(store, ty, rev) 

135 self.assertEqual(func(store, 1, 2, 3.0, 4.0), [4.0, 3.0, 2, 1]) 

136 

137 def test_access_caller(self): 

138 # Test that we get *something* 

139 store = Store() 

140 

141 def runtest(caller): 

142 self.assertEqual(caller.get(''), None) 

143 self.assertEqual(caller.get('x'), None) 

144 self.assertEqual(caller.get('y'), None) 

145 

146 Func(store, FuncType([], []), runtest, access_caller=True)(store) 

147 

148 hit = {} 

149 

150 # Test that `Caller` works and that it's invalidated 

151 def runtest2(caller): 

152 hit['yes'] = True 

153 hit['caller'] = caller 

154 

155 self.assertTrue(caller.get('bar') is None) 

156 mem = caller.get('foo') 

157 self.assertTrue(isinstance(mem, Memory)) 

158 

159 self.assertEqual(mem.data_ptr(caller)[0], ord('f')) 

160 self.assertEqual(mem.data_ptr(caller)[1], ord('o')) 

161 self.assertEqual(mem.data_ptr(caller)[2], ord('o')) 

162 self.assertEqual(mem.data_ptr(caller)[3], 0) 

163 

164 module = Module(store.engine, """ 

165 (module 

166 (import "" "" (func)) 

167 (memory (export "foo") 1) 

168 (start 0) 

169 (data (i32.const 0) "foo") 

170 ) 

171 """) 

172 func = Func(store, FuncType([], []), runtest2, access_caller=True) 

173 Instance(store, module, [func]) 

174 self.assertTrue(hit['yes']) 

175 self.assertTrue(hit['caller'].get('foo') is None) # type: ignore 

176 

177 # Test that `Caller` is invalidated even on exceptions 

178 hit2 = {} 

179 

180 def runtest3(caller): 

181 hit2['caller'] = caller 

182 self.assertTrue(caller['foo'] is not None) 

183 raise WasmtimeError('foo') 

184 

185 func = Func(store, FuncType([], []), runtest3, access_caller=True) 

186 with self.assertRaises(WasmtimeError, msg='foo'): 

187 Instance(store, module, [func]) 

188 self.assertTrue(hit2['caller'].get('foo') is None)