2012-09-29 20:03:37 -06:00
|
|
|
/*
|
2012-03-19 10:12:27 -06:00
|
|
|
https://github.com/peterix/dfhack
|
2012-09-29 20:03:37 -06:00
|
|
|
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
|
2012-03-19 10:12:27 -06:00
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any
|
|
|
|
damages arising from the use of this software.
|
|
|
|
|
|
|
|
Permission is granted to anyone to use this software for any
|
|
|
|
purpose, including commercial applications, and to alter it and
|
|
|
|
redistribute it freely, subject to the following restrictions:
|
|
|
|
|
|
|
|
1. The origin of this software must not be misrepresented; you must
|
|
|
|
not claim that you wrote the original software. If you use this
|
|
|
|
software in a product, an acknowledgment in the product documentation
|
|
|
|
would be appreciated but is not required.
|
|
|
|
|
|
|
|
2. Altered source versions must be plainly marked as such, and
|
|
|
|
must not be misrepresented as being the original software.
|
|
|
|
|
|
|
|
3. This notice may not be removed or altered from any source
|
|
|
|
distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Internal.h"
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
#include <map>
|
2018-06-11 10:57:06 -06:00
|
|
|
#include <cinttypes>
|
2012-03-19 10:12:27 -06:00
|
|
|
|
|
|
|
#include "MemAccess.h"
|
|
|
|
#include "Core.h"
|
|
|
|
#include "VersionInfo.h"
|
|
|
|
#include "tinythread.h"
|
|
|
|
// must be last due to MS stupidity
|
|
|
|
#include "DataDefs.h"
|
|
|
|
#include "DataIdentity.h"
|
2012-03-24 03:25:10 -06:00
|
|
|
#include "LuaWrapper.h"
|
2012-04-02 09:10:57 -06:00
|
|
|
#include "LuaTools.h"
|
2012-03-19 10:12:27 -06:00
|
|
|
|
|
|
|
#include "MiscUtils.h"
|
|
|
|
|
|
|
|
#include <lua.h>
|
|
|
|
#include <lauxlib.h>
|
|
|
|
|
|
|
|
using namespace DFHack;
|
2012-03-24 03:25:10 -06:00
|
|
|
using namespace DFHack::LuaWrapper;
|
2012-03-19 10:12:27 -06:00
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Report an error while accessing a field (index = field name).
|
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
void LuaWrapper::field_error(lua_State *state, int index, const char *err, const char *mode)
|
2012-03-20 03:56:29 -06:00
|
|
|
{
|
2012-04-05 01:59:39 -06:00
|
|
|
if (lua_islightuserdata(state, UPVAL_METATABLE))
|
|
|
|
lua_pushstring(state, "(global)");
|
|
|
|
else
|
|
|
|
lua_getfield(state, UPVAL_METATABLE, "__metatable");
|
2012-03-20 03:56:29 -06:00
|
|
|
const char *cname = lua_tostring(state, -1);
|
2012-03-23 00:56:29 -06:00
|
|
|
const char *fname = index ? lua_tostring(state, index) : "*";
|
2012-03-20 03:56:29 -06:00
|
|
|
luaL_error(state, "Cannot %s field %s.%s: %s.",
|
|
|
|
mode, (cname ? cname : "?"), (fname ? fname : "?"), err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
static int change_error(lua_State *state)
|
|
|
|
{
|
|
|
|
luaL_error(state, "Attempt to change a read-only table.\n");
|
2012-03-22 00:56:32 -06:00
|
|
|
return 0;
|
2012-03-19 10:12:27 -06:00
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Wrap a table so that it can't be modified.
|
|
|
|
*/
|
2012-03-19 10:12:27 -06:00
|
|
|
static void freeze_table(lua_State *state, bool leave_metatable = false, const char *name = NULL)
|
|
|
|
{
|
|
|
|
// rv = {}; setmetatable(rv, { __index = in, __newindex = change_error, __metatable = name })
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
lua_newtable(state);
|
|
|
|
lua_swap(state);
|
|
|
|
lua_setfield(state, base, "__index");
|
2012-03-20 11:34:27 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME);
|
2012-03-19 10:12:27 -06:00
|
|
|
lua_setfield(state, base, "__newindex");
|
|
|
|
lua_newtable(state);
|
|
|
|
lua_swap(state);
|
|
|
|
lua_dup(state);
|
|
|
|
lua_setmetatable(state, base);
|
|
|
|
if (name)
|
|
|
|
{
|
|
|
|
lua_pushstring(state, name);
|
|
|
|
lua_setfield(state, -2, "__metatable");
|
|
|
|
}
|
|
|
|
// result: [frozen table] [metatable]
|
|
|
|
if (!leave_metatable)
|
|
|
|
lua_pop(state, 1);
|
|
|
|
}
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
static void LookupInTable(lua_State *state, LuaToken *tname)
|
2012-03-23 00:56:29 -06:00
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, tname);
|
2012-03-23 00:56:29 -06:00
|
|
|
lua_swap(state);
|
|
|
|
lua_rawget(state, -2);
|
|
|
|
lua_remove(state, -2);
|
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Look up the key on the stack in DFHACK_TYPETABLE;
|
|
|
|
* if found, put result on the stack and return true.
|
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
bool LuaWrapper::LookupTypeInfo(lua_State *state, bool in_method)
|
2012-03-20 11:34:27 -06:00
|
|
|
{
|
|
|
|
// stack: [lookup key]
|
|
|
|
|
|
|
|
if (in_method)
|
|
|
|
{
|
|
|
|
lua_rawget(state, UPVAL_TYPETABLE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
LookupInTable(state, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-20 11:34:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// stack: [info]
|
|
|
|
|
2012-04-01 07:59:47 -06:00
|
|
|
if (lua_isnil(state, -1))
|
2012-03-20 11:34:27 -06:00
|
|
|
{
|
|
|
|
lua_pop(state, 1);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
void LuaWrapper::LookupInTable(lua_State *state, void *id, LuaToken *tname)
|
2012-03-21 03:26:53 -06:00
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, tname);
|
2012-04-01 07:59:47 -06:00
|
|
|
lua_rawgetp(state, -1, id);
|
2012-03-21 03:26:53 -06:00
|
|
|
lua_remove(state, -2);
|
|
|
|
}
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
void LuaWrapper::SaveInTable(lua_State *state, void *node, LuaToken *tname)
|
2012-03-19 10:12:27 -06:00
|
|
|
{
|
2012-03-20 11:34:27 -06:00
|
|
|
// stack: [info]
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, tname);
|
2012-03-20 11:34:27 -06:00
|
|
|
|
2012-04-01 07:59:47 -06:00
|
|
|
lua_pushvalue(state, -2);
|
|
|
|
lua_rawsetp(state, -2, node);
|
2012-03-20 11:34:27 -06:00
|
|
|
|
|
|
|
lua_pushvalue(state, -2);
|
|
|
|
lua_pushlightuserdata(state, node);
|
|
|
|
lua_rawset(state, -3);
|
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
lua_pop(state, 1);
|
2012-03-20 11:34:27 -06:00
|
|
|
// stack: [info]
|
2012-03-19 10:12:27 -06:00
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
void LuaWrapper::SaveTypeInfo(lua_State *state, void *node)
|
2012-03-19 10:12:27 -06:00
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
SaveInTable(state, node, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-20 11:34:27 -06:00
|
|
|
}
|
2012-03-19 10:12:27 -06:00
|
|
|
|
2012-03-20 11:34:27 -06:00
|
|
|
static void BuildTypeMetatable(lua_State *state, type_identity *type);
|
2012-03-19 10:12:27 -06:00
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Push the pointer as DF object ref using metatable on the stack.
|
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
void LuaWrapper::push_object_ref(lua_State *state, void *ptr)
|
2012-03-20 11:34:27 -06:00
|
|
|
{
|
|
|
|
// stack: [metatable]
|
|
|
|
auto ref = (DFRefHeader*)lua_newuserdata(state, sizeof(DFRefHeader));
|
|
|
|
ref->ptr = ptr;
|
2020-04-08 22:02:07 -06:00
|
|
|
ref->field_info = NULL;
|
2021-03-30 14:55:06 -06:00
|
|
|
ref->tag_ptr = NULL;
|
|
|
|
ref->tag_identity = NULL;
|
|
|
|
ref->tag_attr = NULL;
|
2012-03-19 10:12:27 -06:00
|
|
|
|
2012-03-20 11:34:27 -06:00
|
|
|
lua_swap(state);
|
|
|
|
lua_setmetatable(state, -2);
|
|
|
|
// stack: [userdata]
|
|
|
|
}
|
|
|
|
|
2020-04-08 22:02:07 -06:00
|
|
|
DFRefHeader *LuaWrapper::get_object_ref_header(lua_State *state, int val_index)
|
2012-03-23 01:30:54 -06:00
|
|
|
{
|
|
|
|
assert(!lua_islightuserdata(state, val_index));
|
|
|
|
|
|
|
|
auto ref = (DFRefHeader*)lua_touserdata(state, val_index);
|
2020-04-08 22:02:07 -06:00
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *LuaWrapper::get_object_ref(lua_State *state, int val_index)
|
|
|
|
{
|
|
|
|
return get_object_ref_header(state, val_index)->ptr;
|
2012-03-23 01:30:54 -06:00
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Push the pointer using given identity.
|
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
void LuaWrapper::push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method)
|
2012-03-20 11:34:27 -06:00
|
|
|
{
|
2012-03-21 10:04:37 -06:00
|
|
|
/*
|
|
|
|
* If NULL pointer or no type, push something simple
|
|
|
|
*/
|
|
|
|
|
2012-03-20 11:34:27 -06:00
|
|
|
if (!ptr || !type)
|
2012-03-19 10:12:27 -06:00
|
|
|
{
|
2012-03-20 11:34:27 -06:00
|
|
|
if (!ptr)
|
|
|
|
lua_pushnil(state);
|
|
|
|
else
|
|
|
|
lua_pushlightuserdata(state, ptr);
|
2012-03-19 10:12:27 -06:00
|
|
|
|
2012-03-20 11:34:27 -06:00
|
|
|
return;
|
|
|
|
}
|
2012-03-19 10:12:27 -06:00
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/*
|
|
|
|
* Resolve actual class using vtable
|
|
|
|
*/
|
|
|
|
|
2012-03-20 11:34:27 -06:00
|
|
|
if (type->type() == IDTYPE_CLASS)
|
|
|
|
{
|
|
|
|
virtual_identity *class_vid = virtual_identity::get(virtual_ptr(ptr));
|
|
|
|
if (class_vid)
|
|
|
|
type = class_vid;
|
2012-03-19 10:12:27 -06:00
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/*
|
|
|
|
* Resolve metatable by identity, and push the object
|
|
|
|
*/
|
|
|
|
|
2012-03-20 11:34:27 -06:00
|
|
|
lua_pushlightuserdata(state, type); // () -> type
|
|
|
|
|
|
|
|
if (!LookupTypeInfo(state, in_method)) // type -> metatable?
|
|
|
|
BuildTypeMetatable(state, type); // () -> metatable
|
|
|
|
|
|
|
|
push_object_ref(state, ptr); // metatable -> userdata
|
2012-03-19 10:12:27 -06:00
|
|
|
}
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
static void fetch_container_details(lua_State *state, int meta, type_identity **pitem, int *pcount)
|
|
|
|
{
|
|
|
|
if (!meta) return;
|
|
|
|
|
|
|
|
lua_getfield(state, meta, "_field_identity");
|
|
|
|
*pitem = (type_identity*)lua_touserdata(state, -1);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
if (pcount)
|
|
|
|
{
|
|
|
|
lua_getfield(state, meta, "_count");
|
|
|
|
if (lua_isnumber(state, -1))
|
|
|
|
*pcount = lua_tointeger(state, -1);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if type1 and type2 are compatible, possibly using additional metatable data.
|
|
|
|
*/
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
bool LuaWrapper::is_type_compatible(lua_State *state, type_identity *type1, int meta1,
|
|
|
|
type_identity *type2, int meta2, bool exact_equal)
|
2012-03-24 02:43:53 -06:00
|
|
|
{
|
|
|
|
if (type1 == type2)
|
|
|
|
return true;
|
|
|
|
if (!exact_equal && !type1)
|
|
|
|
return true;
|
|
|
|
if (!type1 || !type2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto t1 = type1->type();
|
|
|
|
if (t1 != type2->type())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (t1)
|
|
|
|
{
|
|
|
|
case IDTYPE_POINTER:
|
|
|
|
return is_type_compatible(state,
|
|
|
|
((pointer_identity*)type1)->getTarget(), 0,
|
|
|
|
((pointer_identity*)type2)->getTarget(), 0,
|
|
|
|
exact_equal);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IDTYPE_BUFFER:
|
|
|
|
{
|
|
|
|
auto b1 = (df::buffer_container_identity*)type1;
|
|
|
|
auto b2 = (df::buffer_container_identity*)type2;
|
|
|
|
type_identity *item1 = b1->getItemType(), *item2 = b2->getItemType();
|
|
|
|
int count1 = b1->getSize(), count2 = b2->getSize();
|
|
|
|
|
|
|
|
fetch_container_details(state, meta1, &item1, &count1);
|
|
|
|
fetch_container_details(state, meta2, &item2, &count2);
|
|
|
|
|
|
|
|
return item1 && item2 && count1 == count2 &&
|
|
|
|
is_type_compatible(state, item1, 0, item2, 0, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
case IDTYPE_STL_PTR_VECTOR:
|
|
|
|
{
|
|
|
|
auto b1 = (df::stl_ptr_vector_identity*)type1;
|
|
|
|
auto b2 = (df::stl_ptr_vector_identity*)type2;
|
|
|
|
type_identity *item1 = b1->getItemType(), *item2 = b2->getItemType();
|
|
|
|
|
|
|
|
fetch_container_details(state, meta1, &item1, NULL);
|
|
|
|
fetch_container_details(state, meta1, &item2, NULL);
|
|
|
|
|
|
|
|
return is_type_compatible(state, item1, 0, item2, 0, exact_equal);
|
|
|
|
}
|
|
|
|
|
|
|
|
case IDTYPE_STRUCT:
|
2020-02-29 12:11:23 -07:00
|
|
|
case IDTYPE_UNION:
|
2012-03-24 02:43:53 -06:00
|
|
|
case IDTYPE_CLASS:
|
|
|
|
{
|
|
|
|
auto b1 = (struct_identity*)type1;
|
|
|
|
auto b2 = (struct_identity*)type2;
|
|
|
|
|
|
|
|
return (!exact_equal && b1->is_subclass(b2));
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_type_compatible(lua_State *state, type_identity *type1, int meta1,
|
|
|
|
int meta2, bool exact_equal)
|
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, meta2, &DFHACK_IDENTITY_FIELD_TOKEN);
|
2012-03-24 02:43:53 -06:00
|
|
|
auto type2 = (type_identity*)lua_touserdata(state, -1);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
return is_type_compatible(state, type1, meta1, type2, meta2, exact_equal);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_type_compatible(lua_State *state, int meta1, int meta2, bool exact_equal)
|
|
|
|
{
|
|
|
|
if (lua_rawequal(state, meta1, meta2))
|
|
|
|
return true;
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, meta1, &DFHACK_IDENTITY_FIELD_TOKEN);
|
2012-03-24 02:43:53 -06:00
|
|
|
auto type1 = (type_identity*)lua_touserdata(state, -1);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
return is_type_compatible(state, type1, meta1, meta2, exact_equal);
|
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Verify that the value matches the identity, and return ptr if so.
|
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
void *LuaWrapper::get_object_internal(lua_State *state, type_identity *type, int val_index, bool exact_type, bool in_method)
|
2012-03-20 11:34:27 -06:00
|
|
|
{
|
2012-03-21 10:04:37 -06:00
|
|
|
/*
|
|
|
|
* Non-userdata results in NULL; nil for NULL gets handled here too.
|
|
|
|
*/
|
2012-03-20 11:34:27 -06:00
|
|
|
if (!lua_isuserdata(state, val_index))
|
|
|
|
return NULL;
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/*
|
|
|
|
* Light user data is allowed with null type; otherwise bail out.
|
|
|
|
*/
|
2012-03-20 11:34:27 -06:00
|
|
|
if (!lua_getmetatable(state, val_index)) // () -> metatable?
|
|
|
|
{
|
|
|
|
if (!type && lua_islightuserdata(state, val_index))
|
|
|
|
return lua_touserdata(state, val_index);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/*
|
|
|
|
* Verify that the metatable is known, and refers to the correct type.
|
|
|
|
* Here doing reverse lookup of identity by metatable.
|
|
|
|
*/
|
2012-03-20 11:34:27 -06:00
|
|
|
if (!LookupTypeInfo(state, in_method)) // metatable -> type?
|
|
|
|
return NULL;
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
if (type && lua_touserdata(state, -1) != type)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If valid but different type, do an intelligent comparison.
|
|
|
|
*/
|
|
|
|
lua_pop(state, 1); // type -> ()
|
|
|
|
lua_getmetatable(state, val_index);
|
2012-03-20 11:34:27 -06:00
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
if (!is_type_compatible(state, type, 0, lua_gettop(state), exact_type))
|
|
|
|
{
|
|
|
|
lua_pop(state, 1); // metatable -> ()
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2012-03-20 11:34:27 -06:00
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_pop(state, 1); // type -> ()
|
2012-03-20 11:34:27 -06:00
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/*
|
|
|
|
* Finally decode the reference.
|
|
|
|
*/
|
2012-03-23 01:30:54 -06:00
|
|
|
return get_object_ref(state, val_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-03-24 02:43:53 -06:00
|
|
|
* Check if the object and metatable are a valid DF reference or type.
|
2012-03-23 01:30:54 -06:00
|
|
|
*/
|
2012-03-24 02:43:53 -06:00
|
|
|
static bool is_valid_metatable(lua_State *state, int objidx, int metaidx)
|
2012-03-23 01:30:54 -06:00
|
|
|
{
|
|
|
|
// Verify object type validity
|
|
|
|
if (lua_isuserdata(state, objidx))
|
|
|
|
{
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_pushvalue(state, metaidx);
|
|
|
|
lua_rawget(state, UPVAL_TYPETABLE);
|
2012-03-23 01:30:54 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, objidx);
|
2012-04-01 08:34:04 -06:00
|
|
|
LookupInTable(state, &DFHACK_TYPEID_TABLE_TOKEN);
|
2012-03-23 01:30:54 -06:00
|
|
|
}
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
bool ok = !lua_isnil(state, -1);
|
2012-03-23 01:30:54 -06:00
|
|
|
lua_pop(state, 1);
|
2012-03-24 02:43:53 -06:00
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2012-04-02 09:10:57 -06:00
|
|
|
bool Lua::IsDFNull(lua_State *state, int val_index)
|
|
|
|
{
|
|
|
|
if (lua_isnil(state, val_index))
|
|
|
|
return true;
|
|
|
|
if (lua_islightuserdata(state, val_index))
|
|
|
|
return lua_touserdata(state, val_index) == NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Lua::ObjectClass Lua::IsDFObject(lua_State *state, int val_index)
|
|
|
|
{
|
|
|
|
if (lua_isnil(state, val_index))
|
|
|
|
return Lua::OBJ_NULL;
|
|
|
|
if (lua_islightuserdata(state, val_index))
|
|
|
|
return lua_touserdata(state, val_index) ? Lua::OBJ_VOIDPTR : OBJ_NULL;
|
|
|
|
|
|
|
|
Lua::ObjectClass cls;
|
|
|
|
|
|
|
|
if (lua_istable(state, val_index))
|
|
|
|
{
|
|
|
|
cls = Lua::OBJ_TYPE;
|
|
|
|
lua_pushvalue(state, val_index);
|
|
|
|
LookupInTable(state, &DFHACK_TYPEID_TABLE_TOKEN);
|
|
|
|
}
|
|
|
|
else if (lua_isuserdata(state, val_index))
|
|
|
|
{
|
|
|
|
if (!lua_getmetatable(state, val_index))
|
|
|
|
return Lua::OBJ_INVALID;
|
|
|
|
cls = Lua::OBJ_REF;
|
|
|
|
LookupInTable(state, &DFHACK_TYPETABLE_TOKEN);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return Lua::OBJ_INVALID;
|
|
|
|
|
|
|
|
bool ok = !lua_isnil(state, -1);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
return ok ? cls : Lua::OBJ_INVALID;
|
|
|
|
}
|
|
|
|
|
2012-05-18 09:18:49 -06:00
|
|
|
static const char *const primitive_types[] = {
|
2012-06-13 12:26:54 -06:00
|
|
|
"string",
|
2016-04-03 18:48:57 -06:00
|
|
|
"ptr-string",
|
2016-08-10 22:01:52 -06:00
|
|
|
"char",
|
2012-06-13 12:26:54 -06:00
|
|
|
"int8_t", "uint8_t", "int16_t", "uint16_t",
|
|
|
|
"int32_t", "uint32_t", "int64_t", "uint64_t",
|
2016-08-10 22:01:52 -06:00
|
|
|
"intptr_t", "uintptr_t", "long", "unsigned long",
|
|
|
|
"bool",
|
|
|
|
"float", "double",
|
2016-04-03 18:48:57 -06:00
|
|
|
"pointer",
|
|
|
|
"ptr-vector",
|
|
|
|
"bit-vector",
|
|
|
|
"bit-array",
|
2012-06-13 12:26:54 -06:00
|
|
|
NULL
|
2012-05-18 09:18:49 -06:00
|
|
|
};
|
|
|
|
static type_identity *const primitive_identities[] = {
|
2012-06-13 12:26:54 -06:00
|
|
|
df::identity_traits<std::string>::get(),
|
2016-04-03 18:48:57 -06:00
|
|
|
df::identity_traits<const char*>::get(),
|
2016-08-10 22:01:52 -06:00
|
|
|
df::identity_traits<char>::get(),
|
2012-06-13 12:26:54 -06:00
|
|
|
df::identity_traits<int8_t>::get(), df::identity_traits<uint8_t>::get(),
|
|
|
|
df::identity_traits<int16_t>::get(), df::identity_traits<uint16_t>::get(),
|
|
|
|
df::identity_traits<int32_t>::get(), df::identity_traits<uint32_t>::get(),
|
|
|
|
df::identity_traits<int64_t>::get(), df::identity_traits<uint64_t>::get(),
|
2016-07-28 22:02:51 -06:00
|
|
|
df::identity_traits<intptr_t>::get(), df::identity_traits<uintptr_t>::get(),
|
2016-08-10 22:01:52 -06:00
|
|
|
df::identity_traits<long>::get(), df::identity_traits<unsigned long>::get(),
|
2012-06-13 12:26:54 -06:00
|
|
|
df::identity_traits<bool>::get(),
|
|
|
|
df::identity_traits<float>::get(), df::identity_traits<double>::get(),
|
2016-04-03 18:48:57 -06:00
|
|
|
df::identity_traits<void*>::get(),
|
|
|
|
df::identity_traits<std::vector<void*> >::get(),
|
|
|
|
df::identity_traits<std::vector<bool> >::get(),
|
|
|
|
df::identity_traits<BitArray<int> >::get(),
|
2012-06-13 12:26:54 -06:00
|
|
|
NULL
|
2012-05-18 09:18:49 -06:00
|
|
|
};
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
/**
|
|
|
|
* Given a DF object reference or type, safely retrieve its identity pointer.
|
|
|
|
*/
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
type_identity *LuaWrapper::get_object_identity(lua_State *state, int objidx,
|
|
|
|
const char *ctx, bool allow_type,
|
|
|
|
bool keep_metatable)
|
2012-03-24 02:43:53 -06:00
|
|
|
{
|
2012-05-18 09:18:49 -06:00
|
|
|
if (allow_type && !keep_metatable && lua_isstring(state, objidx))
|
|
|
|
{
|
|
|
|
int idx = luaL_checkoption(state, objidx, NULL, primitive_types);
|
|
|
|
return primitive_identities[idx];
|
|
|
|
}
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
if (!lua_getmetatable(state, objidx))
|
|
|
|
luaL_error(state, "Invalid object in %s", ctx);
|
|
|
|
|
|
|
|
if (!allow_type && !lua_isuserdata(state, objidx))
|
|
|
|
luaL_error(state, "Object expected in %s", ctx);
|
|
|
|
|
|
|
|
if (!is_valid_metatable(state, objidx, -1))
|
|
|
|
luaL_error(state, "Invalid object metatable in %s", ctx);
|
2012-03-23 01:30:54 -06:00
|
|
|
|
|
|
|
// Extract identity from metatable
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, -1, &DFHACK_IDENTITY_FIELD_TOKEN);
|
2012-03-23 01:30:54 -06:00
|
|
|
|
|
|
|
type_identity *id = (type_identity*)lua_touserdata(state, -1);
|
|
|
|
if (!id)
|
|
|
|
luaL_error(state, "Invalid object identity in %s", ctx);
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_pop(state, keep_metatable ? 1 : 2);
|
2012-03-23 01:30:54 -06:00
|
|
|
return id;
|
2012-03-20 11:34:27 -06:00
|
|
|
}
|
|
|
|
|
2012-04-01 07:32:57 -06:00
|
|
|
static bool check_type_compatible(lua_State *state, int obj1, int obj2,
|
2012-03-24 02:43:53 -06:00
|
|
|
type_identity **type1, type_identity **type2,
|
2012-04-01 07:32:57 -06:00
|
|
|
const char *ctx, bool allow_type, bool exact, bool error = true)
|
2012-03-24 02:43:53 -06:00
|
|
|
{
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
|
|
|
*type1 = get_object_identity(state, obj1, ctx, allow_type, true);
|
|
|
|
*type2 = get_object_identity(state, obj2, ctx, allow_type, true);
|
|
|
|
|
|
|
|
if (!is_type_compatible(state, *type1, base+1, *type2, base+2, exact))
|
|
|
|
{
|
2012-04-01 07:32:57 -06:00
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
lua_pop(state, 2);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_getfield(state, base+1, "__metatable");
|
|
|
|
const char *cname1 = lua_tostring(state, -1);
|
|
|
|
lua_getfield(state, base+2, "__metatable");
|
|
|
|
const char *cname2 = lua_tostring(state, -1);
|
|
|
|
|
|
|
|
luaL_error(state, "Types %s and %s incompatible in %s", cname1, cname2, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pop(state, 2);
|
2012-04-01 07:32:57 -06:00
|
|
|
return true;
|
2012-03-24 02:43:53 -06:00
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: compare two DF object references.
|
|
|
|
*
|
|
|
|
* Equal if same pointer and same metatable.
|
|
|
|
*/
|
2012-03-20 11:34:27 -06:00
|
|
|
static int meta_ptr_compare(lua_State *state)
|
|
|
|
{
|
|
|
|
if (!lua_isuserdata(state, 1) || !lua_isuserdata(state, 2) ||
|
2012-03-24 02:43:53 -06:00
|
|
|
!lua_getmetatable(state, 1) || !lua_getmetatable(state, 2) ||
|
|
|
|
get_object_ref(state, 1) != get_object_ref(state, 2) ||
|
|
|
|
!is_type_compatible(state, 3, 4, true))
|
2012-03-20 11:34:27 -06:00
|
|
|
{
|
|
|
|
lua_pushboolean(state, false);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-23 01:30:54 -06:00
|
|
|
lua_pushboolean(state, true);
|
2012-03-20 11:34:27 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-23 01:30:54 -06:00
|
|
|
/**
|
|
|
|
* Method: sizeof for DF object references.
|
|
|
|
*
|
|
|
|
* Returns: size[, address]
|
|
|
|
*/
|
|
|
|
static int meta_sizeof(lua_State *state)
|
|
|
|
{
|
|
|
|
int argc = lua_gettop(state);
|
|
|
|
|
|
|
|
if (argc != 1)
|
|
|
|
luaL_error(state, "Usage: object:sizeof() or df.sizeof(object)");
|
|
|
|
|
|
|
|
// Two special cases: nil and lightuserdata for NULL and void*
|
2012-03-29 04:47:33 -06:00
|
|
|
if (lua_isnil(state, 1) || lua_islightuserdata(state, 1))
|
2012-03-23 01:30:54 -06:00
|
|
|
{
|
|
|
|
lua_pushnil(state);
|
2016-07-28 22:02:51 -06:00
|
|
|
lua_pushinteger(state, (size_t)lua_touserdata(state, 1));
|
2012-03-23 01:30:54 -06:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2012-04-20 03:04:03 -06:00
|
|
|
type_identity *id = get_object_identity(state, 1, "df.sizeof()", true, true);
|
2012-03-23 01:30:54 -06:00
|
|
|
|
2012-04-20 03:04:03 -06:00
|
|
|
// Static arrays need special handling
|
|
|
|
if (id->type() == IDTYPE_BUFFER)
|
|
|
|
{
|
|
|
|
auto buf = (df::buffer_container_identity*)id;
|
|
|
|
type_identity *item = buf->getItemType();
|
|
|
|
int count = buf->getSize();
|
|
|
|
|
|
|
|
fetch_container_details(state, lua_gettop(state), &item, &count);
|
|
|
|
|
|
|
|
lua_pushinteger(state, item->byte_size() * count);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lua_pushinteger(state, id->byte_size());
|
2012-03-23 01:30:54 -06:00
|
|
|
|
2012-04-20 03:04:03 -06:00
|
|
|
// Add the address
|
2012-03-23 01:30:54 -06:00
|
|
|
if (lua_isuserdata(state, 1))
|
|
|
|
{
|
2016-07-28 22:02:51 -06:00
|
|
|
lua_pushinteger(state, (size_t)get_object_ref(state, 1));
|
2012-03-23 01:30:54 -06:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-23 01:54:59 -06:00
|
|
|
/**
|
|
|
|
* Method: displace for DF object references.
|
|
|
|
*
|
|
|
|
* Returns: a reference with the same type, but modified address
|
|
|
|
*/
|
|
|
|
static int meta_displace(lua_State *state)
|
|
|
|
{
|
|
|
|
int argc = lua_gettop(state);
|
|
|
|
|
|
|
|
bool has_step = (argc >= 3);
|
|
|
|
if ((argc < 2 || argc > 3) ||
|
|
|
|
!lua_isnumber(state, 2) ||
|
|
|
|
(has_step && !lua_isnumber(state, 3)))
|
|
|
|
{
|
|
|
|
luaL_error(state, "Usage: object:_displace(index[,step]) or df._displace(object,...)");
|
|
|
|
}
|
|
|
|
|
|
|
|
int index = lua_tointeger(state, 2);
|
|
|
|
int step = has_step ? lua_tointeger(state, 3) : 1;
|
|
|
|
|
|
|
|
// Two special cases: nil and lightuserdata for NULL and void*
|
|
|
|
if (lua_isnil(state, 1))
|
|
|
|
{
|
|
|
|
lua_pushnil(state);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lua_islightuserdata(state, 1))
|
|
|
|
{
|
|
|
|
if (!has_step)
|
|
|
|
luaL_error(state, "Step is mandatory in _displace of void*");
|
|
|
|
|
|
|
|
auto ptr = (uint8_t*)lua_touserdata(state, 1);
|
|
|
|
lua_pushlightuserdata(state, ptr + index*step);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
type_identity *id = get_object_identity(state, 1, "df._displace()");
|
|
|
|
|
|
|
|
if (!has_step)
|
|
|
|
step = id->byte_size();
|
|
|
|
|
|
|
|
if (index == 0 || step == 0)
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto ptr = (uint8_t*)get_object_ref(state, 1);
|
|
|
|
lua_getmetatable(state, 1);
|
|
|
|
push_object_ref(state, ptr + index*step);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
/**
|
|
|
|
* Method: allocation for DF object references.
|
|
|
|
*/
|
|
|
|
static int meta_new(lua_State *state)
|
|
|
|
{
|
|
|
|
int argc = lua_gettop(state);
|
|
|
|
|
2012-06-13 12:26:54 -06:00
|
|
|
if (argc != 1 && argc != 2)
|
|
|
|
luaL_error(state, "Usage: object:new() or df.new(object) or df.new(ptype,count)");
|
2012-03-24 02:43:53 -06:00
|
|
|
|
|
|
|
type_identity *id = get_object_identity(state, 1, "df.new()", true);
|
|
|
|
|
2023-08-04 15:14:08 -06:00
|
|
|
void *ptr = nullptr;
|
|
|
|
std::string err_context;
|
2012-06-13 12:26:54 -06:00
|
|
|
|
|
|
|
// Support arrays of primitive types
|
|
|
|
if (argc == 2)
|
|
|
|
{
|
|
|
|
int cnt = luaL_checkint(state, 2);
|
|
|
|
if (cnt <= 0)
|
|
|
|
luaL_error(state, "Invalid array size in df.new()");
|
|
|
|
if (id->type() != IDTYPE_PRIMITIVE)
|
|
|
|
luaL_error(state, "Cannot allocate arrays of non-primitive types.");
|
|
|
|
|
|
|
|
size_t sz = id->byte_size() * cnt;
|
|
|
|
ptr = malloc(sz);
|
|
|
|
if (ptr)
|
|
|
|
memset(ptr, 0, sz);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-08-04 15:14:08 -06:00
|
|
|
try {
|
|
|
|
ptr = id->allocate();
|
|
|
|
}
|
|
|
|
catch (std::exception &e) {
|
|
|
|
if (e.what()) {
|
|
|
|
err_context = e.what();
|
|
|
|
}
|
|
|
|
}
|
2012-06-13 12:26:54 -06:00
|
|
|
}
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
if (!ptr)
|
2023-08-04 15:14:08 -06:00
|
|
|
luaL_error(state, "Cannot allocate %s%s%s",
|
|
|
|
id->getFullName().c_str(),
|
|
|
|
err_context.empty() ? "" : ": ",
|
|
|
|
err_context.c_str()
|
|
|
|
);
|
2012-03-24 02:43:53 -06:00
|
|
|
|
|
|
|
if (lua_isuserdata(state, 1))
|
|
|
|
{
|
|
|
|
lua_getmetatable(state, 1);
|
|
|
|
push_object_ref(state, ptr);
|
|
|
|
|
|
|
|
id->copy(ptr, get_object_ref(state, 1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push_object_internal(state, id, ptr);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-06-13 12:26:54 -06:00
|
|
|
/**
|
|
|
|
* Method: type casting of pointers.
|
|
|
|
*/
|
|
|
|
static int meta_reinterpret_cast(lua_State *state)
|
|
|
|
{
|
|
|
|
int argc = lua_gettop(state);
|
|
|
|
|
|
|
|
if (argc != 2)
|
|
|
|
luaL_error(state, "Usage: df.reinterpret_cast(type,ptr)");
|
|
|
|
|
|
|
|
type_identity *id = get_object_identity(state, 1, "df.reinterpret_cast()", true);
|
|
|
|
|
|
|
|
// Find the raw pointer value
|
|
|
|
void *ptr;
|
|
|
|
|
|
|
|
if (lua_isnil(state, 2))
|
|
|
|
ptr = NULL;
|
|
|
|
else if (lua_isnumber(state, 2))
|
2012-06-16 07:09:58 -06:00
|
|
|
ptr = (void*)lua_tounsigned(state, 2);
|
2012-06-13 12:26:54 -06:00
|
|
|
else
|
|
|
|
{
|
|
|
|
ptr = get_object_internal(state, NULL, 2, false, true);
|
|
|
|
if (!ptr)
|
|
|
|
luaL_error(state, "Invalid pointer argument in df.reinterpret_cast.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert it to the appropriate representation
|
|
|
|
if (ptr == NULL)
|
|
|
|
{
|
|
|
|
lua_pushnil(state);
|
|
|
|
}
|
|
|
|
else if (lua_isuserdata(state, 1))
|
|
|
|
{
|
|
|
|
lua_getmetatable(state, 1);
|
|
|
|
push_object_ref(state, ptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push_object_internal(state, id, ptr);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
static void invoke_resize(lua_State *state, int table, lua_Integer size)
|
|
|
|
{
|
|
|
|
lua_getfield(state, table, "resize");
|
|
|
|
lua_pushvalue(state, table);
|
|
|
|
lua_pushinteger(state, size);
|
|
|
|
lua_call(state, 2, 0);
|
|
|
|
}
|
|
|
|
|
2012-03-28 01:28:42 -06:00
|
|
|
static void copy_table(lua_State *state, int dest, int src, int skipbase)
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
{
|
2012-03-28 01:28:42 -06:00
|
|
|
// stack: (skipbase) skipkey skipkey |
|
|
|
|
|
|
|
|
int top = lua_gettop(state);
|
|
|
|
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
lua_pushnil(state);
|
|
|
|
|
|
|
|
while (lua_next(state, src))
|
|
|
|
{
|
2012-03-28 01:28:42 -06:00
|
|
|
for (int i = skipbase+1; i <= top; i++)
|
|
|
|
{
|
|
|
|
if (lua_rawequal(state, -2, i))
|
|
|
|
{
|
|
|
|
lua_pop(state, 1);
|
|
|
|
goto next_outer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
{
|
|
|
|
lua_pushvalue(state, -2);
|
|
|
|
lua_swap(state);
|
|
|
|
lua_settable(state, dest);
|
|
|
|
}
|
2012-03-28 01:28:42 -06:00
|
|
|
|
|
|
|
next_outer:;
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
/**
|
|
|
|
* Method: assign data between objects.
|
|
|
|
*/
|
|
|
|
static int meta_assign(lua_State *state)
|
|
|
|
{
|
|
|
|
int argc = lua_gettop(state);
|
|
|
|
|
|
|
|
if (argc != 2)
|
|
|
|
luaL_error(state, "Usage: target:assign(src) or df.assign(target,src)");
|
|
|
|
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
if (!lua_istable(state, 2))
|
|
|
|
{
|
|
|
|
type_identity *id1, *id2;
|
|
|
|
check_type_compatible(state, 1, 2, &id1, &id2, "df.assign()", false, false);
|
|
|
|
|
|
|
|
if (!id1->copy(get_object_ref(state, 1), get_object_ref(state, 2)))
|
|
|
|
luaL_error(state, "No copy support for %s", id1->getFullName().c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
type_identity *id = get_object_identity(state, 1, "df.assign()", false);
|
2012-03-24 02:43:53 -06:00
|
|
|
|
2012-04-06 09:56:19 -06:00
|
|
|
if (lua_getmetatable(state, 2))
|
|
|
|
luaL_error(state, "cannot use lua tables with metatable in df.assign()");
|
|
|
|
|
2012-03-28 01:28:42 -06:00
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
|
|
|
// x:assign{ assign = foo } => x:assign(foo)
|
|
|
|
bool has_assign = false;
|
|
|
|
|
|
|
|
lua_pushstring(state, "assign");
|
|
|
|
lua_dup(state);
|
|
|
|
lua_rawget(state, 2);
|
|
|
|
|
|
|
|
if (!lua_isnil(state,-1))
|
|
|
|
{
|
|
|
|
has_assign = true;
|
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
|
|
|
|
lua_pushvalue(state, 1);
|
|
|
|
lua_pushvalue(state, base+2);
|
|
|
|
lua_call(state, 2, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
// new is used by autovivification and should be skipped
|
|
|
|
lua_pushstring(state, "new");
|
|
|
|
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
if (id->isContainer())
|
|
|
|
{
|
2012-03-28 01:28:42 -06:00
|
|
|
// check resize field
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
lua_pushstring(state, "resize");
|
|
|
|
lua_dup(state);
|
|
|
|
lua_rawget(state, 2);
|
|
|
|
|
2012-03-28 01:28:42 -06:00
|
|
|
if (lua_isnil(state,-1) && !has_assign)
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
{
|
|
|
|
/*
|
2012-03-28 01:28:42 -06:00
|
|
|
* no assign && nil or missing resize field => 1-based lua array
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
*/
|
2012-03-31 02:11:43 -06:00
|
|
|
int size = lua_rawlen(state, 2);
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
invoke_resize(state, 1, size);
|
|
|
|
|
|
|
|
for (int i = 1; i <= size; i++)
|
|
|
|
{
|
|
|
|
lua_pushinteger(state, i-1);
|
|
|
|
lua_rawgeti(state, 2, i);
|
|
|
|
lua_settable(state, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-28 01:28:42 -06:00
|
|
|
if (lua_isboolean(state, -1) || lua_isnil(state, -1))
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
{
|
|
|
|
// resize=false => just assign
|
|
|
|
// resize=true => find the largest index
|
|
|
|
if (lua_toboolean(state, -1))
|
|
|
|
{
|
|
|
|
lua_Integer size = 0;
|
|
|
|
|
|
|
|
lua_pushnil(state);
|
|
|
|
while (lua_next(state, 2))
|
|
|
|
{
|
|
|
|
lua_pop(state, 1);
|
|
|
|
if (lua_isnumber(state,-1))
|
|
|
|
size = std::max(size, lua_tointeger(state,-1)+1);
|
|
|
|
}
|
|
|
|
|
|
|
|
invoke_resize(state, 1, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// otherwise, must be an explicit number
|
|
|
|
if (!lua_isnumber(state,-1))
|
|
|
|
luaL_error(state, "Invalid container.resize value in df.assign()");
|
|
|
|
|
|
|
|
invoke_resize(state, 1, lua_tointeger(state, -1));
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
2012-03-28 01:28:42 -06:00
|
|
|
copy_table(state, 1, 2, base);
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-28 01:28:42 -06:00
|
|
|
|
|
|
|
copy_table(state, 1, 2, base);
|
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.
For structs, the entries in the table are assigned to matching fields.
For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.
For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
2012-03-27 11:47:52 -06:00
|
|
|
}
|
|
|
|
}
|
2012-03-24 02:43:53 -06:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-01 07:32:57 -06:00
|
|
|
/**
|
|
|
|
* Method: check if the two objects are assignment-compatible.
|
|
|
|
*/
|
|
|
|
static int meta_is_instance(lua_State *state)
|
|
|
|
{
|
|
|
|
int argc = lua_gettop(state);
|
|
|
|
|
|
|
|
if (argc != 2)
|
|
|
|
luaL_error(state, "Usage: type:is_instance(obj) or df.is_instance(type,obj)");
|
|
|
|
|
|
|
|
// If garbage as second argument, return nil
|
|
|
|
if (!lua_istable(state, 2) && (!lua_isuserdata(state,2) || lua_islightuserdata(state, 2)))
|
|
|
|
{
|
|
|
|
lua_pushnil(state);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
type_identity *id1, *id2;
|
|
|
|
bool ok = check_type_compatible(state, 1, 2, &id1, &id2, "df.is_instance()", true, false, false);
|
|
|
|
|
|
|
|
lua_pushboolean(state, ok);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-25 05:48:18 -06:00
|
|
|
/**
|
|
|
|
* Method: deallocation for DF object references.
|
|
|
|
*/
|
|
|
|
static int meta_delete(lua_State *state)
|
|
|
|
{
|
|
|
|
int argc = lua_gettop(state);
|
|
|
|
|
|
|
|
if (argc != 1)
|
|
|
|
luaL_error(state, "Usage: object:delete() or df.delete(object)");
|
|
|
|
|
|
|
|
if (lua_isnil(state, 1))
|
|
|
|
{
|
|
|
|
lua_pushboolean(state, true);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
type_identity *id = get_object_identity(state, 1, "df.delete()", false);
|
|
|
|
|
|
|
|
bool ok = id->destroy(get_object_ref(state, 1));
|
|
|
|
|
|
|
|
lua_pushboolean(state, ok);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Verify that the object is a DF ref with UPVAL_METATABLE.
|
|
|
|
* If everything ok, extract the address.
|
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
uint8_t *LuaWrapper::get_object_addr(lua_State *state, int obj, int field, const char *mode)
|
2012-03-20 11:34:27 -06:00
|
|
|
{
|
|
|
|
if (!lua_isuserdata(state, obj) ||
|
|
|
|
!lua_getmetatable(state, obj))
|
|
|
|
field_error(state, field, "invalid object", mode);
|
|
|
|
|
2012-03-23 00:56:29 -06:00
|
|
|
if (!lua_rawequal(state, -1, UPVAL_METATABLE))
|
2012-03-20 11:34:27 -06:00
|
|
|
field_error(state, field, "invalid object metatable", mode);
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
2012-03-23 01:30:54 -06:00
|
|
|
return (uint8_t*)get_object_ref(state, obj);
|
2012-03-20 11:34:27 -06:00
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: represent a type node as string.
|
|
|
|
*/
|
2012-03-20 11:34:27 -06:00
|
|
|
static int meta_type_tostring(lua_State *state)
|
|
|
|
{
|
|
|
|
if (!lua_getmetatable(state, 1))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lua_getfield(state, -1, "__metatable");
|
|
|
|
const char *cname = lua_tostring(state, -1);
|
|
|
|
|
|
|
|
lua_pushstring(state, stl_sprintf("<type: %s>", cname).c_str());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: represent a DF object reference as string.
|
|
|
|
*/
|
2012-03-20 11:34:27 -06:00
|
|
|
static int meta_ptr_tostring(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 0, "access");
|
|
|
|
|
2016-08-13 17:52:14 -06:00
|
|
|
bool has_length = false;
|
|
|
|
uint64_t length = 0;
|
|
|
|
auto *cid = dynamic_cast<df::container_identity*>(get_object_identity(state, 1, "__tostring()", true, true));
|
|
|
|
|
|
|
|
if (cid && (cid->type() == IDTYPE_CONTAINER || cid->type() == IDTYPE_STL_PTR_VECTOR))
|
|
|
|
{
|
|
|
|
has_length = true;
|
|
|
|
length = cid->lua_item_count(state, ptr, container_identity::COUNT_LEN);
|
|
|
|
}
|
|
|
|
|
2012-03-20 11:34:27 -06:00
|
|
|
lua_getfield(state, UPVAL_METATABLE, "__metatable");
|
|
|
|
const char *cname = lua_tostring(state, -1);
|
|
|
|
|
2016-08-13 17:52:14 -06:00
|
|
|
if (has_length)
|
2018-06-11 10:57:06 -06:00
|
|
|
lua_pushstring(state, stl_sprintf("<%s[%" PRIu64 "]: %p>", cname, length, (void*)ptr).c_str());
|
2016-08-13 17:52:14 -06:00
|
|
|
else
|
|
|
|
lua_pushstring(state, stl_sprintf("<%s: %p>", cname, (void*)ptr).c_str());
|
2012-03-20 11:34:27 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-25 09:12:59 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: __index for enum.attrs
|
|
|
|
*/
|
|
|
|
static int meta_enum_attr_index(lua_State *state)
|
|
|
|
{
|
|
|
|
if (!lua_isnumber(state, 2))
|
|
|
|
lua_rawget(state, UPVAL_FIELDTABLE);
|
|
|
|
if (!lua_isnumber(state, 2))
|
|
|
|
luaL_error(state, "Invalid index in enum.attrs[]");
|
|
|
|
|
|
|
|
auto id = (enum_identity*)lua_touserdata(state, lua_upvalueindex(2));
|
2018-04-09 14:12:03 -06:00
|
|
|
auto *complex = id->getComplex();
|
2012-03-25 09:12:59 -06:00
|
|
|
|
|
|
|
int64_t idx = lua_tonumber(state, 2);
|
2018-04-09 14:12:03 -06:00
|
|
|
if (complex)
|
|
|
|
{
|
|
|
|
auto it = complex->value_index_map.find(idx);
|
|
|
|
if (it != complex->value_index_map.end())
|
|
|
|
idx = int64_t(it->second);
|
|
|
|
else
|
|
|
|
idx = id->getLastItem() + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (idx < id->getFirstItem() || idx > id->getLastItem())
|
|
|
|
idx = id->getLastItem()+1;
|
|
|
|
idx -= id->getFirstItem();
|
|
|
|
}
|
2012-03-25 09:12:59 -06:00
|
|
|
|
|
|
|
uint8_t *ptr = (uint8_t*)id->getAttrs();
|
|
|
|
auto atype = id->getAttrType();
|
|
|
|
|
|
|
|
push_object_internal(state, atype, ptr + unsigned(atype->byte_size()*idx));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-04-02 09:10:57 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: df.isvalid(obj[,allow_null])
|
|
|
|
*/
|
|
|
|
static int meta_isvalid(lua_State *state)
|
|
|
|
{
|
|
|
|
luaL_checkany(state, 1);
|
|
|
|
|
|
|
|
switch (Lua::IsDFObject(state, 1))
|
|
|
|
{
|
|
|
|
case Lua::OBJ_NULL:
|
|
|
|
lua_settop(state, 2);
|
|
|
|
if (lua_toboolean(state, 2))
|
|
|
|
lua_pushvalue(state, lua_upvalueindex(1));
|
|
|
|
else
|
|
|
|
lua_pushnil(state);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case Lua::OBJ_TYPE:
|
|
|
|
lua_pushvalue(state, lua_upvalueindex(2));
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case Lua::OBJ_VOIDPTR:
|
|
|
|
lua_pushvalue(state, lua_upvalueindex(3));
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case Lua::OBJ_REF:
|
|
|
|
lua_pushvalue(state, lua_upvalueindex(4));
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case Lua::OBJ_INVALID:
|
|
|
|
default:
|
|
|
|
lua_pushnil(state);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: df.isnull(obj)
|
|
|
|
*/
|
|
|
|
static int meta_isnull(lua_State *state)
|
|
|
|
{
|
|
|
|
luaL_checkany(state, 1);
|
|
|
|
lua_pushboolean(state, Lua::IsDFNull(state, 1));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
static int meta_nodata(lua_State *state)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: __pairs, returning 1st upvalue as iterator
|
|
|
|
*/
|
|
|
|
static int meta_pairs(lua_State *state)
|
|
|
|
{
|
|
|
|
luaL_checkany(state, 1);
|
|
|
|
lua_pushvalue(state, lua_upvalueindex(1));
|
|
|
|
lua_pushvalue(state, 1);
|
|
|
|
lua_pushnil(state);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
2012-03-24 03:25:10 -06:00
|
|
|
* Make a metatable with most common fields, and an empty table for UPVAL_FIELDTABLE.
|
2012-03-21 10:04:37 -06:00
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
void LuaWrapper::MakeMetatable(lua_State *state, type_identity *type, const char *kind)
|
2012-03-21 03:26:53 -06:00
|
|
|
{
|
2012-03-24 03:25:10 -06:00
|
|
|
int base = lua_gettop(state);
|
|
|
|
lua_newtable(state); // metatable
|
2012-03-21 03:26:53 -06:00
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
lua_pushstring(state, type->getFullName().c_str());
|
|
|
|
lua_setfield(state, base+1, "__metatable");
|
2012-03-21 03:26:53 -06:00
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
lua_pushlightuserdata(state, type);
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawsetp(state, base+1, &DFHACK_IDENTITY_FIELD_TOKEN);
|
2012-03-21 10:04:37 -06:00
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
LookupInTable(state, type, &DFHACK_TYPEID_TABLE_TOKEN);
|
2012-03-24 03:25:10 -06:00
|
|
|
if (lua_isnil(state, -1))
|
2012-03-21 10:04:37 -06:00
|
|
|
{
|
2012-03-24 03:25:10 -06:00
|
|
|
// Copy the string from __metatable if no real type
|
2012-03-21 10:04:37 -06:00
|
|
|
lua_pop(state, 1);
|
2012-03-24 03:25:10 -06:00
|
|
|
lua_getfield(state, base+1, "__metatable");
|
2012-03-21 10:04:37 -06:00
|
|
|
}
|
2012-03-24 03:25:10 -06:00
|
|
|
lua_setfield(state, base+1, "_type");
|
2012-03-21 10:04:37 -06:00
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
lua_pushstring(state, kind);
|
|
|
|
lua_setfield(state, base+1, "_kind");
|
2012-03-23 00:56:29 -06:00
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
// Create the field table
|
|
|
|
lua_newtable(state);
|
2012-03-20 03:56:29 -06:00
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Enable a metafield by injecting an entry into a UPVAL_FIELDTABLE.
|
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
void LuaWrapper::EnableMetaField(lua_State *state, int ftable_idx, const char *name, void *id)
|
2012-03-21 03:26:53 -06:00
|
|
|
{
|
|
|
|
lua_pushlightuserdata(state, id);
|
|
|
|
lua_setfield(state, ftable_idx, name);
|
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Set metatable properties common to all actual DF object references.
|
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
void LuaWrapper::SetPtrMethods(lua_State *state, int meta_idx, int read_idx)
|
2012-03-20 11:34:27 -06:00
|
|
|
{
|
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME);
|
|
|
|
lua_setfield(state, meta_idx, "__eq");
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-20 11:34:27 -06:00
|
|
|
lua_pushvalue(state, meta_idx);
|
|
|
|
lua_pushcclosure(state, meta_ptr_tostring, 2);
|
|
|
|
lua_setfield(state, meta_idx, "__tostring");
|
|
|
|
|
2012-03-21 03:26:53 -06:00
|
|
|
EnableMetaField(state, read_idx, "_type");
|
2012-03-21 10:04:37 -06:00
|
|
|
EnableMetaField(state, read_idx, "_kind");
|
2012-03-23 01:54:59 -06:00
|
|
|
|
2012-03-23 02:55:29 -06:00
|
|
|
EnableMetaField(state, read_idx, "_field");
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
|
|
|
|
lua_setfield(state, meta_idx, "sizeof");
|
2012-03-23 01:30:54 -06:00
|
|
|
EnableMetaField(state, read_idx, "sizeof");
|
2012-03-24 02:43:53 -06:00
|
|
|
|
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
|
|
|
|
lua_setfield(state, meta_idx, "new");
|
|
|
|
EnableMetaField(state, read_idx, "new");
|
|
|
|
|
2012-03-25 05:48:18 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DELETE_NAME);
|
|
|
|
lua_setfield(state, meta_idx, "delete");
|
|
|
|
EnableMetaField(state, read_idx, "delete");
|
|
|
|
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
|
|
|
|
lua_setfield(state, meta_idx, "assign");
|
|
|
|
EnableMetaField(state, read_idx, "assign");
|
|
|
|
|
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME);
|
|
|
|
lua_setfield(state, meta_idx, "_displace");
|
2012-03-23 01:54:59 -06:00
|
|
|
EnableMetaField(state, read_idx, "_displace");
|
2012-03-20 11:34:27 -06:00
|
|
|
}
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
/**
|
|
|
|
* Add a __pairs/__ipairs metamethod using iterator on the top of stack.
|
|
|
|
*/
|
|
|
|
void LuaWrapper::SetPairsMethod(lua_State *state, int meta_idx, const char *name)
|
|
|
|
{
|
|
|
|
if (lua_isnil(state, -1))
|
|
|
|
{
|
|
|
|
lua_pop(state, 1);
|
|
|
|
lua_pushcfunction(state, meta_nodata);
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushcclosure(state, meta_pairs, 1);
|
|
|
|
lua_setfield(state, meta_idx, name);
|
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Add a struct-style (3 upvalues) metamethod to the metatable.
|
|
|
|
*/
|
2012-03-29 04:39:13 -06:00
|
|
|
void LuaWrapper::PushStructMethod(lua_State *state, int meta_idx, int ftable_idx,
|
|
|
|
lua_CFunction function)
|
2012-03-20 11:34:27 -06:00
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-20 11:34:27 -06:00
|
|
|
lua_pushvalue(state, meta_idx);
|
|
|
|
lua_pushvalue(state, ftable_idx);
|
|
|
|
lua_pushcclosure(state, function, 3);
|
2012-03-29 04:39:13 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a struct-style (3 upvalues) metamethod to the metatable.
|
|
|
|
*/
|
|
|
|
void LuaWrapper::SetStructMethod(lua_State *state, int meta_idx, int ftable_idx,
|
|
|
|
lua_CFunction function, const char *name)
|
|
|
|
{
|
|
|
|
PushStructMethod(state, meta_idx, ftable_idx, function);
|
2012-03-20 11:34:27 -06:00
|
|
|
lua_setfield(state, meta_idx, name);
|
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Add a 6 upvalue metamethod to the metatable.
|
|
|
|
*/
|
2012-03-29 04:39:13 -06:00
|
|
|
void LuaWrapper::PushContainerMethod(lua_State *state, int meta_idx, int ftable_idx,
|
|
|
|
lua_CFunction function,
|
|
|
|
type_identity *container, type_identity *item, int count)
|
2012-03-20 03:56:29 -06:00
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-21 03:26:53 -06:00
|
|
|
lua_pushvalue(state, meta_idx);
|
|
|
|
lua_pushvalue(state, ftable_idx);
|
|
|
|
|
|
|
|
lua_pushlightuserdata(state, container);
|
|
|
|
lua_pushlightuserdata(state, item);
|
|
|
|
if (count < 0)
|
|
|
|
lua_pushnil(state);
|
|
|
|
else
|
|
|
|
lua_pushinteger(state, count);
|
|
|
|
|
|
|
|
lua_pushcclosure(state, function, 6);
|
2012-03-29 04:39:13 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a 6 upvalue metamethod to the metatable.
|
|
|
|
*/
|
|
|
|
void LuaWrapper::SetContainerMethod(lua_State *state, int meta_idx, int ftable_idx,
|
|
|
|
lua_CFunction function, const char *name,
|
|
|
|
type_identity *container, type_identity *item, int count)
|
|
|
|
{
|
|
|
|
PushContainerMethod(state, meta_idx, ftable_idx, function, container, item, count);
|
2012-03-21 03:26:53 -06:00
|
|
|
lua_setfield(state, meta_idx, name);
|
2012-03-20 03:56:29 -06:00
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* If ienum refers to a valid enum, attach its keys to UPVAL_FIELDTABLE,
|
2012-03-29 04:39:13 -06:00
|
|
|
* and the enum itself to the _enum metafield. Pushes the key table on the stack
|
2012-03-21 10:04:37 -06:00
|
|
|
*/
|
2012-03-24 03:25:10 -06:00
|
|
|
void LuaWrapper::AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum)
|
2012-03-21 03:26:53 -06:00
|
|
|
{
|
2012-03-29 04:39:13 -06:00
|
|
|
EnableMetaField(state, ftable_idx, "_enum");
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
LookupInTable(state, ienum, &DFHACK_TYPEID_TABLE_TOKEN);
|
2012-03-29 04:39:13 -06:00
|
|
|
lua_setfield(state, meta_idx, "_enum");
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
LookupInTable(state, ienum, &DFHACK_ENUM_TABLE_TOKEN);
|
2012-03-21 03:26:53 -06:00
|
|
|
|
|
|
|
if (!lua_isnil(state, -1))
|
|
|
|
{
|
2012-03-29 04:39:13 -06:00
|
|
|
lua_dup(state);
|
2012-03-21 03:26:53 -06:00
|
|
|
lua_newtable(state);
|
|
|
|
lua_swap(state);
|
|
|
|
lua_setfield(state, -2, "__index");
|
2012-03-24 03:25:10 -06:00
|
|
|
lua_setmetatable(state, ftable_idx);
|
2012-03-21 03:26:53 -06:00
|
|
|
}
|
|
|
|
else
|
2012-03-29 04:39:13 -06:00
|
|
|
{
|
2012-03-21 03:26:53 -06:00
|
|
|
lua_pop(state, 1);
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_EMPTY_TABLE_TOKEN);
|
2012-03-29 04:39:13 -06:00
|
|
|
}
|
2012-03-21 10:04:37 -06:00
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
lua_dup(state);
|
|
|
|
lua_setfield(state, meta_idx, "_index_table");
|
2012-03-21 03:26:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void BuildTypeMetatable(lua_State *state, type_identity *type)
|
|
|
|
{
|
|
|
|
type->build_metatable(state);
|
|
|
|
|
2012-03-23 00:56:29 -06:00
|
|
|
lua_pop(state, 1);
|
2012-03-21 03:26:53 -06:00
|
|
|
|
|
|
|
SaveTypeInfo(state, type);
|
|
|
|
}
|
2012-03-20 11:34:27 -06:00
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/*
|
|
|
|
* Recursive walk of scopes to construct the df... tree.
|
|
|
|
*/
|
|
|
|
|
2012-10-17 10:58:37 -06:00
|
|
|
static int wtype_pnext(lua_State *L)
|
|
|
|
{
|
|
|
|
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
|
|
|
|
if (lua_next(L, lua_upvalueindex(1)))
|
|
|
|
return 2;
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wtype_pairs(lua_State *state)
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, lua_upvalueindex(1));
|
|
|
|
lua_pushcclosure(state, wtype_pnext, 1);
|
|
|
|
lua_pushnil(state);
|
|
|
|
lua_pushnil(state);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wtype_inext(lua_State *L)
|
|
|
|
{
|
|
|
|
int i = luaL_checkint(L, 2);
|
|
|
|
i++; /* next value */
|
|
|
|
if (i <= lua_tointeger(L, lua_upvalueindex(2)))
|
|
|
|
{
|
|
|
|
lua_pushinteger(L, i);
|
|
|
|
lua_rawgeti(L, lua_upvalueindex(1), i);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wtype_ipairs(lua_State *state)
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, lua_upvalueindex(1));
|
|
|
|
lua_pushvalue(state, lua_upvalueindex(3));
|
|
|
|
lua_pushcclosure(state, wtype_inext, 2);
|
|
|
|
lua_pushnil(state);
|
|
|
|
lua_pushvalue(state, lua_upvalueindex(2));
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
2015-11-07 13:22:30 -07:00
|
|
|
static int wtype_next_item(lua_State *state)
|
|
|
|
{
|
|
|
|
int first = lua_tointeger(state, lua_upvalueindex(1)),
|
|
|
|
last = lua_tointeger(state, lua_upvalueindex(2)),
|
|
|
|
cur = luaL_checkint(state, lua_gettop(state) > 1 ? 2 : 1); // 'self' optional
|
|
|
|
if (cur < last)
|
|
|
|
lua_pushinteger(state, cur + 1);
|
|
|
|
else
|
|
|
|
lua_pushinteger(state, first);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-04-09 09:04:37 -06:00
|
|
|
/*
|
|
|
|
* Complex enums
|
|
|
|
*
|
|
|
|
* upvalues for all of these:
|
|
|
|
* 1: key table? unsure, taken from wtype stuff
|
|
|
|
* 2: enum_identity::ComplexData
|
|
|
|
*/
|
|
|
|
|
2018-04-09 17:46:12 -06:00
|
|
|
static bool complex_enum_next_item_helper(lua_State *L, int64_t &item, bool wrap = false)
|
2018-04-09 09:04:37 -06:00
|
|
|
{
|
|
|
|
const auto *complex = (enum_identity::ComplexData*)lua_touserdata(L, lua_upvalueindex(2));
|
|
|
|
auto it = complex->value_index_map.find(item);
|
|
|
|
if (it != complex->value_index_map.end())
|
|
|
|
{
|
|
|
|
size_t index = it->second;
|
2018-04-09 17:46:12 -06:00
|
|
|
if (!wrap && index >= complex->size() - 1)
|
2018-04-09 09:04:37 -06:00
|
|
|
return false;
|
|
|
|
|
2018-04-09 17:46:12 -06:00
|
|
|
item = complex->index_value_map[(index + 1) % complex->size()];
|
2018-04-09 09:04:37 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int complex_enum_inext(lua_State *L)
|
|
|
|
{
|
2018-04-09 09:27:02 -06:00
|
|
|
bool is_first = lua_isuserdata(L, 2);
|
|
|
|
int64_t i = (is_first)
|
|
|
|
? ((enum_identity::ComplexData*)lua_touserdata(L, lua_upvalueindex(2)))->index_value_map[0]
|
|
|
|
: luaL_checkint(L, 2);
|
|
|
|
if (is_first || complex_enum_next_item_helper(L, i))
|
2018-04-09 09:04:37 -06:00
|
|
|
{
|
|
|
|
lua_pushinteger(L, i);
|
|
|
|
lua_rawgeti(L, lua_upvalueindex(1), i);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-09 17:46:12 -06:00
|
|
|
static int complex_enum_next_item(lua_State *L)
|
|
|
|
{
|
|
|
|
int64_t cur = luaL_checkint(L, lua_gettop(L) > 1 ? 2 : 1); // 'self' optional
|
|
|
|
complex_enum_next_item_helper(L, cur, true);
|
|
|
|
lua_pushinteger(L, cur);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-04-09 09:04:37 -06:00
|
|
|
static int complex_enum_ipairs(lua_State *L)
|
|
|
|
{
|
|
|
|
lua_pushvalue(L, lua_upvalueindex(1));
|
|
|
|
lua_pushvalue(L, lua_upvalueindex(2));
|
|
|
|
lua_pushcclosure(L, complex_enum_inext, 2);
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_pushlightuserdata(L, (void*)1);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children);
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
void LuaWrapper::AssociateId(lua_State *state, int table, int val, const char *name)
|
2012-03-21 10:04:37 -06:00
|
|
|
{
|
|
|
|
lua_pushinteger(state, val);
|
|
|
|
lua_pushstring(state, name);
|
|
|
|
lua_dup(state);
|
|
|
|
lua_pushinteger(state, val);
|
|
|
|
|
|
|
|
lua_rawset(state, table);
|
|
|
|
lua_rawset(state, table);
|
|
|
|
}
|
|
|
|
|
2012-10-17 10:58:37 -06:00
|
|
|
static void FillEnumKeys(lua_State *state, int ix_meta, int ftable, enum_identity *eid)
|
2012-03-25 05:20:58 -06:00
|
|
|
{
|
|
|
|
const char *const *keys = eid->getKeys();
|
|
|
|
|
2012-03-25 09:12:59 -06:00
|
|
|
// Create a new table attached to ftable as __index
|
|
|
|
lua_newtable(state);
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
lua_newtable(state);
|
|
|
|
|
2018-04-09 09:04:37 -06:00
|
|
|
auto *complex = eid->getComplex();
|
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
// For enums, set mapping between keys and values
|
2018-04-09 09:04:37 -06:00
|
|
|
if (complex)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < complex->size(); i++)
|
|
|
|
{
|
|
|
|
if (keys[i])
|
|
|
|
AssociateId(state, base+1, complex->index_value_map[i], keys[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2012-03-25 05:20:58 -06:00
|
|
|
{
|
2018-04-09 09:04:37 -06:00
|
|
|
for (int64_t i = eid->getFirstItem(), j = 0; i <= eid->getLastItem(); i++, j++)
|
|
|
|
{
|
|
|
|
if (keys[j])
|
|
|
|
AssociateId(state, base+1, i, keys[j]);
|
|
|
|
}
|
2012-03-25 05:20:58 -06:00
|
|
|
}
|
|
|
|
|
2018-04-09 09:04:37 -06:00
|
|
|
if (complex)
|
2012-03-25 05:20:58 -06:00
|
|
|
{
|
2018-04-09 09:04:37 -06:00
|
|
|
lua_pushvalue(state, base + 1);
|
|
|
|
lua_pushlightuserdata(state, (void*)complex);
|
|
|
|
lua_pushcclosure(state, complex_enum_ipairs, 2);
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_setfield(state, ix_meta, "__ipairs");
|
2018-04-09 14:11:47 -06:00
|
|
|
|
2018-04-09 17:46:12 -06:00
|
|
|
lua_pushinteger(state, 0); // unused; to align ComplexData
|
|
|
|
lua_pushlightuserdata(state, (void*)complex);
|
|
|
|
lua_pushcclosure(state, complex_enum_next_item, 2);
|
|
|
|
lua_setfield(state, ftable, "next_item");
|
|
|
|
|
2018-04-09 14:11:47 -06:00
|
|
|
lua_pushinteger(state, eid->getFirstItem());
|
|
|
|
lua_setfield(state, ftable, "_first_item");
|
|
|
|
|
|
|
|
lua_pushinteger(state, eid->getLastItem());
|
|
|
|
lua_setfield(state, ftable, "_last_item");
|
2018-04-09 17:46:12 -06:00
|
|
|
|
|
|
|
lua_pushboolean(state, true);
|
|
|
|
lua_setfield(state, ftable, "_complex");
|
2018-04-09 09:04:37 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (eid->getFirstItem() <= eid->getLastItem())
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, base + 1);
|
|
|
|
lua_pushinteger(state, eid->getFirstItem() - 1);
|
|
|
|
lua_pushinteger(state, eid->getLastItem());
|
|
|
|
lua_pushcclosure(state, wtype_ipairs, 3);
|
|
|
|
lua_setfield(state, ix_meta, "__ipairs");
|
|
|
|
|
|
|
|
lua_pushinteger(state, eid->getFirstItem());
|
|
|
|
lua_pushinteger(state, eid->getLastItem());
|
|
|
|
lua_pushcclosure(state, wtype_next_item, 2);
|
|
|
|
lua_setfield(state, ftable, "next_item");
|
|
|
|
|
|
|
|
lua_pushinteger(state, eid->getFirstItem());
|
|
|
|
lua_setfield(state, ftable, "_first_item");
|
|
|
|
|
|
|
|
lua_pushinteger(state, eid->getLastItem());
|
|
|
|
lua_setfield(state, ftable, "_last_item");
|
2018-04-09 17:46:12 -06:00
|
|
|
|
|
|
|
lua_pushboolean(state, false);
|
|
|
|
lua_setfield(state, ftable, "_complex");
|
2018-04-09 09:04:37 -06:00
|
|
|
}
|
2012-03-25 05:20:58 -06:00
|
|
|
}
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN);
|
2012-03-25 09:12:59 -06:00
|
|
|
|
|
|
|
// Add an attribute table if any
|
|
|
|
if (eid->getAttrs())
|
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-25 09:12:59 -06:00
|
|
|
lua_pushlightuserdata(state, eid);
|
|
|
|
lua_pushvalue(state, base+1);
|
|
|
|
lua_pushcclosure(state, meta_enum_attr_index, 3);
|
|
|
|
|
|
|
|
freeze_table(state, false, (eid->getFullName()+".attrs").c_str());
|
|
|
|
lua_setfield(state, ftable, "attrs");
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_setfield(state, base, "__index");
|
2012-04-01 07:32:57 -06:00
|
|
|
lua_setmetatable(state, ftable);
|
2012-03-25 05:20:58 -06:00
|
|
|
}
|
|
|
|
|
2012-10-17 10:58:37 -06:00
|
|
|
static void FillBitfieldKeys(lua_State *state, int ix_meta, int ftable, bitfield_identity *eid)
|
2012-03-25 05:20:58 -06:00
|
|
|
{
|
2012-04-01 07:32:57 -06:00
|
|
|
// Create a new table attached to ftable as __index
|
|
|
|
lua_newtable(state);
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
lua_newtable(state);
|
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
auto bits = eid->getBits();
|
|
|
|
|
|
|
|
for (int i = 0; i < eid->getNumBits(); i++)
|
|
|
|
{
|
|
|
|
if (bits[i].name)
|
2012-04-01 07:32:57 -06:00
|
|
|
AssociateId(state, base+1, i, bits[i].name);
|
2012-03-25 05:20:58 -06:00
|
|
|
if (bits[i].size > 1)
|
|
|
|
i += bits[i].size-1;
|
|
|
|
}
|
|
|
|
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_pushvalue(state, base+1);
|
|
|
|
lua_pushinteger(state, -1);
|
|
|
|
lua_pushinteger(state, eid->getNumBits()-1);
|
|
|
|
lua_pushcclosure(state, wtype_ipairs, 3);
|
|
|
|
lua_setfield(state, ix_meta, "__ipairs");
|
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
lua_pushinteger(state, 0);
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_setfield(state, ftable, "_first_item");
|
2012-03-25 05:20:58 -06:00
|
|
|
|
|
|
|
lua_pushinteger(state, eid->getNumBits()-1);
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_setfield(state, ftable, "_last_item");
|
2012-03-25 05:20:58 -06:00
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN);
|
2012-04-01 07:32:57 -06:00
|
|
|
|
|
|
|
lua_setfield(state, base, "__index");
|
|
|
|
lua_setmetatable(state, ftable);
|
2012-03-25 05:20:58 -06:00
|
|
|
}
|
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
static void RenderType(lua_State *state, compound_identity *node)
|
|
|
|
{
|
|
|
|
assert(node->getName());
|
2012-03-21 03:26:53 -06:00
|
|
|
std::string name = node->getFullName();
|
2012-03-19 10:12:27 -06:00
|
|
|
|
2012-10-17 10:58:37 -06:00
|
|
|
// Frame:
|
|
|
|
// base+1 - outer table
|
|
|
|
// base+2 - metatable of outer table
|
|
|
|
// base+3 - inner table
|
|
|
|
// base+4 - pairs table
|
|
|
|
Lua::StackUnwinder base(state);
|
2012-03-25 05:20:58 -06:00
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
lua_newtable(state);
|
|
|
|
if (!lua_checkstack(state, 20))
|
|
|
|
return;
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
SaveInTable(state, node, &DFHACK_TYPEID_TABLE_TOKEN);
|
2012-03-25 05:20:58 -06:00
|
|
|
|
|
|
|
// metatable
|
|
|
|
lua_newtable(state);
|
2012-10-17 10:58:37 -06:00
|
|
|
int ix_meta = base+2;
|
2012-03-25 05:20:58 -06:00
|
|
|
|
|
|
|
lua_dup(state);
|
|
|
|
lua_setmetatable(state, base+1);
|
|
|
|
|
|
|
|
lua_pushstring(state, name.c_str());
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_setfield(state, ix_meta, "__metatable");
|
2012-03-25 05:20:58 -06:00
|
|
|
|
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME);
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_setfield(state, ix_meta, "__tostring");
|
2012-03-25 05:20:58 -06:00
|
|
|
|
|
|
|
lua_pushlightuserdata(state, node);
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_rawsetp(state, ix_meta, &DFHACK_IDENTITY_FIELD_TOKEN);
|
2012-03-25 05:20:58 -06:00
|
|
|
|
|
|
|
// inner table
|
|
|
|
lua_newtable(state);
|
2012-10-17 10:58:37 -06:00
|
|
|
int ftable = base+3;
|
2012-03-25 05:20:58 -06:00
|
|
|
|
|
|
|
lua_dup(state);
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_setfield(state, ix_meta, "__index");
|
2012-03-25 05:20:58 -06:00
|
|
|
|
2020-04-06 17:12:53 -06:00
|
|
|
// pairs table - reuse index table
|
|
|
|
lua_dup(state);
|
2012-10-17 10:58:37 -06:00
|
|
|
int ptable = base+4;
|
|
|
|
|
|
|
|
lua_pushvalue(state, ptable);
|
|
|
|
lua_pushcclosure(state, wtype_pairs, 1);
|
|
|
|
lua_setfield(state, ix_meta, "__pairs");
|
2012-03-19 10:12:27 -06:00
|
|
|
|
|
|
|
switch (node->type())
|
|
|
|
{
|
2012-03-21 10:04:37 -06:00
|
|
|
case IDTYPE_STRUCT:
|
2020-02-29 12:11:23 -07:00
|
|
|
case IDTYPE_UNION: // TODO: change this to union-type? what relies on this?
|
2012-03-21 10:04:37 -06:00
|
|
|
lua_pushstring(state, "struct-type");
|
2012-03-25 05:20:58 -06:00
|
|
|
lua_setfield(state, ftable, "_kind");
|
2012-10-17 10:58:37 -06:00
|
|
|
IndexStatics(state, ix_meta, ftable, (struct_identity*)node);
|
2012-03-21 10:04:37 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IDTYPE_CLASS:
|
|
|
|
lua_pushstring(state, "class-type");
|
2012-03-25 05:20:58 -06:00
|
|
|
lua_setfield(state, ftable, "_kind");
|
2012-10-17 10:58:37 -06:00
|
|
|
IndexStatics(state, ix_meta, ftable, (struct_identity*)node);
|
2012-03-21 10:04:37 -06:00
|
|
|
break;
|
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
case IDTYPE_ENUM:
|
2012-03-25 05:20:58 -06:00
|
|
|
lua_pushstring(state, "enum-type");
|
|
|
|
lua_setfield(state, ftable, "_kind");
|
2012-10-17 10:58:37 -06:00
|
|
|
FillEnumKeys(state, ix_meta, ftable, (enum_identity*)node);
|
2012-03-19 10:12:27 -06:00
|
|
|
break;
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
case IDTYPE_BITFIELD:
|
2012-03-25 05:20:58 -06:00
|
|
|
lua_pushstring(state, "bitfield-type");
|
|
|
|
lua_setfield(state, ftable, "_kind");
|
2012-10-17 10:58:37 -06:00
|
|
|
FillBitfieldKeys(state, ix_meta, ftable, (bitfield_identity*)node);
|
2012-03-25 05:20:58 -06:00
|
|
|
break;
|
2012-03-21 10:04:37 -06:00
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
case IDTYPE_GLOBAL:
|
2012-04-02 09:10:57 -06:00
|
|
|
lua_pushstring(state, "global");
|
|
|
|
lua_setfield(state, ftable, "_kind");
|
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
{
|
|
|
|
RenderTypeChildren(state, node->getScopeChildren());
|
2012-03-21 10:04:37 -06:00
|
|
|
|
2012-04-02 09:10:57 -06:00
|
|
|
lua_pushlightuserdata(state, node);
|
|
|
|
lua_setfield(state, ftable, "_identity");
|
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
BuildTypeMetatable(state, node);
|
2012-03-21 10:04:37 -06:00
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
lua_dup(state);
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_setmetatable(state, ftable);
|
2012-03-21 10:04:37 -06:00
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
lua_getfield(state, -1, "__newindex");
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_setfield(state, ix_meta, "__newindex");
|
2012-03-29 04:39:13 -06:00
|
|
|
lua_getfield(state, -1, "__pairs");
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_setfield(state, ix_meta, "__pairs");
|
2012-03-21 10:04:37 -06:00
|
|
|
|
2012-10-17 10:58:37 -06:00
|
|
|
base += 1;
|
2012-03-25 05:20:58 -06:00
|
|
|
return;
|
2012-03-21 10:04:37 -06:00
|
|
|
}
|
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
RenderTypeChildren(state, node->getScopeChildren());
|
|
|
|
|
2012-04-02 09:10:57 -06:00
|
|
|
lua_pushlightuserdata(state, node);
|
|
|
|
lua_setfield(state, ftable, "_identity");
|
|
|
|
|
2012-03-23 01:30:54 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
|
2012-03-25 05:20:58 -06:00
|
|
|
lua_setfield(state, ftable, "sizeof");
|
2012-03-23 01:30:54 -06:00
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
|
|
|
|
lua_setfield(state, ftable, "new");
|
2012-03-20 11:34:27 -06:00
|
|
|
|
2012-04-01 07:32:57 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME);
|
|
|
|
lua_setfield(state, ftable, "is_instance");
|
|
|
|
|
2012-10-17 10:58:37 -06:00
|
|
|
base += 1;
|
2012-03-19 10:12:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children)
|
|
|
|
{
|
2012-10-17 10:58:37 -06:00
|
|
|
// fieldtable pairstable |
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
for (size_t i = 0; i < children.size(); i++)
|
|
|
|
{
|
|
|
|
RenderType(state, children[i]);
|
|
|
|
lua_pushstring(state, children[i]->getName());
|
|
|
|
lua_swap(state);
|
2012-10-17 10:58:37 -06:00
|
|
|
|
|
|
|
// save in both tables
|
|
|
|
lua_pushvalue(state, -2);
|
|
|
|
lua_pushvalue(state, -2);
|
|
|
|
lua_rawset(state, base);
|
|
|
|
lua_rawset(state, base-1);
|
2012-03-19 10:12:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-31 02:11:43 -06:00
|
|
|
static int DoAttach(lua_State *state)
|
2012-03-19 10:12:27 -06:00
|
|
|
{
|
2012-03-23 02:55:29 -06:00
|
|
|
lua_newtable(state);
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_PTR_IDTABLE_TOKEN);
|
2012-03-23 02:55:29 -06:00
|
|
|
|
2012-03-20 11:34:27 -06:00
|
|
|
lua_newtable(state);
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPEID_TABLE_TOKEN);
|
2012-03-20 11:34:27 -06:00
|
|
|
|
2012-03-21 03:26:53 -06:00
|
|
|
lua_newtable(state);
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_ENUM_TABLE_TOKEN);
|
2012-03-21 03:26:53 -06:00
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
lua_newtable(state);
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EMPTY_TABLE_TOKEN);
|
2012-03-29 04:39:13 -06:00
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
lua_pushcfunction(state, change_error);
|
2012-03-20 11:34:27 -06:00
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME);
|
|
|
|
|
|
|
|
lua_pushcfunction(state, meta_ptr_compare);
|
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME);
|
|
|
|
|
|
|
|
lua_pushcfunction(state, meta_type_tostring);
|
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME);
|
2012-03-19 10:12:27 -06:00
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_pushcclosure(state, meta_sizeof, 1);
|
2012-03-23 01:30:54 -06:00
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_pushcclosure(state, meta_displace, 1);
|
2012-03-23 01:54:59 -06:00
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME);
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_pushcclosure(state, meta_new, 1);
|
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
|
|
|
|
|
2012-06-13 12:26:54 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
|
|
|
lua_pushcclosure(state, meta_reinterpret_cast, 1);
|
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CAST_NAME);
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_pushcclosure(state, meta_assign, 1);
|
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-04-01 07:32:57 -06:00
|
|
|
lua_pushcclosure(state, meta_is_instance, 1);
|
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME);
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-03-25 05:48:18 -06:00
|
|
|
lua_pushcclosure(state, meta_delete, 1);
|
|
|
|
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_DELETE_NAME);
|
|
|
|
|
2012-03-19 10:12:27 -06:00
|
|
|
{
|
|
|
|
// Assign df a metatable with read-only contents
|
|
|
|
lua_newtable(state);
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_newtable(state);
|
2012-03-19 10:12:27 -06:00
|
|
|
|
|
|
|
// Render the type structure
|
|
|
|
RenderTypeChildren(state, compound_identity::getTopScope());
|
|
|
|
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_swap(state); // -> pairstable fieldtable
|
|
|
|
|
2012-03-23 01:30:54 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
|
|
|
|
lua_setfield(state, -2, "sizeof");
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
|
|
|
|
lua_setfield(state, -2, "new");
|
2012-03-25 05:48:18 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DELETE_NAME);
|
|
|
|
lua_setfield(state, -2, "delete");
|
2012-03-23 01:54:59 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME);
|
|
|
|
lua_setfield(state, -2, "_displace");
|
2012-03-24 02:43:53 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
|
|
|
|
lua_setfield(state, -2, "assign");
|
2012-04-01 07:32:57 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME);
|
|
|
|
lua_setfield(state, -2, "is_instance");
|
2012-06-13 12:26:54 -06:00
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CAST_NAME);
|
|
|
|
lua_setfield(state, -2, "reinterpret_cast");
|
2012-03-23 01:30:54 -06:00
|
|
|
|
2012-03-29 04:47:33 -06:00
|
|
|
lua_pushlightuserdata(state, NULL);
|
|
|
|
lua_setfield(state, -2, "NULL");
|
|
|
|
lua_pushlightuserdata(state, NULL);
|
|
|
|
lua_setglobal(state, "NULL");
|
|
|
|
|
2012-04-02 09:10:57 -06:00
|
|
|
lua_pushstring(state, "null");
|
|
|
|
lua_pushstring(state, "type");
|
|
|
|
lua_pushstring(state, "voidptr");
|
|
|
|
lua_pushstring(state, "ref");
|
|
|
|
lua_pushcclosure(state, meta_isvalid, 4);
|
|
|
|
lua_setfield(state, -2, "isvalid");
|
|
|
|
|
|
|
|
lua_pushcfunction(state, meta_isnull);
|
|
|
|
lua_setfield(state, -2, "isnull");
|
|
|
|
|
2012-10-17 10:58:37 -06:00
|
|
|
freeze_table(state, true, "df");
|
|
|
|
|
2012-10-20 10:14:50 -06:00
|
|
|
// pairstable dftable dfmeta
|
|
|
|
|
|
|
|
lua_pushvalue(state, -3);
|
2012-10-17 10:58:37 -06:00
|
|
|
lua_pushcclosure(state, wtype_pairs, 1);
|
|
|
|
lua_setfield(state, -2, "__pairs");
|
|
|
|
lua_pop(state, 1);
|
2012-10-20 10:14:50 -06:00
|
|
|
lua_remove(state, -2);
|
2012-03-19 10:12:27 -06:00
|
|
|
}
|
|
|
|
|
2012-03-31 02:11:43 -06:00
|
|
|
return 1;
|
2012-03-19 10:12:27 -06:00
|
|
|
}
|
|
|
|
|
2012-03-21 10:04:37 -06:00
|
|
|
/**
|
|
|
|
* Initialize access to DF objects from the interpreter
|
|
|
|
* context, unless it has already been done.
|
|
|
|
*/
|
2012-03-31 05:40:54 -06:00
|
|
|
void LuaWrapper::AttachDFGlobals(lua_State *state)
|
2012-03-19 10:12:27 -06:00
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
|
|
|
|
|
|
|
if (lua_isnil(state, -1))
|
2012-03-31 02:11:43 -06:00
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_pop(state, 1);
|
|
|
|
lua_newtable(state);
|
|
|
|
lua_dup(state);
|
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
|
|
|
|
2012-03-31 02:11:43 -06:00
|
|
|
luaL_requiref(state, "df", DoAttach, 1);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
}
|
2012-03-19 10:12:27 -06:00
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
}
|
2012-04-01 08:34:04 -06:00
|
|
|
|
|
|
|
namespace DFHack { namespace LuaWrapper {
|
|
|
|
struct LuaToken { int reserved; };
|
|
|
|
|
|
|
|
LuaToken DFHACK_IDENTITY_FIELD_TOKEN;
|
|
|
|
LuaToken DFHACK_TYPETABLE_TOKEN;
|
|
|
|
LuaToken DFHACK_TYPEID_TABLE_TOKEN;
|
|
|
|
LuaToken DFHACK_ENUM_TABLE_TOKEN;
|
|
|
|
LuaToken DFHACK_PTR_IDTABLE_TOKEN;
|
|
|
|
LuaToken DFHACK_EMPTY_TABLE_TOKEN;
|
|
|
|
}}
|