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