1 // sol3
2 
3 // The MIT License (MIT)
4 
5 // Copyright (c) 2013-2021 Rapptz, ThePhD and contributors
6 
7 // Permission is hereby granted, free of charge, to any person obtaining a copy of
8 // this software and associated documentation files (the "Software"), to deal in
9 // the Software without restriction, including without limitation the rights to
10 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 // the Software, and to permit persons to whom the Software is furnished to do so,
12 // subject to the following conditions:
13 
14 // The above copyright notice and this permission notice shall be included in all
15 // copies or substantial portions of the Software.
16 
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 
24 // we have a test for weirdly aligned wrappers
25 // do not make lots of noise about it
26 #ifdef _MSC_VER
27 #pragma warning(disable : 4324)
28 #endif
29 
30 #include "sol_test.hpp"
31 
32 #include "common_classes.hpp"
33 
34 #include <catch2/catch.hpp>
35 
something_func_true()36 bool something_func_true() {
37 	return true;
38 }
39 
40 struct ext_getset {
41 
42 	int bark = 24;
43 	const int meow = 56;
44 
45 	ext_getset() = default;
ext_getsetext_getset46 	ext_getset(int v) : bark(v) {
47 	}
48 	ext_getset(ext_getset&&) = default;
49 	ext_getset(const ext_getset&) = delete;
50 	ext_getset& operator=(ext_getset&&) = delete;
51 	ext_getset& operator=(const ext_getset&) = delete;
~ext_getsetext_getset52 	~ext_getset() {
53 	}
54 
xext_getset55 	std::string x() {
56 		return "bark bark bark";
57 	}
58 
x2ext_getset59 	int x2(std::string x) {
60 		return static_cast<int>(x.length());
61 	}
62 
setext_getset63 	void set(sol::variadic_args, sol::this_state, int x) {
64 		bark = x;
65 	}
66 
getext_getset67 	int get(sol::this_state, sol::variadic_args) {
68 		return bark;
69 	}
70 
s_setext_getset71 	static void s_set(int) {
72 	}
73 
s_getext_getset74 	static int s_get(int x) {
75 		return x + 20;
76 	}
77 };
78 
79 template <typename T>
des(T & e)80 void des(T& e) {
81 	e.~T();
82 }
83 
84 template <typename SelfType>
85 struct alignas(16) weird_aligned_wrapper {
86 	template <typename F, std::enable_if_t<!std::is_same_v<weird_aligned_wrapper, F>, std::nullptr_t> = nullptr>
weird_aligned_wrapperweird_aligned_wrapper87 	weird_aligned_wrapper(F&& f) : lambda(std::forward<F>(f)) {
88 	}
operator ()weird_aligned_wrapper89 	void operator()(SelfType& self, sol::object param) const {
90 		lambda(self, param.as<float>());
91 	}
92 	std::function<void(SelfType&, float)> lambda;
93 };
94 
95 TEST_CASE("usertype/properties", "Check if member properties/variables work") {
96 	struct bark {
97 		int var = 50;
98 		int var2 = 25;
99 
get_var2bark100 		int get_var2() const {
101 			return var2;
102 		}
103 
get_var3bark104 		int get_var3() {
105 			return var2;
106 		}
107 
set_var2bark108 		void set_var2(int x) {
109 			var2 = x;
110 		}
111 	};
112 
113 	sol::state lua;
114 	sol::stack_guard luasg(lua);
115 	lua.open_libraries(sol::lib::base);
116 
117 	lua.new_usertype<bark>("bark",
118 	     "var",
119 	     &bark::var,
120 	     "var2",
121 	     sol::readonly(&bark::var2),
122 	     "a",
123 	     sol::property(&bark::get_var2, &bark::set_var2),
124 	     "b",
125 	     sol::property(&bark::get_var2),
126 	     "c",
127 	     sol::property(&bark::get_var3),
128 	     "d",
129 	     sol::property(&bark::set_var2));
130 
131 	bark b;
132 	lua.set("b", &b);
133 
134 	lua.safe_script("b.a = 59");
135 	lua.safe_script("var2_0 = b.a");
136 	lua.safe_script("var2_1 = b.b");
137 	lua.safe_script("b.d = 1568");
138 	lua.safe_script("var2_2 = b.c");
139 
140 	int var2_0 = lua["var2_0"];
141 	int var2_1 = lua["var2_1"];
142 	int var2_2 = lua["var2_2"];
143 	REQUIRE(var2_0 == 59);
144 	REQUIRE(var2_1 == 59);
145 	REQUIRE(var2_2 == 1568);
146 
147 	{
148 		auto result = lua.safe_script("b.var2 = 24", sol::script_pass_on_error);
149 		REQUIRE_FALSE(result.valid());
150 	}
151 	{
152 		auto result = lua.safe_script("r = b.d", sol::script_pass_on_error);
153 		REQUIRE_FALSE(result.valid());
154 	}
155 	{
156 		auto result = lua.safe_script("r = b.d", sol::script_pass_on_error);
157 		REQUIRE_FALSE(result.valid());
158 	}
159 	{
160 		auto result = lua.safe_script("b.b = 25", sol::script_pass_on_error);
161 		REQUIRE_FALSE(result.valid());
162 	}
163 	{
164 		auto result = lua.safe_script("b.c = 11", sol::script_pass_on_error);
165 		REQUIRE_FALSE(result.valid());
166 	}
167 }
168 
169 TEST_CASE("usertype/copyability", "make sure user can write to a class variable even if the class itself isn't copy-safe") {
170 	struct NoCopy {
getNoCopy171 		int get() const {
172 			return _you_can_copy_me;
173 		}
setNoCopy174 		void set(int val) {
175 			_you_can_copy_me = val;
176 		}
177 
178 		int _you_can_copy_me;
179 		non_copyable _haha_you_cant_copy_me;
180 	};
181 
182 	sol::state lua;
183 	sol::stack_guard luasg(lua);
184 
185 	lua.new_usertype<NoCopy>("NoCopy", "val", sol::property(&NoCopy::get, &NoCopy::set));
186 
187 	REQUIRE_NOTHROW(lua.safe_script(R"__(
188 nocopy = NoCopy.new()
189 nocopy.val = 5
190                )__"));
191 }
192 
193 TEST_CASE("usertype/protect", "users should be allowed to manually protect a function") {
194 	struct protect_me {
genprotect_me195 		int gen(int x) {
196 			return x;
197 		}
198 	};
199 
200 	sol::state lua;
201 	lua.open_libraries(sol::lib::base);
202 	lua.new_usertype<protect_me>("protect_me", "gen", sol::protect(&protect_me::gen));
203 
204 	REQUIRE_NOTHROW(lua.safe_script(R"__(
205 pm = protect_me.new()
206 value = pcall(pm.gen,pm)
207 )__"));
208 	bool value = lua["value"];
209 	REQUIRE_FALSE(value);
210 }
211 
212 TEST_CASE("usertype/static-properties", "allow for static functions to get and set things as a property") {
213 	static int b = 50;
214 	struct test_t {
s_functest_t215 		static double s_func() {
216 			return b + 0.5;
217 		}
218 
g_functest_t219 		static void g_func(int v) {
220 			b = v;
221 		}
222 
functest_t223 		std::size_t func() {
224 			return 24;
225 		}
226 	};
227 	test_t manager;
228 
229 	sol::state lua;
230 	sol::stack_guard luasg(lua);
231 
232 	lua.new_usertype<test_t>(
233 	     "test", "f", std::function<std::size_t()>(std::bind(std::mem_fn(&test_t::func), &manager)), "g", sol::property(&test_t::s_func, &test_t::g_func));
234 
235 	lua.safe_script("v1 = test.f()");
236 	lua.safe_script("v2 = test.g");
237 	lua.safe_script("test.g = 60");
238 	lua.safe_script("v2a = test.g");
239 	int v1 = lua["v1"];
240 	REQUIRE(v1 == 24);
241 	double v2 = lua["v2"];
242 	REQUIRE(v2 == 50.5);
243 	double v2a = lua["v2a"];
244 	REQUIRE(v2a == 60.5);
245 }
246 
247 TEST_CASE("usertype/var with string literals", "String literals are the bane of my existence and one day C++ will make them not be fucking arrays") {
248 	struct blah { };
249 
250 	sol::state lua;
251 	sol::usertype<blah> x = lua.new_usertype<blah>("blah");
252 	x["__className"] = sol::var("Entity");
253 
254 	std::string cxx_name = x["__className"];
255 	std::string lua_name = lua.script("return blah.__className");
256 	REQUIRE(cxx_name == lua_name);
257 	REQUIRE(cxx_name == "Entity");
258 	REQUIRE(lua_name == "Entity");
259 }
260 
261 TEST_CASE("usertype/var-and-property", "make sure const vars are readonly and properties can handle lambdas") {
262 	const static int arf = 20;
263 
264 	struct test {
265 		int value = 10;
266 	};
267 
268 	sol::state lua;
269 	sol::stack_guard luasg(lua);
270 	lua.open_libraries();
271 
272 	lua.new_usertype<test>(
__anon9334b35e0202(test& t, int x) 273 	     "test", "prop", sol::property([](test& t) { return t.value; }, [](test& t, int x) { t.value = x; }), "global", sol::var(std::ref(arf)));
274 
275 	lua.safe_script(R"(
276 t = test.new()
277 print(t.prop)
278 t.prop = 50
279 print(t.prop)
280 	)");
281 
282 	test& t = lua["t"];
283 	REQUIRE(t.value == 50);
284 
285 	lua.safe_script(R"(
286 t = test.new()
287 print(t.global)
288 	)");
289 	{
290 		auto result = lua.safe_script("t.global = 20", sol::script_pass_on_error);
291 		REQUIRE_FALSE(result.valid());
292 	}
293 	lua.safe_script("print(t.global)");
294 }
295 
296 TEST_CASE("usertype/coverage", "try all the things") {
297 	sol::state lua;
298 	lua.open_libraries(sol::lib::base);
299 
300 	lua.new_usertype<ext_getset>("ext_getset",
301 	     sol::call_constructor,
302 	     sol::constructors<sol::types<>, sol::types<int>>(),
303 	     sol::meta_function::garbage_collect,
304 	     sol::destructor(des<ext_getset>),
305 	     "x",
__anon9334b35e0302(ext_getset& m, std::string x, int y) 306 	     sol::overload(&ext_getset::x, &ext_getset::x2, [](ext_getset& m, std::string x, int y) { return m.meow + 50 + y + static_cast<int>(x.length()); }),
307 	     "bark",
308 	     &ext_getset::bark,
309 	     "meow",
310 	     &ext_getset::meow,
311 	     "readonlybark",
312 	     sol::readonly(&ext_getset::bark),
313 	     "set",
314 	     &ext_getset::set,
315 	     "get",
316 	     &ext_getset::get,
317 	     "sset",
318 	     &ext_getset::s_set,
319 	     "sget",
320 	     &ext_getset::s_get,
321 	     "propbark",
322 	     sol::property(&ext_getset::set, &ext_getset::get),
323 	     "readonlypropbark",
324 	     sol::property(&ext_getset::get),
325 	     "writeonlypropbark",
326 	     sol::property(&ext_getset::set));
327 
328 	INFO("usertype created");
329 
330 	lua.safe_script(R"(
331 e = ext_getset()
332 w = e:x(e:x(), e:x(e:x()))
333 print(w)
334 )");
335 
336 	int w = lua["w"];
337 	REQUIRE(w == (56 + 50 + 14 + 14));
338 
339 	INFO("REQUIRE(w) successful");
340 
341 	lua.safe_script(R"(
342 e:set(500)
343 e.sset(24)
344 x = e:get()
345 y = e.sget(20)
346 )");
347 
348 	int x = lua["x"];
349 	int y = lua["y"];
350 	REQUIRE(x == 500);
351 	REQUIRE(y == 40);
352 
353 	INFO("REQUIRE(x, y) successful");
354 
355 	lua.safe_script(R"(
356 e.bark = 5001
357 a = e:get()
358 print(e.bark)
359 print(a)
360 
361 e.propbark = 9700
362 b = e:get()
363 print(e.propbark)
364 print(b)
365 )");
366 	int a = lua["a"];
367 	int b = lua["b"];
368 
369 	REQUIRE(a == 5001);
370 	REQUIRE(b == 9700);
371 
372 	INFO("REQUIRE(a, b) successful");
373 
374 	lua.safe_script(R"(
375 c = e.readonlybark
376 d = e.meow
377 print(e.readonlybark)
378 print(c)
379 print(e.meow)
380 print(d)
381 )");
382 
383 	int c = lua["c"];
384 	int d = lua["d"];
385 	REQUIRE(c == 9700);
386 	REQUIRE(d == 56);
387 
388 	INFO("REQUIRE(c, d) successful");
389 
390 	lua.safe_script(R"(
391 e.writeonlypropbark = 500
392 z = e.readonlypropbark
393 print(e.readonlybark)
394 print(e.bark)
395 )");
396 
397 	int z = lua["z"];
398 	REQUIRE(z == 500);
399 
400 	INFO("REQUIRE(z) successful");
401 	{
402 		auto result = lua.safe_script("e.readonlybark = 24", sol::script_pass_on_error);
403 		REQUIRE_FALSE(result.valid());
404 		INFO("REQUIRE_FALSE 1 successful");
405 	}
406 	{
407 		auto result = lua.safe_script("e.readonlypropbark = 500", sol::script_pass_on_error);
408 		REQUIRE_FALSE(result.valid());
409 		INFO("REQUIRE_FALSE 2 successful");
410 	}
411 	{
412 		auto result = lua.safe_script("y = e.writeonlypropbark", sol::script_pass_on_error);
413 		REQUIRE_FALSE(result.valid());
414 		INFO("REQUIRE_FALSE 3 successful");
415 	}
416 }
417 
418 TEST_CASE("usertype/alignment", "ensure that alignment does not trigger weird aliasing issues") {
419 	struct aligned_base { };
420 	struct aligned_derived : aligned_base { };
421 
422 	sol::state lua;
423 	sol::stack_guard luasg(lua);
424 
__anon9334b35e0402(aligned_base&, float d) 425 	auto f = [](aligned_base&, float d) { REQUIRE(d == 5.0f); };
426 	lua.new_usertype<aligned_base>("Base", "x", sol::writeonly_property(weird_aligned_wrapper<aligned_base>(std::ref(f))));
427 	lua.new_usertype<aligned_derived>("Derived", sol::base_classes, sol::bases<aligned_base>());
428 
429 	aligned_derived d;
430 	lua["d"] = d;
431 
432 	auto result = lua.safe_script("d.x = 5");
433 	REQUIRE(result.valid());
434 }
435 
436 TEST_CASE("usertype/readonly-and-static-functions", "Check if static functions can be called on userdata and from their originating (meta)tables") {
437 	struct bark {
438 		int var = 50;
439 
funcbark440 		void func() {
441 		}
442 
oh_boybark443 		static void oh_boy() {
444 		}
445 
oh_boybark446 		static int oh_boy(std::string name) {
447 			return static_cast<int>(name.length());
448 		}
449 
operator ()bark450 		int operator()(int x) {
451 			return x;
452 		}
453 	};
454 
455 	sol::state lua;
456 	lua.open_libraries(sol::lib::base);
457 	lua.new_usertype<bark>(
458 	     "bark",
459 	     "var",
460 	     &bark::var,
461 	     "var2",
462 	     sol::readonly(&bark::var),
463 	     "something",
464 	     something_func_true,
465 	     "something2",
__anon9334b35e0502(int x, int y) 466 	     [](int x, int y) { return x + y; },
467 	     "func",
468 	     &bark::func,
469 	     "oh_boy",
470 	     sol::overload(sol::resolve<void()>(&bark::oh_boy), sol::resolve<int(std::string)>(&bark::oh_boy)),
471 	     sol::meta_function::call_function,
472 	     &bark::operator());
473 
474 	{
475 		auto result = lua.safe_script("assert(bark.oh_boy('woo') == 3)", sol::script_pass_on_error);
476 		REQUIRE(result.valid());
477 	}
478 	{
479 		auto result = lua.safe_script("bark.oh_boy()", sol::script_pass_on_error);
480 		REQUIRE(result.valid());
481 	}
482 
483 	bark b;
484 	lua.set("b", &b);
485 
486 	sol::table b_table = lua["b"];
487 	sol::function member_func = b_table["func"];
488 	sol::function s = b_table["something"];
489 	sol::function s2 = b_table["something2"];
490 
491 	sol::table b_metatable = b_table[sol::metatable_key];
492 	bool isvalidmt = b_metatable.valid();
493 	REQUIRE(isvalidmt);
494 	sol::function b_call = b_metatable["__call"];
495 	sol::function b_as_function = lua["b"];
496 
497 	int x = b_as_function(1);
498 	int y = b_call(b, 1);
499 	bool z = s();
500 	int w = s2(2, 3);
501 	REQUIRE(x == 1);
502 	REQUIRE(y == 1);
503 	REQUIRE(z);
504 	REQUIRE(w == 5);
505 
506 	lua.safe_script(R"(
507 lx = b(1)
508 ly = getmetatable(b).__call(b, 1)
509 lz = b.something()
510 lz2 = bark.something()
511 lw = b.something2(2, 3)
512 lw2 = bark.something2(2, 3)
513     )");
514 
515 	int lx = lua["lx"];
516 	int ly = lua["ly"];
517 	bool lz = lua["lz"];
518 	int lw = lua["lw"];
519 	bool lz2 = lua["lz2"];
520 	int lw2 = lua["lw2"];
521 	REQUIRE(lx == 1);
522 	REQUIRE(ly == 1);
523 	REQUIRE(lz);
524 	REQUIRE(lz2);
525 	REQUIRE(lw == 5);
526 	REQUIRE(lw2 == 5);
527 	REQUIRE(lx == ly);
528 	REQUIRE(lz == lz2);
529 	REQUIRE(lw == lw2);
530 
531 	auto result = lua.safe_script("b.var2 = 2", sol::script_pass_on_error);
532 	REQUIRE_FALSE(result.valid());
533 }
534 
535 TEST_CASE("usertype/vars", "usertype vars can bind various class items") {
536 	static int muh_variable = 25;
537 	static int through_variable = 10;
538 
539 	sol::state lua;
540 	lua.open_libraries();
541 	struct test { };
542 	lua.new_usertype<test>("test",
543 	     "straight",
544 	     sol::var(2),
545 	     "global",
546 	     sol::var(muh_variable),
547 	     "ref_global",
548 	     sol::var(std::ref(muh_variable)),
549 	     "global2",
550 	     sol::var(through_variable),
551 	     "ref_global2",
552 	     sol::var(std::ref(through_variable)));
553 
554 	int prets = lua["test"]["straight"];
555 	int pretg = lua["test"]["global"];
556 	int pretrg = lua["test"]["ref_global"];
557 	int pretg2 = lua["test"]["global2"];
558 	int pretrg2 = lua["test"]["ref_global2"];
559 
560 	REQUIRE(prets == 2);
561 	REQUIRE(pretg == 25);
562 	REQUIRE(pretrg == 25);
563 	REQUIRE(pretg2 == 10);
564 	REQUIRE(pretrg2 == 10);
565 
566 	lua.safe_script(R"(
567 print(test.straight)
568 test.straight = 50
569 print(test.straight)
570 )");
571 	int s = lua["test"]["straight"];
572 	REQUIRE(s == 50);
573 
574 	lua.safe_script(R"(
575 t = test.new()
576 print(t.global)
577 t.global = 50
578 print(t.global)
579 )");
580 	int mv = lua["test"]["global"];
581 	REQUIRE(mv == 50);
582 	REQUIRE(muh_variable == 25);
583 
584 	lua.safe_script(R"(
585 print(t.ref_global)
586 t.ref_global = 50
587 print(t.ref_global)
588 )");
589 	int rmv = lua["test"]["ref_global"];
590 	REQUIRE(rmv == 50);
591 	REQUIRE(muh_variable == 50);
592 
593 	REQUIRE(through_variable == 10);
594 	lua.safe_script(R"(
595 print(test.global2)
596 test.global2 = 35
597 print(test.global2)
598 )");
599 	int tv = lua["test"]["global2"];
600 	REQUIRE(through_variable == 10);
601 	REQUIRE(tv == 35);
602 
603 	lua.safe_script(R"(
604 print(test.ref_global2)
605 test.ref_global2 = 35
606 print(test.ref_global2)
607 )");
608 	int rtv = lua["test"]["ref_global2"];
609 	REQUIRE(rtv == 35);
610 	REQUIRE(through_variable == 35);
611 }
612