1 #include <gtest/gtest.h>
2 #include <wasmtime.hh>
3
4 using namespace wasmtime;
5
make_engine()6 static Engine make_engine() {
7 Config config;
8 config.wasm_exceptions(true);
9 return Engine(std::move(config));
10 }
11
TEST(Exception,ConstructAndExamine)12 TEST(Exception, ConstructAndExamine) {
13 auto engine = make_engine();
14 Store store(engine);
15 auto cx = store.context();
16
17 // Create a tag type with (i32, i64) payload.
18 FuncType ft({ValKind::I32, ValKind::I64}, {});
19 TagType tt(ft);
20
21 // Create a tag instance.
22 auto tag = Tag::create(cx, tt).unwrap();
23
24 // Create an exception with payload (42, 100).
25 std::vector<Val> fields = {Val(int32_t(42)), Val(int64_t(100))};
26 auto exn = Exn::create(cx, tag, fields).unwrap();
27
28 // Read back the tag and verify identity.
29 auto exn_tag = exn.tag(cx).unwrap();
30 EXPECT_TRUE(tag.eq(cx, exn_tag));
31
32 // Read field count.
33 EXPECT_EQ(exn.field_count(cx), 2u);
34
35 // Read fields.
36 auto f0 = exn.field(cx, 0).unwrap();
37 EXPECT_EQ(f0.kind(), ValKind::I32);
38 EXPECT_EQ(f0.i32(), 42);
39
40 auto f1 = exn.field(cx, 1).unwrap();
41 EXPECT_EQ(f1.kind(), ValKind::I64);
42 EXPECT_EQ(f1.i64(), 100);
43 }
44
TEST(Exception,TagFromModule)45 TEST(Exception, TagFromModule) {
46 auto engine = make_engine();
47 Store store(engine);
48 auto cx = store.context();
49
50 Module module = Module::compile(engine, "(module"
51 " (tag $t (param i32))"
52 " (export \"t\" (tag $t))"
53 ")")
54 .unwrap();
55
56 auto instance = Instance::create(cx, module, {}).unwrap();
57 auto ext = instance.get(cx, "t");
58 ASSERT_TRUE(ext.has_value());
59 auto tag = std::get<Tag>(*ext);
60
61 auto tt = tag.type(cx);
62 auto func = tt->functype();
63 EXPECT_EQ(func->params().size(), 1u);
64 EXPECT_EQ(func->params().begin()->kind(), ValKind::I32);
65 }
66
TEST(Exception,HostThrowWasmCatch)67 TEST(Exception, HostThrowWasmCatch) {
68 auto engine = make_engine();
69 Store store(engine);
70 auto cx = store.context();
71
72 FuncType tag_ft({ValKind::I32}, {});
73 TagType tt(tag_ft);
74 auto tag = Tag::create(cx, tt).unwrap();
75
76 Module module =
77 Module::compile(engine, "(module"
78 " (import \"host\" \"throw\" (func $throw))"
79 " (import \"host\" \"tag\" (tag $t (param i32)))"
80 " (func (export \"run\") (result i32)"
81 " (block $done (result i32)"
82 " (try_table (catch 0 $done)"
83 " (call $throw)"
84 " )"
85 " (unreachable)"
86 " )"
87 " )"
88 ")")
89 .unwrap();
90
91 FuncType throw_ft({}, {});
92 Func throw_fn(cx, throw_ft,
93 [&](Caller caller, Span<const Val> args,
94 Span<Val> results) -> Result<std::monostate, Trap> {
95 (void)args;
96 (void)results;
97 auto cx2 = caller.context();
98
99 std::vector<Val> fields = {Val(int32_t(99))};
100 auto exn = Exn::create(cx2, tag, fields).unwrap();
101 return cx2.throw_exception(std::move(exn));
102 });
103
104 std::vector<Extern> imports = {throw_fn, tag};
105 auto instance = Instance::create(cx, module, imports).unwrap();
106
107 auto run_ext = instance.get(cx, "run");
108 ASSERT_TRUE(run_ext.has_value());
109 auto run_fn = std::get<Func>(*run_ext);
110 auto result = run_fn.call(cx, {});
111 ASSERT_TRUE(result);
112 ASSERT_EQ(result.ok().size(), 1u);
113 EXPECT_EQ(result.ok()[0].i32(), 99);
114 }
115
TEST(Exception,ExnRefRoundTripThroughVal)116 TEST(Exception, ExnRefRoundTripThroughVal) {
117 auto engine = make_engine();
118 Store store(engine);
119 auto cx = store.context();
120
121 // A module that throws an exception and catches it as an exnref.
122 Module module =
123 Module::compile(engine, "(module"
124 " (tag $t (param i32))"
125 " (export \"tag\" (tag $t))"
126 " (func (export \"make_exnref\") (result exnref)"
127 " (block $done (result exnref)"
128 " (try_table (catch_all_ref $done)"
129 " (throw $t (i32.const 55))"
130 " )"
131 " (unreachable)"
132 " )"
133 " )"
134 ")")
135 .unwrap();
136
137 auto instance = Instance::create(cx, module, {}).unwrap();
138
139 auto tag_ext = instance.get(cx, "tag");
140 ASSERT_TRUE(tag_ext.has_value());
141 auto tag = std::get<Tag>(*tag_ext);
142
143 auto make_fn = std::get<Func>(*instance.get(cx, "make_exnref"));
144 auto result = make_fn.call(cx, {});
145 ASSERT_TRUE(result);
146 ASSERT_EQ(result.ok().size(), 1u);
147
148 // The returned value should be an exnref.
149 auto &exnref_val = result.ok()[0];
150 EXPECT_EQ(exnref_val.kind(), ValKind::ExnRef);
151
152 // Pass the exnref back into a wasm function that extracts the i32 payload.
153 Module module2 =
154 Module::compile(engine,
155 "(module"
156 " (import \"host\" \"tag\" (tag $t (param i32)))"
157 " (func (export \"read\") (param exnref) (result i32)"
158 " (block $done (result i32)"
159 " (try_table (catch 0 $done)"
160 " (throw_ref (local.get 0))"
161 " )"
162 " (unreachable)"
163 " )"
164 " )"
165 ")")
166 .unwrap();
167
168 std::vector<Extern> imports2 = {tag};
169 auto instance2 = Instance::create(cx, module2, imports2).unwrap();
170 auto read_fn = std::get<Func>(*instance2.get(cx, "read"));
171
172 // Call with the exnref we got back from the first module.
173 std::vector<Val> args = {std::move(exnref_val)};
174 auto result2 = read_fn.call(cx, args);
175 ASSERT_TRUE(result2);
176 ASSERT_EQ(result2.ok().size(), 1u);
177 EXPECT_EQ(result2.ok()[0].i32(), 55);
178 }
179
TEST(Exception,WasmThrowHostCatch)180 TEST(Exception, WasmThrowHostCatch) {
181 auto engine = make_engine();
182 Store store(engine);
183 auto cx = store.context();
184
185 Module module = Module::compile(engine, "(module"
186 " (tag $t (param i32))"
187 " (export \"tag\" (tag $t))"
188 " (func (export \"throw\")"
189 " (throw $t (i32.const 77))"
190 " )"
191 ")")
192 .unwrap();
193
194 auto instance = Instance::create(cx, module, {}).unwrap();
195
196 auto tag_ext = instance.get(cx, "tag");
197 ASSERT_TRUE(tag_ext.has_value());
198 auto tag = std::get<Tag>(*tag_ext);
199
200 auto throw_ext = instance.get(cx, "throw");
201 ASSERT_TRUE(throw_ext.has_value());
202 auto throw_fn = std::get<Func>(*throw_ext);
203
204 auto result = throw_fn.call(cx, {});
205 ASSERT_FALSE(result);
206
207 ASSERT_TRUE(cx.has_exception());
208 auto exn = cx.take_exception();
209 ASSERT_TRUE(exn.has_value());
210
211 auto exn_tag = exn->tag(cx).unwrap();
212 EXPECT_TRUE(tag.eq(cx, exn_tag));
213
214 EXPECT_EQ(exn->field_count(cx), 1u);
215 auto f0 = exn->field(cx, 0).unwrap();
216 EXPECT_EQ(f0.i32(), 77);
217 }
218