1 #include <gtest/gtest.h> 2 #include <wasmtime.hh> 3 4 using namespace wasmtime; 5 6 static Engine make_engine() { 7 Config config; 8 config.wasm_exceptions(true); 9 return Engine(std::move(config)); 10 } 11 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 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 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 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 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