You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1350 lines
36 KiB
C
1350 lines
36 KiB
C
/* Copyright 2013-2020 Philipp Janda <siffiejoe@gmx.net>
|
|
*
|
|
* You may do anything with this work that copyright law would normally
|
|
* restrict, so long as you retain the above notice(s) and this license
|
|
* in all redistributed copies and derived works. There is no warranty.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
#include "moon.h"
|
|
|
|
/* don't compile it again if it's already included via moon.h */
|
|
#ifndef MOON_C_
|
|
#define MOON_C_
|
|
|
|
#if LUA_VERSION_NUM < 502
|
|
MOON_API int moon_absindex(lua_State *L, int i)
|
|
{
|
|
if (i < 0 && i > LUA_REGISTRYINDEX)
|
|
i += lua_gettop(L) + 1;
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
/* struct that is part of some moon objects (those created via
|
|
* `moon_newfield`) and contains a function pointer and a data pointer
|
|
* that can be used to check whether the object is still valid. The
|
|
* check is done automatically in `moon_{check,test}object`, and is
|
|
* primarily needed to safely support tagged unions. */
|
|
typedef struct moon_object_vcheck_
|
|
{
|
|
int (*check)(void *);
|
|
void *tagp;
|
|
struct moon_object_vcheck_ *next; /* linked list */
|
|
} moon_object_vcheck_;
|
|
|
|
/* For keeping memory consumption as low as possible multiple C
|
|
* values might be stored side-by-side in the same memory block, and
|
|
* we have to figure out the alignment to use for those values. */
|
|
typedef union
|
|
{
|
|
lua_Number n;
|
|
double d;
|
|
lua_Integer i;
|
|
long l;
|
|
size_t s;
|
|
void *p;
|
|
void (*fp)(void);
|
|
} moon_object_alignment_u_;
|
|
|
|
/* Newer versions of C and C++ have a way to figure out alignment
|
|
* built into the language, and we use those if available. There's
|
|
* also a fallback implementation for older versions. */
|
|
#ifndef MOON_ALIGNOF_
|
|
#if defined(__cplusplus) && __cplusplus >= 201103L
|
|
/* alignof is an operator in newer C++ versions */
|
|
#define MOON_ALIGNOF_(_t) alignof(_t)
|
|
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
|
/* newer C versions have it in stdalign.h */
|
|
#include <stdalign.h>
|
|
#define MOON_ALIGNOF_(_t) alignof(_t)
|
|
#elif defined(__cplusplus)
|
|
/* Same as below, but MSVC++ has problems with that code, so we use a
|
|
* template instead of a type-as-expression. */
|
|
template <typename T>
|
|
struct moon_alignof_struct_
|
|
{
|
|
char _1;
|
|
T _2;
|
|
};
|
|
#define MOON_ALIGNOF_(_t) \
|
|
(offsetof(moon_alignof_struct_<_t>, _2) > sizeof(_t) \
|
|
? sizeof(_t) \
|
|
: offsetof(moon_alignof_struct_<_t>, _2))
|
|
#else
|
|
/* Calculate the alignment ourselves. This may give smaller values
|
|
* than using `sizeof( _t )` which is also a portable solution. */
|
|
#define MOON_ALIGNOF_(_t) \
|
|
(offsetof( \
|
|
struct \
|
|
{ \
|
|
char _1; \
|
|
_t _2; \
|
|
}, \
|
|
_2) > sizeof(_t) \
|
|
? sizeof(_t) \
|
|
: offsetof( \
|
|
struct \
|
|
{ \
|
|
char _1; \
|
|
_t _2; \
|
|
}, \
|
|
_2))
|
|
#endif
|
|
#endif
|
|
|
|
#define MOON_OBJ_ALIGNMENT_ MOON_ALIGNOF_(moon_object_alignment_u_)
|
|
#define MOON_PTR_ALIGNMENT_ MOON_ALIGNOF_(void *)
|
|
#define MOON_GCF_ALIGNMENT_ MOON_ALIGNOF_(moon_object_destructor)
|
|
#define MOON_VCK_ALIGNMENT_ MOON_ALIGNOF_(moon_object_vcheck_)
|
|
#define MOON_ROUNDTO_(_s, _a) ((((_s) + (_a)-1) / (_a)) * (_a))
|
|
#define MOON_PTR_(_p, _o) ((void *)(((char *)(_p)) + (_o)))
|
|
|
|
/* Raise properly formatted argument error messages. */
|
|
static int moon_type_error_(lua_State *L, int i, char const *t1,
|
|
char const *t2)
|
|
{
|
|
char const *msg = lua_pushfstring(L, "%s expected, got %s", t1, t2);
|
|
return luaL_argerror(L, i, msg);
|
|
}
|
|
|
|
static int moon_type_error_invalid_(lua_State *L, int i,
|
|
char const *tname)
|
|
{
|
|
char const *msg = lua_pushfstring(L, "invalid '%s' object", tname);
|
|
return luaL_argerror(L, i, msg);
|
|
}
|
|
|
|
static int moon_type_error_version_(lua_State *L, int i)
|
|
{
|
|
char const *msg = lua_pushfstring(L, "not a moon %d.%d object",
|
|
MOON_VERSION_MAJOR,
|
|
MOON_VERSION_MINOR);
|
|
return luaL_argerror(L, i, msg);
|
|
}
|
|
|
|
static int moon_is_property(char const *name)
|
|
{
|
|
return name[0] == '.';
|
|
}
|
|
static int moon_is_meta(char const *name)
|
|
{
|
|
return name[0] == '_' && name[1] == '_';
|
|
}
|
|
static int moon_is_method(char const *name)
|
|
{
|
|
return !moon_is_meta(name) && !moon_is_property(name);
|
|
}
|
|
|
|
/* Default implementation of a __tostring metamethod for moon objects
|
|
* which displays the type name and memory address. */
|
|
MOON_LLINKAGE_BEGIN
|
|
static int moon_object_default_tostring_(lua_State *L)
|
|
{
|
|
void *ptr = lua_touserdata(L, 1);
|
|
char const *name = lua_tostring(L, lua_upvalueindex(1));
|
|
char const *basename = strrchr(name, '.');
|
|
if (basename != NULL)
|
|
name = basename + 1;
|
|
lua_pushfstring(L, "[%s]: %p", name, ptr);
|
|
return 1;
|
|
}
|
|
MOON_LLINKAGE_END
|
|
|
|
/* Run the destructor and mark the object as invalid/destroyed. */
|
|
static void moon_object_run_destructor_(moon_object_header *h)
|
|
{
|
|
if (h->cleanup_offset > 0 && (h->flags & MOON_OBJECT_IS_VALID))
|
|
{
|
|
void *p = MOON_PTR_(h, h->object_offset);
|
|
moon_object_destructor *gc = NULL;
|
|
gc = (moon_object_destructor *)MOON_PTR_(h, h->cleanup_offset);
|
|
if (h->flags & MOON_OBJECT_IS_POINTER)
|
|
p = *((void **)p);
|
|
if (*gc != 0 && p != NULL)
|
|
(*gc)(p);
|
|
}
|
|
h->flags &= ~MOON_OBJECT_IS_VALID;
|
|
}
|
|
|
|
/* Common __gc metamethod for all moon objects. The actual finalizer
|
|
* function is stored in the userdata to support different lifetimes.
|
|
*/
|
|
MOON_LLINKAGE_BEGIN
|
|
static int moon_object_default_gc_(lua_State *L)
|
|
{
|
|
moon_object_header *h = (moon_object_header *)lua_touserdata(L, 1);
|
|
moon_object_run_destructor_(h);
|
|
return 0;
|
|
}
|
|
MOON_LLINKAGE_END
|
|
|
|
static int moon_index_dispatch_methods_(lua_State *L)
|
|
{
|
|
if (lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE)
|
|
{
|
|
lua_pushvalue(L, 2); /* duplicate key */
|
|
lua_rawget(L, lua_upvalueindex(1));
|
|
if (!lua_isnil(L, -1))
|
|
return 1;
|
|
lua_pop(L, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int moon_index_dispatch_properties_(lua_State *L)
|
|
{
|
|
if (lua_type(L, lua_upvalueindex(2)) == LUA_TTABLE)
|
|
{
|
|
lua_pushvalue(L, 2); /* duplicate key */
|
|
lua_rawget(L, lua_upvalueindex(2));
|
|
if (!lua_isnil(L, -1))
|
|
{
|
|
lua_pushvalue(L, 1);
|
|
lua_call(L, 1, 1);
|
|
return 1;
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int moon_index_dispatch_function_(lua_State *L)
|
|
{
|
|
if (lua_type(L, lua_upvalueindex(3)) == LUA_TFUNCTION)
|
|
{
|
|
lua_pushvalue(L, lua_upvalueindex(3));
|
|
lua_pushvalue(L, 1);
|
|
lua_pushvalue(L, 2);
|
|
lua_call(L, 2, 1);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int moon_newindex_dispatch_properties_(lua_State *L)
|
|
{
|
|
if (lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE)
|
|
{
|
|
lua_pushvalue(L, 2); /* duplicate key */
|
|
lua_rawget(L, lua_upvalueindex(1));
|
|
if (lua_isfunction(L, -1))
|
|
{
|
|
lua_pushvalue(L, 1);
|
|
lua_pushvalue(L, 2);
|
|
lua_pushvalue(L, 3);
|
|
lua_call(L, 3, 0);
|
|
return 1;
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int moon_newindex_dispatch_function_(lua_State *L)
|
|
{
|
|
if (lua_type(L, lua_upvalueindex(2)) == LUA_TFUNCTION)
|
|
{
|
|
lua_pushvalue(L, lua_upvalueindex(2));
|
|
lua_pushvalue(L, 1);
|
|
lua_pushvalue(L, 2);
|
|
lua_pushvalue(L, 3);
|
|
lua_call(L, 3, 0);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Check the methods and properties tables for a given key and then
|
|
* (if unsuccessful) call the registered C function for looking up
|
|
* properties. */
|
|
MOON_LLINKAGE_BEGIN
|
|
static int moon_index_dispatch_(lua_State *L)
|
|
{
|
|
if (!moon_index_dispatch_methods_(L) &&
|
|
!moon_index_dispatch_properties_(L) &&
|
|
!moon_index_dispatch_function_(L))
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
static int moon_newindex_dispatch_(lua_State *L)
|
|
{
|
|
if (!moon_newindex_dispatch_properties_(L) &&
|
|
!moon_newindex_dispatch_function_(L))
|
|
luaL_error(L, "attempt to set invalid field");
|
|
return 0;
|
|
}
|
|
MOON_LLINKAGE_END
|
|
|
|
static lua_CFunction moon_getf_(lua_State *L, char const *name,
|
|
lua_CFunction def)
|
|
{
|
|
lua_CFunction f = 0;
|
|
lua_pushliteral(L, "__moon");
|
|
lua_rawget(L, LUA_REGISTRYINDEX);
|
|
if (lua_type(L, -1) != LUA_TTABLE)
|
|
{
|
|
lua_pop(L, 1);
|
|
lua_newtable(L);
|
|
lua_pushliteral(L, "__moon");
|
|
lua_pushvalue(L, -2);
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
}
|
|
lua_getfield(L, -1, name);
|
|
f = lua_tocfunction(L, -1);
|
|
lua_pop(L, 1);
|
|
if (!f)
|
|
{
|
|
f = def;
|
|
lua_pushcfunction(L, def);
|
|
lua_setfield(L, -2, name);
|
|
}
|
|
lua_pop(L, 1);
|
|
return f;
|
|
}
|
|
|
|
static void moon_pushreg_(lua_State *L, luaL_Reg const funcs[],
|
|
int (*predicate)(char const *),
|
|
int nups, int firstupvalue, int skip)
|
|
{
|
|
if (funcs != NULL)
|
|
{
|
|
lua_newtable(L);
|
|
for (; funcs->func; ++funcs)
|
|
{
|
|
if (predicate(funcs->name))
|
|
{
|
|
int i = 0;
|
|
for (i = 0; i < nups; ++i)
|
|
lua_pushvalue(L, firstupvalue + i);
|
|
lua_pushcclosure(L, funcs->func, nups);
|
|
lua_setfield(L, -2, funcs->name + skip);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
lua_pushnil(L);
|
|
}
|
|
|
|
static void moon_pushfunction_(lua_State *L, lua_CFunction func,
|
|
int nups, int firstupvalue)
|
|
{
|
|
if (func != 0)
|
|
{
|
|
int i = 0;
|
|
for (i = 0; i < nups; ++i)
|
|
lua_pushvalue(L, firstupvalue + i);
|
|
lua_pushcclosure(L, func, nups);
|
|
}
|
|
else
|
|
lua_pushnil(L);
|
|
}
|
|
|
|
/* Depending on the availability of a methods table and/or a C
|
|
* function for looking up properties this function creates an __index
|
|
* metamethod (function or table). */
|
|
static void moon_makeindex_(lua_State *L, luaL_Reg const methods[],
|
|
luaL_Reg const properties[],
|
|
lua_CFunction pindex, int nups)
|
|
{
|
|
int firstupvalue = lua_gettop(L) + 1 - nups;
|
|
if (!properties && !pindex)
|
|
{ /* methods only (maybe) */
|
|
moon_pushreg_(L, methods, moon_is_method, nups, firstupvalue, 0);
|
|
if (nups > 0)
|
|
{
|
|
lua_replace(L, firstupvalue);
|
|
lua_pop(L, nups - 1);
|
|
}
|
|
}
|
|
else if (!methods && !properties)
|
|
{ /* index function only */
|
|
lua_pushcclosure(L, pindex, nups);
|
|
}
|
|
else
|
|
{
|
|
lua_CFunction dispatch = moon_getf_(L, "index", moon_index_dispatch_);
|
|
moon_pushreg_(L, methods, moon_is_method, nups, firstupvalue, 0);
|
|
moon_pushreg_(L, properties, moon_is_property, nups, firstupvalue, 1);
|
|
moon_pushfunction_(L, pindex, nups, firstupvalue);
|
|
lua_pushcclosure(L, dispatch, 3);
|
|
if (nups > 0)
|
|
{
|
|
lua_replace(L, firstupvalue);
|
|
lua_pop(L, nups - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void moon_makenewindex_(lua_State *L, luaL_Reg const properties[],
|
|
lua_CFunction pnewindex, int nups)
|
|
{
|
|
if (!properties && !pnewindex)
|
|
{
|
|
lua_pop(L, nups);
|
|
lua_pushnil(L);
|
|
}
|
|
else if (!properties)
|
|
{
|
|
lua_pushcclosure(L, pnewindex, nups);
|
|
}
|
|
else
|
|
{
|
|
int firstupvalue = lua_gettop(L) + 1 - nups;
|
|
lua_CFunction dispatch = moon_getf_(L, "newindex", moon_newindex_dispatch_);
|
|
moon_pushreg_(L, properties, moon_is_property, nups, firstupvalue, 1);
|
|
moon_pushfunction_(L, pnewindex, nups, firstupvalue);
|
|
lua_pushcclosure(L, dispatch, 2);
|
|
if (nups > 0)
|
|
{
|
|
lua_replace(L, firstupvalue);
|
|
lua_pop(L, nups - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Type names used by the moon toolkit must not start with a double
|
|
* underscore, because they might be used as keys in the metatable. */
|
|
static void moon_check_tname_(lua_State *L, char const *tname)
|
|
{
|
|
if (tname == NULL || tname[0] == '_')
|
|
luaL_error(L, "invalid type name: '%s'", tname ? tname : "NULL");
|
|
}
|
|
|
|
MOON_API void moon_defobject(lua_State *L, char const *tname,
|
|
size_t sz, luaL_Reg const *methods,
|
|
int nups)
|
|
{
|
|
int has_methods = 0;
|
|
int has_properties = 0;
|
|
lua_CFunction index = 0;
|
|
lua_CFunction newindex = 0;
|
|
moon_check_tname_(L, tname);
|
|
luaL_checkstack(L, 2 * nups + 4, "moon_defobject");
|
|
/* we don't use luaL_newmetatable to make sure that we never have a
|
|
* half-constructed metatable in the registry! */
|
|
luaL_getmetatable(L, tname);
|
|
if (!lua_isnil(L, -1))
|
|
luaL_error(L, "type '%s' is already defined", tname);
|
|
lua_pop(L, 1);
|
|
lua_newtable(L);
|
|
lua_pushstring(L, tname);
|
|
lua_pushcclosure(L, moon_object_default_tostring_, 1);
|
|
lua_setfield(L, -2, "__tostring");
|
|
if (methods != NULL)
|
|
{
|
|
luaL_Reg const *l = methods;
|
|
int i = 0;
|
|
for (; l->func != NULL; ++l)
|
|
{
|
|
if (moon_is_meta(l->name))
|
|
{
|
|
int is_index = 0 == strcmp(l->name + 2, "index");
|
|
int is_newindex = 0 == strcmp(l->name + 2, "newindex");
|
|
if (!is_index && !is_newindex)
|
|
{
|
|
for (i = 0; i < nups; ++i)
|
|
lua_pushvalue(L, -nups - 1);
|
|
lua_pushcclosure(L, l->func, nups);
|
|
lua_setfield(L, -2, l->name);
|
|
}
|
|
else if (is_index) /* handle __index later */
|
|
index = l->func;
|
|
else /* handle __newindex later */
|
|
newindex = l->func;
|
|
}
|
|
else if (moon_is_property(l->name))
|
|
{
|
|
has_properties = 1;
|
|
}
|
|
else
|
|
has_methods = 1;
|
|
}
|
|
}
|
|
if (has_methods || has_properties || index)
|
|
{
|
|
int i = 0;
|
|
for (i = 0; i < nups; ++i)
|
|
lua_pushvalue(L, -nups - 1);
|
|
moon_makeindex_(L, has_methods ? methods : NULL,
|
|
has_properties ? methods : NULL, index, nups);
|
|
lua_setfield(L, -2, "__index");
|
|
}
|
|
if (has_properties || newindex)
|
|
{
|
|
int i = 0;
|
|
for (i = 0; i < nups; ++i)
|
|
lua_pushvalue(L, -nups - 1);
|
|
moon_makenewindex_(L, methods, newindex, nups);
|
|
lua_setfield(L, -2, "__newindex");
|
|
}
|
|
lua_pushboolean(L, 0);
|
|
lua_setfield(L, -2, "__metatable");
|
|
lua_pushstring(L, tname);
|
|
lua_setfield(L, -2, "__name");
|
|
lua_pushcfunction(L, moon_object_default_gc_);
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, -3, "__gc");
|
|
lua_setfield(L, -2, "__close");
|
|
lua_pushinteger(L, MOON_VERSION);
|
|
lua_setfield(L, -2, "__moon_version");
|
|
lua_pushinteger(L, (lua_Integer)sz);
|
|
lua_setfield(L, -2, "__moon_size");
|
|
lua_setfield(L, LUA_REGISTRYINDEX, tname);
|
|
lua_pop(L, nups);
|
|
}
|
|
|
|
/* Verify that the value at the stack top is the metatable for a moon
|
|
* object type. */
|
|
static void moon_check_metatable_(lua_State *L, char const *tname)
|
|
{
|
|
if (!lua_istable(L, -1))
|
|
luaL_error(L, "no metatable for type '%s' defined", tname);
|
|
lua_getfield(L, -1, "__moon_version");
|
|
if (lua_tointeger(L, -1) != MOON_VERSION)
|
|
luaL_error(L, "'%s' is not a moon %d.%d object type", tname,
|
|
MOON_VERSION_MAJOR, MOON_VERSION_MINOR);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
/* Pushes the metatable for the given type onto the Lua stack, and
|
|
* makes sure that the given type is a moon object type. */
|
|
static void moon_push_metatable_(lua_State *L, char const *tname)
|
|
{
|
|
moon_check_tname_(L, tname);
|
|
luaL_getmetatable(L, tname);
|
|
moon_check_metatable_(L, tname);
|
|
}
|
|
|
|
MOON_API void *moon_newobject(lua_State *L, char const *tname,
|
|
void (*gc)(void *))
|
|
{
|
|
moon_object_header *obj = NULL;
|
|
size_t off1 = 0;
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4116)
|
|
#endif
|
|
size_t off2 = MOON_ROUNDTO_(sizeof(moon_object_header),
|
|
MOON_OBJ_ALIGNMENT_);
|
|
size_t sz = 0;
|
|
luaL_checkstack(L, 2, "moon_newobject");
|
|
moon_push_metatable_(L, tname);
|
|
lua_getfield(L, -1, "__moon_size");
|
|
sz = lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
if (sz == 0)
|
|
luaL_error(L, "type '%s' is incomplete (size is 0)", tname);
|
|
if (gc != 0)
|
|
{
|
|
off1 = MOON_ROUNDTO_(sizeof(moon_object_header),
|
|
MOON_GCF_ALIGNMENT_);
|
|
off2 = MOON_ROUNDTO_(off1 + sizeof(moon_object_destructor),
|
|
MOON_OBJ_ALIGNMENT_);
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
}
|
|
obj = (moon_object_header *)lua_newuserdata(L, sz + off2);
|
|
if (off1 > 0)
|
|
{
|
|
moon_object_destructor *cl = NULL;
|
|
cl = (moon_object_destructor *)MOON_PTR_(obj, off1);
|
|
*cl = gc;
|
|
}
|
|
obj->cleanup_offset = off1;
|
|
obj->object_offset = off2;
|
|
obj->vcheck_offset = 0;
|
|
obj->flags = MOON_OBJECT_IS_VALID;
|
|
lua_insert(L, -2);
|
|
lua_setmetatable(L, -2);
|
|
return MOON_PTR_(obj, off2);
|
|
}
|
|
|
|
MOON_API void **moon_newpointer(lua_State *L, char const *tname,
|
|
void (*gc)(void *))
|
|
{
|
|
moon_object_header *obj = NULL;
|
|
void **p = NULL;
|
|
size_t off1 = 0;
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4116)
|
|
#endif
|
|
size_t off2 = MOON_ROUNDTO_(sizeof(moon_object_header),
|
|
MOON_PTR_ALIGNMENT_);
|
|
luaL_checkstack(L, 2, "moon_newpointer");
|
|
moon_push_metatable_(L, tname);
|
|
if (gc != 0)
|
|
{
|
|
off1 = MOON_ROUNDTO_(sizeof(moon_object_header),
|
|
MOON_GCF_ALIGNMENT_);
|
|
off2 = MOON_ROUNDTO_(off1 + sizeof(moon_object_destructor),
|
|
MOON_PTR_ALIGNMENT_);
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
}
|
|
obj = (moon_object_header *)lua_newuserdata(L, sizeof(void *) + off2);
|
|
p = (void **)MOON_PTR_(obj, off2);
|
|
*p = NULL;
|
|
if (off1 > 0)
|
|
{
|
|
moon_object_destructor *cl = NULL;
|
|
cl = (moon_object_destructor *)MOON_PTR_(obj, off1);
|
|
*cl = gc;
|
|
}
|
|
obj->cleanup_offset = off1;
|
|
obj->object_offset = off2;
|
|
obj->vcheck_offset = 0;
|
|
obj->flags = MOON_OBJECT_IS_VALID | MOON_OBJECT_IS_POINTER;
|
|
lua_insert(L, -2);
|
|
lua_setmetatable(L, -2);
|
|
return p;
|
|
}
|
|
|
|
MOON_API void **moon_newfield(lua_State *L, char const *tname,
|
|
int idx, int (*isvalid)(void *),
|
|
void *tagp)
|
|
{
|
|
moon_object_header *obj = NULL;
|
|
moon_object_vcheck_ *nextcheck = NULL;
|
|
void **p = NULL;
|
|
size_t off1 = 0;
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4116)
|
|
#endif
|
|
size_t off2 = MOON_ROUNDTO_(sizeof(moon_object_header),
|
|
MOON_PTR_ALIGNMENT_);
|
|
luaL_checkstack(L, 3, "moon_newfield");
|
|
if (idx != 0)
|
|
{
|
|
moon_object_header *h = (moon_object_header *)lua_touserdata(L, idx);
|
|
idx = moon_absindex(L, idx);
|
|
if (h != NULL && lua_getmetatable(L, idx))
|
|
{
|
|
lua_getfield(L, -1, "__moon_version");
|
|
if (lua_tointeger(L, -1) == MOON_VERSION)
|
|
{
|
|
if (h->vcheck_offset > 0)
|
|
{
|
|
moon_object_vcheck_ *vc = NULL;
|
|
vc = (moon_object_vcheck_ *)MOON_PTR_(h, h->vcheck_offset);
|
|
if (isvalid == 0)
|
|
{ /* inherit vcheck from idx object */
|
|
isvalid = vc->check;
|
|
tagp = vc->tagp;
|
|
nextcheck = vc->next;
|
|
}
|
|
else /* add it to the chain */
|
|
nextcheck = vc;
|
|
}
|
|
}
|
|
lua_pop(L, 2);
|
|
}
|
|
}
|
|
moon_push_metatable_(L, tname);
|
|
if (isvalid != 0)
|
|
{
|
|
off1 = MOON_ROUNDTO_(sizeof(moon_object_header),
|
|
MOON_VCK_ALIGNMENT_);
|
|
off2 = MOON_ROUNDTO_(off1 + sizeof(moon_object_vcheck_),
|
|
MOON_PTR_ALIGNMENT_);
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
}
|
|
obj = (moon_object_header *)lua_newuserdata(L, sizeof(void *) + off2);
|
|
p = (void **)MOON_PTR_(obj, off2);
|
|
*p = NULL;
|
|
obj->vcheck_offset = off1;
|
|
obj->object_offset = off2;
|
|
obj->cleanup_offset = 0;
|
|
obj->flags = MOON_OBJECT_IS_VALID | MOON_OBJECT_IS_POINTER;
|
|
if (off1 > 0)
|
|
{
|
|
moon_object_vcheck_ *vc = NULL;
|
|
vc = (moon_object_vcheck_ *)MOON_PTR_(obj, off1);
|
|
vc->check = isvalid;
|
|
vc->tagp = tagp;
|
|
vc->next = nextcheck;
|
|
}
|
|
lua_insert(L, -2);
|
|
lua_setmetatable(L, -2);
|
|
if (idx != 0)
|
|
{
|
|
lua_newtable(L);
|
|
lua_pushvalue(L, idx);
|
|
lua_rawseti(L, -2, 1);
|
|
#if LUA_VERSION_NUM < 502
|
|
lua_setfenv(L, -2);
|
|
#else
|
|
lua_setuservalue(L, -2);
|
|
#endif
|
|
}
|
|
return p;
|
|
}
|
|
|
|
MOON_API int moon_getmethods(lua_State *L, char const *tname)
|
|
{
|
|
int t = 0;
|
|
luaL_checkstack(L, 2, "moon_getmethods");
|
|
moon_push_metatable_(L, tname);
|
|
lua_getfield(L, -1, "__index");
|
|
lua_replace(L, -2);
|
|
t = lua_type(L, -1);
|
|
if (t == LUA_TTABLE)
|
|
return t;
|
|
else if (t == LUA_TFUNCTION)
|
|
{
|
|
lua_CFunction dispatch = moon_getf_(L, "index",
|
|
moon_index_dispatch_);
|
|
if (lua_tocfunction(L, -1) == dispatch &&
|
|
lua_getupvalue(L, -1, 1) != NULL)
|
|
{
|
|
lua_replace(L, -2);
|
|
if (lua_type(L, -1) == LUA_TTABLE)
|
|
return LUA_TTABLE;
|
|
}
|
|
}
|
|
lua_pop(L, 1);
|
|
return LUA_TNIL;
|
|
}
|
|
|
|
MOON_API void moon_killobject(lua_State *L, int idx)
|
|
{
|
|
moon_object_header *h = (moon_object_header *)lua_touserdata(L, idx);
|
|
luaL_checkstack(L, 2, "moon_killobject");
|
|
if (h == NULL || !lua_getmetatable(L, idx))
|
|
moon_type_error_version_(L, idx);
|
|
lua_getfield(L, -1, "__moon_version");
|
|
if (lua_tointeger(L, -1) != MOON_VERSION)
|
|
moon_type_error_version_(L, idx);
|
|
lua_pop(L, 2);
|
|
moon_object_run_destructor_(h);
|
|
}
|
|
|
|
MOON_API void moon_defcast(lua_State *L, char const *tname1,
|
|
char const *tname2,
|
|
moon_object_cast cast)
|
|
{
|
|
luaL_checkstack(L, 2, "moon_defcast");
|
|
moon_push_metatable_(L, tname1);
|
|
moon_check_tname_(L, tname2);
|
|
lua_pushcfunction(L, (lua_CFunction)(void (*)(void))cast);
|
|
lua_setfield(L, -2, tname2);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
/* Validates a chain of vcheck objects. The chain won't be long, so
|
|
* a recursive approach should be fine! */
|
|
static int moon_validate_vcheck_(moon_object_vcheck_ const *vc)
|
|
{
|
|
if (vc->next != NULL)
|
|
return moon_validate_vcheck_(vc->next) &&
|
|
(vc->check == 0 || vc->check(vc->tagp));
|
|
else
|
|
return vc->check == 0 || vc->check(vc->tagp);
|
|
}
|
|
|
|
MOON_API void *moon_checkobject(lua_State *L, int idx,
|
|
char const *tname)
|
|
{
|
|
moon_object_header *h = (moon_object_header *)lua_touserdata(L, idx);
|
|
void *p = NULL;
|
|
int res = 0;
|
|
moon_object_cast cast = 0;
|
|
moon_check_tname_(L, tname);
|
|
luaL_checkstack(L, 3, "moon_checkobject");
|
|
idx = moon_absindex(L, idx);
|
|
if (h == NULL)
|
|
moon_type_error_(L, idx, tname, luaL_typename(L, idx));
|
|
if (lua_islightuserdata(L, idx))
|
|
moon_type_error_(L, idx, tname, "lightuserdata");
|
|
if (!lua_getmetatable(L, idx))
|
|
moon_type_error_(L, idx, tname, "userdata");
|
|
lua_getfield(L, -1, "__moon_version");
|
|
if (lua_tointeger(L, -1) != MOON_VERSION)
|
|
{
|
|
lua_pop(L, 2);
|
|
moon_type_error_version_(L, idx);
|
|
}
|
|
lua_pop(L, 1);
|
|
luaL_getmetatable(L, tname);
|
|
res = lua_rawequal(L, -1, -2);
|
|
lua_pop(L, 1);
|
|
if (!res)
|
|
{
|
|
lua_getfield(L, -1, tname);
|
|
cast = (moon_object_cast)(void (*)(void))lua_tocfunction(L, -1);
|
|
lua_pop(L, 1);
|
|
if (cast == 0)
|
|
{
|
|
char const *name = NULL;
|
|
lua_getfield(L, -1, "__name");
|
|
name = lua_tostring(L, -1);
|
|
moon_type_error_(L, idx, tname, name ? name : "userdata");
|
|
}
|
|
}
|
|
lua_pop(L, 1);
|
|
if (!(h->flags & MOON_OBJECT_IS_VALID))
|
|
moon_type_error_invalid_(L, idx, tname);
|
|
if (h->vcheck_offset > 0)
|
|
{
|
|
moon_object_vcheck_ *vc = NULL;
|
|
vc = (moon_object_vcheck_ *)MOON_PTR_(h, h->vcheck_offset);
|
|
if (!moon_validate_vcheck_(vc))
|
|
moon_type_error_invalid_(L, idx, tname);
|
|
}
|
|
p = MOON_PTR_(h, h->object_offset);
|
|
if (h->flags & MOON_OBJECT_IS_POINTER)
|
|
p = *((void **)p);
|
|
if (p == NULL)
|
|
moon_type_error_invalid_(L, idx, tname);
|
|
if (cast != 0)
|
|
{
|
|
p = cast(p);
|
|
if (p == NULL)
|
|
moon_type_error_invalid_(L, idx, tname);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
MOON_API void *moon_testobject(lua_State *L, int idx,
|
|
char const *tname)
|
|
{
|
|
moon_object_header *h = (moon_object_header *)lua_touserdata(L, idx);
|
|
void *p = NULL;
|
|
int res = 0;
|
|
moon_object_cast cast = 0;
|
|
moon_check_tname_(L, tname);
|
|
luaL_checkstack(L, 2, "moon_testobject");
|
|
if (h == NULL || !lua_getmetatable(L, idx))
|
|
return NULL;
|
|
lua_getfield(L, -1, "__moon_version");
|
|
if (lua_tointeger(L, -1) != MOON_VERSION)
|
|
{
|
|
lua_pop(L, 2);
|
|
return NULL;
|
|
}
|
|
lua_pop(L, 1);
|
|
luaL_getmetatable(L, tname);
|
|
res = lua_rawequal(L, -1, -2);
|
|
lua_pop(L, 1);
|
|
if (!res)
|
|
{
|
|
lua_getfield(L, -1, tname);
|
|
cast = (moon_object_cast)(void (*)(void))lua_tocfunction(L, -1);
|
|
lua_pop(L, 2);
|
|
if (cast == 0)
|
|
return NULL;
|
|
}
|
|
else
|
|
lua_pop(L, 1);
|
|
if (!(h->flags & MOON_OBJECT_IS_VALID))
|
|
return NULL;
|
|
if (h->vcheck_offset > 0)
|
|
{
|
|
moon_object_vcheck_ *vc = NULL;
|
|
vc = (moon_object_vcheck_ *)MOON_PTR_(h, h->vcheck_offset);
|
|
if (!moon_validate_vcheck_(vc))
|
|
return NULL;
|
|
}
|
|
p = MOON_PTR_(h, h->object_offset);
|
|
if (h->flags & MOON_OBJECT_IS_POINTER)
|
|
p = *((void **)p);
|
|
if (cast != 0)
|
|
p = cast(p);
|
|
return p;
|
|
}
|
|
|
|
static void *moon_cast_id_(void *p)
|
|
{
|
|
return p;
|
|
}
|
|
|
|
static void moon_copy_table_(lua_State *L, int src, int dst)
|
|
{
|
|
src = moon_absindex(L, src);
|
|
dst = moon_absindex(L, dst);
|
|
lua_pushnil(L);
|
|
while (lua_next(L, src))
|
|
{
|
|
lua_pushvalue(L, -2); /* duplicate key */
|
|
lua_pushvalue(L, -2); /* duplicate value */
|
|
lua_rawset(L, dst);
|
|
lua_pop(L, 1); /* pop value */
|
|
}
|
|
}
|
|
|
|
MOON_LLINKAGE_BEGIN
|
|
MOON_API int moon_derive(lua_State *L)
|
|
{
|
|
char const *newtype = luaL_checkstring(L, 1);
|
|
char const *oldtype = luaL_checkstring(L, 2);
|
|
int t = 0;
|
|
lua_settop(L, 2);
|
|
moon_check_tname_(L, newtype);
|
|
lua_pushvalue(L, 1);
|
|
lua_rawget(L, LUA_REGISTRYINDEX);
|
|
luaL_argcheck(L, lua_isnil(L, -1), 1,
|
|
"attempt to redefine type");
|
|
lua_pop(L, 1);
|
|
lua_pushvalue(L, 2);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* 3: old metatable */
|
|
moon_check_metatable_(L, oldtype);
|
|
/* clone metatable */
|
|
lua_newtable(L); /* 4: new metatable */
|
|
moon_copy_table_(L, 3, 4);
|
|
/* replace __tostring */
|
|
lua_pushvalue(L, 1);
|
|
lua_pushcclosure(L, moon_object_default_tostring_, 1);
|
|
lua_setfield(L, 4, "__tostring");
|
|
/* add cast to old type */
|
|
lua_pushvalue(L, 2);
|
|
lua_pushcfunction(L, moon_getf_(L, "cast",
|
|
(lua_CFunction)(void (*)(void))moon_cast_id_));
|
|
lua_rawset(L, 4);
|
|
/* replace __index metamethod */
|
|
lua_pushliteral(L, "__index");
|
|
lua_rawget(L, 4); /* 5: __index metamethod */
|
|
t = lua_type(L, 5);
|
|
lua_newtable(L); /* 6: new methods table */
|
|
lua_pushliteral(L, "__index"); /* 7: string "__index" */
|
|
if (t == LUA_TTABLE)
|
|
{
|
|
moon_copy_table_(L, 5, 6);
|
|
lua_pushvalue(L, 6); /* 8: new methods table */
|
|
}
|
|
else if (t == LUA_TFUNCTION)
|
|
{
|
|
lua_CFunction dispatch = moon_getf_(L, "index",
|
|
moon_index_dispatch_);
|
|
if (lua_tocfunction(L, 5) == dispatch)
|
|
{
|
|
lua_getupvalue(L, 5, 1); /* 8: old methods table */
|
|
if (lua_istable(L, -1))
|
|
moon_copy_table_(L, -1, 6);
|
|
lua_pop(L, 1);
|
|
lua_pushvalue(L, 6); /* 8: new methods table */
|
|
lua_getupvalue(L, 5, 2); /* 9: properties */
|
|
lua_getupvalue(L, 5, 3); /* 10: index func */
|
|
lua_pushcclosure(L, dispatch, 3); /* 8: dispatcher */
|
|
}
|
|
else
|
|
{
|
|
lua_pushvalue(L, 6); /* 8: new methods table */
|
|
lua_pushnil(L); /* 9: no properties */
|
|
lua_pushvalue(L, 5); /* 10: index func */
|
|
lua_pushcclosure(L, dispatch, 3); /* 8: dispatcher */
|
|
}
|
|
}
|
|
else
|
|
lua_pushvalue(L, 6); /* 8: new methods table */
|
|
lua_rawset(L, 4);
|
|
/* register new type */
|
|
lua_pushvalue(L, 1);
|
|
lua_pushvalue(L, 4);
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
return 1;
|
|
}
|
|
MOON_LLINKAGE_END
|
|
|
|
MOON_LLINKAGE_BEGIN
|
|
MOON_API int moon_downcast(lua_State *L)
|
|
{
|
|
void *h = lua_touserdata(L, 1);
|
|
char const *tname = luaL_checkstring(L, 2);
|
|
moon_object_cast cast = 0, id_cast = 0;
|
|
lua_settop(L, 2);
|
|
luaL_argcheck(L, h != NULL && lua_getmetatable(L, 1), 1,
|
|
"object expected"); /* 3: metatable */
|
|
luaL_argcheck(L, lua_istable(L, -1), 1, "object expected");
|
|
lua_getfield(L, -1, "__moon_version");
|
|
luaL_argcheck(L, lua_tointeger(L, -1) == MOON_VERSION, 1,
|
|
"object expected");
|
|
lua_pop(L, 1);
|
|
moon_check_tname_(L, tname);
|
|
lua_getfield(L, -1, "__name"); /* 4: __name */
|
|
lua_pushvalue(L, 2);
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* 5: metatable */
|
|
moon_check_metatable_(L, tname);
|
|
lua_pushvalue(L, 4);
|
|
lua_rawget(L, 5); /* 6: cast function */
|
|
cast = (moon_object_cast)(void (*)(void))lua_tocfunction(L, -1);
|
|
id_cast = (moon_object_cast)(void (*)(void))moon_getf_(L, "cast", (lua_CFunction)(void (*)(void))moon_cast_id_);
|
|
luaL_argcheck(L, cast == id_cast, 1, "invalid downcast");
|
|
lua_pop(L, 1);
|
|
lua_setmetatable(L, 1);
|
|
lua_settop(L, 1);
|
|
return 1;
|
|
}
|
|
MOON_LLINKAGE_END
|
|
|
|
MOON_API lua_Integer moon_checkint(lua_State *L, int idx,
|
|
lua_Integer low,
|
|
lua_Integer high)
|
|
{
|
|
lua_Integer v = luaL_checkinteger(L, idx);
|
|
int valid = (high < low) ? (low <= v || v <= high)
|
|
: (low <= v && v <= high);
|
|
if (!valid)
|
|
{
|
|
char const *msg = NULL;
|
|
#if LUA_VERSION_NUM >= 503
|
|
msg = lua_pushfstring(L, "value out of range [%I,%I]", low, high);
|
|
#else
|
|
if (low >= INT_MIN && low <= INT_MAX &&
|
|
high >= INT_MIN && high <= INT_MAX)
|
|
msg = lua_pushfstring(L, "value out of range [%d,%d]",
|
|
(int)low, (int)high);
|
|
else
|
|
msg = lua_pushfstring(L, "value out of range [%f,%f]",
|
|
(lua_Number)low, (lua_Number)high);
|
|
#endif
|
|
luaL_argerror(L, idx, msg);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
MOON_API lua_Integer moon_optint(lua_State *L, int idx,
|
|
lua_Integer low, lua_Integer high,
|
|
lua_Integer def)
|
|
{
|
|
if (!lua_isnoneornil(L, idx))
|
|
def = moon_checkint(L, idx, low, high);
|
|
return def;
|
|
}
|
|
|
|
MOON_API int *moon_atexit(lua_State *L, lua_CFunction func)
|
|
{
|
|
int *flag = NULL;
|
|
luaL_checkstack(L, 3, "moon_atexit");
|
|
flag = (int *)lua_newuserdata(L, sizeof(int));
|
|
*flag = 0;
|
|
/* setmetatable( flag, { __gc = func } ) */
|
|
lua_newtable(L);
|
|
lua_pushcfunction(L, func);
|
|
lua_setfield(L, -2, "__gc");
|
|
lua_setmetatable(L, -2);
|
|
/* registry[ flag ] = flag */
|
|
lua_pushvalue(L, -1);
|
|
lua_pushvalue(L, -2);
|
|
lua_settable(L, LUA_REGISTRYINDEX);
|
|
return flag;
|
|
}
|
|
|
|
MOON_API void moon_setuvfield(lua_State *L, int i, char const *key)
|
|
{
|
|
luaL_checkstack(L, 2, "moon_setuvfield");
|
|
#if LUA_VERSION_NUM < 502
|
|
lua_getfenv(L, i);
|
|
#else
|
|
lua_getuservalue(L, i);
|
|
#endif
|
|
if (!lua_istable(L, -1))
|
|
luaL_error(L, "attempt to add to non-table uservalue");
|
|
lua_pushvalue(L, -2);
|
|
lua_setfield(L, -2, key);
|
|
lua_pop(L, 2);
|
|
}
|
|
|
|
MOON_API int moon_getuvfield(lua_State *L, int i, char const *key)
|
|
{
|
|
luaL_checkstack(L, 2, "moon_getuvfield");
|
|
#if LUA_VERSION_NUM < 502
|
|
lua_getfenv(L, i);
|
|
#else
|
|
lua_getuservalue(L, i);
|
|
#endif
|
|
if (lua_istable(L, -1))
|
|
{
|
|
int t = 0;
|
|
lua_getfield(L, -1, key);
|
|
lua_replace(L, -2);
|
|
t = lua_type(L, -1);
|
|
if (t != LUA_TNIL)
|
|
return t;
|
|
}
|
|
lua_pop(L, 1);
|
|
return LUA_TNIL;
|
|
}
|
|
|
|
MOON_API void moon_getcache(lua_State *L, int index)
|
|
{
|
|
static char xyz = 0; /* used as a unique key */
|
|
luaL_checkstack(L, 3, "moon_getcache");
|
|
index = moon_absindex(L, index);
|
|
lua_pushlightuserdata(L, &xyz);
|
|
lua_rawget(L, index);
|
|
if (!lua_istable(L, -1))
|
|
{
|
|
lua_pop(L, 1);
|
|
lua_createtable(L, 0, 1);
|
|
lua_pushliteral(L, "__mode");
|
|
lua_pushliteral(L, "v");
|
|
lua_rawset(L, -3);
|
|
lua_pushvalue(L, -1);
|
|
lua_setmetatable(L, -2);
|
|
lua_pushlightuserdata(L, &xyz);
|
|
lua_pushvalue(L, -2);
|
|
lua_rawset(L, index);
|
|
}
|
|
}
|
|
|
|
static int moon_check_type_(lua_State *L, int idx, char const *spec)
|
|
{
|
|
int t = lua_type(L, idx);
|
|
while (*spec != '\0')
|
|
{
|
|
switch (*spec)
|
|
{
|
|
case 'n':
|
|
if (t == LUA_TNIL)
|
|
return 1;
|
|
break;
|
|
case 'b':
|
|
if (t == LUA_TBOOLEAN)
|
|
return 1;
|
|
break;
|
|
case 'l':
|
|
if (t == LUA_TLIGHTUSERDATA)
|
|
return 1;
|
|
break;
|
|
case 'i':
|
|
if (t == LUA_TNUMBER
|
|
#if LUA_VERSION_NUM >= 503
|
|
&& lua_isinteger(L, idx)
|
|
#endif
|
|
)
|
|
return 1;
|
|
break;
|
|
case 'd':
|
|
if (t == LUA_TNUMBER)
|
|
return 1;
|
|
break;
|
|
case 's':
|
|
if (t == LUA_TSTRING)
|
|
return 1;
|
|
break;
|
|
case 't':
|
|
if (t == LUA_TTABLE)
|
|
return 1;
|
|
break;
|
|
case 'f':
|
|
if (t == LUA_TFUNCTION)
|
|
return 1;
|
|
break;
|
|
case 'u':
|
|
if (t == LUA_TUSERDATA)
|
|
return 1;
|
|
break;
|
|
case 'c':
|
|
if (t == LUA_TTHREAD)
|
|
return 1;
|
|
break;
|
|
case 'a':
|
|
if (t != LUA_TNIL)
|
|
return 1;
|
|
break;
|
|
}
|
|
++spec;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static char const *moon_type2spec_(int t)
|
|
{
|
|
switch (t)
|
|
{
|
|
case LUA_TNONE:
|
|
case LUA_TNIL:
|
|
return "n";
|
|
case LUA_TBOOLEAN:
|
|
return "b";
|
|
case LUA_TLIGHTUSERDATA:
|
|
return "l";
|
|
case LUA_TNUMBER:
|
|
return "d";
|
|
case LUA_TSTRING:
|
|
return "s";
|
|
case LUA_TTABLE:
|
|
return "t";
|
|
case LUA_TFUNCTION:
|
|
return "f";
|
|
case LUA_TUSERDATA:
|
|
return "u";
|
|
case LUA_TTHREAD:
|
|
return "c";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
#define MOON_SEP_ \
|
|
"######################################################################"
|
|
|
|
MOON_API void moon_stack_assert_(lua_State *L, char const *file,
|
|
int line, char const *func, ...)
|
|
{
|
|
int top = lua_gettop(L);
|
|
int nargs = 0;
|
|
int skip = 0;
|
|
int have_error = 0;
|
|
int i = 0;
|
|
char const *arg;
|
|
va_list ap;
|
|
va_start(ap, func);
|
|
while ((arg = va_arg(ap, char const *)) != NULL)
|
|
++nargs;
|
|
va_end(ap);
|
|
if (nargs > top)
|
|
{
|
|
fputs(MOON_SEP_ "\n", stderr);
|
|
fprintf(stderr, "Lua stack assertion failed:"
|
|
" less stack slots (%d) than expected (%d)!\n",
|
|
top, nargs);
|
|
have_error = 1;
|
|
skip = nargs - top;
|
|
}
|
|
va_start(ap, func);
|
|
for (i = 0; i < skip; ++i)
|
|
arg = va_arg(ap, char const *);
|
|
for (i = top + skip - nargs + 1; i <= top; ++i)
|
|
{
|
|
arg = va_arg(ap, char const *);
|
|
if (!moon_check_type_(L, i, arg))
|
|
{
|
|
if (!have_error)
|
|
fputs(MOON_SEP_ "\n", stderr);
|
|
fprintf(stderr, "Lua stack assertion failed:"
|
|
" '%s' at slot %d (expected %s'%s')!\n",
|
|
moon_type2spec_(lua_type(L, i)), i,
|
|
(arg[0] != '\0' && arg[1] == 0) ? "" : "one of ",
|
|
arg);
|
|
have_error = 1;
|
|
}
|
|
}
|
|
va_end(ap);
|
|
if (have_error)
|
|
{
|
|
moon_stack_(L, file, line, func);
|
|
luaL_error(L, "Lua stack assertion failed!");
|
|
}
|
|
}
|
|
|
|
MOON_API void moon_stack_(lua_State *L, char const *file, int line,
|
|
char const *func)
|
|
{
|
|
int n = lua_gettop(L);
|
|
int i = 0;
|
|
fputs(MOON_SEP_ "\n", stderr);
|
|
fprintf(stderr, "Lua stack in function '%s' at '%s:%d':\n",
|
|
func != NULL ? func : "(unknown)", file, line);
|
|
for (i = 1; i <= n; ++i)
|
|
{
|
|
fprintf(stderr, "%02d: %s ", i, luaL_typename(L, i));
|
|
switch (lua_type(L, i))
|
|
{
|
|
case LUA_TNONE:
|
|
fputs("(none)\n", stderr);
|
|
break;
|
|
case LUA_TNIL:
|
|
fputs("(nil)\n", stderr);
|
|
break;
|
|
case LUA_TBOOLEAN:
|
|
fprintf(stderr, "(%s)\n", lua_toboolean(L, i) ? "true" : "false");
|
|
break;
|
|
case LUA_TNUMBER:
|
|
#if LUA_VERSION_NUM >= 503
|
|
if (lua_isinteger(L, i))
|
|
fprintf(stderr, "(" LUA_INTEGER_FMT ")\n",
|
|
lua_tointeger(L, i));
|
|
else
|
|
#endif
|
|
fprintf(stderr, "(" LUA_NUMBER_FMT ")\n",
|
|
lua_tonumber(L, i));
|
|
break;
|
|
case LUA_TSTRING:
|
|
{
|
|
char const *str = lua_tostring(L, i);
|
|
char const *ellipsis = "";
|
|
#if LUA_VERSION_NUM < 502
|
|
unsigned len = (unsigned)lua_objlen(L, i);
|
|
#else
|
|
unsigned len = (unsigned)lua_rawlen(L, i);
|
|
#endif
|
|
unsigned i = 0;
|
|
fprintf(stderr, "(len: %u, str: \"", len);
|
|
if (len > 15)
|
|
{
|
|
len = 15;
|
|
ellipsis = "...";
|
|
}
|
|
for (i = 0; i < len; ++i)
|
|
{
|
|
if (isprint((unsigned char)str[i]))
|
|
putc((unsigned char)str[i], stderr);
|
|
else
|
|
fprintf(stderr, "\\x%02X", (unsigned)str[i]);
|
|
}
|
|
fputs(ellipsis, stderr);
|
|
fputs("\")\n", stderr);
|
|
break;
|
|
}
|
|
case LUA_TLIGHTUSERDATA:
|
|
fprintf(stderr, "(%p)\n", lua_touserdata(L, i));
|
|
break;
|
|
case LUA_TUSERDATA:
|
|
if (luaL_getmetafield(L, i, "__name"))
|
|
{
|
|
char const *name = lua_tostring(L, -1);
|
|
fprintf(stderr, "(type: \"%s\", size: %u, ptr: %p)\n",
|
|
name != NULL ? name : "(NULL)",
|
|
#if LUA_VERSION_NUM < 502
|
|
(unsigned)lua_objlen(L, i),
|
|
#else
|
|
(unsigned)lua_rawlen(L, i),
|
|
#endif
|
|
lua_touserdata(L, i));
|
|
lua_pop(L, 1);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "(size: %u, ptr: %p)\n",
|
|
#if LUA_VERSION_NUM < 502
|
|
(unsigned)lua_objlen(L, i),
|
|
#else
|
|
(unsigned)lua_rawlen(L, i),
|
|
#endif
|
|
lua_touserdata(L, i));
|
|
}
|
|
break;
|
|
default: /* thread, table, function */
|
|
fprintf(stderr, "(%p)\n", lua_topointer(L, i));
|
|
break;
|
|
}
|
|
}
|
|
fputs(MOON_SEP_ "\n", stderr);
|
|
}
|
|
|
|
#undef MOON_SEP_
|
|
|
|
/* clean up internally used macros */
|
|
#undef MOON_ALIGNOF_
|
|
#undef MOON_OBJ_ALIGNMENT_
|
|
#undef MOON_PTR_ALIGNMENT_
|
|
#undef MOON_GCF_ALIGNMENT_
|
|
#undef MOON_VCK_ALIGNMENT_
|
|
#undef MOON_ROUNDTO_
|
|
#undef MOON_PTR_
|
|
|
|
#endif /* MOON_C_ */
|