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
« prev ^ index » next coverage.py v7.11.3, created at 2026-05-07 14:30 +0000
1import unittest
3from wasmtime import *
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))
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)
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)
26 ty = FuncType([ValType.i64()], [])
27 func = Func(store, ty, lambda a: self.assertEqual(a, 2))
28 func(store, Val.i64(2))
30 ty = FuncType([ValType.f32()], [])
31 func = Func(store, ty, lambda a: self.assertEqual(a, 3.0))
32 func(store, 3.0)
34 ty = FuncType([ValType.f64()], [])
35 func = Func(store, ty, lambda a: self.assertEqual(a, 4.0))
36 func(store, 4.0)
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])
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)
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()], [])
63 func = Func(store, ty, lambda x: x)
64 with self.assertRaises(WasmtimeError, msg="produced results"):
65 func(store, 1)
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)
77 def test_host_exception(self):
78 store = Store()
79 ty = FuncType([], [])
81 def do_raise():
82 raise Exception("hello")
84 func = Func(store, ty, do_raise)
85 with self.assertRaises(Exception, msg="hello"):
86 func(store)
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([], [])
97 def raise_keyboard_interrupt():
98 raise KeyboardInterrupt
100 func = Func(store, ty, raise_keyboard_interrupt)
101 with self.assertRaises(KeyboardInterrupt):
102 func(store)
104 def raise_system_exit():
105 raise SystemExit(0)
107 func = Func(store, ty, raise_system_exit)
108 with self.assertRaises(SystemExit):
109 func(store)
111 class CustomBaseException(BaseException):
112 pass
114 def raise_custom():
115 raise CustomBaseException("custom")
117 func = Func(store, ty, raise_custom)
118 with self.assertRaises(CustomBaseException):
119 func(store)
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])
129 def rev(*args):
130 ret = list(args)
131 ret.reverse()
132 return ret
134 func = Func(store, ty, rev)
135 self.assertEqual(func(store, 1, 2, 3.0, 4.0), [4.0, 3.0, 2, 1])
137 def test_access_caller(self):
138 # Test that we get *something*
139 store = Store()
141 def runtest(caller):
142 self.assertEqual(caller.get(''), None)
143 self.assertEqual(caller.get('x'), None)
144 self.assertEqual(caller.get('y'), None)
146 Func(store, FuncType([], []), runtest, access_caller=True)(store)
148 hit = {}
150 # Test that `Caller` works and that it's invalidated
151 def runtest2(caller):
152 hit['yes'] = True
153 hit['caller'] = caller
155 self.assertTrue(caller.get('bar') is None)
156 mem = caller.get('foo')
157 self.assertTrue(isinstance(mem, Memory))
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)
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
177 # Test that `Caller` is invalidated even on exceptions
178 hit2 = {}
180 def runtest3(caller):
181 hit2['caller'] = caller
182 self.assertTrue(caller['foo'] is not None)
183 raise WasmtimeError('foo')
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)