// sol3 // The MIT License (MIT) // Copyright (c) 2013-2021 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "sol_test.hpp" #include #include #include TEST_CASE("policies/self", "ensure we return a direct reference to the lua userdata rather than creating a new one") { struct vec2 { float x = 20.f; float y = 20.f; vec2& normalize() { float len2 = x * x + y * y; if (len2 != 0) { float len = sqrtf(len2); x /= len; y /= len; } return *this; } ~vec2() { x = std::numeric_limits::lowest(); y = std::numeric_limits::lowest(); } }; sol::state lua; lua.open_libraries(sol::lib::base); lua.new_usertype("vec2", "x", &vec2::x, "y", &vec2::y, "normalize", sol::policies(&vec2::normalize, sol::returns_self())); auto result1 = lua.safe_script(R"( v1 = vec2.new() print('v1:', v1.x, v1.y) v2 = v1:normalize() print('v1:', v1.x, v1.y) print('v2:', v2.x, v2.y) print(v1, v2) assert(rawequal(v1, v2)) v1 = nil collectgarbage() print(v2) -- v2 points to same, is not destroyed )", sol::script_pass_on_error); REQUIRE(result1.valid()); } TEST_CASE("policies/self_dependency", "ensure we can keep a userdata instance alive by attaching it to the lifetime of another userdata") { struct dep; struct gc_test; static std::vector deps_destroyed; static std::vector gc_tests_destroyed; struct dep { int value = 20; ~dep() { std::cout << "\t" << "[C++] ~dep" << std::endl; value = std::numeric_limits::max(); deps_destroyed.push_back(this); } }; struct gc_test { dep d; ~gc_test() { std::cout << "\t" << "[C++] ~gc_test" << std::endl; gc_tests_destroyed.push_back(this); } }; sol::state lua; lua.open_libraries(sol::lib::base); lua.new_usertype("dep", "value", &dep::value, sol::meta_function::to_string, [](dep& d) { return "{ " + std::to_string(d.value) + " }"; }); lua.new_usertype("gc_test", "d", sol::policies(&gc_test::d, sol::self_dependency()), sol::meta_function::to_string, [](gc_test& g) { return "{ d: { " + std::to_string(g.d.value) + " } }"; }); auto result1 = lua.safe_script(R"( g = gc_test.new() d = g.d print("new gc_test, d = g.d") print("", g) )", sol::script_pass_on_error); REQUIRE(result1.valid()); REQUIRE(deps_destroyed.empty()); REQUIRE(gc_tests_destroyed.empty()); gc_test* g = lua["g"]; dep* d = lua["d"]; auto result2 = lua.safe_script(R"( print("g = nil, collectgarbage") g = nil collectgarbage() print("", d) )", sol::script_pass_on_error); REQUIRE(result2.valid()); REQUIRE(deps_destroyed.empty()); REQUIRE(gc_tests_destroyed.empty()); auto result3 = lua.safe_script(R"( print("d = nil, collectgarbage") d = nil collectgarbage() )", sol::script_pass_on_error); REQUIRE(result3.valid()); REQUIRE(deps_destroyed.size() == 1); REQUIRE(gc_tests_destroyed.size() == 1); REQUIRE(deps_destroyed[0] == d); REQUIRE(gc_tests_destroyed[0] == g); } TEST_CASE("policies/stack_dependencies", "ensure we can take dependencies even to arguments pushed on the stack") { struct holder; struct depends_on_reference; struct composition_related; static std::vector composition_relateds_destroyed; static std::vector holders_destroyed; static std::vector depends_on_references_destroyed; struct composition_related { std::string text = "bark"; ~composition_related() { std::cout << "[C++] ~composition_related" << std::endl; text = ""; composition_relateds_destroyed.push_back(this); } }; struct holder { int value = 20; ~holder() { std::cout << "[C++] ~holder" << std::endl; value = std::numeric_limits::max(); holders_destroyed.push_back(this); } }; struct depends_on_reference { std::reference_wrapper href; composition_related comp; depends_on_reference(holder& h) : href(h) { } ~depends_on_reference() { std::cout << "[C++] ~depends_on_reference" << std::endl; depends_on_references_destroyed.push_back(this); } }; sol::state lua; lua.open_libraries(sol::lib::base); lua.new_usertype("holder", "value", &holder::value); lua.new_usertype("depends_on_reference", "new", sol::policies(sol::constructors(), sol::stack_dependencies(-1, 2)), "comp", &depends_on_reference::comp); auto result1 = lua.safe_script(R"( h = holder.new() dor = depends_on_reference.new(h) c = dor.comp )", sol::script_pass_on_error); REQUIRE(result1.valid()); REQUIRE(composition_relateds_destroyed.empty()); REQUIRE(holders_destroyed.empty()); REQUIRE(depends_on_references_destroyed.empty()); holder* h = lua["h"]; composition_related* c = lua["c"]; depends_on_reference* dor = lua["dor"]; REQUIRE(h == &dor->href.get()); REQUIRE(c == &dor->comp); auto result2 = lua.safe_script(R"( h = nil collectgarbage() )"); REQUIRE(result2.valid()); REQUIRE(composition_relateds_destroyed.empty()); REQUIRE(holders_destroyed.empty()); REQUIRE(depends_on_references_destroyed.empty()); auto result3 = lua.safe_script(R"( c = nil collectgarbage() )", sol::script_pass_on_error); REQUIRE(result3.valid()); REQUIRE(composition_relateds_destroyed.empty()); REQUIRE(holders_destroyed.empty()); REQUIRE(depends_on_references_destroyed.empty()); auto result4 = lua.safe_script(R"( dor = nil collectgarbage() )", sol::script_pass_on_error); REQUIRE(result4.valid()); REQUIRE(composition_relateds_destroyed.size() == 1); REQUIRE(holders_destroyed.size() == 1); REQUIRE(depends_on_references_destroyed.size() == 1); REQUIRE(composition_relateds_destroyed[0] == c); REQUIRE(holders_destroyed[0] == h); REQUIRE(depends_on_references_destroyed[0] == dor); } int always_return_24(lua_State* L, int) { return sol::stack::push(L, 24); } TEST_CASE("policies/custom", "ensure we can return dependencies on multiple things in the stack") { sol::state lua; lua.set_function("f", sol::policies([]() { return std::string("hi there"); }, always_return_24)); int value = lua["f"](); REQUIRE(value == 24); }