2012-09-29 20:03:37 -06:00
|
|
|
/*
|
2012-03-24 03:25:10 -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-24 03:25:10 -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>
|
|
|
|
|
|
|
|
#include "MemAccess.h"
|
|
|
|
#include "Core.h"
|
2012-04-05 09:55:59 -06:00
|
|
|
#include "Error.h"
|
2012-03-24 03:25:10 -06:00
|
|
|
#include "VersionInfo.h"
|
|
|
|
#include "tinythread.h"
|
|
|
|
// must be last due to MS stupidity
|
|
|
|
#include "DataDefs.h"
|
|
|
|
#include "DataIdentity.h"
|
|
|
|
#include "LuaWrapper.h"
|
2012-08-19 04:27:44 -06:00
|
|
|
#include "LuaTools.h"
|
2012-03-25 04:06:05 -06:00
|
|
|
#include "DataFuncs.h"
|
2012-03-24 03:25:10 -06:00
|
|
|
|
2015-08-11 20:59:19 -06:00
|
|
|
#include "PluginManager.h"
|
2012-03-24 03:25:10 -06:00
|
|
|
#include "MiscUtils.h"
|
|
|
|
|
|
|
|
#include <lua.h>
|
|
|
|
#include <lauxlib.h>
|
|
|
|
|
|
|
|
using namespace DFHack;
|
|
|
|
using namespace DFHack::LuaWrapper;
|
|
|
|
|
2015-08-24 15:51:39 -06:00
|
|
|
#ifdef _DARWIN
|
|
|
|
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_6
|
|
|
|
size_t strnlen (const char *str, size_t max)
|
|
|
|
{
|
|
|
|
const char *end = (const char*)memchr(str, 0, max);
|
|
|
|
return end ? (size_t)(end - str) : max;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
/**************************************
|
|
|
|
* Identity object read/write methods *
|
|
|
|
**************************************/
|
|
|
|
|
2012-03-25 04:06:05 -06:00
|
|
|
void function_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr)
|
|
|
|
{
|
|
|
|
field_error(state, fname_idx, "executable code", "read");
|
|
|
|
}
|
|
|
|
|
|
|
|
void function_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
|
|
|
|
{
|
|
|
|
field_error(state, fname_idx, "executable code", "write");
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
|
|
|
|
{
|
|
|
|
push_object_internal(state, this, ptr);
|
|
|
|
}
|
|
|
|
|
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_assign(lua_State *state, type_identity *id, void *ptr, int val_index)
|
|
|
|
{
|
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
|
|
|
|
push_object_internal(state, id, ptr);
|
|
|
|
lua_pushvalue(state, val_index);
|
|
|
|
lua_call(state, 2, 0);
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
|
|
|
|
{
|
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, val_index))
|
|
|
|
{
|
|
|
|
invoke_assign(state, this, ptr, val_index);
|
|
|
|
}
|
2012-04-11 06:20:16 -06:00
|
|
|
// Allow by-value assignment for wrapped function parameters
|
|
|
|
else if (fname_idx == UPVAL_METHOD_NAME && lua_isuserdata(state, val_index))
|
|
|
|
{
|
|
|
|
void *nval = get_object_internal(state, this, val_index, false);
|
|
|
|
if (!nval)
|
|
|
|
field_error(state, fname_idx, "incompatible type in complex assignment", "write");
|
|
|
|
if (!copy(ptr, nval))
|
|
|
|
field_error(state, fname_idx, "no copy support", "write");
|
|
|
|
}
|
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
|
|
|
|
field_error(state, fname_idx, "complex object", "write");
|
2012-03-24 03:25:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
|
|
|
|
{
|
|
|
|
base_type->lua_read(state, fname_idx, ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void enum_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
|
|
|
|
{
|
|
|
|
base_type->lua_write(state, fname_idx, ptr, val_index);
|
|
|
|
}
|
|
|
|
|
2016-07-28 14:36:02 -06:00
|
|
|
void df::integer_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr)
|
|
|
|
{
|
|
|
|
lua_pushinteger(state, read(ptr));
|
|
|
|
}
|
|
|
|
|
|
|
|
void df::integer_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
|
|
|
|
{
|
2017-03-18 15:55:39 -06:00
|
|
|
int is_num = 0;
|
|
|
|
auto value = lua_tointegerx(state, val_index, &is_num);
|
|
|
|
if (!is_num)
|
2016-07-28 14:36:02 -06:00
|
|
|
field_error(state, fname_idx, "integer expected", "write");
|
2017-03-18 15:55:39 -06:00
|
|
|
write(ptr, value);
|
2016-07-28 14:36:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void df::float_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr)
|
2012-03-24 03:25:10 -06:00
|
|
|
{
|
|
|
|
lua_pushnumber(state, read(ptr));
|
|
|
|
}
|
|
|
|
|
2016-07-28 14:36:02 -06:00
|
|
|
void df::float_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
|
2012-03-24 03:25:10 -06:00
|
|
|
{
|
|
|
|
if (!lua_isnumber(state, val_index))
|
|
|
|
field_error(state, fname_idx, "number expected", "write");
|
|
|
|
|
|
|
|
write(ptr, lua_tonumber(state, val_index));
|
|
|
|
}
|
|
|
|
|
|
|
|
void df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
|
|
|
|
{
|
|
|
|
lua_pushboolean(state, *(bool*)ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
|
|
|
|
{
|
|
|
|
char *pb = (char*)ptr;
|
|
|
|
|
|
|
|
if (lua_isboolean(state, val_index) || lua_isnil(state, val_index))
|
|
|
|
*pb = lua_toboolean(state, val_index);
|
|
|
|
else if (lua_isnumber(state, val_index))
|
|
|
|
*pb = lua_tointeger(state, val_index);
|
|
|
|
else
|
|
|
|
field_error(state, fname_idx, "boolean or number expected", "write");
|
|
|
|
}
|
|
|
|
|
2012-03-25 09:12:59 -06:00
|
|
|
void df::ptr_string_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
|
|
|
|
{
|
|
|
|
auto pstr = (char**)ptr;
|
|
|
|
if (*pstr)
|
|
|
|
lua_pushstring(state, *pstr);
|
|
|
|
else
|
|
|
|
lua_pushnil(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
void df::ptr_string_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
|
|
|
|
{
|
|
|
|
field_error(state, fname_idx, "raw pointer string", "write");
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
void df::stl_string_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
|
|
|
|
{
|
|
|
|
auto pstr = (std::string*)ptr;
|
|
|
|
lua_pushlstring(state, pstr->data(), pstr->size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
|
|
|
|
{
|
|
|
|
size_t size;
|
|
|
|
const char *bytes = lua_tolstring(state, val_index, &size);
|
|
|
|
if (!bytes)
|
|
|
|
field_error(state, fname_idx, "string expected", "write");
|
|
|
|
|
|
|
|
*(std::string*)ptr = std::string(bytes, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target)
|
|
|
|
{
|
|
|
|
push_object_internal(state, target, *(void**)ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
|
|
|
|
{
|
|
|
|
lua_read(state, fname_idx, ptr, target);
|
|
|
|
}
|
|
|
|
|
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 autovivify_ptr(lua_State *state, int fname_idx, void **pptr,
|
|
|
|
type_identity *target, int val_index)
|
|
|
|
{
|
|
|
|
lua_getfield(state, val_index, "new");
|
|
|
|
|
2012-03-28 01:28:42 -06:00
|
|
|
// false or nil => bail out
|
|
|
|
if (!lua_toboolean(state, -1))
|
|
|
|
field_error(state, fname_idx, "null and autovivify not requested", "write");
|
|
|
|
|
|
|
|
// not 'true' => call df.new()
|
|
|
|
if (!lua_isboolean(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
|
|
|
{
|
|
|
|
int top = lua_gettop(state);
|
|
|
|
|
2012-03-28 01:28:42 -06:00
|
|
|
// Verify new points to a reasonable type of object
|
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 *suggested = get_object_identity(state, top, "autovivify", true, true);
|
|
|
|
|
|
|
|
if (!is_type_compatible(state, target, 0, suggested, top+1, false))
|
|
|
|
field_error(state, fname_idx, "incompatible suggested autovivify type", "write");
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
2012-03-28 01:28:42 -06:00
|
|
|
// Invoke df.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
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
|
|
|
|
lua_swap(state);
|
|
|
|
lua_call(state, 1, 1);
|
|
|
|
|
2012-03-28 01:28:42 -06:00
|
|
|
// Retrieve the 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
|
|
|
void *nval = get_object_internal(state, target, top, false);
|
|
|
|
|
2012-03-28 01:28:42 -06:00
|
|
|
// shouldn't happen: this means suggested type is compatible,
|
|
|
|
// but its new() result isn't for some reason.
|
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 (!nval)
|
|
|
|
field_error(state, fname_idx, "inconsistent autovivify type", "write");
|
|
|
|
|
|
|
|
*pptr = nval;
|
|
|
|
}
|
2012-03-28 01:28:42 -06:00
|
|
|
// otherwise use the target type
|
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
|
|
|
|
{
|
|
|
|
if (!target)
|
|
|
|
field_error(state, fname_idx, "trying to autovivify void*", "write");
|
|
|
|
|
|
|
|
*pptr = target->allocate();
|
|
|
|
|
|
|
|
if (!*pptr)
|
|
|
|
field_error(state, fname_idx, "could not allocate in autovivify", "write");
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
}
|
|
|
|
|
2012-03-29 04:47:33 -06:00
|
|
|
static bool is_null(lua_State *state, int val_index)
|
|
|
|
{
|
|
|
|
return lua_isnil(state, val_index) ||
|
|
|
|
(lua_islightuserdata(state, val_index) &&
|
|
|
|
!lua_touserdata(state, val_index));
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr,
|
|
|
|
type_identity *target, int val_index)
|
|
|
|
{
|
|
|
|
auto pptr = (void**)ptr;
|
|
|
|
|
2012-03-29 04:47:33 -06:00
|
|
|
if (is_null(state, val_index))
|
2012-03-24 03:25:10 -06:00
|
|
|
*pptr = NULL;
|
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 if (lua_istable(state, val_index))
|
|
|
|
{
|
|
|
|
if (!*pptr)
|
|
|
|
autovivify_ptr(state, fname_idx, pptr, target, val_index);
|
|
|
|
|
|
|
|
invoke_assign(state, target, *pptr, val_index);
|
|
|
|
}
|
2012-03-24 03:25:10 -06:00
|
|
|
else
|
|
|
|
{
|
|
|
|
void *nval = get_object_internal(state, target, val_index, false);
|
|
|
|
if (nval)
|
|
|
|
*pptr = nval;
|
|
|
|
else
|
|
|
|
field_error(state, fname_idx, "incompatible pointer type", "write");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
|
|
|
|
{
|
|
|
|
lua_write(state, fname_idx, ptr, target, val_index);
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:28:53 -06:00
|
|
|
int container_identity::lua_item_count(lua_State *state, void *ptr, CountMode mode)
|
2012-03-24 03:25:10 -06:00
|
|
|
{
|
|
|
|
if (lua_isnumber(state, UPVAL_ITEM_COUNT))
|
|
|
|
return lua_tointeger(state, UPVAL_ITEM_COUNT);
|
|
|
|
else
|
2012-03-24 06:28:53 -06:00
|
|
|
return item_count(ptr, mode);
|
2012-03-24 03:25:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void container_identity::lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx)
|
|
|
|
{
|
|
|
|
auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
|
|
|
|
void *pitem = item_pointer(id, ptr, idx);
|
|
|
|
push_object_internal(state, id, pitem);
|
|
|
|
}
|
|
|
|
|
|
|
|
void container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx)
|
|
|
|
{
|
|
|
|
auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
|
|
|
|
void *pitem = item_pointer(id, ptr, idx);
|
|
|
|
id->lua_read(state, fname_idx, pitem);
|
|
|
|
}
|
|
|
|
|
|
|
|
void container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index)
|
|
|
|
{
|
2012-08-18 04:34:20 -06:00
|
|
|
if (is_readonly())
|
|
|
|
field_error(state, fname_idx, "container is read-only", "write");
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
|
|
|
|
void *pitem = item_pointer(id, ptr, idx);
|
|
|
|
id->lua_write(state, fname_idx, pitem, val_index);
|
|
|
|
}
|
|
|
|
|
2016-07-26 21:47:53 -06:00
|
|
|
bool container_identity::lua_insert2(lua_State *state, int fname_idx, void *ptr, int idx, int val_index)
|
2012-03-24 06:28:53 -06:00
|
|
|
{
|
|
|
|
auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
|
|
|
|
|
|
|
|
char tmp[32];
|
|
|
|
void *pitem = &tmp;
|
|
|
|
|
|
|
|
if (id->isPrimitive())
|
|
|
|
{
|
|
|
|
if (id->isConstructed())
|
|
|
|
luaL_error(state, "Temporaries of type %s not supported", id->getFullName().c_str());
|
|
|
|
|
|
|
|
assert(id->byte_size() <= sizeof(tmp));
|
|
|
|
id->lua_write(state, fname_idx, pitem, val_index);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pitem = get_object_internal(state, id, val_index, false);
|
|
|
|
if (!pitem)
|
|
|
|
field_error(state, fname_idx, "incompatible object type", "insert");
|
|
|
|
}
|
|
|
|
|
|
|
|
return insert(ptr, idx, pitem);
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
void ptr_container_identity::lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx)
|
|
|
|
{
|
|
|
|
auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
|
|
|
|
void *pitem = item_pointer(id, ptr, idx);
|
|
|
|
push_adhoc_pointer(state, pitem, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ptr_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx)
|
|
|
|
{
|
|
|
|
auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
|
|
|
|
void *pitem = item_pointer(&df::identity_traits<void*>::identity, ptr, idx);
|
|
|
|
df::pointer_identity::lua_read(state, fname_idx, pitem, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index)
|
|
|
|
{
|
|
|
|
auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
|
|
|
|
void *pitem = item_pointer(&df::identity_traits<void*>::identity, ptr, idx);
|
|
|
|
df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index);
|
|
|
|
}
|
|
|
|
|
2016-07-26 21:47:53 -06:00
|
|
|
bool ptr_container_identity::lua_insert2(lua_State *state, int fname_idx, void *ptr, int idx, int val_index)
|
2012-03-24 06:28:53 -06:00
|
|
|
{
|
|
|
|
auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
|
|
|
|
|
|
|
|
void *pitem = NULL;
|
|
|
|
df::pointer_identity::lua_write(state, fname_idx, &pitem, id, val_index);
|
|
|
|
|
|
|
|
return insert(ptr, idx, pitem);
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
void bit_container_identity::lua_item_reference(lua_State *state, int, void *, int)
|
|
|
|
{
|
|
|
|
lua_pushnil(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bit_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx)
|
|
|
|
{
|
|
|
|
lua_pushboolean(state, get_item(ptr, idx));
|
|
|
|
}
|
|
|
|
|
|
|
|
void bit_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index)
|
|
|
|
{
|
|
|
|
if (lua_isboolean(state, val_index) || lua_isnil(state, val_index))
|
|
|
|
set_item(ptr, idx, lua_toboolean(state, val_index));
|
|
|
|
else if (lua_isnumber(state, val_index))
|
|
|
|
set_item(ptr, idx, lua_tointeger(state, val_index) != 0);
|
|
|
|
else
|
|
|
|
field_error(state, fname_idx, "boolean or number expected", "write");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolve the field name in UPVAL_FIELDTABLE, die if not found.
|
|
|
|
*/
|
|
|
|
static void lookup_field(lua_State *state, int index, const char *mode)
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, index);
|
|
|
|
lua_gettable(state, UPVAL_FIELDTABLE); // uses metatable with enum keys
|
|
|
|
|
|
|
|
if (lua_isnil(state, -1))
|
|
|
|
field_error(state, index, "not found", mode);
|
|
|
|
}
|
|
|
|
|
2012-03-25 04:06:05 -06:00
|
|
|
// Resolve the field in the metatable and return
|
|
|
|
static int get_metafield(lua_State *state)
|
|
|
|
{
|
|
|
|
lua_rawget(state, UPVAL_METATABLE);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
static void *find_field(lua_State *state, int index, const char *mode)
|
|
|
|
{
|
|
|
|
lookup_field(state, index, mode);
|
|
|
|
|
2012-03-25 04:06:05 -06:00
|
|
|
// Methods
|
|
|
|
if (lua_isfunction(state, -1))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Otherwise must be a pointer
|
|
|
|
if (!lua_isuserdata(state, -1))
|
|
|
|
field_error(state, index, "corrupted field table", mode);
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
void *p = lua_touserdata(state, -1);
|
|
|
|
lua_pop(state, 1);
|
2012-03-25 04:06:05 -06:00
|
|
|
|
|
|
|
// NULL => metafield
|
|
|
|
if (!p)
|
|
|
|
get_metafield(state);
|
2012-03-24 03:25:10 -06:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
static int cur_iter_index(lua_State *state, int len, int fidx, int first_idx = -1)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if (lua_isnil(state, fidx))
|
|
|
|
rv = first_idx;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (lua_isnumber(state, fidx))
|
|
|
|
rv = lua_tointeger(state, fidx);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, fidx);
|
|
|
|
lua_rawget(state, UPVAL_FIELDTABLE);
|
|
|
|
if (!lua_isnumber(state, -1))
|
|
|
|
field_error(state, fidx, "index not found", "iterate");
|
|
|
|
rv = lua_tointeger(state, -1);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rv < 0 || rv >= len)
|
|
|
|
field_error(state, fidx, "index out of bounds", "iterate");
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iter_idx_to_name(lua_State *state, int idx)
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, idx);
|
|
|
|
lua_rawget(state, UPVAL_FIELDTABLE);
|
|
|
|
if (lua_isnil(state, -1))
|
|
|
|
lua_pop(state, 1);
|
|
|
|
else
|
|
|
|
lua_replace(state, idx);
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:28:53 -06:00
|
|
|
static uint8_t *check_method_call(lua_State *state, int min_args, int max_args)
|
|
|
|
{
|
|
|
|
int argc = lua_gettop(state)-1;
|
|
|
|
if (argc < min_args || argc > max_args)
|
|
|
|
field_error(state, UPVAL_METHOD_NAME, "wrong argument count", "call");
|
|
|
|
|
|
|
|
return get_object_addr(state, 1, UPVAL_METHOD_NAME, "call");
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
static void GetAdHocMetatable(lua_State *state, const struct_field_info *field);
|
|
|
|
|
|
|
|
static void read_field(lua_State *state, const struct_field_info *field, void *ptr)
|
|
|
|
{
|
|
|
|
switch (field->mode)
|
|
|
|
{
|
|
|
|
case struct_field_info::STATIC_STRING:
|
|
|
|
{
|
|
|
|
int len = strnlen((char*)ptr, field->count);
|
|
|
|
lua_pushlstring(state, (char*)ptr, len);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-25 04:06:05 -06:00
|
|
|
case struct_field_info::OBJ_METHOD:
|
|
|
|
case struct_field_info::CLASS_METHOD:
|
|
|
|
// error
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
case struct_field_info::PRIMITIVE:
|
|
|
|
case struct_field_info::SUBSTRUCT:
|
|
|
|
field->type->lua_read(state, 2, ptr);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case struct_field_info::POINTER:
|
|
|
|
df::pointer_identity::lua_read(state, 2, ptr, field->type);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case struct_field_info::CONTAINER:
|
2020-03-21 12:21:35 -06:00
|
|
|
if (!field->extra || !field->extra->index_enum || !field->type->isContainer() ||
|
|
|
|
field->extra->index_enum == ((container_identity*)field->type)->getIndexEnumType())
|
2012-03-24 03:25:10 -06:00
|
|
|
{
|
|
|
|
field->type->lua_read(state, 2, ptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// fallthrough
|
|
|
|
|
|
|
|
case struct_field_info::STATIC_ARRAY:
|
|
|
|
case struct_field_info::STL_VECTOR_PTR:
|
|
|
|
GetAdHocMetatable(state, field);
|
|
|
|
push_object_ref(state, ptr);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case struct_field_info::END:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushnil(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void field_reference(lua_State *state, const struct_field_info *field, void *ptr)
|
|
|
|
{
|
|
|
|
switch (field->mode)
|
|
|
|
{
|
|
|
|
case struct_field_info::PRIMITIVE:
|
|
|
|
case struct_field_info::SUBSTRUCT:
|
|
|
|
push_object_internal(state, field->type, ptr);
|
2020-04-08 22:02:07 -06:00
|
|
|
get_object_ref_header(state, -1)->field_info = field;
|
2012-03-24 03:25:10 -06:00
|
|
|
return;
|
|
|
|
|
|
|
|
case struct_field_info::POINTER:
|
|
|
|
push_adhoc_pointer(state, ptr, field->type);
|
|
|
|
return;
|
|
|
|
|
2012-03-25 04:06:05 -06:00
|
|
|
case struct_field_info::OBJ_METHOD:
|
|
|
|
case struct_field_info::CLASS_METHOD:
|
|
|
|
// error
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
case struct_field_info::CONTAINER:
|
|
|
|
read_field(state, field, ptr);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case struct_field_info::STATIC_STRING:
|
|
|
|
case struct_field_info::STATIC_ARRAY:
|
|
|
|
case struct_field_info::STL_VECTOR_PTR:
|
|
|
|
GetAdHocMetatable(state, field);
|
|
|
|
push_object_ref(state, ptr);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case struct_field_info::END:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushnil(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx)
|
|
|
|
{
|
|
|
|
switch (field->mode)
|
|
|
|
{
|
|
|
|
case struct_field_info::STATIC_STRING:
|
|
|
|
{
|
|
|
|
size_t size;
|
|
|
|
const char *str = lua_tolstring(state, value_idx, &size);
|
|
|
|
if (!str)
|
|
|
|
field_error(state, 2, "string expected", "write");
|
|
|
|
memcpy(ptr, str, std::min(size+1, size_t(field->count)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-25 04:06:05 -06:00
|
|
|
case struct_field_info::OBJ_METHOD:
|
|
|
|
case struct_field_info::CLASS_METHOD:
|
|
|
|
// error
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
case struct_field_info::PRIMITIVE:
|
|
|
|
case struct_field_info::SUBSTRUCT:
|
|
|
|
case struct_field_info::CONTAINER:
|
|
|
|
field->type->lua_write(state, 2, ptr, value_idx);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case struct_field_info::POINTER:
|
|
|
|
df::pointer_identity::lua_write(state, 2, ptr, field->type, value_idx);
|
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
|
|
|
return;
|
2012-03-24 03:25:10 -06:00
|
|
|
|
|
|
|
case struct_field_info::STATIC_ARRAY:
|
|
|
|
case struct_field_info::STL_VECTOR_PTR:
|
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_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
|
|
|
|
read_field(state, field, ptr);
|
|
|
|
lua_pushvalue(state, value_idx);
|
|
|
|
lua_call(state, 2, 0);
|
|
|
|
return;
|
2012-03-24 03:25:10 -06:00
|
|
|
|
|
|
|
case struct_field_info::END:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: __index for structures.
|
|
|
|
*/
|
|
|
|
static int meta_struct_index(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "read");
|
|
|
|
auto field = (struct_field_info*)find_field(state, 2, "read");
|
|
|
|
if (!field)
|
2012-03-25 04:06:05 -06:00
|
|
|
return 1;
|
2012-03-24 03:25:10 -06:00
|
|
|
read_field(state, field, ptr + field->offset);
|
2021-03-30 14:55:06 -06:00
|
|
|
if (field->mode == struct_field_info::SUBSTRUCT || field->mode == struct_field_info::CONTAINER)
|
|
|
|
{
|
|
|
|
auto struct_type = (struct_identity*)get_object_identity(state, 1, "read", false);
|
|
|
|
if (auto tag_field = find_union_tag(struct_type, field))
|
|
|
|
{
|
|
|
|
get_object_ref_header(state, -1)->tag_ptr = ptr + tag_field->offset;
|
|
|
|
get_object_ref_header(state, -1)->tag_identity = tag_field->type;
|
|
|
|
get_object_ref_header(state, -1)->tag_attr = field->extra ? field->extra->union_tag_attr : nullptr;
|
|
|
|
}
|
|
|
|
}
|
2012-03-24 03:25:10 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Method: _field for structures.
|
|
|
|
*/
|
|
|
|
static int meta_struct_field_reference(lua_State *state)
|
|
|
|
{
|
|
|
|
if (lua_gettop(state) != 2)
|
|
|
|
luaL_error(state, "Usage: object._field(name)");
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "reference");
|
|
|
|
auto field = (struct_field_info*)find_field(state, 2, "reference");
|
|
|
|
if (!field)
|
2012-03-25 04:06:05 -06:00
|
|
|
field_error(state, 2, "builtin property or method", "reference");
|
2012-03-24 03:25:10 -06:00
|
|
|
field_reference(state, field, ptr + field->offset);
|
2021-03-30 14:55:06 -06:00
|
|
|
if (field->mode == struct_field_info::SUBSTRUCT || field->mode == struct_field_info::CONTAINER)
|
|
|
|
{
|
|
|
|
auto struct_type = (struct_identity*)get_object_identity(state, 1, "reference", false);
|
|
|
|
if (auto tag_field = find_union_tag(struct_type, field))
|
|
|
|
{
|
|
|
|
get_object_ref_header(state, -1)->tag_ptr = ptr + tag_field->offset;
|
|
|
|
get_object_ref_header(state, -1)->tag_identity = tag_field->type;
|
|
|
|
get_object_ref_header(state, -1)->tag_attr = field->extra ? field->extra->union_tag_attr : nullptr;
|
|
|
|
}
|
|
|
|
}
|
2012-03-24 03:25:10 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-03-20 08:41:58 -06:00
|
|
|
/**
|
|
|
|
* Method: _field for df.global.
|
|
|
|
*/
|
|
|
|
static int meta_global_field_reference(lua_State *state)
|
|
|
|
{
|
|
|
|
if (lua_gettop(state) != 2)
|
|
|
|
luaL_error(state, "Usage: object._field(name)");
|
|
|
|
auto field = (struct_field_info*)find_field(state, 2, "reference");
|
|
|
|
if (!field)
|
|
|
|
field_error(state, 2, "builtin property or method", "reference");
|
2023-08-11 00:31:54 -06:00
|
|
|
void *ptr = *(void**)field->offset;
|
|
|
|
if (!ptr)
|
|
|
|
field_error(state, 2, "global address not known", "reference");
|
2020-03-20 08:41:58 -06:00
|
|
|
field_reference(state, field, *(void**)field->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: __newindex for structures.
|
|
|
|
*/
|
|
|
|
static int meta_struct_newindex(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "write");
|
|
|
|
auto field = (struct_field_info*)find_field(state, 2, "write");
|
|
|
|
if (!field)
|
2012-03-25 04:06:05 -06:00
|
|
|
field_error(state, 2, "builtin property or method", "write");
|
2012-03-24 03:25:10 -06:00
|
|
|
write_field(state, field, ptr + field->offset, 3);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: iterator for structures.
|
|
|
|
*/
|
|
|
|
static int meta_struct_next(lua_State *state)
|
|
|
|
{
|
|
|
|
if (lua_gettop(state) < 2) lua_pushnil(state);
|
|
|
|
|
2012-03-31 02:11:43 -06:00
|
|
|
int len = lua_rawlen(state, UPVAL_FIELDTABLE);
|
2012-03-29 04:39:13 -06:00
|
|
|
int idx = cur_iter_index(state, len+1, 2, 0);
|
|
|
|
if (idx == len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lua_rawgeti(state, UPVAL_FIELDTABLE, idx+1);
|
|
|
|
lua_dup(state);
|
|
|
|
lua_gettable(state, 1);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2021-03-30 14:55:06 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: iterator for unions.
|
|
|
|
*/
|
|
|
|
static int meta_union_next(lua_State *state)
|
|
|
|
{
|
|
|
|
if (lua_gettop(state) < 2) lua_pushnil(state);
|
|
|
|
|
|
|
|
int len = lua_rawlen(state, UPVAL_FIELDTABLE);
|
|
|
|
int idx = cur_iter_index(state, len+1, 2, 0);
|
|
|
|
if (idx == len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
auto header = get_object_ref_header(state, 1);
|
|
|
|
if (header->tag_ptr)
|
|
|
|
{
|
|
|
|
if (idx != 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
auto enum_id = (enum_identity*)header->tag_identity;
|
|
|
|
auto tag_val = *(int64_t*)header->tag_ptr;
|
|
|
|
size_t tag_shift = 64 - 8 * enum_id->byte_size();
|
|
|
|
tag_val <<= tag_shift;
|
|
|
|
tag_val >>= tag_shift;
|
|
|
|
|
|
|
|
size_t tag_index = tag_val - enum_id->getFirstItem();
|
|
|
|
if (auto complex = enum_id->getComplex())
|
|
|
|
tag_index = complex->value_index_map.count(tag_val) ? complex->value_index_map.at(tag_val) : size_t(-1);
|
|
|
|
|
|
|
|
if (tag_index >= size_t(enum_id->getCount()))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
const char *tag_name = nullptr;
|
|
|
|
if (header->tag_attr)
|
|
|
|
{
|
|
|
|
for (auto enum_field = enum_id->getAttrType()->getFields(); enum_field->mode != struct_field_info::END; enum_field++)
|
|
|
|
{
|
|
|
|
if (!strcmp(enum_field->name, header->tag_attr))
|
|
|
|
{
|
|
|
|
if (enum_field->type == df::identity_traits<const char*>::get())
|
|
|
|
{
|
|
|
|
auto attrs = ((uint8_t*)enum_id->getAttrs()) + (tag_index * enum_id->getAttrType()->byte_size());
|
|
|
|
tag_name = *(const char **)(attrs + enum_field->offset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tag_name = enum_id->getKeys()[tag_index];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tag_name)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lua_getfield(state, UPVAL_FIELDTABLE, tag_name);
|
|
|
|
if (lua_isnil(state, lua_gettop(state)))
|
|
|
|
{
|
|
|
|
lua_pop(state, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
lua_pushstring(state, tag_name);
|
|
|
|
lua_getfield(state, 1, tag_name);
|
|
|
|
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_rawgeti(state, UPVAL_FIELDTABLE, idx+1);
|
|
|
|
lua_dup(state);
|
|
|
|
lua_gettable(state, 1);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2012-06-13 12:40:39 -06:00
|
|
|
/**
|
|
|
|
* Field lookup for primitive refs: behave as a quasi-array with numeric indices.
|
|
|
|
*/
|
|
|
|
static type_identity *find_primitive_field(lua_State *state, int field, const char *mode, uint8_t **ptr)
|
|
|
|
{
|
|
|
|
if (lua_type(state, field) == LUA_TNUMBER)
|
|
|
|
{
|
|
|
|
int idx = lua_tointeger(state, field);
|
|
|
|
if (idx < 0)
|
|
|
|
field_error(state, 2, "negative index", mode);
|
|
|
|
|
|
|
|
lua_rawgetp(state, UPVAL_METATABLE, &DFHACK_IDENTITY_FIELD_TOKEN);
|
|
|
|
auto id = (type_identity *)lua_touserdata(state, -1);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
*ptr += int(id->byte_size()) * idx;
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (type_identity*)find_field(state, field, mode);
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: __index for primitives, i.e. simple object references.
|
|
|
|
* Fields point to identity, or NULL for metafields.
|
|
|
|
*/
|
|
|
|
static int meta_primitive_index(lua_State *state)
|
|
|
|
{
|
2020-07-25 18:22:52 -06:00
|
|
|
if (lua_type(state, -1) == LUA_TSTRING)
|
|
|
|
{
|
|
|
|
const char *attr = lua_tostring(state, -1);
|
|
|
|
if (strcmp(attr, "ref_target") == 0) {
|
|
|
|
const struct_field_info *field_info = get_object_ref_header(state, 1)->field_info;
|
|
|
|
if (field_info && field_info->extra && field_info->extra->ref_target) {
|
|
|
|
LookupInTable(state, field_info->extra->ref_target, &DFHACK_TYPEID_TABLE_TOKEN);
|
|
|
|
} else {
|
|
|
|
lua_pushnil(state);
|
|
|
|
}
|
|
|
|
return 1;
|
2020-04-08 22:02:07 -06:00
|
|
|
}
|
|
|
|
}
|
2020-04-27 23:21:11 -06:00
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "read");
|
2012-06-13 12:40:39 -06:00
|
|
|
auto type = find_primitive_field(state, 2, "read", &ptr);
|
2012-03-24 03:25:10 -06:00
|
|
|
if (!type)
|
2012-03-25 04:06:05 -06:00
|
|
|
return 1;
|
2012-03-24 03:25:10 -06:00
|
|
|
type->lua_read(state, 2, ptr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: __newindex for primitives.
|
|
|
|
*/
|
|
|
|
static int meta_primitive_newindex(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "write");
|
2012-06-13 12:40:39 -06:00
|
|
|
auto type = find_primitive_field(state, 2, "write", &ptr);
|
2012-03-24 03:25:10 -06:00
|
|
|
if (!type)
|
2012-03-25 04:06:05 -06:00
|
|
|
field_error(state, 2, "builtin property or method", "write");
|
2012-03-24 03:25:10 -06:00
|
|
|
type->lua_write(state, 2, ptr, 3);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: __len for containers.
|
|
|
|
*/
|
|
|
|
static int meta_container_len(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 0, "get length");
|
|
|
|
auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
2012-03-24 06:28:53 -06:00
|
|
|
int len = id->lua_item_count(state, ptr, container_identity::COUNT_LEN);
|
2012-03-24 03:25:10 -06:00
|
|
|
lua_pushinteger(state, len);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Field lookup for containers:
|
|
|
|
*
|
|
|
|
* - Numbers are indices and handled directly.
|
|
|
|
* - NULL userdata are metafields; push and exit;
|
|
|
|
*/
|
|
|
|
static int lookup_container_field(lua_State *state, int field, const char *mode = NULL)
|
|
|
|
{
|
|
|
|
if (lua_type(state, field) == LUA_TNUMBER)
|
|
|
|
return field;
|
|
|
|
|
|
|
|
lookup_field(state, field, mode ? mode : "read");
|
|
|
|
|
|
|
|
if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1))
|
|
|
|
{
|
|
|
|
if (mode)
|
2012-03-25 04:06:05 -06:00
|
|
|
field_error(state, field, "builtin property or method", mode);
|
2012-03-24 03:25:10 -06:00
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
get_metafield(state);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Index verification: number and in range.
|
|
|
|
*/
|
|
|
|
static int check_container_index(lua_State *state, int len,
|
2012-04-17 02:15:45 -06:00
|
|
|
int fidx, int iidx, const char *mode,
|
|
|
|
bool is_insert = false)
|
2012-03-24 03:25:10 -06:00
|
|
|
{
|
2012-04-17 02:15:45 -06:00
|
|
|
if (is_insert && len >= 0)
|
|
|
|
{
|
|
|
|
if (lua_type(state, iidx) == LUA_TSTRING
|
|
|
|
&& strcmp(lua_tostring(state, iidx), "#") == 0)
|
|
|
|
return len;
|
|
|
|
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
if (!lua_isnumber(state, iidx))
|
|
|
|
field_error(state, fidx, "invalid index", mode);
|
|
|
|
|
|
|
|
int idx = lua_tointeger(state, iidx);
|
2012-03-24 06:28:53 -06:00
|
|
|
if (idx < 0 || (idx >= len && len >= 0))
|
2012-03-24 03:25:10 -06:00
|
|
|
field_error(state, fidx, "index out of bounds", mode);
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2021-03-30 14:55:06 -06:00
|
|
|
static void attach_container_item_tagged_union(lua_State *state, int container, int item, int idx)
|
|
|
|
{
|
|
|
|
auto header = get_object_ref_header(state, container);
|
|
|
|
if (header->tag_ptr)
|
|
|
|
{
|
|
|
|
// TODO: handle bit vector for tag
|
|
|
|
|
|
|
|
auto tag_container = (container_identity*)header->tag_identity;
|
|
|
|
|
|
|
|
auto ref = get_object_ref_header(state, item);
|
|
|
|
|
|
|
|
// on both msvc and gcc, vectors have the same memory layout
|
|
|
|
auto item_type = tag_container->getItemType();
|
|
|
|
ref->tag_ptr = (*(uint8_t**)header->tag_ptr) + size_t(idx) * item_type->byte_size();
|
|
|
|
ref->tag_identity = item_type;
|
|
|
|
ref->tag_attr = header->tag_attr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: __index for containers.
|
|
|
|
*/
|
|
|
|
static int meta_container_index(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "read");
|
|
|
|
int iidx = lookup_container_field(state, 2);
|
|
|
|
if (!iidx)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
2012-03-24 06:28:53 -06:00
|
|
|
int len = id->lua_item_count(state, ptr, container_identity::COUNT_READ);
|
2012-03-24 03:25:10 -06:00
|
|
|
int idx = check_container_index(state, len, 2, iidx, "read");
|
|
|
|
id->lua_item_read(state, 2, ptr, idx);
|
2021-03-30 14:55:06 -06:00
|
|
|
attach_container_item_tagged_union(state, 1, -1, idx);
|
2012-03-24 03:25:10 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Method: _field for containers.
|
|
|
|
*/
|
|
|
|
static int meta_container_field_reference(lua_State *state)
|
|
|
|
{
|
|
|
|
if (lua_gettop(state) != 2)
|
|
|
|
luaL_error(state, "Usage: object._field(index)");
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "reference");
|
|
|
|
int iidx = lookup_container_field(state, 2, "reference");
|
|
|
|
|
|
|
|
auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
2012-03-24 06:28:53 -06:00
|
|
|
int len = id->lua_item_count(state, ptr, container_identity::COUNT_WRITE);
|
2012-03-24 03:25:10 -06:00
|
|
|
int idx = check_container_index(state, len, 2, iidx, "reference");
|
|
|
|
id->lua_item_reference(state, 2, ptr, idx);
|
2021-03-30 14:55:06 -06:00
|
|
|
attach_container_item_tagged_union(state, 1, -1, idx);
|
2012-03-24 03:25:10 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: __index for containers.
|
|
|
|
*/
|
|
|
|
static int meta_container_newindex(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "write");
|
|
|
|
int iidx = lookup_container_field(state, 2, "write");
|
|
|
|
|
|
|
|
auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
2012-03-24 06:28:53 -06:00
|
|
|
int len = id->lua_item_count(state, ptr, container_identity::COUNT_WRITE);
|
2012-03-24 03:25:10 -06:00
|
|
|
int idx = check_container_index(state, len, 2, iidx, "write");
|
|
|
|
id->lua_item_write(state, 2, ptr, idx, 3);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: integer iterator for containers.
|
|
|
|
*/
|
|
|
|
static int meta_container_nexti(lua_State *state)
|
|
|
|
{
|
|
|
|
if (lua_gettop(state) < 2) lua_pushnil(state);
|
|
|
|
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "iterate");
|
|
|
|
|
|
|
|
auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
|
|
|
int len = id->lua_item_count(state, ptr, container_identity::COUNT_LEN);
|
|
|
|
int idx = cur_iter_index(state, len, 2);
|
|
|
|
|
|
|
|
if (++idx >= len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lua_pushinteger(state, idx);
|
|
|
|
id->lua_item_read(state, 2, ptr, idx);
|
2021-03-30 14:55:06 -06:00
|
|
|
attach_container_item_tagged_union(state, 1, -1, idx);
|
2012-03-29 04:39:13 -06:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: name iterator for containers.
|
|
|
|
*/
|
|
|
|
static int meta_container_next(lua_State *state)
|
|
|
|
{
|
|
|
|
if (!meta_container_nexti(state))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iter_idx_to_name(state, lua_gettop(state)-1);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:28:53 -06:00
|
|
|
/**
|
|
|
|
* Method: resize container
|
|
|
|
*/
|
|
|
|
static int method_container_resize(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = check_method_call(state, 1, 1);
|
|
|
|
|
|
|
|
auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
|
|
|
int idx = check_container_index(state, -1, UPVAL_METHOD_NAME, 2, "call");
|
|
|
|
|
|
|
|
if (!id->resize(ptr, idx))
|
|
|
|
field_error(state, UPVAL_METHOD_NAME, "not supported", "call");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Method: erase item from container
|
|
|
|
*/
|
|
|
|
static int method_container_erase(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = check_method_call(state, 1, 1);
|
|
|
|
|
|
|
|
auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
|
|
|
int len = id->lua_item_count(state, ptr, container_identity::COUNT_LEN);
|
|
|
|
int idx = check_container_index(state, len, UPVAL_METHOD_NAME, 2, "call");
|
|
|
|
|
|
|
|
if (!id->erase(ptr, idx))
|
|
|
|
field_error(state, UPVAL_METHOD_NAME, "not supported", "call");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Method: insert item into container
|
|
|
|
*/
|
|
|
|
static int method_container_insert(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = check_method_call(state, 2, 2);
|
|
|
|
|
|
|
|
auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
|
|
|
int len = id->lua_item_count(state, ptr, container_identity::COUNT_LEN);
|
2012-04-17 02:15:45 -06:00
|
|
|
int idx = check_container_index(state, len, UPVAL_METHOD_NAME, 2, "call", true);
|
2012-03-24 06:28:53 -06:00
|
|
|
|
2016-07-26 21:47:53 -06:00
|
|
|
if (!id->lua_insert2(state, UPVAL_METHOD_NAME, ptr, idx, 3))
|
2012-03-24 06:28:53 -06:00
|
|
|
field_error(state, UPVAL_METHOD_NAME, "not supported", "call");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: __len for bitfields.
|
|
|
|
*/
|
|
|
|
static int meta_bitfield_len(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 0, "get size");
|
2016-10-14 22:37:18 -06:00
|
|
|
(void)ptr;
|
2012-03-24 03:25:10 -06:00
|
|
|
auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
|
|
|
lua_pushinteger(state, id->getNumBits());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
static void read_bitfield(lua_State *state, uint8_t *ptr, bitfield_identity *id, int idx)
|
|
|
|
{
|
2012-09-01 01:25:24 -06:00
|
|
|
int size = std::max(1, id->getBits()[idx].size);
|
2012-03-29 04:39:13 -06:00
|
|
|
|
|
|
|
int value = getBitfieldField(ptr, idx, size);
|
|
|
|
if (size <= 1)
|
|
|
|
lua_pushboolean(state, value != 0);
|
|
|
|
else
|
|
|
|
lua_pushinteger(state, value);
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: __index for bitfields.
|
|
|
|
*/
|
|
|
|
static int meta_bitfield_index(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "read");
|
|
|
|
int iidx = lookup_container_field(state, 2);
|
|
|
|
if (!iidx)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
|
|
|
|
|
|
|
// whole
|
|
|
|
if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id)
|
|
|
|
{
|
|
|
|
size_t intv = 0;
|
|
|
|
memcpy(&intv, ptr, std::min(sizeof(intv), size_t(id->byte_size())));
|
2016-12-10 10:27:43 -07:00
|
|
|
lua_pushinteger(state, intv);
|
2012-03-24 03:25:10 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int idx = check_container_index(state, id->getNumBits(), 2, iidx, "read");
|
2012-03-29 04:39:13 -06:00
|
|
|
read_bitfield(state, ptr, id, idx);
|
2012-03-24 03:25:10 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: __newindex for bitfields.
|
|
|
|
*/
|
|
|
|
static int meta_bitfield_newindex(lua_State *state)
|
|
|
|
{
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "write");
|
|
|
|
int iidx = lookup_container_field(state, 2, "write");
|
|
|
|
|
|
|
|
auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
|
|
|
|
|
|
|
// whole
|
|
|
|
if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id)
|
|
|
|
{
|
|
|
|
if (!lua_isnumber(state, 3))
|
|
|
|
field_error(state, 2, "number expected", "write");
|
|
|
|
|
|
|
|
size_t intv = (size_t)lua_tonumber(state, 3);
|
|
|
|
memcpy(ptr, &intv, std::min(sizeof(intv), size_t(id->byte_size())));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int idx = check_container_index(state, id->getNumBits(), 2, iidx, "write");
|
2012-09-01 01:25:24 -06:00
|
|
|
int size = std::max(1, id->getBits()[idx].size);
|
2012-03-24 03:25:10 -06:00
|
|
|
|
|
|
|
if (lua_isboolean(state, 3) || lua_isnil(state, 3))
|
|
|
|
setBitfieldField(ptr, idx, size, lua_toboolean(state, 3));
|
|
|
|
else if (lua_isnumber(state, 3))
|
|
|
|
setBitfieldField(ptr, idx, size, lua_tointeger(state, 3));
|
|
|
|
else
|
|
|
|
field_error(state, 2, "number or boolean expected", "write");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: integer iterator for bitfields.
|
|
|
|
*/
|
|
|
|
static int meta_bitfield_nexti(lua_State *state)
|
|
|
|
{
|
|
|
|
if (lua_gettop(state) < 2) lua_pushnil(state);
|
|
|
|
|
|
|
|
uint8_t *ptr = get_object_addr(state, 1, 2, "iterate");
|
|
|
|
|
|
|
|
auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
|
|
|
int len = id->getNumBits();
|
|
|
|
int idx = cur_iter_index(state, len, 2);
|
|
|
|
|
|
|
|
if (idx < 0)
|
|
|
|
idx = 0;
|
|
|
|
else
|
|
|
|
idx += std::max(1, (int)id->getBits()[idx].size);
|
|
|
|
|
|
|
|
if (idx >= len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lua_pushinteger(state, idx);
|
|
|
|
read_bitfield(state, ptr, id, idx);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: name iterator for bitfields.
|
|
|
|
*/
|
|
|
|
static int meta_bitfield_next(lua_State *state)
|
|
|
|
{
|
|
|
|
if (!meta_bitfield_nexti(state))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iter_idx_to_name(state, lua_gettop(state)-1);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: __index for df.global
|
|
|
|
*/
|
|
|
|
static int meta_global_index(lua_State *state)
|
|
|
|
{
|
|
|
|
auto field = (struct_field_info*)find_field(state, 2, "read");
|
|
|
|
if (!field)
|
2012-03-25 04:06:05 -06:00
|
|
|
return 1;
|
2012-03-24 03:25:10 -06:00
|
|
|
void *ptr = *(void**)field->offset;
|
|
|
|
if (!ptr)
|
|
|
|
field_error(state, 2, "global address not known", "read");
|
|
|
|
read_field(state, field, ptr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: __newindex for df.global
|
|
|
|
*/
|
|
|
|
static int meta_global_newindex(lua_State *state)
|
|
|
|
{
|
|
|
|
auto field = (struct_field_info*)find_field(state, 2, "write");
|
|
|
|
if (!field)
|
2012-03-25 04:06:05 -06:00
|
|
|
field_error(state, 2, "builtin property or method", "write");
|
2012-03-24 03:25:10 -06:00
|
|
|
void *ptr = *(void**)field->offset;
|
|
|
|
if (!ptr)
|
|
|
|
field_error(state, 2, "global address not known", "write");
|
|
|
|
write_field(state, field, ptr, 3);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-25 04:06:05 -06:00
|
|
|
/**
|
|
|
|
* Wrapper for c++ methods and functions.
|
|
|
|
*/
|
|
|
|
static int meta_call_function(lua_State *state)
|
|
|
|
{
|
|
|
|
auto id = (function_identity_base*)lua_touserdata(state, UPVAL_CONTAINER_ID);
|
Allow plugins to export functions to lua with safe reload support.
- To ensure reload safety functions have to be wrapped. Every call
checks the loaded state and locks a mutex in Plugin. If the plugin
is unloaded, calling its functions throws a lua error. Therefore,
plugins may not create closures or export yieldable functions.
- The set of function argument and return types supported by
LuaWrapper is severely limited when compared to being compiled
inside the main library.
Currently supported types: numbers, bool, std::string, df::foo,
df::foo*, std::vector<bool>, std::vector<df::foo*>.
- To facilitate postponing initialization until after all plugins
have been loaded, the core sends a SC_CORE_INITIALIZED event.
- As an example, the burrows plugin now exports its functions.
2012-04-14 09:44:07 -06:00
|
|
|
|
|
|
|
return method_wrapper_core(state, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
int LuaWrapper::method_wrapper_core(lua_State *state, function_identity_base *id)
|
|
|
|
{
|
2012-04-15 09:09:25 -06:00
|
|
|
if (id->adjustArgs())
|
|
|
|
lua_settop(state, id->getNumArgs());
|
|
|
|
else if (lua_gettop(state) != id->getNumArgs())
|
2012-03-25 04:06:05 -06:00
|
|
|
field_error(state, UPVAL_METHOD_NAME, "invalid argument count", "invoke");
|
2012-04-05 09:55:59 -06:00
|
|
|
|
|
|
|
try {
|
|
|
|
id->invoke(state, 1);
|
|
|
|
}
|
2018-02-05 17:18:35 -07:00
|
|
|
catch (Error::All &e) {
|
|
|
|
field_error(state, UPVAL_METHOD_NAME, e.what(), "invoke");
|
2017-12-25 18:13:15 -07:00
|
|
|
}
|
2012-04-05 09:55:59 -06:00
|
|
|
catch (std::exception &e) {
|
|
|
|
std::string tmp = stl_sprintf("C++ exception: %s", e.what());
|
|
|
|
field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke");
|
|
|
|
}
|
|
|
|
|
2012-03-25 04:06:05 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-08-19 04:27:44 -06:00
|
|
|
int Lua::CallWithCatch(lua_State *state, int (*fn)(lua_State*), const char *context)
|
|
|
|
{
|
|
|
|
if (!context)
|
|
|
|
context = "native code";
|
|
|
|
|
|
|
|
try {
|
|
|
|
return fn(state);
|
|
|
|
}
|
2018-02-05 17:18:35 -07:00
|
|
|
catch (Error::All &e) {
|
|
|
|
return luaL_error(state, "%s: %s", context, e.what());
|
2017-12-25 18:13:15 -07:00
|
|
|
}
|
2012-08-19 04:27:44 -06:00
|
|
|
catch (std::exception &e) {
|
|
|
|
return luaL_error(state, "%s: C++ exception: %s", context, e.what());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-25 04:06:05 -06:00
|
|
|
/**
|
2012-04-15 09:09:25 -06:00
|
|
|
* Push a closure invoking the given function.
|
2012-03-25 04:06:05 -06:00
|
|
|
*/
|
2012-04-15 09:09:25 -06:00
|
|
|
void LuaWrapper::PushFunctionWrapper(lua_State *state, int meta_idx,
|
|
|
|
const char *name, function_identity_base *fun)
|
2012-03-25 04:06:05 -06:00
|
|
|
{
|
2012-04-01 08:34:04 -06:00
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
|
2012-04-05 01:59:39 -06:00
|
|
|
if (meta_idx)
|
|
|
|
lua_pushvalue(state, meta_idx);
|
|
|
|
else
|
|
|
|
lua_pushlightuserdata(state, NULL); // can't be a metatable
|
2012-03-25 04:06:05 -06:00
|
|
|
lua_pushfstring(state, "%s()", name);
|
|
|
|
lua_pushlightuserdata(state, fun);
|
|
|
|
lua_pushcclosure(state, meta_call_function, 4);
|
2012-04-15 09:09:25 -06:00
|
|
|
}
|
2012-03-25 04:06:05 -06:00
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
/**
|
|
|
|
* Create a closure invoking the given function, and add it to the field table.
|
|
|
|
*/
|
|
|
|
static void AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
|
|
|
|
const char *name, function_identity_base *fun)
|
|
|
|
{
|
|
|
|
PushFunctionWrapper(state, meta_idx, name, fun);
|
2012-03-25 04:06:05 -06:00
|
|
|
lua_setfield(state, field_idx, name);
|
|
|
|
}
|
|
|
|
|
2012-04-05 09:55:59 -06:00
|
|
|
/**
|
|
|
|
* Wrap functions and add them to the table on the top of the stack.
|
|
|
|
*/
|
|
|
|
void LuaWrapper::SetFunctionWrappers(lua_State *state, const FunctionReg *reg)
|
|
|
|
{
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
|
|
|
for (; reg && reg->name; ++reg)
|
|
|
|
AddMethodWrapper(state, 0, base, reg->name, reg->identity);
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
/**
|
|
|
|
* Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack.
|
2023-08-12 01:09:18 -06:00
|
|
|
*
|
|
|
|
* flags:
|
|
|
|
* GLOBALS: if true, pstruct is a global_identity and fields with addresses of 0 are skipped
|
|
|
|
* RAW: if true, no fields are skipped (supersedes `GLOBALS` flag) and
|
|
|
|
* special-case fields like OBJ_METHODs are not added to the metatable
|
|
|
|
*
|
|
|
|
* Stack in & out:
|
|
|
|
* base+1: metatable
|
|
|
|
* base+2: fields table (to be populated, map of name -> struct_field_info*)
|
|
|
|
* base+3: field iter table (to be populated, bimap of name <-> integer index)
|
2012-03-24 03:25:10 -06:00
|
|
|
*/
|
2023-08-12 01:09:18 -06:00
|
|
|
namespace IndexFieldsFlags {
|
|
|
|
enum IndexFieldsFlags {
|
|
|
|
GLOBALS = 1 << 0,
|
|
|
|
RAW = 1 << 1,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
static void IndexFields(lua_State *state, int base, struct_identity *pstruct, int flags)
|
2012-03-24 03:25:10 -06:00
|
|
|
{
|
2012-03-29 04:39:13 -06:00
|
|
|
if (pstruct->getParent())
|
2023-08-12 01:09:18 -06:00
|
|
|
IndexFields(state, base, pstruct->getParent(), flags);
|
2012-03-24 03:25:10 -06:00
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
auto fields = pstruct->getFields();
|
|
|
|
if (!fields)
|
|
|
|
return;
|
2012-03-24 03:25:10 -06:00
|
|
|
|
2012-03-31 02:11:43 -06:00
|
|
|
int cnt = lua_rawlen(state, base+3); // field iter table
|
2012-03-29 04:39:13 -06:00
|
|
|
|
|
|
|
for (int i = 0; fields[i].mode != struct_field_info::END; ++i)
|
2012-03-24 03:25:10 -06:00
|
|
|
{
|
2012-03-29 04:39:13 -06:00
|
|
|
// Qualify conflicting field names with the type
|
|
|
|
std::string name = fields[i].name;
|
2012-03-24 03:25:10 -06:00
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
lua_getfield(state, base+2, name.c_str());
|
|
|
|
if (!lua_isnil(state, -1))
|
|
|
|
name = pstruct->getName() + ("." + name);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
2012-05-03 01:47:04 -06:00
|
|
|
bool add_to_enum = true;
|
|
|
|
|
2023-08-12 01:09:18 -06:00
|
|
|
if (!(flags & IndexFieldsFlags::RAW))
|
2012-03-29 04:39:13 -06:00
|
|
|
// Handle the field
|
|
|
|
switch (fields[i].mode)
|
2012-03-24 03:25:10 -06:00
|
|
|
{
|
2012-03-29 04:39:13 -06:00
|
|
|
case struct_field_info::OBJ_METHOD:
|
|
|
|
AddMethodWrapper(state, base+1, base+2, name.c_str(),
|
|
|
|
(function_identity_base*)fields[i].type);
|
2012-05-03 01:47:04 -06:00
|
|
|
continue;
|
2012-03-24 03:25:10 -06:00
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
case struct_field_info::CLASS_METHOD:
|
2012-05-03 01:47:04 -06:00
|
|
|
continue;
|
|
|
|
|
|
|
|
case struct_field_info::POINTER:
|
2021-03-30 14:55:06 -06:00
|
|
|
// Skip potentially bad pointers
|
|
|
|
if ((fields[i].count & 2) != 0 && fields[i].type)
|
2012-05-03 01:47:04 -06:00
|
|
|
add_to_enum = false;
|
2012-03-29 04:39:13 -06:00
|
|
|
break;
|
2012-03-25 04:06:05 -06:00
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
default:
|
|
|
|
break;
|
2012-03-25 05:20:58 -06:00
|
|
|
}
|
2012-05-03 01:47:04 -06:00
|
|
|
|
|
|
|
// Do not add invalid globals to the enumeration order
|
2023-08-12 01:09:18 -06:00
|
|
|
if ((flags & IndexFieldsFlags::GLOBALS) && !*(void**)fields[i].offset)
|
2012-05-03 01:47:04 -06:00
|
|
|
add_to_enum = false;
|
|
|
|
|
2023-08-12 01:09:18 -06:00
|
|
|
if (add_to_enum || (flags & IndexFieldsFlags::RAW))
|
2012-05-03 01:47:04 -06:00
|
|
|
AssociateId(state, base+3, ++cnt, name.c_str());
|
|
|
|
|
|
|
|
lua_pushlightuserdata(state, (void*)&fields[i]);
|
|
|
|
lua_setfield(state, base+2, name.c_str());
|
2012-03-25 05:20:58 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-12 20:12:45 -06:00
|
|
|
static void PushTypeIdentity(lua_State *state, const type_identity *id)
|
|
|
|
{
|
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPEID_TABLE_TOKEN);
|
|
|
|
lua_rawgetp(state, -1, id);
|
|
|
|
lua_remove(state, -2); // TYPEID_TABLE
|
|
|
|
}
|
|
|
|
|
2023-08-12 17:28:02 -06:00
|
|
|
static void PushFieldInfoSubTable(lua_State *state, const struct_field_info *field)
|
|
|
|
{
|
2023-08-12 18:18:22 -06:00
|
|
|
if (!field) {
|
|
|
|
lua_pushnil(state);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-12 17:28:02 -06:00
|
|
|
lua_newtable(state); // new field info
|
|
|
|
Lua::TableInsert(state, "mode", field->mode);
|
|
|
|
Lua::TableInsert(state, "name", field->name);
|
|
|
|
Lua::TableInsert(state, "offset", field->offset);
|
|
|
|
Lua::TableInsert(state, "count", field->count);
|
|
|
|
|
|
|
|
if (field->type) {
|
|
|
|
Lua::TableInsert(state, "type_name", field->type->getFullName());
|
|
|
|
|
|
|
|
lua_pushlightuserdata(state, field->type);
|
|
|
|
lua_setfield(state, -2, "type_identity");
|
|
|
|
|
2023-08-12 20:12:45 -06:00
|
|
|
PushTypeIdentity(state, field->type);
|
|
|
|
lua_setfield(state, -2, "type");
|
2023-08-12 17:28:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (field->extra) {
|
2023-08-12 20:12:45 -06:00
|
|
|
if (field->extra->index_enum) {
|
|
|
|
PushTypeIdentity(state, field->extra->index_enum);
|
|
|
|
lua_setfield(state, -2, "index_enum");
|
|
|
|
}
|
|
|
|
if (field->extra->ref_target) {
|
|
|
|
PushTypeIdentity(state, field->extra->ref_target);
|
|
|
|
lua_setfield(state, -2, "ref_target");
|
|
|
|
}
|
2023-08-12 17:28:02 -06:00
|
|
|
if (field->extra->union_tag_field) {
|
|
|
|
Lua::TableInsert(state, "union_tag_field", field->extra->union_tag_field);
|
|
|
|
}
|
|
|
|
if (field->extra->union_tag_attr) {
|
|
|
|
Lua::TableInsert(state, "union_tag_attr", field->extra->union_tag_attr);
|
|
|
|
}
|
|
|
|
if (field->extra->original_name) {
|
|
|
|
Lua::TableInsert(state, "original_name", field->extra->original_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-12 18:18:22 -06:00
|
|
|
/**
|
|
|
|
* Metamethod: __index for struct._fields
|
|
|
|
*
|
|
|
|
* upvalue 1: name -> struct_field_info* table
|
|
|
|
*/
|
|
|
|
static int meta_fieldinfo_index(lua_State *state)
|
|
|
|
{
|
|
|
|
luaL_checktype(state, -1, LUA_TSTRING);
|
|
|
|
|
|
|
|
lua_gettable(state, lua_upvalueindex(1));
|
|
|
|
auto field = static_cast<const struct_field_info*>(lua_touserdata(state, -1));
|
|
|
|
lua_pop(state, 1);
|
|
|
|
PushFieldInfoSubTable(state, field);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Metamethod: iterator for struct._fields
|
|
|
|
*
|
|
|
|
* upvalue 1: name -> struct_field_info* table
|
|
|
|
* upvalue 3: field table (int <-> name)
|
|
|
|
*/
|
|
|
|
static int meta_fieldinfo_next(lua_State *state)
|
|
|
|
{
|
|
|
|
if (lua_gettop(state) < 2) lua_pushnil(state);
|
|
|
|
|
|
|
|
int len = lua_rawlen(state, UPVAL_FIELDTABLE);
|
|
|
|
int idx = cur_iter_index(state, len+1, 2, 0);
|
|
|
|
if (idx == len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lua_rawgeti(state, UPVAL_FIELDTABLE, idx+1);
|
|
|
|
|
|
|
|
// modified from meta_struct_next:
|
|
|
|
// retrieve the struct_field_info* from the table and convert it
|
|
|
|
lua_dup(state);
|
|
|
|
lua_gettable(state, lua_upvalueindex(1));
|
|
|
|
auto field = static_cast<const struct_field_info*>(lua_touserdata(state, -1));
|
|
|
|
lua_pop(state, 1);
|
|
|
|
PushFieldInfoSubTable(state, field);
|
|
|
|
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2023-08-12 01:09:18 -06:00
|
|
|
static void AddFieldInfoTable(lua_State *state, int ftable_idx, struct_identity *pstruct)
|
|
|
|
{
|
|
|
|
Lua::StackUnwinder base{state};
|
|
|
|
|
|
|
|
// metatable
|
|
|
|
lua_newtable(state);
|
|
|
|
int ix_meta = lua_gettop(state);
|
|
|
|
|
|
|
|
// field info table (name -> struct_field_info*)
|
|
|
|
lua_newtable(state);
|
|
|
|
int ix_fieldinfo = lua_gettop(state);
|
|
|
|
|
|
|
|
// field iter table (int <-> name)
|
|
|
|
lua_newtable(state);
|
|
|
|
int ix_fielditer = lua_gettop(state);
|
|
|
|
IndexFields(state, base, pstruct, IndexFieldsFlags::RAW);
|
|
|
|
|
2023-08-12 18:18:22 -06:00
|
|
|
PushStructMethod(state, ix_meta, ix_fielditer, meta_fieldinfo_next);
|
|
|
|
// change upvalue 1 to the field info table since we don't need the original
|
|
|
|
lua_pushvalue(state, ix_fieldinfo);
|
|
|
|
lua_setupvalue(state, -2, 1);
|
2023-08-12 01:09:18 -06:00
|
|
|
SetPairsMethod(state, ix_meta, "__pairs");
|
|
|
|
|
|
|
|
// field table (name -> table representation of struct_field_info)
|
|
|
|
lua_newtable(state);
|
|
|
|
int ix_fields = lua_gettop(state);
|
|
|
|
|
|
|
|
// wrapper table (empty, indexes into field table with metamethods)
|
|
|
|
lua_newtable(state);
|
|
|
|
int ix_wrapper = lua_gettop(state);
|
|
|
|
|
|
|
|
// set up metatable for the wrapper
|
|
|
|
// use field table for __index
|
|
|
|
lua_pushstring(state, "__index");
|
2023-08-12 18:18:22 -06:00
|
|
|
lua_pushvalue(state, ix_fieldinfo);
|
|
|
|
lua_pushcclosure(state, meta_fieldinfo_index, 1);
|
2023-08-12 01:09:18 -06:00
|
|
|
lua_settable(state, ix_meta);
|
|
|
|
|
|
|
|
// use change_error() for __newindex
|
|
|
|
lua_pushstring(state, "__newindex");
|
|
|
|
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME);
|
|
|
|
lua_settable(state, ix_meta);
|
|
|
|
|
|
|
|
lua_pushvalue(state, ix_meta);
|
|
|
|
lua_setmetatable(state, ix_wrapper);
|
|
|
|
|
|
|
|
// convert field info table (struct_field_info) to field table (lua tables)
|
|
|
|
lua_pushnil(state); // initial key for next()
|
|
|
|
while (lua_next(state, ix_fieldinfo)) {
|
|
|
|
auto field = static_cast<const struct_field_info*>(lua_touserdata(state, -1));
|
|
|
|
lua_pushvalue(state, -2); // field name
|
2023-08-12 17:28:02 -06:00
|
|
|
PushFieldInfoSubTable(state, field);
|
2023-08-12 01:09:18 -06:00
|
|
|
lua_settable(state, ix_fields);
|
2023-08-12 17:28:02 -06:00
|
|
|
lua_pop(state, 1); // struct_field_info
|
2023-08-12 01:09:18 -06:00
|
|
|
}
|
|
|
|
|
2023-08-12 17:00:24 -06:00
|
|
|
// lua_pushvalue(state, ix_fields);
|
|
|
|
// freeze_table(state); // TODO: figure out why this creates an __index cycle for nonexistent fields
|
2023-08-12 01:09:18 -06:00
|
|
|
lua_pushvalue(state, ix_wrapper);
|
|
|
|
lua_setfield(state, ftable_idx, "_fields");
|
|
|
|
}
|
|
|
|
|
2012-03-25 05:20:58 -06:00
|
|
|
void LuaWrapper::IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct)
|
|
|
|
{
|
|
|
|
// stack: metatable fieldtable
|
2023-08-12 01:09:18 -06:00
|
|
|
AddFieldInfoTable(state, ftable_idx, pstruct);
|
2012-03-25 05:20:58 -06:00
|
|
|
for (struct_identity *p = pstruct; p; p = p->getParent())
|
|
|
|
{
|
|
|
|
auto fields = p->getFields();
|
|
|
|
if (!fields)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (int i = 0; fields[i].mode != struct_field_info::END; ++i)
|
|
|
|
{
|
|
|
|
switch (fields[i].mode)
|
|
|
|
{
|
|
|
|
case struct_field_info::CLASS_METHOD:
|
|
|
|
AddMethodWrapper(state, meta_idx, ftable_idx, fields[i].name,
|
|
|
|
(function_identity_base*)fields[i].type);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2012-03-25 04:06:05 -06:00
|
|
|
}
|
2012-03-24 03:25:10 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make a struct-style object metatable.
|
|
|
|
*/
|
|
|
|
static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct,
|
2021-03-30 14:55:06 -06:00
|
|
|
lua_CFunction reader, lua_CFunction writer,
|
|
|
|
lua_CFunction iterator, bool globals = false)
|
2012-03-24 03:25:10 -06:00
|
|
|
{
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
|
|
|
MakeMetatable(state, pstruct, "struct"); // meta, fields
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
// Index the fields
|
|
|
|
lua_newtable(state);
|
2023-08-12 01:09:18 -06:00
|
|
|
IndexFields(state, base, pstruct, globals ? IndexFieldsFlags::GLOBALS : 0);
|
2012-03-29 04:39:13 -06:00
|
|
|
|
|
|
|
// Add the iteration metamethods
|
2021-03-30 14:55:06 -06:00
|
|
|
PushStructMethod(state, base+1, base+3, iterator);
|
2012-03-29 04:39:13 -06:00
|
|
|
SetPairsMethod(state, base+1, "__pairs");
|
|
|
|
lua_pushnil(state);
|
|
|
|
SetPairsMethod(state, base+1, "__ipairs");
|
|
|
|
|
|
|
|
lua_setfield(state, base+1, "_index_table");
|
2012-03-24 03:25:10 -06:00
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
// Add the indexing metamethods
|
2012-03-24 03:25:10 -06:00
|
|
|
SetStructMethod(state, base+1, base+2, reader, "__index");
|
|
|
|
SetStructMethod(state, base+1, base+2, writer, "__newindex");
|
|
|
|
|
|
|
|
// returns: [metatable readfields writefields];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make a primitive-style metatable
|
|
|
|
*/
|
|
|
|
static void MakePrimitiveMetatable(lua_State *state, type_identity *type)
|
|
|
|
{
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
|
|
|
MakeMetatable(state, type, "primitive");
|
|
|
|
|
|
|
|
SetPtrMethods(state, base+1, base+2);
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
// Index the fields
|
|
|
|
lua_newtable(state);
|
|
|
|
|
2014-09-24 12:47:04 -06:00
|
|
|
if (type->type() != IDTYPE_OPAQUE)
|
|
|
|
{
|
|
|
|
EnableMetaField(state, base+2, "value", type);
|
|
|
|
AssociateId(state, base+3, 1, "value");
|
2020-07-14 00:31:18 -06:00
|
|
|
|
|
|
|
EnableMetaField(state, base+2, "ref_target", NULL);
|
2014-09-24 12:47:04 -06:00
|
|
|
}
|
2012-03-29 04:39:13 -06:00
|
|
|
|
|
|
|
// Add the iteration metamethods
|
|
|
|
PushStructMethod(state, base+1, base+3, meta_struct_next);
|
|
|
|
SetPairsMethod(state, base+1, "__pairs");
|
|
|
|
lua_pushnil(state);
|
|
|
|
SetPairsMethod(state, base+1, "__ipairs");
|
2012-03-24 03:25:10 -06:00
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
lua_setfield(state, base+1, "_index_table");
|
|
|
|
|
|
|
|
// Add the indexing metamethods
|
2012-03-24 03:25:10 -06:00
|
|
|
SetStructMethod(state, base+1, base+2, meta_primitive_index, "__index");
|
|
|
|
SetStructMethod(state, base+1, base+2, meta_primitive_newindex, "__newindex");
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:28:53 -06:00
|
|
|
static void AddContainerMethodFun(lua_State *state, int meta_idx, int field_idx,
|
|
|
|
lua_CFunction function, const char *name,
|
|
|
|
type_identity *container, type_identity *item, int count)
|
|
|
|
{
|
|
|
|
lua_pushfstring(state, "%s()", name);
|
|
|
|
SetContainerMethod(state, meta_idx, lua_gettop(state), function, name, container, item, count);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
EnableMetaField(state, field_idx, name);
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
/**
|
|
|
|
* Make a container-style object metatable.
|
|
|
|
*/
|
|
|
|
static void MakeContainerMetatable(lua_State *state, container_identity *type,
|
|
|
|
type_identity *item, int count, type_identity *ienum)
|
|
|
|
{
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
|
|
|
MakeMetatable(state, type, "container");
|
|
|
|
SetPtrMethods(state, base+1, base+2);
|
|
|
|
|
|
|
|
// Update the type name using full info
|
|
|
|
lua_pushstring(state, type->getFullName(item).c_str());
|
|
|
|
lua_dup(state);
|
|
|
|
lua_setfield(state, base+1, "__metatable");
|
|
|
|
lua_setfield(state, base+1, "_type");
|
|
|
|
|
|
|
|
lua_pushlightuserdata(state, item);
|
|
|
|
lua_setfield(state, base+1, "_field_identity");
|
|
|
|
|
|
|
|
if (count >= 0)
|
|
|
|
{
|
|
|
|
lua_pushinteger(state, count);
|
|
|
|
lua_setfield(state, base+1, "_count");
|
|
|
|
}
|
|
|
|
|
|
|
|
SetContainerMethod(state, base+1, base+2, meta_container_len, "__len", type, item, count);
|
|
|
|
SetContainerMethod(state, base+1, base+2, meta_container_index, "__index", type, item, count);
|
|
|
|
SetContainerMethod(state, base+1, base+2, meta_container_newindex, "__newindex", type, item, count);
|
|
|
|
|
|
|
|
SetContainerMethod(state, base+1, base+2, meta_container_field_reference, "_field", type, item, count);
|
|
|
|
|
2012-03-24 06:28:53 -06:00
|
|
|
AddContainerMethodFun(state, base+1, base+2, method_container_resize, "resize", type, item, count);
|
|
|
|
AddContainerMethodFun(state, base+1, base+2, method_container_erase, "erase", type, item, count);
|
|
|
|
AddContainerMethodFun(state, base+1, base+2, method_container_insert, "insert", type, item, count);
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
// push the index table
|
2012-03-24 03:25:10 -06:00
|
|
|
AttachEnumKeys(state, base+1, base+2, ienum);
|
2012-03-29 04:39:13 -06:00
|
|
|
|
|
|
|
PushContainerMethod(state, base+1, base+3, meta_container_next, type, item, count);
|
|
|
|
SetPairsMethod(state, base+1, "__pairs");
|
|
|
|
PushContainerMethod(state, base+1, base+3, meta_container_nexti, type, item, count);
|
|
|
|
SetPairsMethod(state, base+1, "__ipairs");
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
2012-03-24 03:25:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Metatable construction identity methods.
|
|
|
|
*/
|
|
|
|
void type_identity::build_metatable(lua_State *state)
|
|
|
|
{
|
|
|
|
MakePrimitiveMetatable(state, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void container_identity::build_metatable(lua_State *state)
|
|
|
|
{
|
|
|
|
MakeContainerMetatable(state, this, getItemType(), -1, getIndexEnumType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void bitfield_identity::build_metatable(lua_State *state)
|
|
|
|
{
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
|
|
|
MakeMetatable(state, this, "bitfield");
|
|
|
|
|
|
|
|
SetPtrMethods(state, base+1, base+2);
|
|
|
|
|
|
|
|
SetContainerMethod(state, base+1, base+2, meta_bitfield_len, "__len", this, NULL, -1);
|
|
|
|
SetContainerMethod(state, base+1, base+2, meta_bitfield_index, "__index", this, NULL, -1);
|
|
|
|
SetContainerMethod(state, base+1, base+2, meta_bitfield_newindex, "__newindex", this, NULL, -1);
|
|
|
|
|
|
|
|
AttachEnumKeys(state, base+1, base+2, this);
|
|
|
|
|
2012-03-29 04:39:13 -06:00
|
|
|
PushContainerMethod(state, base+1, base+3, meta_bitfield_next, this, NULL, -1);
|
|
|
|
SetPairsMethod(state, base+1, "__pairs");
|
|
|
|
PushContainerMethod(state, base+1, base+3, meta_bitfield_nexti, this, NULL, -1);
|
|
|
|
SetPairsMethod(state, base+1, "__ipairs");
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
EnableMetaField(state, base+2, "whole", this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void struct_identity::build_metatable(lua_State *state)
|
|
|
|
{
|
|
|
|
int base = lua_gettop(state);
|
2021-03-30 14:55:06 -06:00
|
|
|
MakeFieldMetatable(state, this, meta_struct_index, meta_struct_newindex, meta_struct_next);
|
|
|
|
SetStructMethod(state, base+1, base+2, meta_struct_field_reference, "_field");
|
|
|
|
SetPtrMethods(state, base+1, base+2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void union_identity::build_metatable(lua_State *state)
|
|
|
|
{
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
MakeFieldMetatable(state, this, meta_struct_index, meta_struct_newindex, meta_union_next);
|
2012-03-24 03:25:10 -06:00
|
|
|
SetStructMethod(state, base+1, base+2, meta_struct_field_reference, "_field");
|
|
|
|
SetPtrMethods(state, base+1, base+2);
|
|
|
|
}
|
|
|
|
|
2020-04-28 12:55:34 -06:00
|
|
|
void other_vectors_identity::build_metatable(lua_State *state)
|
|
|
|
{
|
|
|
|
int base = lua_gettop(state);
|
2021-03-30 14:55:06 -06:00
|
|
|
MakeFieldMetatable(state, this, meta_struct_index, meta_struct_newindex, meta_struct_next);
|
2020-04-28 12:55:34 -06:00
|
|
|
|
|
|
|
EnableMetaField(state, base+2, "_enum");
|
|
|
|
|
|
|
|
LookupInTable(state, index_enum, &DFHACK_TYPEID_TABLE_TOKEN);
|
|
|
|
lua_setfield(state, base+1, "_enum");
|
|
|
|
|
|
|
|
auto keys = &index_enum->getKeys()[-index_enum->getFirstItem()];
|
|
|
|
|
2020-07-26 00:11:20 -06:00
|
|
|
for (int64_t i = 0; i <= index_enum->getLastItem(); i++)
|
2020-04-28 12:55:34 -06:00
|
|
|
{
|
|
|
|
lua_getfield(state, base+2, keys[i]);
|
|
|
|
lua_rawseti(state, base+2, int(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
SetStructMethod(state, base+1, base+2, meta_struct_field_reference, "_field");
|
|
|
|
SetPtrMethods(state, base+1, base+2);
|
|
|
|
}
|
|
|
|
|
2012-03-24 03:25:10 -06:00
|
|
|
void global_identity::build_metatable(lua_State *state)
|
|
|
|
{
|
2020-03-20 08:41:58 -06:00
|
|
|
int base = lua_gettop(state);
|
2021-03-30 14:55:06 -06:00
|
|
|
MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex, meta_struct_next, true);
|
2020-03-20 08:41:58 -06:00
|
|
|
SetStructMethod(state, base+1, base+2, meta_global_field_reference, "_field");
|
|
|
|
SetPtrMethods(state, base+1, base+2);
|
2012-03-24 03:25:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a metatable for an object type folded into the field descriptor.
|
|
|
|
* This is done to reduce compile-time symbol table bloat due to templates.
|
|
|
|
*/
|
|
|
|
static void GetAdHocMetatable(lua_State *state, const struct_field_info *field)
|
|
|
|
{
|
|
|
|
lua_pushlightuserdata(state, (void*)field);
|
|
|
|
|
|
|
|
if (!LookupTypeInfo(state, true))
|
|
|
|
{
|
|
|
|
switch (field->mode)
|
|
|
|
{
|
|
|
|
case struct_field_info::CONTAINER:
|
|
|
|
{
|
|
|
|
auto ctype = (container_identity*)field->type;
|
2020-03-21 12:21:35 -06:00
|
|
|
MakeContainerMetatable(state, ctype, ctype->getItemType(), -1, field->extra ? field->extra->index_enum : nullptr);
|
2012-03-24 03:25:10 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case struct_field_info::STATIC_STRING:
|
|
|
|
MakeContainerMetatable(state, &df::buffer_container_identity::base_instance,
|
|
|
|
&df::identity_traits<char>::identity, field->count, NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case struct_field_info::STATIC_ARRAY:
|
|
|
|
MakeContainerMetatable(state, &df::buffer_container_identity::base_instance,
|
2020-03-21 12:21:35 -06:00
|
|
|
field->type, field->count, field->extra ? field->extra->index_enum : nullptr);
|
2012-03-24 03:25:10 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case struct_field_info::STL_VECTOR_PTR:
|
|
|
|
MakeContainerMetatable(state, &df::identity_traits<std::vector<void*> >::identity,
|
2020-03-21 12:21:35 -06:00
|
|
|
field->type, -1, field->extra ? field->extra->index_enum : nullptr);
|
2012-03-24 03:25:10 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
luaL_error(state, "Invalid ad-hoc field: %d", field->mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
SaveTypeInfo(state, (void*)field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaWrapper::push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target)
|
|
|
|
{
|
|
|
|
if (!target)
|
|
|
|
{
|
|
|
|
push_object_internal(state, &df::identity_traits<void*>::identity, ptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
LookupInTable(state, target, &DFHACK_PTR_IDTABLE_TOKEN);
|
2012-03-24 03:25:10 -06:00
|
|
|
|
|
|
|
type_identity *id = (type_identity*)lua_touserdata(state, -1);
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
if (!id)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* HACK: relies on
|
|
|
|
* 1) pointer_identity destructor being no-op
|
|
|
|
* 2) lua gc never moving objects in memory
|
|
|
|
*/
|
|
|
|
|
|
|
|
void *newobj = lua_newuserdata(state, sizeof(pointer_identity));
|
|
|
|
id = new (newobj) pointer_identity(target);
|
|
|
|
|
2012-04-01 08:34:04 -06:00
|
|
|
SaveInTable(state, target, &DFHACK_PTR_IDTABLE_TOKEN);
|
2012-03-24 03:25:10 -06:00
|
|
|
lua_pop(state, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
push_object_internal(state, id, ptr);
|
|
|
|
}
|