1 /**
2 Internal module for pushing and getting _functions and delegates.
3 
4 LuaD allows for pushing of all D function or delegate types with return type and parameter types compatible with LuaD (see $(DPMODULE stack)).
5 
6 For a fixed number of multiple return values, return a $(STDREF typecons,Tuple) or a static array. For a variable number of return values, return $(MREF LuaVariableReturn).
7 
8 As a special case for $(D const(char)[]) parameter types in _functions pushed to Lua, no copy of the string is made when called; take care not to escape such references, they are effectively $(D scope) parameters.
9 When a copy is desired, use $(D char[]) or $(D string), or $(D dup) or $(D idup) the string manually.
10 
11 If a function with the $(D lua_CFunction) signature is encountered, it is pushed directly with no inserted conversions or overhead.
12 
13 Typesafe varargs is supported when pushing _functions to Lua, but as of DMD 2.054, compiler bugs prevent getting delegates with varargs from Lua (use $(DPREF lfunction,LuaFunction) instead).
14 */
15 module luad.conversions.functions;
16 
17 import core.memory;
18 import std.range;
19 import std..string : toStringz;
20 import std.traits;
21 import std.typetuple;
22 
23 import luad.c.all;
24 
25 import luad.stack;
26 
27 private void argsError(lua_State* L, int nargs, ptrdiff_t expected)
28 {
29 	lua_Debug debugInfo;
30 	lua_getstack(L, 0, &debugInfo);
31 	lua_getinfo(L, "n", &debugInfo);
32 	luaL_error(L, "call to %s '%s': got %d arguments, expected %d",
33 		debugInfo.namewhat, debugInfo.name, nargs, expected);
34 }
35 
36 template StripHeadQual(T : const(T*))
37 {
38 	alias const(T)* StripHeadQual;
39 }
40 
41 template StripHeadQual(T : const(T[]))
42 {
43 	alias const(T)[] StripHeadQual;
44 }
45 
46 template StripHeadQual(T : immutable(T*))
47 {
48 	alias immutable(T)* StripHeadQual;
49 }
50 
51 template StripHeadQual(T : immutable(T[]))
52 {
53 	alias immutable(T)[] StripHeadQual;
54 }
55 
56 template StripHeadQual(T : T[])
57 {
58 	alias T[] StripHeadQual;
59 }
60 
61 template StripHeadQual(T : T*)
62 {
63 	alias T* StripHeadQual;
64 }
65 
66 template StripHeadQual(T : T[N], size_t N)
67 {
68 	alias T[N] StripHeadQual;
69 }
70 
71 template StripHeadQual(T)
72 {
73 	alias T StripHeadQual;
74 }
75 
76 template FillableParameterTypeTuple(T)
77 {
78 	alias staticMap!(StripHeadQual, ParameterTypeTuple!T) FillableParameterTypeTuple;
79 }
80 
81 template BindableReturnType(T)
82 {
83 	alias StripHeadQual!(ReturnType!T) BindableReturnType;
84 }
85 
86 //Call with or without return value, propagating Exceptions as Lua errors.
87 //This should rather be throwing a userdata with __tostring and a reference to
88 //the thrown exception, as it is now, everything but the error type and message is lost.
89 int callFunction(T)(lua_State* L, T func, ParameterTypeTuple!T args)
90 	if(!is(BindableReturnType!T == const) &&
91 	   !is(BindableReturnType!T == immutable))
92 {
93 	alias BindableReturnType!T RetType;
94 	enum hasReturnValue = !is(RetType == void);
95 
96 	static if(hasReturnValue)
97 		RetType ret;
98 
99 	try
100 	{
101 		static if(hasReturnValue)
102 			ret = func(args);
103 		else
104 			func(args);
105 	}
106 	catch(Exception e)
107 	{
108 		luaL_error(L, "%s", toStringz(e.toString()));
109 	}
110 
111 	static if(hasReturnValue)
112 		return pushReturnValues(L, ret);
113 	else
114 		return 0;
115 }
116 
117 // Ditto, but wrap the try-catch in a nested function because the return value's
118 // declaration and initialization cannot be separated.
119 int callFunction(T)(lua_State* L, T func, ParameterTypeTuple!T args)
120 	if(is(BindableReturnType!T == const) ||
121 	   is(BindableReturnType!T == immutable))
122 {
123 	auto ref call()
124 	{
125 		try
126 			return func(args);
127 		catch(Exception e)
128 			luaL_error(L, "%s", e.toString().toStringz());
129 	}
130 
131 	return pushReturnValues(L, call());
132 }
133 
134 private:
135 
136 // TODO: right now, virtual functions on specialized classes can be called with base classes as 'self', not safe!
137 extern(C) int methodWrapper(T, Class, bool virtual)(lua_State* L)
138 {
139 	alias ParameterTypeTuple!T Args;
140 
141 	static assert ((variadicFunctionStyle!T != Variadic.d && variadicFunctionStyle!T != Variadic.c),
142 		"Non-typesafe variadic functions are not supported.");
143 
144 	//Check arguments
145 	int top = lua_gettop(L);
146 
147 	static if (variadicFunctionStyle!T == Variadic.typesafe)
148 		enum requiredArgs = Args.length;
149 	else
150 		enum requiredArgs = Args.length + 1;
151 
152 	if(top < requiredArgs)
153 		argsError(L, top, requiredArgs);
154 
155 	Class self =  *cast(Class*)luaL_checkudata(L, 1, toStringz(Class.mangleof));
156 
157 	static if(virtual)
158 	{
159 		alias ReturnType!T function(Class, Args) VirtualWrapper;
160 		VirtualWrapper func = cast(VirtualWrapper)lua_touserdata(L, lua_upvalueindex(1));
161 	}
162 	else
163 	{
164 		T func;
165 		func.ptr = cast(void*)self;
166 		func.funcptr = cast(typeof(func.funcptr))lua_touserdata(L, lua_upvalueindex(1));
167 	}
168 
169 	//Assemble arguments
170 	static if(virtual)
171 	{
172 		ParameterTypeTuple!VirtualWrapper allArgs;
173 		allArgs[0] = self;
174 		alias allArgs[1..$] args;
175 	}
176 	else
177 	{
178 		Args allArgs;
179 		alias allArgs args;
180 	}
181 
182 	foreach(i, Arg; Args)
183 		args[i] = getArgument!(T, i)(L, i + 2);
184 
185 	return callFunction!(typeof(func))(L, func, allArgs);
186 }
187 
188 extern(C) int functionWrapper(T)(lua_State* L)
189 {
190 	alias FillableParameterTypeTuple!T Args;
191 
192 	static assert ((variadicFunctionStyle!T != Variadic.d && variadicFunctionStyle!T != Variadic.c),
193 		"Non-typesafe variadic functions are not supported.");
194 
195 	//Check arguments
196 	int top = lua_gettop(L);
197 
198 	static if (variadicFunctionStyle!T == Variadic.typesafe)
199 		enum requiredArgs = Args.length - 1;
200 	else
201 		enum requiredArgs = Args.length;
202 
203 	if(top < requiredArgs)
204 		argsError(L, top, requiredArgs);
205 
206 	//Get function
207 	static if(isFunctionPointer!T)
208 		T func = cast(T)lua_touserdata(L, lua_upvalueindex(1));
209 	else
210 		T func = *cast(T*)lua_touserdata(L, lua_upvalueindex(1));
211 
212 	//Assemble arguments
213 	Args args;
214 	foreach(i, Arg; Args)
215 		args[i] = getArgument!(T, i)(L, i + 1);
216 
217 	return callFunction!T(L, func, args);
218 }
219 
220 extern(C) int functionCleaner(lua_State* L)
221 {
222 	GC.removeRoot(lua_touserdata(L, 1));
223 	return 0;
224 }
225 
226 public:
227 
228 void pushFunction(T)(lua_State* L, T func) if (isSomeFunction!T)
229 {
230 	static if(isFunctionPointer!T)
231 		lua_pushlightuserdata(L, func);
232 	else
233 	{
234 		T* udata = cast(T*)lua_newuserdata(L, T.sizeof);
235 		*udata = func;
236 
237 		GC.addRoot(udata);
238 
239 		if(luaL_newmetatable(L, "__dcall") == 1)
240 		{
241 			lua_pushcfunction(L, &functionCleaner);
242 			lua_setfield(L, -2, "__gc");
243 		}
244 
245 		lua_setmetatable(L, -2);
246 	}
247 
248 	lua_pushcclosure(L, &functionWrapper!T, 1);
249 }
250 
251 // TODO: optimize for non-virtual functions
252 void pushMethod(Class, string member)(lua_State* L) if (isSomeFunction!(__traits(getMember, Class, member)))
253 {
254 	alias typeof(mixin("&Class.init." ~ member)) T;
255 
256 	// Delay vtable lookup until the right time
257 	static ReturnType!T virtualWrapper(Class self, ParameterTypeTuple!T args)
258 	{
259 		return mixin("self." ~ member)(args);
260 	}
261 
262 	lua_pushlightuserdata(L, &virtualWrapper);
263 	lua_pushcclosure(L, &methodWrapper!(T, Class, true), 1);
264 }
265 
266 /**
267  * Currently this function allocates a reference in the registry that is never deleted,
268  * one for each call... see code comments
269  */
270 T getFunction(T)(lua_State* L, int idx) if (is(T == delegate))
271 {
272 	auto func = new class
273 	{
274 		int lref;
275 		this()
276 		{
277 			lua_pushvalue(L, idx);
278 			lref = luaL_ref(L, LUA_REGISTRYINDEX);
279 		}
280 
281 		//Alright... how to fix this?
282 		//The problem is that this object tends to be finalized after L is freed (by LuaState's destructor or otherwise).
283 		//If you have a good solution to the problem of dangling references to a lua_State,
284 		//please contact me :)
285 
286 		/+~this()
287 		{
288 			luaL_unref(L, LUA_REGISTRYINDEX, lref);
289 		}+/
290 
291 		void push()
292 		{
293 			lua_rawgeti(L, LUA_REGISTRYINDEX, lref);
294 		}
295 	};
296 
297 	alias ReturnType!T RetType;
298 	alias ParameterTypeTuple!T Args;
299 
300 	return delegate RetType(Args args)
301 	{
302 		func.push();
303 		foreach(arg; args)
304 			pushValue(L, arg);
305 
306 		return callWithRet!RetType(L, args.length);
307 	};
308 }
309 
310 /**
311  * Type for efficiently returning a variable number of return values
312  * from a function.
313  *
314  * Use $(D variableReturn) to instantiate it.
315  * Params:
316  *   Range = any input range
317  */
318 struct LuaVariableReturn(Range) if(isInputRange!Range)
319 {
320 	alias WrappedType = Range; /// The type of the wrapped input range.
321 	Range returnValues; /// The wrapped input range.
322 }
323 
324 /**
325  * Create a LuaVariableReturn object for efficiently returning
326  * a variable number of values from a function.
327  * Params:
328  *   returnValues = any input range
329  * Example:
330 -----------------------------
331 	LuaVariableReturn!(uint[]) makeList(uint n)
332 	{
333 		uint[] list;
334 
335 		foreach(i; 1 .. n + 1)
336 		{
337 			list ~= i;
338 		}
339 
340 		return variableReturn(list);
341 	}
342 
343 	lua["makeList"] = &makeList;
344 
345 	lua.doString(`
346 		local one, two, three, four = makeList(4)
347 		assert(one == 1)
348 		assert(two == 2)
349 		assert(three == 3)
350 		assert(four == 4)
351 	`);
352 -----------------------------
353  */
354 LuaVariableReturn!Range variableReturn(Range)(Range returnValues)
355 	if(isInputRange!Range)
356 {
357 	return typeof(return)(returnValues);
358 }
359 
360 version(unittest)
361 {
362 	import luad.testing;
363 	import std.typecons;
364 	private lua_State* L;
365 }
366 
367 unittest
368 {
369 	L = luaL_newstate();
370 	luaL_openlibs(L);
371 
372 	//functions
373 	static const(char)[] func(const(char)[] s)
374 	{
375 		return "Hello, " ~ s;
376 	}
377 
378 	pushValue(L, &func);
379 	assert(lua_isfunction(L, -1));
380 	lua_setglobal(L, "sayHello");
381 
382 	unittest_lua(L, `
383 		local ret = sayHello("foo")
384 		local expect = "Hello, foo"
385 		assert(ret == expect,
386 			("sayHello return type - got '%s', expected '%s'"):format(ret, expect)
387 		)
388 	`);
389 
390 	static uint countSpaces(const(char)[] s)
391 	{
392 		uint n = 0;
393 		foreach(dchar c; s)
394 			if(c == ' ')
395 				++n;
396 
397 		return n;
398 	}
399 
400 	pushValue(L, &countSpaces);
401 	assert(lua_isfunction(L, -1));
402 	lua_setglobal(L, "countSpaces");
403 
404 	unittest_lua(L, `
405 		assert(countSpaces("Hello there, world!") == 2)
406 	`);
407 
408 	//delegates
409 	double curry = 3.14 * 2;
410 	double closure(double x)
411 	{
412 		return curry * x;
413 	}
414 
415 	pushValue(L, &closure);
416 	assert(lua_isfunction(L, -1));
417 	lua_setglobal(L, "circle");
418 
419 	unittest_lua(L, `
420 		assert(circle(2) == 3.14 * 4, "closure return type mismatch!")
421 	`);
422 
423 	// Const parameters
424 	static bool isEmpty(const(char[]) str) { return str.length == 0; }
425 	static bool isEmpty2(in char[] str) { return str.length == 0; }
426 
427 	pushValue(L, &isEmpty);
428 	lua_setglobal(L, "isEmpty");
429 
430 	pushValue(L, &isEmpty2);
431 	lua_setglobal(L, "isEmpty2");
432 
433 	unittest_lua(L, `
434 		assert(isEmpty(""))
435 		assert(isEmpty2(""))
436 		assert(not isEmpty("a"))
437 		assert(not isEmpty2("a"))
438 	`);
439 
440 	// Immutable parameters
441 	static immutable(char[]) returnArg(immutable(char[]) str) { return str; }
442 
443 	pushValue(L, &returnArg);
444 	lua_setglobal(L, "returnArg");
445 
446 	unittest_lua(L, `assert(returnArg("foo") == "foo")`);
447 }
448 
449 version(unittest) import luad.base;
450 
451 // multiple return values
452 unittest
453 {
454 	// tuple returns
455 	auto nameInfo = ["foo"];
456 	auto ageInfo = [42];
457 
458 	alias Tuple!(string, "name", uint, "age") GetInfoResult;
459 	GetInfoResult getInfo(int idx)
460 	{
461 		GetInfoResult result;
462 		result.name = nameInfo[idx];
463 		result.age = ageInfo[idx];
464 		return result;
465 	}
466 
467 	pushValue(L, &getInfo);
468 	lua_setglobal(L, "getInfo");
469 
470 	unittest_lua(L, `
471 		local name, age = getInfo(0)
472 		assert(name == "foo")
473 		assert(age == 42)
474 	`);
475 
476 	// static array returns
477 	static string[2] getName()
478 	{
479 		string[2] ret;
480 		ret[0] = "Foo";
481 		ret[1] = "Bar";
482 		return ret;
483 	}
484 
485 	pushValue(L, &getName);
486 	lua_setglobal(L, "getName");
487 
488 	unittest_lua(L, `
489 		local first, last = getName()
490 		assert(first == "Foo")
491 		assert(last == "Bar")
492 	`);
493 
494 	// variable length returns
495 	LuaVariableReturn!(uint[]) makeList(uint n)
496 	{
497 		uint[] list;
498 
499 		foreach(i; 1 .. n + 1)
500 		{
501 			list ~= i;
502 		}
503 
504 		return variableReturn(list);
505 	}
506 
507 	auto makeList2(uint n)
508 	{
509 		return variableReturn(iota(1, n + 1));
510 	}
511 
512 	pushValue(L, &makeList);
513 	lua_setglobal(L, "makeList");
514 	pushValue(L, &makeList2);
515 	lua_setglobal(L, "makeList2");
516 
517 	unittest_lua(L, `
518 		for i, f in pairs{makeList, makeList2} do
519 			local one, two, three, four = f(4)
520 			assert(one == 1)
521 			assert(two == 2)
522 			assert(three == 3)
523 			assert(four == 4)
524 		end
525 	`);
526 }
527 
528 // Variadic function arguments
529 unittest
530 {
531 	static string concat(const(char)[][] pieces...)
532 	{
533 		string result;
534 		foreach(piece; pieces)
535 			result ~= piece;
536 		return result;
537 	}
538 
539 	pushValue(L, &concat);
540 	lua_setglobal(L, "concat");
541 
542 	unittest_lua(L, `
543 		local whole = concat("he", "llo", ", ", "world!")
544 		assert(whole == "hello, world!")
545 	`);
546 
547 	//Test with zero parameters.
548 	unittest_lua(L, `
549 		local blank = concat()
550 		assert (string.len(blank) == 0)
551 	`);
552 
553 	static const(char)[] concat2(char separator, const(char)[][] pieces...)
554 	{
555 		if(pieces.length == 0)
556 			return "";
557 
558 		string result;
559 		foreach(piece; pieces[0..$-1])
560 			result ~= piece ~ separator;
561 
562 		return result ~ pieces[$-1];
563 	}
564 
565 	pushValue(L, &concat2);
566 	lua_setglobal(L, "concat2");
567 
568 	unittest_lua(L, `
569 		local whole = concat2(",", "one", "two", "three", "four")
570 		assert(whole == "one,two,three,four")
571 	`);
572 
573 	//C- and D-style variadic versions of concat for
574 	//future use if/when these are supported.
575 
576 	//C varargs require at least one fixed argument.
577 	import core.vararg;
578 	// C-style varargs broken on Linux for 2.066.1?
579 	version(none) extern(C) static string concat_cvar (size_t count, ...)
580 	{
581 		string result;
582 
583 		va_list args;
584 
585 		va_start(args, count);
586 
587 		foreach(immutable i; 0 .. count)
588 		{
589 			auto arg = va_arg!LuaObject(args);
590 			result ~= arg.toString();
591 		}
592 
593 		va_end(args);
594 
595 		return result;
596 	}
597 
598 	//D-style variadics have an _arguments array that specifies
599 	//the type of each passed argument.
600 	static string concat_dvar (...) {
601 		string result;
602 
603 		foreach (argtype; _arguments) {
604 			assert (argtype == typeid(LuaObject));
605 			auto arg = va_arg!(LuaObject)(_argptr);
606 
607 			result ~= arg.toString();
608 		}
609 
610 		return result;
611 	}
612 }
613 
614 // get delegates from Lua
615 unittest
616 {
617 	lua_getglobal(L, "string");
618 	lua_getfield(L, -1, "match");
619 	auto match = popValue!(string delegate(string, string))(L);
620 	lua_pop(L, 1);
621 
622 	auto result = match("foobar@example.com", "([^@]+)@example.com");
623 	assert(result == "foobar");
624 
625 	// multiple return values
626 	luaL_dostring(L, `function multRet(a) return "foo", a end`);
627 	lua_getglobal(L, "multRet");
628 	auto multRet = popValue!(Tuple!(string, int) delegate(int))(L);
629 
630 	auto results = multRet(42);
631 	assert(results[0] == "foo");
632 	assert(results[1] == 42);
633 }
634 
635 // Nested call stack testing
636 unittest
637 {
638 	alias string delegate(string) MyFun;
639 
640 	MyFun[string] funcs;
641 
642 	pushValue(L, (string name, MyFun fun) {
643 		funcs[name] = fun;
644 	});
645 	lua_setglobal(L, "addFun");
646 
647 	pushValue(L, (string name, string arg) {
648 		auto top = lua_gettop(L);
649 		auto result = funcs[name](arg);
650 		assert(lua_gettop(L) == top);
651 		return result;
652 	});
653 	lua_setglobal(L, "callFun");
654 
655 	auto top = lua_gettop(L);
656 
657 	luaL_dostring(L, q{
658 		addFun("echo", function(s) return s end)
659 		local result = callFun("echo", "test")
660 		assert(result == "test")
661 	});
662 
663 	assert(lua_gettop(L) == top);
664 }
665 
666 unittest
667 {
668 	assert(lua_gettop(L) == 0);
669 	lua_close(L);
670 }