From 08435127134d06e4deaa51f5ea9be692d7565846 Mon Sep 17 00:00:00 2001 From: mihacooper Date: Fri, 29 Sep 2017 00:22:15 +0300 Subject: [PATCH] Fix methods params handling (#74) --- libs/u-test | 2 +- src/cpp/channel.cpp | 22 +++-- src/cpp/channel.h | 4 +- src/cpp/garbage-collector.cpp | 12 +-- src/cpp/lua-helpers.cpp | 4 +- src/cpp/lua-helpers.h | 21 +++++ src/cpp/lua-module.cpp | 18 ++-- src/cpp/shared-table.cpp | 65 ++++++++++---- src/cpp/shared-table.h | 14 +-- src/cpp/stored-object.cpp | 2 +- src/cpp/threading.cpp | 23 +++-- src/cpp/threading.h | 2 +- src/cpp/utils.h | 1 + src/lua/effil.lua | 14 ++- tests/lua/gc.lua | 2 +- tests/lua/run_tests | 1 + tests/lua/type_mismatch.lua | 156 ++++++++++++++++++++++++++++++++++ 17 files changed, 288 insertions(+), 75 deletions(-) create mode 100644 tests/lua/type_mismatch.lua diff --git a/libs/u-test b/libs/u-test index 37bf08d..34ee5de 160000 --- a/libs/u-test +++ b/libs/u-test @@ -1 +1 @@ -Subproject commit 37bf08dac653b258ceb468cbdf8fcb402ba58c3a +Subproject commit 34ee5de696c2c12d8ded5793f2c5504f2e6ee168 diff --git a/src/cpp/channel.cpp b/src/cpp/channel.cpp index 8d7bd13..f690502 100644 --- a/src/cpp/channel.cpp +++ b/src/cpp/channel.cpp @@ -14,10 +14,12 @@ void Channel::exportAPI(sol::state_view& lua) { sol::stack::pop(lua); } -Channel::Channel(sol::optional capacity) : data_(std::make_shared()) { - if (capacity) { - REQUIRE(capacity.value() >= 0) << "Invalid capacity value = " << capacity.value(); - data_->capacity_ = static_cast(capacity.value()); +Channel::Channel(const sol::stack_object& capacity) : data_(std::make_shared()){ + if (capacity.valid()) { + REQUIRE(capacity.get_type() == sol::type::number) << "bad argument #1 to 'effil.channel' (number expected, got " + << luaTypename(capacity) << ")"; + REQUIRE(capacity.as() >= 0) << "effil.channel: invalid capacity value = " << capacity.as(); + data_->capacity_ = capacity.as(); } else { data_->capacity_ = 0; @@ -33,11 +35,13 @@ bool Channel::push(const sol::variadic_args& args) { return false; effil::StoredArray array; for (const auto& arg : args) { - auto obj = createStoredObject(arg.get()); - - addReference(obj->gcHandle()); - obj->releaseStrongReference(); - array.emplace_back(obj); + try { + auto obj = createStoredObject(arg.get()); + addReference(obj->gcHandle()); + obj->releaseStrongReference(); + array.emplace_back(obj); + } + RETHROW_WITH_PREFIX("effil.channel:push"); } if (data_->channel_.empty()) data_->cv_.notify_one(); diff --git a/src/cpp/channel.h b/src/cpp/channel.h index 98ba676..1cbbefc 100644 --- a/src/cpp/channel.h +++ b/src/cpp/channel.h @@ -10,12 +10,12 @@ namespace effil { class Channel : public GCObject { public: - Channel(sol::optional capacity); + Channel(const sol::stack_object& capacity); static void exportAPI(sol::state_view& lua); bool push(const sol::variadic_args& args); StoredArray pop(const sol::optional& duration, - const sol::optional& period); + const sol::optional& period); size_t size(); protected: diff --git a/src/cpp/garbage-collector.cpp b/src/cpp/garbage-collector.cpp index 3e122ab..adb20b2 100644 --- a/src/cpp/garbage-collector.cpp +++ b/src/cpp/garbage-collector.cpp @@ -1,6 +1,7 @@ #include "garbage-collector.h" #include "utils.h" +#include "lua-helpers.h" #include @@ -64,11 +65,12 @@ sol::table GC::exportAPI(sol::state_view& lua) { api["pause"] = [] { instance().pause(); }; api["resume"] = [] { instance().resume(); }; api["enabled"] = [] { return instance().enabled(); }; - api["step"] = [](sol::optional newStep){ + api["step"] = [](const sol::stack_object& newStep){ auto previous = instance().step(); - if (newStep) { - REQUIRE(*newStep <= 0) << "gc.step have to be > 0"; - instance().step(static_cast(*newStep)); + if (newStep.valid()) { + REQUIRE(newStep.get_type() == sol::type::number) << "bad argument #1 to 'effil.gc.step' (number expected, got " << luaTypename(newStep) << ")"; + REQUIRE(newStep.as() >= 0) << "effil.gc.step: invalid capacity value = " << newStep.as(); + instance().step(newStep.as()); } return previous; }; @@ -78,4 +80,4 @@ sol::table GC::exportAPI(sol::state_view& lua) { return api; } -} // effil \ No newline at end of file +} // effil diff --git a/src/cpp/lua-helpers.cpp b/src/cpp/lua-helpers.cpp index d1df401..a9c5cfd 100644 --- a/src/cpp/lua-helpers.cpp +++ b/src/cpp/lua-helpers.cpp @@ -51,13 +51,13 @@ sol::function loadString(const sol::state_view& lua, const std::string& str) { std::chrono::milliseconds fromLuaTime(int duration, const sol::optional& period) { using namespace std::chrono; - REQUIRE(duration >= 0) << "Invalid duration interval: " << duration; + REQUIRE(duration >= 0) << "invalid duration interval: " << duration; std::string metric = period ? period.value() : "s"; if (metric == "ms") return milliseconds(duration); else if (metric == "s") return seconds(duration); else if (metric == "m") return minutes(duration); - else throw sol::error("invalid time identification: " + metric); + else throw sol::error("invalid time metric: " + metric); } } // namespace effil diff --git a/src/cpp/lua-helpers.h b/src/cpp/lua-helpers.h index bceedeb..25990bd 100644 --- a/src/cpp/lua-helpers.h +++ b/src/cpp/lua-helpers.h @@ -6,10 +6,31 @@ namespace effil { +class SharedTable; +class Channel; +class Thread; + std::string dumpFunction(const sol::function& f); sol::function loadString(const sol::state_view& lua, const std::string& str); std::chrono::milliseconds fromLuaTime(int duration, const sol::optional& period); +template +std::string luaTypename(const SolObject& obj) { + if (obj.get_type() == sol::type::userdata) { + if (obj.template is()) + return "effil.table"; + else if (obj.template is()) + return "effil.channel"; + else if (obj.template is>()) + return "effil.thread"; + else + return "userdata"; + } + else { + return lua_typename(obj.lua_state(), (int)obj.get_type()); + } +} + typedef std::vector StoredArray; } // namespace effil diff --git a/src/cpp/lua-module.cpp b/src/cpp/lua-module.cpp index dd200fc..d11bbab 100644 --- a/src/cpp/lua-module.cpp +++ b/src/cpp/lua-module.cpp @@ -28,23 +28,15 @@ sol::object createTable(sol::this_state lua, const sol::optional& t return sol::make_object(lua, GC::instance().create()); } -sol::object createChannel(sol::optional capacity, sol::this_state lua) { +sol::object createChannel(const sol::stack_object& capacity, sol::this_state lua) { return sol::make_object(lua, GC::instance().create(capacity)); } SharedTable globalTable = GC::instance().create(); -std::string userdataType(const sol::object& something) { - assert(something.get_type() == sol::type::userdata); - if (something.template is()) { - return "effil.table"; - } else if (something.template is()) { - return "effil.channel"; - } else if (something.template is>()) { - return "effil.thread"; - } else { - return "userdata"; - } +std::string getLuaTypename(const sol::stack_object& obj) +{ + return luaTypename<>(obj); } } // namespace @@ -72,7 +64,7 @@ int luaopen_libeffil(lua_State* L) { "G", sol::make_object(lua, globalTable), "gc", GC::exportAPI(lua), "channel", createChannel, - "userdata_type", userdataType, + "type", getLuaTypename, "pairs", SharedTable::globalLuaPairs, "ipairs", SharedTable::globalLuaIPairs ); diff --git a/src/cpp/shared-table.cpp b/src/cpp/shared-table.cpp index c288f39..32d7950 100644 --- a/src/cpp/shared-table.cpp +++ b/src/cpp/shared-table.cpp @@ -14,6 +14,11 @@ bool isSharedTable(const SolObject& obj) { return obj.valid() && obj.get_type() == sol::type::userdata && obj.template is(); } +template +bool isAnyTable(const SolObject& obj) { + return obj.valid() && ((obj.get_type() == sol::type::userdata && obj.template is()) || obj.get_type() == sol::type::table); +} + } // namespace SharedTable::SharedTable() : data_(std::make_shared()) {} @@ -164,12 +169,16 @@ void SharedTable::luaNewIndex(const sol::stack_object& luaKey, const sol::stack_ } } } - rawSet(luaKey, luaValue); + try { + rawSet(luaKey, luaValue); + } RETHROW_WITH_PREFIX("effil.table"); } sol::object SharedTable::luaIndex(const sol::stack_object& luaKey, sol::this_state state) { DEFFINE_METAMETHOD_CALL("__index", *this, luaKey) - return rawGet(luaKey, state); + try { + return rawGet(luaKey, state); + } RETHROW_WITH_PREFIX("effil.table"); } StoredArray SharedTable::luaCall(sol::this_state state, const sol::variadic_args& args) { @@ -258,8 +267,12 @@ SharedTable::PairsIterator SharedTable::luaIPairs(sol::this_state state) { * Lua static API functions */ -SharedTable SharedTable::luaSetMetatable(SharedTable& stable, const sol::stack_object& mt) { - REQUIRE(mt.get_type() == sol::type::table || isSharedTable(mt)) << "Unexpected type of setmetatable argument"; +SharedTable SharedTable::luaSetMetatable(const sol::stack_object& tbl, const sol::stack_object& mt) { + REQUIRE(isAnyTable(tbl)) << "bad argument #1 to 'effil.setmetatable' (table expected, got " << luaTypename(tbl) << ")"; + REQUIRE(isAnyTable(mt)) << "bad argument #2 to 'effil.setmetatable' (table expected, got " << luaTypename(mt) << ")"; + + SharedTable stable = GC::instance().get(createStoredObject(tbl)->gcHandle()); + std::lock_guard lock(stable.data_->lock); if (stable.data_->metatable != GCNull) { stable.removeReference(stable.data_->metatable); @@ -272,32 +285,50 @@ SharedTable SharedTable::luaSetMetatable(SharedTable& stable, const sol::stack_o return stable; } -sol::object SharedTable::luaGetMetatable(const SharedTable& stable, const sol::this_state& state) { +sol::object SharedTable::luaGetMetatable(const sol::stack_object& tbl, sol::this_state state) { + REQUIRE(isSharedTable(tbl)) << "bad argument #1 to 'effil.getmetatable' (effil.table expected, got " << luaTypename(tbl) << ")"; + auto& stable = tbl.as(); + std::lock_guard lock(stable.data_->lock); return stable.data_->metatable == GCNull ? sol::nil : sol::make_object(state, GC::instance().get(stable.data_->metatable)); } -sol::object SharedTable::luaRawGet(const SharedTable& stable, const sol::stack_object& key, sol::this_state state) { - return stable.rawGet(key, state); +sol::object SharedTable::luaRawGet(const sol::stack_object& tbl, const sol::stack_object& key, sol::this_state state) { + REQUIRE(isSharedTable(tbl)) << "bad argument #1 to 'effil.rawget' (effil.table expected, got " << luaTypename(tbl) << ")"; + try { + return tbl.as().rawGet(key, state); + } RETHROW_WITH_PREFIX("effil.rawget"); } -SharedTable SharedTable::luaRawSet(SharedTable& stable, const sol::stack_object& key, const sol::stack_object& value) { - stable.rawSet(key, value); - return stable; +SharedTable SharedTable::luaRawSet(const sol::stack_object& tbl, const sol::stack_object& key, const sol::stack_object& value) { + REQUIRE(isSharedTable(tbl)) << "bad argument #1 to 'effil.rawset' (effil.table expected, got " << luaTypename(tbl) << ")"; + try { + auto& stable = tbl.as(); + stable.rawSet(key, value); + return stable; + } RETHROW_WITH_PREFIX("effil.rawset"); } -size_t SharedTable::luaSize(SharedTable& stable) { - std::lock_guard g(stable.data_->lock); - return stable.data_->entries.size(); +size_t SharedTable::luaSize(const sol::stack_object& tbl) { + REQUIRE(isSharedTable(tbl)) << "bad argument #1 to 'effil.size' (effil.table expected, got " << luaTypename(tbl) << ")"; + try { + auto& stable = tbl.as(); + std::lock_guard g(stable.data_->lock); + return stable.data_->entries.size(); + } RETHROW_WITH_PREFIX("effil.size"); } -SharedTable::PairsIterator SharedTable::globalLuaPairs(sol::this_state state, SharedTable& obj) { - return obj.luaPairs(state); +SharedTable::PairsIterator SharedTable::globalLuaPairs(sol::this_state state, const sol::stack_object& obj) { + REQUIRE(isSharedTable(obj)) << "bad argument #1 to 'effil.pairs' (effil.table expected, got " << luaTypename(obj) << ")"; + auto& tbl = obj.as(); + return tbl.luaPairs(state); } -SharedTable::PairsIterator SharedTable::globalLuaIPairs(sol::this_state state, SharedTable& obj) { - return obj.luaIPairs(state); +SharedTable::PairsIterator SharedTable::globalLuaIPairs(sol::this_state state, const sol::stack_object& obj) { + REQUIRE(isSharedTable(obj)) << "bad argument #1 to 'effil.ipairs' (effil.table expected, got " << luaTypename(obj) << ")"; + auto& tbl = obj.as(); + return tbl.luaIPairs(state); } #undef DEFFINE_METAMETHOD_CALL_0 diff --git a/src/cpp/shared-table.h b/src/cpp/shared-table.h index ae341d0..889136d 100644 --- a/src/cpp/shared-table.h +++ b/src/cpp/shared-table.h @@ -52,13 +52,13 @@ public: static sol::object luaConcat(sol::this_state, const sol::stack_object&, const sol::stack_object&); // Stand alone functions for effil::table available in Lua - static SharedTable luaSetMetatable(SharedTable& stable, const sol::stack_object& mt); - static sol::object luaGetMetatable(const SharedTable& stable, const sol::this_state& state); - static sol::object luaRawGet(const SharedTable& stable, const sol::stack_object& key, sol::this_state state); - static SharedTable luaRawSet(SharedTable& stable, const sol::stack_object& key, const sol::stack_object& value); - static size_t luaSize(SharedTable& stable); - static PairsIterator globalLuaPairs(sol::this_state state, SharedTable& obj); - static PairsIterator globalLuaIPairs(sol::this_state state, SharedTable& obj); + static SharedTable luaSetMetatable(const sol::stack_object& tbl, const sol::stack_object& mt); + static sol::object luaGetMetatable(const sol::stack_object& tbl, const sol::this_state state); + static sol::object luaRawGet(const sol::stack_object& tbl, const sol::stack_object& key, sol::this_state state); + static SharedTable luaRawSet(const sol::stack_object& tbl, const sol::stack_object& key, const sol::stack_object& value); + static size_t luaSize(const sol::stack_object& tbl); + static PairsIterator globalLuaPairs(sol::this_state state, const sol::stack_object& obj); + static PairsIterator globalLuaIPairs(sol::this_state state, const sol::stack_object& obj); private: PairsIterator getNext(const sol::object& key, sol::this_state lua); diff --git a/src/cpp/stored-object.cpp b/src/cpp/stored-object.cpp index e8e6f22..793ad4f 100644 --- a/src/cpp/stored-object.cpp +++ b/src/cpp/stored-object.cpp @@ -185,7 +185,7 @@ StoredObject fromSolObject(const SolObject& luaObject) { return std::make_unique>(table.handle()); } default: - throw Exception() << "Unable to store object of that type: " << (int)luaObject.get_type() << "\n"; + throw Exception() << "unable to store object of " << luaTypename(luaObject) << " type"; } return nullptr; } diff --git a/src/cpp/threading.cpp b/src/cpp/threading.cpp index b07c844..eae3e75 100644 --- a/src/cpp/threading.cpp +++ b/src/cpp/threading.cpp @@ -212,11 +212,18 @@ void yield() { std::this_thread::yield(); } -void sleep(const sol::optional& duration, const sol::optional& period) { - if (duration) - std::this_thread::sleep_for(fromLuaTime(*duration, period)); - else +void sleep(const sol::stack_object& duration, const sol::stack_object& metric) { + if (duration.valid()) { + REQUIRE(duration.get_type() == sol::type::number) << "bad argument #1 to 'effil.sleep' (number expected, got " << luaTypename(duration) << ")"; + if (metric.valid()) + REQUIRE(metric.get_type() == sol::type::string) << "bad argument #2 to 'effil.sleep' (string expected, got " << luaTypename(metric) << ")"; + try { + std::this_thread::sleep_for(fromLuaTime(duration.as(), metric.valid() ? metric.as() : sol::optional())); + } RETHROW_WITH_PREFIX("effil.sleep"); + } + else { yield(); + } } Thread::Thread(const std::string& path, @@ -236,9 +243,11 @@ Thread::Thread(const std::string& path, std::string strFunction = dumpFunction(function); effil::StoredArray arguments; - for (const auto& arg : variadicArgs) { - arguments.emplace_back(createStoredObject(arg.get())); - } + try { + for (const auto& arg : variadicArgs) { + arguments.emplace_back(createStoredObject(arg.get())); + } + } RETHROW_WITH_PREFIX("effil.thread"); std::thread thr(&runThread, handle_, diff --git a/src/cpp/threading.h b/src/cpp/threading.h index 44204ba..881f0da 100644 --- a/src/cpp/threading.h +++ b/src/cpp/threading.h @@ -8,7 +8,7 @@ namespace effil { // Lua this thread API std::string threadId(); void yield(); -void sleep(const sol::optional&, const sol::optional&); +void sleep(const sol::stack_object& duration, const sol::stack_object& metric); class ThreadHandle; diff --git a/src/cpp/utils.h b/src/cpp/utils.h index 9b790d6..d21695e 100644 --- a/src/cpp/utils.h +++ b/src/cpp/utils.h @@ -49,6 +49,7 @@ private: } // effil #define REQUIRE(cond) if (!(cond)) throw effil::Exception() +#define RETHROW_WITH_PREFIX(preff) catch(const effil::Exception& err) { throw effil::Exception() << preff << ": " << err.what(); } #ifdef NDEBUG #define DEBUG if (false) std::cout diff --git a/src/lua/effil.lua b/src/lua/effil.lua index addf9cd..ae3b1a7 100644 --- a/src/lua/effil.lua +++ b/src/lua/effil.lua @@ -13,19 +13,11 @@ local api = { G = capi.G, gc = capi.gc, channel = capi.channel, + type = capi.type, pairs = capi.pairs, ipairs = capi.ipairs } -api.type = function (something) - local t = type(something) - if (t ~= "userdata") then - return t - else - return capi.userdata_type(something) - end -end - api.size = function (something) local t = api.type(something) if t == "effil.table" then @@ -48,6 +40,10 @@ end -- step - who fast reacte on state changing -- __call - run thread, can be invoked multiple times api.thread = function (f) + if type(f) ~= "function" then + error("bad argument #1 to 'effil.thread' (function expected, got " .. effil.type(f) .. ")") + end + local thread_config = { path = package.path, cpath = package.cpath, diff --git a/tests/lua/gc.lua b/tests/lua/gc.lua index fc55129..c979019 100644 --- a/tests/lua/gc.lua +++ b/tests/lua/gc.lua @@ -47,7 +47,7 @@ test.gc.store_same_value = function() c:push(a) end - local c = effil.channel {} + local c = effil.channel() fill(c) c:pop() diff --git a/tests/lua/run_tests b/tests/lua/run_tests index ee8cd28..5b34784 100755 --- a/tests/lua/run_tests +++ b/tests/lua/run_tests @@ -13,6 +13,7 @@ require "channel" require "thread" require "shared-table" require "metatable" +require "type_mismatch" if os.getenv("STRESS") then require "channel-stress" diff --git a/tests/lua/type_mismatch.lua b/tests/lua/type_mismatch.lua new file mode 100644 index 0000000..f08a06c --- /dev/null +++ b/tests/lua/type_mismatch.lua @@ -0,0 +1,156 @@ +require "bootstrap-tests" + +local basic_type_mismatch_test = function(err_msg, wrong_arg_num, func_name , ...) + local func_to_call = func_name + if type(func_name) == "string" then + func_to_call = effil + for word in string.gmatch(func_name, "[^%.]+") do + func_to_call = func_to_call[word] + test.is_not_nil(func_to_call) + end + end + + local ret, err = pcall(func_to_call, ...) + test.is_false(ret) + print("Original error: '" .. err .. "'") + + -- because error may start with trace back + local trunc_err = err + if string.len(err) > string.len(err_msg) then + trunc_err = string.sub(err, string.len(err) - string.len(err_msg) + 1, string.len(err)) + end + test.equal(trunc_err, err_msg) +end + +test.type_mismatch.input_types_mismatch = function(wrong_arg_num, expected_type, func_name, ...) + local args = {...} + local err_msg = "bad argument #" .. wrong_arg_num .. " to " .. + (type(func_name) == "string" and "'effil." .. func_name or func_name.name) .. + "' (" .. expected_type .. " expected, got " .. effil.type(args[wrong_arg_num]) .. ")" + basic_type_mismatch_test(err_msg, wrong_arg_num, func_name, ...) +end + +test.type_mismatch.unsupported_type = function(wrong_arg_num, func_name, ...) + local args = {...} + local err_msg = (type(func_name) == "string" and "effil." .. func_name or func_name.name) + .. ": unable to store object of " .. effil.type(args[wrong_arg_num]) .. " type" + basic_type_mismatch_test(err_msg, wrong_arg_num, func_name, ...) +end + +local function generate_tests() + local function create_object_generator(name, func) + return setmetatable({ name = name }, { + __call = func, + __tostring = function() return name end + }) + end + + local channel_push_generator = create_object_generator("effil.channel:push", + function(_, ...) + return effil.channel():push(...) + end + ) + + local thread_runner_generator = create_object_generator("effil.thread", + function(_, ...) + return effil.thread(function()end)(...) + end + ) + + local table_set_value_generator = create_object_generator("effil.table", + function(_, key, value) + effil.table()[key] = value + end + ) + + local table_get_value_generator = create_object_generator("effil.table", + function(_, key) + return effil.table()[key] + end + ) + + local func = function()end + local stable = effil.table() + local thread = effil.thread(func)() + thread:wait() + local lua_thread = coroutine.create(func) + + local all_types = { 22, "s", true, {}, stable, func, thread, effil.channel(), lua_thread } + + for _, type_instance in ipairs(all_types) do + local typename = effil.type(type_instance) + + -- effil.getmetatable + if typename ~= "effil.table" then + test.type_mismatch.input_types_mismatch(1, "effil.table", "getmetatable", type_instance) + end + + -- effil.setmetatable + if typename ~= "table" and typename ~= "effil.table" then + test.type_mismatch.input_types_mismatch(1, "table", "setmetatable", type_instance, 44) + test.type_mismatch.input_types_mismatch(2, "table", "setmetatable", {}, type_instance) + end + + if typename ~= "effil.table" then + -- effil.rawset + test.type_mismatch.input_types_mismatch(1, "effil.table", "rawset", type_instance, 44, 22) + -- effil.rawget + test.type_mismatch.input_types_mismatch(1, "effil.table", "rawget", type_instance, 44) + -- effil.ipairs + test.type_mismatch.input_types_mismatch(1, "effil.table", "ipairs", type_instance) + -- effil.pairs + test.type_mismatch.input_types_mismatch(1, "effil.table", "pairs", type_instance) + end + + -- effil.thread + if typename ~= "function" then + test.type_mismatch.input_types_mismatch(1, "function", "thread", type_instance) + end + + -- effil.sleep + if typename ~= "number" then + test.type_mismatch.input_types_mismatch(1, "number", "sleep", type_instance, "s") + end + if typename ~= "string" then + test.type_mismatch.input_types_mismatch(2, "string", "sleep", 1, type_instance) + end + + if typename ~= "number" then + -- effil.channel + test.type_mismatch.input_types_mismatch(1, "number", "channel", type_instance) + + -- effil.gc.step + test.type_mismatch.input_types_mismatch(1, "number", "gc.step", type_instance) + end + end + + -- Below presented tests which support everything except coroutines + + -- effil.rawset + test.type_mismatch.unsupported_type(2, "rawset", stable, lua_thread, 22) + test.type_mismatch.unsupported_type(3, "rawset", stable, 44, lua_thread) + + -- effil.rawget + test.type_mismatch.unsupported_type(2, "rawget", stable, lua_thread) + + -- effil.channel:push() + test.type_mismatch.unsupported_type(1, channel_push_generator, lua_thread) + + -- effil.thread()() + test.type_mismatch.unsupported_type(1, thread_runner_generator, lua_thread) + + -- effil.table[key] = value + test.type_mismatch.unsupported_type(1, table_set_value_generator, lua_thread, 2) + test.type_mismatch.unsupported_type(2, table_set_value_generator, 2, lua_thread) + -- effil.table[key] + test.type_mismatch.unsupported_type(1, table_get_value_generator, lua_thread) +end + +-- Put it to function to limit the lifetime of objects +generate_tests() + +test.type_mismatch.gc_checks_after_tests = function () + collectgarbage() + effil.gc.collect() + test.equal(effil.gc.count(), 1) +end