1 /* tolua: functions to check types.
2 ** Support code for Lua bindings.
3 ** Written by Waldemar Celes
4 ** TeCGraf/PUC-Rio
5 ** Apr 2003
6 ** $Id: $
7 */
8 
9 /* This code is free software; you can redistribute it and/or modify it.
10 ** The software provided hereunder is on an "as is" basis, and
11 ** the author has no obligation to provide maintenance, support, updates,
12 ** enhancements, or modifications.
13 */
14 
15 #include "tolua++.h"
16 #include "lauxlib.h"
17 
18 #include <stdlib.h>
19 #include <string.h>
20 
21 /* a fast check if a is b, without parameter validation
22  i.e. if b is equal to a or a superclass of a. */
tolua_fast_isa(lua_State * L,int mt_indexa,int mt_indexb,int super_index)23 TOLUA_API int tolua_fast_isa(lua_State *L, int mt_indexa, int mt_indexb, int super_index)
24 {
25 	int result;
26 	if (lua_rawequal(L, mt_indexa, mt_indexb))
27 		result = 1;
28 	else
29 	{
30 		if (super_index)
31 		{
32 			lua_pushvalue(L, super_index);
33 		}
34 		else
35 		{
36 			lua_pushliteral(L, "tolua_super");
37 			lua_rawget(L, LUA_REGISTRYINDEX); /* stack: super */
38 		}
39 		lua_pushvalue(L, mt_indexa);	  /* stack: super mta */
40 		lua_rawget(L, -2);				  /* stack: super super[mta] */
41 		lua_pushvalue(L, mt_indexb);	  /* stack: super super[mta] mtb */
42 		lua_rawget(L, LUA_REGISTRYINDEX); /* stack: super super[mta] typenameB */
43 		lua_rawget(L, -2);				  /* stack: super super[mta] bool */
44 		result = lua_toboolean(L, -1);
45 		lua_pop(L, 3);
46 	}
47 	return result;
48 }
49 
50 /* Push and returns the corresponding object typename */
tolua_typename(lua_State * L,int lo)51 TOLUA_API const char *tolua_typename(lua_State *L, int lo)
52 {
53 	int tag = lua_type(L, lo);
54 	if (tag == LUA_TNONE)
55 		lua_pushstring(L, "[no object]");
56 	else if (tag != LUA_TUSERDATA && tag != LUA_TTABLE)
57 		lua_pushstring(L, lua_typename(L, tag));
58 	else if (tag == LUA_TUSERDATA)
59 	{
60 		if (!lua_getmetatable(L, lo))
61 			lua_pushstring(L, lua_typename(L, tag));
62 		else
63 		{
64 			lua_rawget(L, LUA_REGISTRYINDEX);
65 			if (!lua_isstring(L, -1))
66 			{
67 				lua_pop(L, 1);
68 				lua_pushstring(L, "[undefined]");
69 			}
70 		}
71 	}
72 	else /* is table */
73 	{
74 		lua_pushvalue(L, lo);
75 		lua_rawget(L, LUA_REGISTRYINDEX);
76 		if (!lua_isstring(L, -1))
77 		{
78 			lua_pop(L, 1);
79 			lua_pushstring(L, "table");
80 		}
81 		else
82 		{
83 			lua_pushstring(L, "class ");
84 			lua_insert(L, -2);
85 			lua_concat(L, 2);
86 		}
87 	}
88 	return lua_tostring(L, -1);
89 }
90 
tolua_error(lua_State * L,const char * msg,tolua_Error * err)91 TOLUA_API void tolua_error(lua_State *L, const char *msg, tolua_Error *err)
92 {
93 	if (msg[0] == '#')
94 	{
95 		const char *expected = err->type;
96 		const char *provided = tolua_typename(L, err->index);
97 		if (msg[1] == 'f')
98 		{
99 			int narg = err->index;
100 			if (err->array)
101 				luaL_error(L, "%s\n     argument #%d is array of '%s'; array of '%s' expected.\n",
102 						   msg + 2, narg, provided, expected);
103 			else
104 				luaL_error(L, "%s\n     argument #%d is '%s'; '%s' expected.\n",
105 						   msg + 2, narg, provided, expected);
106 		}
107 		else if (msg[1] == 'v')
108 		{
109 			if (err->array)
110 				luaL_error(L, "%s\n     value is array of '%s'; array of '%s' expected.\n",
111 						   msg + 2, provided, expected);
112 			else
113 				luaL_error(L, "%s\n     value is '%s'; '%s' expected.\n",
114 						   msg + 2, provided, expected);
115 		}
116 	}
117 	else
118 		luaL_error(L, msg);
119 }
120 
121 /* the equivalent of lua_is* for usertable */
lua_isusertable(lua_State * L,int lo,const char * type)122 static int lua_isusertable(lua_State *L, int lo, const char *type)
123 {
124 	int r = 0;
125 	if (lo < 0)
126 		lo = lua_gettop(L) + lo + 1;
127 	lua_pushvalue(L, lo);
128 	lua_rawget(L, LUA_REGISTRYINDEX); /* get registry[t] */
129 	if (lua_isstring(L, -1))
130 	{
131 		r = strcmp(lua_tostring(L, -1), type) == 0;
132 		if (!r)
133 		{
134 			/* try const */
135 			lua_pushstring(L, "const ");
136 			lua_insert(L, -2);
137 			lua_concat(L, 2);
138 			r = lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), type) == 0;
139 		}
140 	}
141 	lua_pop(L, 1);
142 	return r;
143 }
144 
push_table_instance(lua_State * L,int lo)145 int push_table_instance(lua_State *L, int lo)
146 {
147 
148 	if (lua_istable(L, lo))
149 	{
150 
151 		lua_pushstring(L, ".c_instance");
152 		lua_gettable(L, lo);
153 		if (lua_isuserdata(L, -1))
154 		{
155 
156 			lua_replace(L, lo);
157 			return 1;
158 		}
159 		else
160 		{
161 
162 			lua_pop(L, 1);
163 			return 0;
164 		}
165 	}
166 	else
167 	{
168 		return 0;
169 	}
170 
171 	return 0;
172 }
173 
174 /* the equivalent of lua_is* for usertype */
lua_isusertype(lua_State * L,int lo,const char * type)175 static int lua_isusertype(lua_State *L, int lo, const char *type)
176 {
177 	if (!lua_isuserdata(L, lo))
178 	{
179 		if (!push_table_instance(L, lo))
180 		{
181 			return 0;
182 		}
183 	}
184 	{
185 		/* check if it is of the same type */
186 		int r;
187 		const char *tn;
188 		if (lua_getmetatable(L, lo)) /* if metatable? */
189 		{
190 			lua_rawget(L, LUA_REGISTRYINDEX); /* get registry[mt] */
191 			tn = lua_tostring(L, -1);
192 			r = tn && (strcmp(tn, type) == 0);
193 			lua_pop(L, 1);
194 			if (r)
195 				return 1;
196 			else
197 			{
198 				/* check if it is a specialized class */
199 				lua_pushstring(L, "tolua_super");
200 				lua_rawget(L, LUA_REGISTRYINDEX); /* get super */
201 				lua_getmetatable(L, lo);
202 				lua_rawget(L, -2); /* get super[mt] */
203 				if (lua_istable(L, -1))
204 				{
205 					int b;
206 					lua_pushstring(L, type);
207 					lua_rawget(L, -2); /* get super[mt][type] */
208 					b = lua_toboolean(L, -1);
209 					lua_pop(L, 3);
210 					if (b)
211 						return 1;
212 				}
213 			}
214 		}
215 	}
216 	return 0;
217 }
218 
tolua_isnoobj(lua_State * L,int lo,tolua_Error * err)219 TOLUA_API int tolua_isnoobj(lua_State *L, int lo, tolua_Error *err)
220 {
221 	if (lua_gettop(L) < abs(lo))
222 		return 1;
223 	err->index = lo;
224 	err->array = 0;
225 	err->type = "[no object]";
226 	return 0;
227 }
228 
tolua_isboolean(lua_State * L,int lo,int def,tolua_Error * err)229 TOLUA_API int tolua_isboolean(lua_State *L, int lo, int def, tolua_Error *err)
230 {
231 	if (def && lua_gettop(L) < abs(lo))
232 		return 1;
233 	if (lua_isnil(L, lo) || lua_isboolean(L, lo))
234 		return 1;
235 	err->index = lo;
236 	err->array = 0;
237 	err->type = "boolean";
238 	return 0;
239 }
240 
tolua_isnumber(lua_State * L,int lo,int def,tolua_Error * err)241 TOLUA_API int tolua_isnumber(lua_State *L, int lo, int def, tolua_Error *err)
242 {
243 	if (def && lua_gettop(L) < abs(lo))
244 		return 1;
245 	if (lua_isnumber(L, lo))
246 		return 1;
247 	err->index = lo;
248 	err->array = 0;
249 	err->type = "number";
250 	return 0;
251 }
252 
tolua_isstring(lua_State * L,int lo,int def,tolua_Error * err)253 TOLUA_API int tolua_isstring(lua_State *L, int lo, int def, tolua_Error *err)
254 {
255 	if (def && lua_gettop(L) < abs(lo))
256 		return 1;
257 	if (lua_isnil(L, lo) || lua_isstring(L, lo))
258 		return 1;
259 	err->index = lo;
260 	err->array = 0;
261 	err->type = "string";
262 	return 0;
263 }
264 
tolua_istable(lua_State * L,int lo,int def,tolua_Error * err)265 TOLUA_API int tolua_istable(lua_State *L, int lo, int def, tolua_Error *err)
266 {
267 	if (def && lua_gettop(L) < abs(lo))
268 		return 1;
269 	if (lua_istable(L, lo))
270 		return 1;
271 	err->index = lo;
272 	err->array = 0;
273 	err->type = "table";
274 	return 0;
275 }
276 
tolua_isusertable(lua_State * L,int lo,const char * type,int def,tolua_Error * err)277 TOLUA_API int tolua_isusertable(lua_State *L, int lo, const char *type, int def, tolua_Error *err)
278 {
279 	if (def && lua_gettop(L) < abs(lo))
280 		return 1;
281 	if (lua_isusertable(L, lo, type))
282 		return 1;
283 	err->index = lo;
284 	err->array = 0;
285 	err->type = type;
286 	return 0;
287 }
288 
tolua_isuserdata(lua_State * L,int lo,int def,tolua_Error * err)289 TOLUA_API int tolua_isuserdata(lua_State *L, int lo, int def, tolua_Error *err)
290 {
291 	if (def && lua_gettop(L) < abs(lo))
292 		return 1;
293 	if (lua_isnil(L, lo) || lua_isuserdata(L, lo))
294 		return 1;
295 	err->index = lo;
296 	err->array = 0;
297 	err->type = "userdata";
298 	return 0;
299 }
300 
tolua_isvaluenil(lua_State * L,int lo,tolua_Error * err)301 TOLUA_API int tolua_isvaluenil(lua_State *L, int lo, tolua_Error *err)
302 {
303 
304 	if (lua_gettop(L) < abs(lo))
305 		return 0; /* somebody else should chack this */
306 	if (!lua_isnil(L, lo))
307 		return 0;
308 
309 	err->index = lo;
310 	err->array = 0;
311 	err->type = "value";
312 	return 1;
313 }
314 
tolua_isvalue(lua_State * L,int lo,int def,tolua_Error * err)315 TOLUA_API int tolua_isvalue(lua_State *L, int lo, int def, tolua_Error *err)
316 {
317 	if (def || abs(lo) <= lua_gettop(L)) /* any valid index */
318 		return 1;
319 	err->index = lo;
320 	err->array = 0;
321 	err->type = "value";
322 	return 0;
323 }
324 
tolua_isusertype(lua_State * L,int lo,const char * type,int def,tolua_Error * err)325 TOLUA_API int tolua_isusertype(lua_State *L, int lo, const char *type, int def, tolua_Error *err)
326 {
327 	if (def && lua_gettop(L) < abs(lo))
328 		return 1;
329 	if (lua_isnil(L, lo) || lua_isusertype(L, lo, type))
330 		return 1;
331 	err->index = lo;
332 	err->array = 0;
333 	err->type = type;
334 	return 0;
335 }
336 
tolua_isvaluearray(lua_State * L,int lo,int dim,int def,tolua_Error * err)337 TOLUA_API int tolua_isvaluearray(lua_State *L, int lo, int dim, int def, tolua_Error *err)
338 {
339 	(void)dim;
340 	if (!tolua_istable(L, lo, def, err))
341 		return 0;
342 	else
343 		return 1;
344 }
345 
tolua_isbooleanarray(lua_State * L,int lo,int dim,int def,tolua_Error * err)346 TOLUA_API int tolua_isbooleanarray(lua_State *L, int lo, int dim, int def, tolua_Error *err)
347 {
348 	if (!tolua_istable(L, lo, def, err))
349 		return 0;
350 	else
351 	{
352 		int i;
353 		for (i = 1; i <= dim; ++i)
354 		{
355 			lua_pushnumber(L, i);
356 			lua_gettable(L, lo);
357 			if (!(lua_isnil(L, -1) || lua_isboolean(L, -1)) &&
358 				!(def && lua_isnil(L, -1)))
359 			{
360 				err->index = lo;
361 				err->array = 1;
362 				err->type = "boolean";
363 				return 0;
364 			}
365 			lua_pop(L, 1);
366 		}
367 	}
368 	return 1;
369 }
370 
tolua_isnumberarray(lua_State * L,int lo,int dim,int def,tolua_Error * err)371 TOLUA_API int tolua_isnumberarray(lua_State *L, int lo, int dim, int def, tolua_Error *err)
372 {
373 	if (!tolua_istable(L, lo, def, err))
374 		return 0;
375 	else
376 	{
377 		int i;
378 		for (i = 1; i <= dim; ++i)
379 		{
380 			lua_pushnumber(L, i);
381 			lua_gettable(L, lo);
382 			if (!lua_isnumber(L, -1) &&
383 				!(def && lua_isnil(L, -1)))
384 			{
385 				err->index = lo;
386 				err->array = 1;
387 				err->type = "number";
388 				return 0;
389 			}
390 			lua_pop(L, 1);
391 		}
392 	}
393 	return 1;
394 }
395 
tolua_isstringarray(lua_State * L,int lo,int dim,int def,tolua_Error * err)396 TOLUA_API int tolua_isstringarray(lua_State *L, int lo, int dim, int def, tolua_Error *err)
397 {
398 	if (!tolua_istable(L, lo, def, err))
399 		return 0;
400 	else
401 	{
402 		int i;
403 		for (i = 1; i <= dim; ++i)
404 		{
405 			lua_pushnumber(L, i);
406 			lua_gettable(L, lo);
407 			if (!(lua_isnil(L, -1) || lua_isstring(L, -1)) &&
408 				!(def && lua_isnil(L, -1)))
409 			{
410 				err->index = lo;
411 				err->array = 1;
412 				err->type = "string";
413 				return 0;
414 			}
415 			lua_pop(L, 1);
416 		}
417 	}
418 	return 1;
419 }
420 
tolua_istablearray(lua_State * L,int lo,int dim,int def,tolua_Error * err)421 TOLUA_API int tolua_istablearray(lua_State *L, int lo, int dim, int def, tolua_Error *err)
422 {
423 	if (!tolua_istable(L, lo, def, err))
424 		return 0;
425 	else
426 	{
427 		int i;
428 		for (i = 1; i <= dim; ++i)
429 		{
430 			lua_pushnumber(L, i);
431 			lua_gettable(L, lo);
432 			if (!lua_istable(L, -1) &&
433 				!(def && lua_isnil(L, -1)))
434 			{
435 				err->index = lo;
436 				err->array = 1;
437 				err->type = "table";
438 				return 0;
439 			}
440 			lua_pop(L, 1);
441 		}
442 	}
443 	return 1;
444 }
445 
tolua_isuserdataarray(lua_State * L,int lo,int dim,int def,tolua_Error * err)446 TOLUA_API int tolua_isuserdataarray(lua_State *L, int lo, int dim, int def, tolua_Error *err)
447 {
448 	if (!tolua_istable(L, lo, def, err))
449 		return 0;
450 	else
451 	{
452 		int i;
453 		for (i = 1; i <= dim; ++i)
454 		{
455 			lua_pushnumber(L, i);
456 			lua_gettable(L, lo);
457 			if (!(lua_isnil(L, -1) || lua_isuserdata(L, -1)) &&
458 				!(def && lua_isnil(L, -1)))
459 			{
460 				err->index = lo;
461 				err->array = 1;
462 				err->type = "userdata";
463 				return 0;
464 			}
465 			lua_pop(L, 1);
466 		}
467 	}
468 	return 1;
469 }
470 
tolua_isusertypearray(lua_State * L,int lo,const char * type,int dim,int def,tolua_Error * err)471 TOLUA_API int tolua_isusertypearray(lua_State *L, int lo, const char *type, int dim, int def, tolua_Error *err)
472 {
473 	if (!tolua_istable(L, lo, def, err))
474 		return 0;
475 	else
476 	{
477 		int i;
478 		for (i = 1; i <= dim; ++i)
479 		{
480 			lua_pushnumber(L, i);
481 			lua_gettable(L, lo);
482 			if (!(lua_isnil(L, -1) || lua_isuserdata(L, -1)) &&
483 				!(def && lua_isnil(L, -1)))
484 			{
485 				err->index = lo;
486 				err->type = type;
487 				err->array = 1;
488 				return 0;
489 			}
490 			lua_pop(L, 1);
491 		}
492 	}
493 	return 1;
494 }
495 
496 #if 0
497 int tolua_isbooleanfield
498  (lua_State* L, int lo, int i, int def, tolua_Error* err)
499 {
500 	lua_pushnumber(L,i);
501 	lua_gettable(L,lo);
502 	if (!(lua_isnil(L,-1) || lua_isboolean(L,-1)) &&
503 			  !(def && lua_isnil(L,-1))
504 				)
505 	{
506 		err->index = lo;
507 		err->array = 1;
508 		err->type = "boolean";
509 		return 0;
510 	}
511 	lua_pop(L,1);
512  return 1;
513 }
514 
515 int tolua_isnumberfield
516  (lua_State* L, int lo, int i, int def, tolua_Error* err)
517 {
518 	lua_pushnumber(L,i);
519 	lua_gettable(L,lo);
520 	if (!lua_isnumber(L,-1) &&
521 			  !(def && lua_isnil(L,-1))
522 				)
523 	{
524 		err->index = lo;
525 		err->array = 1;
526 		err->type = "number";
527 		return 0;
528 	}
529 	lua_pop(L,1);
530  return 1;
531 }
532 
533 int tolua_isstringfield
534  (lua_State* L, int lo, int i, int def, tolua_Error* err)
535 {
536 	lua_pushnumber(L,i);
537 	lua_gettable(L,lo);
538  if (!(lua_isnil(L,-1) || lua_isstring(L,-1)) &&
539 	    !(def && lua_isnil(L,-1))
540 				)
541 	{
542 		err->index = lo;
543 		err->array = 1;
544 		err->type = "string";
545 		return 0;
546 	}
547 	lua_pop(L,1);
548  return 1;
549 }
550 
551 int tolua_istablefield
552  (lua_State* L, int lo, int i, int def, tolua_Error* err)
553 {
554 	lua_pushnumber(L,i+1);
555 	lua_gettable(L,lo);
556 	if (! lua_istable(L,-1) &&
557 	    !(def && lua_isnil(L,-1))
558 				)
559 	{
560 		err->index = lo;
561 		err->array = 1;
562 		err->type = "table";
563 		return 0;
564 	}
565 	lua_pop(L,1);
566 }
567 
568 int tolua_isusertablefield
569  (lua_State* L, int lo, const char* type, int i, int def, tolua_Error* err)
570 {
571 	lua_pushnumber(L,i);
572 	lua_gettable(L,lo);
573 	if (! lua_isusertable(L,-1,type) &&
574 	    !(def && lua_isnil(L,-1))
575 				)
576 	{
577 		err->index = lo;
578 		err->array = 1;
579 		err->type = type;
580 		return 0;
581 	}
582 	lua_pop(L,1);
583  return 1;
584 }
585 
586 int tolua_isuserdatafield
587  (lua_State* L, int lo, int i, int def, tolua_Error* err)
588 {
589 	lua_pushnumber(L,i);
590 	lua_gettable(L,lo);
591 	if (!(lua_isnil(L,-1) || lua_isuserdata(L,-1)) &&
592 	    !(def && lua_isnil(L,-1))
593 				)
594 	{
595 		err->index = lo;
596 		err->array = 1;
597 		err->type = "userdata";
598 		return 0;
599 	}
600 	lua_pop(L,1);
601  return 1;
602 }
603 
604 int tolua_isusertypefield
605  (lua_State* L, int lo, const char* type, int i, int def, tolua_Error* err)
606 {
607 	lua_pushnumber(L,i);
608 	lua_gettable(L,lo);
609 	if (!(lua_isnil(L,-1) || lua_isusertype(L,-1,type)) &&
610 	    !(def && lua_isnil(L,-1))
611 				)
612 	{
613 		err->index = lo;
614 		err->type = type;
615 		err->array = 1;
616 		return 0;
617 	}
618 	lua_pop(L,1);
619  return 1;
620 }
621 
622 #endif
623