1 /++
2 This internal module, with the help of the luad.conversions package, takes care of converting between D and Lua types.
3 
4 The conversion rules are as follows, where conversion goes both ways:
5 $(DL
6 	$(DT boolean
7 		$(DD $(D bool))
8 	)
9 	$(DT number
10 		$(DD $(D lua_Integer) (default $(D int)))
11 		$(DD $(D lua_Number) (default $(D double)))
12 	)
13 	$(DT string
14 		$(DD $(D string), $(D const(char)[]), $(D char[]))
15 		$(DD $(D const(char)*))
16 		$(DD $(D char))
17 		$(DD $(D immutable(void)[]), $(D const(void)[]), $(D void[]) (binary data))
18 	)
19 	$(DT table
20 		$(DD associative arrays (see $(DPMODULE2 conversions,assocarrays)))
21 		$(DD arrays (see $(DPMODULE2 conversions,arrays)))
22 		$(DD structs (see $(DPMODULE2 conversions,structs)))
23 		$(DD $(DPREF table,LuaTable))
24 	)
25 	$(DT function (see $(DPMODULE2 conversions,functions))
26 		$(DD function pointers)
27 		$(DD delegates)
28 		$(DD $(DPREF lfunction,LuaFunction))
29 	)
30 	$(DT userdata
31 		$(DD classes (see $(DPMODULE2 conversions,classes)))
32 	)
33 	$(DT nil
34 		$(DD the special identifier $(D nil))
35 		$(DD $(D null) class references)
36 	)
37 	$(DT any of the above
38 		$(DD $(DPREF base,LuaObject))
39 		$(DD $(DPREF dynamic,LuaDynamic))
40 		$(DD $(D Algebraic), when given a compatible value (see $(DPMODULE2 conversions,variant)))
41 	)
42 )
43 
44 The conversions are checked in the specified order. For example, even though $(D bool) is implicitly convertible
45 to $(D lua_Integer), it will be converted to a boolean because boolean has precedence.
46 
47 $(D wchar) and $(D dchar) are explicitly disallowed. Lua strings consist of 8-bit characters, if you want to push UTF-16 or UTF-32 strings, convert to UTF-8 first.
48 
49 Additionally, the following types are pushable to Lua, but can't be retrieved back:
50 $(DL
51 	$(DT function
52 		$(DD $(D lua_CFunction))
53 	)
54 )
55 +/
56 module luad.stack;
57 
58 import std.range;
59 import std.traits;
60 import std.typecons;
61 
62 import luad.c.all;
63 
64 import luad.base;
65 import luad.table;
66 import luad.lfunction;
67 import luad.dynamic;
68 
69 import luad.conversions.functions;
70 import luad.conversions.arrays;
71 import luad.conversions.structs;
72 import luad.conversions.assocarrays;
73 import luad.conversions.classes;
74 import luad.conversions.variant;
75 
76 /**
77  * Push a value of any type to the stack.
78  * Params:
79  *	 L = stack to push to
80  *	 value = value to push
81  */
82 void pushValue(T)(lua_State* L, T value)
83 {
84 	static if(is(T : LuaObject))
85 		value.push();
86 
87 	else static if(is(T == LuaDynamic))
88 		value.object.push();
89 
90 	else static if(is(T == Nil))
91 		lua_pushnil(L);
92 
93 	else static if(is(T == bool))
94 		lua_pushboolean(L, cast(bool)value);
95 
96 	else static if(is(T == char))
97 		lua_pushlstring(L, &value, 1);
98 
99 	else static if(is(T : lua_Integer))
100 		lua_pushinteger(L, value);
101 
102 	else static if(is(T : lua_Number))
103 		lua_pushnumber(L, value);
104 
105 	else static if(is(T : const(char)[]))
106 		lua_pushlstring(L, value.ptr, value.length);
107 
108 	else static if(isVoidArray!T)
109 		lua_pushlstring(L, cast(const(char)*)value.ptr, value.length);
110 
111 	else static if(is(T : const(char)*))
112 		lua_pushstring(L, value);
113 
114 	else static if(isVariant!T)
115 		pushVariant(L, value);
116 
117 	else static if(isAssociativeArray!T)
118 		pushAssocArray(L, value);
119 
120 	else static if(isArray!T)
121 		pushArray(L, value);
122 
123 	else static if(is(T == struct))
124 		pushStruct(L, value);
125 
126 	// luaCFunction's are directly pushed
127 	else static if(is(T == lua_CFunction) && functionLinkage!T == "C")
128 		lua_pushcfunction(L, value);
129 
130 	// other functions are wrapped
131 	else static if(isSomeFunction!T)
132 		pushFunction(L, value);
133 
134 	else static if(is(T == class))
135 	{
136 		if(value is null)
137 			lua_pushnil(L);
138 		else
139 			pushClassInstance(L, value);
140 	}
141 	else
142 		static assert(false, "Unsupported type `" ~ T.stringof ~ "` in stack push operation");
143 }
144 
145 template isVoidArray(T)
146 {
147 	enum isVoidArray = is(T == void[]) ||
148 	                   is(T == const(void)[]) ||
149 	                   is(T == const(void[])) ||
150 	                   is(T == immutable(void)[]) ||
151 	                   is(T == immutable(void[]));
152 }
153 
154 /**
155  * Get the associated Lua type for T.
156  * Returns: Lua type for T
157  */
158 template luaTypeOf(T)
159 {
160 	static if(is(T == bool))
161 		enum luaTypeOf = LUA_TBOOLEAN;
162 
163 	else static if(is(T == Nil))
164 		enum luaTypeOf = LUA_TNIL;
165 
166 	else static if(is(T : const(char)[]) || is(T : const(char)*) || is(T == char) || isVoidArray!T)
167 		enum luaTypeOf = LUA_TSTRING;
168 
169 	else static if(is(T : lua_Integer) || is(T : lua_Number))
170 		enum luaTypeOf = LUA_TNUMBER;
171 
172 	else static if(isSomeFunction!T || is(T == LuaFunction))
173 		enum luaTypeOf = LUA_TFUNCTION;
174 
175 	else static if(isArray!T || isAssociativeArray!T || is(T == struct) || is(T == LuaTable))
176 		enum luaTypeOf = LUA_TTABLE;
177 
178 	else static if(is(T : Object))
179 		enum luaTypeOf = LUA_TUSERDATA;
180 
181 	else
182 		static assert(false, "No Lua type defined for `" ~ T.stringof ~ "`");
183 }
184 
185 // generic type mismatch message
186 private void defaultTypeMismatch(lua_State* L, int idx, int expectedType)
187 {
188 	luaL_error(L, "expected %s, got %s", lua_typename(L, expectedType), luaL_typename(L, idx));
189 }
190 
191 // type mismatch for function arguments of unexpected type
192 private void argumentTypeMismatch(lua_State* L, int idx, int expectedType)
193 {
194 	luaL_typerror(L, idx, lua_typename(L, expectedType));
195 }
196 
197 /**
198  * Get a value of any type from the stack.
199  * Params:
200  *	 T = type of value
201  *	 typeMismatchHandler = function called to produce an error in case of an invalid conversion.
202  *	 L = stack to get from
203  *	 idx = value stack index
204  */
205 T getValue(T, alias typeMismatchHandler = defaultTypeMismatch)(lua_State* L, int idx)
206 {
207 	debug //ensure unchanged stack
208 	{
209 		int _top = lua_gettop(L);
210 		scope(success) assert(lua_gettop(L) == _top);
211 	}
212 
213 	//ambiguous types
214 	static if(is(T == wchar) || is(T : const(wchar)[]) ||
215 			  is(T == dchar) || is(T : const(dchar)[]))
216 	{
217 		static assert("Ambiguous type " ~ T.stringof ~ " in stack push operation. Consider converting before pushing.");
218 	}
219 
220 	static if(!is(T == LuaObject) && !is(T == LuaDynamic) && !isVariant!T)
221 	{
222 		int type = lua_type(L, idx);
223 		enum expectedType = luaTypeOf!T;
224 
225 		//if a class reference, return null for nil values
226 		static if(is(T : Object))
227 		{
228 			if(type == LuaType.Nil)
229 				return null;
230 		}
231 
232 		if(type != expectedType)
233 			typeMismatchHandler(L, idx, expectedType);
234 	}
235 
236 	static if(is(T == LuaFunction)) // WORKAROUND: bug #6036
237 	{
238 		LuaFunction func;
239 		func.object = LuaObject(L, idx);
240 		return func;
241 	}
242 	else static if(is(T == LuaDynamic)) // ditto
243 	{
244 		LuaDynamic obj;
245 		obj.object = LuaObject(L, idx);
246 		return obj;
247 	}
248 	else static if(is(T : LuaObject))
249 		return T(L, idx);
250 
251 	else static if(is(T == Nil))
252 		return nil;
253 
254 	else static if(is(T == bool))
255 		return lua_toboolean(L, idx);
256 
257 	else static if(is(T == char))
258 		return *lua_tostring(L, idx); // TODO: better define this
259 
260 	else static if(is(T : lua_Integer))
261 		return cast(T)lua_tointeger(L, idx);
262 
263 	else static if(is(T : lua_Number))
264 		return cast(T)lua_tonumber(L, idx);
265 
266 	else static if(is(T : const(char)[]) || isVoidArray!T)
267 	{
268 		size_t len;
269 		const(char)* str = lua_tolstring(L, idx, &len);
270 		static if(is(T == char[]) || is(T == void[]))
271 			return str[0 .. len].dup;
272 		else
273 			return str[0 .. len].idup;
274 	}
275 	else static if(is(T : const(char)*))
276 		return lua_tostring(L, idx);
277 
278 	else static if(isAssociativeArray!T)
279 		return getAssocArray!T(L, idx);
280 
281 	else static if(isArray!T)
282 		return getArray!T(L, idx);
283 
284 	else static if(isVariant!T)
285 	{
286 		if(!isAllowedType!T(L, idx))
287 			luaL_error(L, "Type not allowed in Variant: %s", luaL_typename(L, idx));
288 
289 		return getVariant!T(L, idx);
290 	}
291 	else static if(is(T == struct))
292 		return getStruct!T(L, idx);
293 
294 	else static if(isSomeFunction!T)
295 		return getFunction!T(L, idx);
296 
297 	else static if(is(T : Object))
298 		return getClassInstance!T(L, idx);
299 
300 	else
301 	{
302 		static assert(false, "Unsupported type `" ~ T.stringof ~ "` in stack read operation");
303 	}
304 }
305 
306 /**
307  * Same as calling getValue!(T, typeMismatchHandler)(L, -1), then popping one value from the stack.
308  * See_Also: $(MREF getValue)
309  */
310 T popValue(T, alias typeMismatchHandler = defaultTypeMismatch)(lua_State* L)
311 {
312 	scope(success) lua_pop(L, 1);
313 	return getValue!(T, typeMismatchHandler)(L, -1);
314 }
315 
316 /**
317  * Pop a number of elements from the stack.
318  * Params:
319  *   T = element type
320  *   L = stack to pop from
321  *   n = number of elements to pop
322  * Returns:
323  *	 array of popped elements, or a $(D null) array if n = 0
324  */
325 T[] popStack(T = LuaObject)(lua_State* L, size_t n)
326 {
327 	if(n == 0) // Don't allocate an array in this case
328 		return null;
329 
330 	auto stack = new T[n];
331 	foreach(i; 0 .. n)
332 	{
333 		stack[i] = getValue!T(L, cast(int)(-n + i));
334 	}
335 
336 	lua_pop(L, cast(int)n);
337 	return stack;
338 }
339 
340 /// Get a function argument from the stack.
341 auto getArgument(T, int narg)(lua_State* L, int idx)
342 {
343 	alias ParameterTypeTuple!T Args;
344 
345 	static if(narg == -1) // varargs causes this
346 		alias ForeachType!(Args[$-1]) Arg;
347 	else
348 		alias Args[narg] Arg;
349 
350 	enum isVarargs = variadicFunctionStyle!T == Variadic.typesafe;
351 
352 	static if(isVarargs && narg == Args.length-1)
353 	{
354 		alias Args[narg] LastArg;
355 		alias ForeachType!LastArg ElemType;
356 
357 		auto top = lua_gettop(L);
358 		auto size = top - idx + 1;
359 		LastArg result = new LastArg(size);
360 		foreach(i; 0 .. size)
361 		{
362 			result[i] = getArgument!(T, -1)(L, idx + i);
363 		}
364 		return result;
365 	}
366 	else static if(is(Arg == const(char)[]) || is(Arg == const(void)[]) ||
367 				   is(Arg == const(char[])) || is(Arg == const(void[])))
368 	{
369 		if(lua_type(L, idx) != LUA_TSTRING)
370 			argumentTypeMismatch(L, idx, LUA_TSTRING);
371 
372 		size_t len;
373 		const(char)* cstr = lua_tolstring(L, idx, &len);
374 		return cstr[0 .. len];
375 	}
376 	else
377 		return getValue!(Arg, argumentTypeMismatch)(L, idx);
378 }
379 
380 template isVariableReturnType(T : LuaVariableReturn!U, U)
381 {
382 	enum isVariableReturnType = true;
383 }
384 
385 template isVariableReturnType(T)
386 {
387 	enum isVariableReturnType = false;
388 }
389 
390 /// Used for getting a suitable nresults argument to $(D lua_call) or $(D lua_pcall).
391 template returnTypeSize(T)
392 {
393 	static if(isVariableReturnType!T)
394 		enum returnTypeSize = LUA_MULTRET;
395 
396 	else static if(isTuple!T)
397 		enum returnTypeSize = T.Types.length;
398 
399 	else static if(isStaticArray!T)
400 		enum returnTypeSize = T.length;
401 
402 	else static if(is(T == void))
403 		enum returnTypeSize = 0;
404 
405 	else
406 		enum returnTypeSize = 1;
407 }
408 
409 /**
410  * Pop return values from stack.
411  * Defaults to $(MREF popValue), but has special handling for $(DPREF2 conversions, functions, LuaVariableReturn),
412  * $(STDREF typecons, Tuple), static arrays and $(D void).
413  * Params:
414  *    nret = number of return values
415  * Returns:
416  *    Return value, collection of return values, or nothing
417  */
418 T popReturnValues(T)(lua_State* L, size_t nret)
419 {
420 	static if(isVariableReturnType!T)
421 		return variableReturn(popStack!(ElementType!(T.WrappedType))(L, nret));
422 
423 	else static if(isTuple!T)
424 	{
425 		if(nret < T.Types.length)
426 			luaL_error(L, "expected %d return values, got %d", T.Types.length, nret);
427 
428 		return popTuple!T(L);
429 	}
430 	else static if(isStaticArray!T)
431 	{
432 		T ret;
433 		fillStaticArray(L, ret);
434 		return ret;
435 	}
436 	else static if(is(T == void))
437 		return;
438 
439 	else
440 	{
441 		if(nret < 1)
442 			luaL_error(L, "expected return value of type %s, got nil", lua_typename(L, luaTypeOf!T));
443 
444 		return popValue!T(L);
445 	}
446 }
447 
448 /**
449  * Push return values to the stack.
450  * Defaults to $(MREF pushValue), but has special handling for $(DPREF2 conversions, functions, LuaVariableReturn),
451  * $(STDREF typecons, Tuple) and static arrays.
452  */
453 int pushReturnValues(T)(lua_State* L, T value)
454 {
455 	static if(isVariableReturnType!T)
456 	{
457 		enum calculateLength = !hasLength!(typeof(value.returnValues));
458 
459 		static if(calculateLength)
460 			int length;
461 
462 		foreach(obj; value.returnValues)
463 		{
464 			pushValue(L, obj);
465 
466 			static if(calculateLength)
467 				++length;
468 		}
469 
470 		static if(calculateLength)
471 			return length;
472 		else
473 			return cast(int)value.returnValues.length;
474 	}
475 	else static if(isTuple!T)
476 	{
477 		pushTuple(L, value);
478 		return cast(int)T.Types.length;
479 	}
480 	else static if(isStaticArray!T)
481 	{
482 		pushStaticArray(L, value);
483 		return cast(int)value.length;
484 	}
485 	else
486 	{
487 		pushValue(L, value);
488 		return 1;
489 	}
490 }
491 
492 /// Pops a $(STDREF typecons, Tuple) from the values at the top of the stack.
493 T popTuple(T)(lua_State* L) if(isTuple!T)
494 {
495 	T tup;
496 	foreach(i, Elem; T.Types)
497 		tup[i] = getValue!Elem(L, cast(int)(-T.Types.length + i));
498 
499 	lua_pop(L, T.Types.length);
500 	return tup;
501 }
502 
503 /// Pushes all the values in a $(STDREF typecons, Tuple) to the stack.
504 void pushTuple(T)(lua_State* L, ref T tup) if(isTuple!T)
505 {
506 	foreach(i, Elem; T.Types)
507 		pushValue(L, tup[i]);
508 }
509 
510 /**
511  * Call a Lua function and handle its return values.
512  * Params:
513  *    T = type of return value or container of return values
514  *    nargs = number of arguments
515  * Returns:
516  * Zero, one or all return values as $(D T), taking into account $(D void),
517  * $(DPREF2 conversions, functions, LuaVariableReturn) and $(STDREF typecons, Tuple) returns
518  */
519 T callWithRet(T)(lua_State* L, int nargs)
520 {
521 	static if(isVariableReturnType!T)
522 		auto frame = lua_gettop(L) - nargs - 1; // the size of the stack before arguments and the function
523 
524 	lua_call(L, nargs, returnTypeSize!T);
525 
526 	static if(isVariableReturnType!T)
527 		auto nret = lua_gettop(L) - frame;
528 	else
529 		auto nret = returnTypeSize!T;
530 
531 	return popReturnValues!T(L, nret);
532 }
533 
534 private extern(C) int printf(const(char)* fmt, ...);
535 
536 /// Print the Lua stack to $(D stdout).
537 void printStack(lua_State* L)
538 {
539 	auto top = lua_gettop(L);
540 
541 	foreach(n; 0 .. top)
542 	{
543 		auto str = luaL_tolstring(L, n + 1, null);
544 		printf("\t[%d] %s (%s)\r\n", n + 1, str, luaL_typename(L, n + 1));
545 	}
546 
547 	lua_pop(L, top); // luaL_tolstring always pushes one
548 }
549 
550 version(unittest) import luad.testing;
551 
552 unittest
553 {
554 	lua_State* L = luaL_newstate();
555 	scope(success) lua_close(L);
556 
557 	// pushValue and popValue
558 	//number
559 	pushValue(L, cast(ubyte)123);
560 	assert(lua_isnumber(L, -1) && (popValue!ubyte(L) == 123));
561 
562 	pushValue(L, cast(short)123);
563 	assert(lua_isnumber(L, -1) && (popValue!short(L) == 123));
564 
565 	pushValue(L, 123);
566 	assert(lua_isnumber(L, -1) && (popValue!int(L) == 123));
567 
568 	pushValue(L, 123UL);
569 	assert(lua_isnumber(L, -1) && (popValue!ulong(L) == 123));
570 
571 	pushValue(L, 1.2f);
572 	assert(lua_isnumber(L, -1) && (popValue!float(L) == 1.2f));
573 
574 	pushValue(L, 1.23);
575 	assert(lua_isnumber(L, -1) && (popValue!double(L) == 1.23));
576 
577 	//string
578 	string istr = "foobar";
579 	pushValue(L, istr);
580 	assert(lua_isstring(L, -1) && (popValue!string(L) == "foobar"));
581 
582 	char[] str = "baz".dup;
583 	pushValue(L, str);
584 	assert(lua_isstring(L, -1) && (popValue!(char[])(L) == "baz"));
585 
586 	const(char)* cstr = "hi";
587 	pushValue(L, cstr);
588 	assert(lua_isstring(L, -1) && (strcmp(cstr, popValue!(const(char)*)(L)) == 0));
589 
590 	//char
591 	pushValue(L, '\t');
592 	assert(lua_isstring(L, -1) && getValue!string(L, -1) == "\t");
593 	assert(popValue!char(L) == '\t');
594 
595 	//boolean
596 	pushValue(L, true);
597 	assert(lua_isboolean(L, -1) && (popValue!bool(L) == true));
598 
599 	assert(lua_gettop(L) == 0, "bad popValue semantics for primitives");
600 
601 	//void arrays
602 	immutable void[] iarr = "foobar";
603 	pushValue(L, iarr);
604 	assert(lua_isstring(L, -1) && popValue!(typeof(iarr))(L) == "foobar");
605 
606 	void[] arr ="baz".dup;
607 	pushValue(L, arr);
608 	assert(lua_isstring(L, -1) && popValue!(void[])(L) == "baz");
609 
610 	//popStack
611 	extern(C) static int luacfunc(lua_State* L)
612 	{
613 		return 0;
614 	}
615 
616 	pushValue(L, &luacfunc);
617 	pushValue(L, "test");
618 	pushValue(L, 123);
619 	pushValue(L, true);
620 
621 	assert(lua_gettop(L) == 4);
622 
623 	auto stack = popStack(L, lua_gettop(L));
624 	assert(lua_gettop(L) == 0);
625 	assert(stack[0].type == LuaType.Function);
626 	assert(stack[1].type == LuaType.String);
627 	assert(stack[2].type == LuaType.Number);
628 	assert(stack[3].type == LuaType.Boolean);
629 }