Expose virtual methods in the lua wrapper.

develop
Alexander Gavrilov 2012-03-25 14:06:05 +04:00
parent 5d471a2a74
commit 8d345be6e7
6 changed files with 268 additions and 35 deletions

@ -8,6 +8,7 @@
#include "df/ui.h"
#include "DataIdentity.h"
#include "DataFuncs.h"
#include <stddef.h>
@ -43,6 +44,7 @@ namespace df {
#define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name)
#define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name
#define METHOD(mode, name) struct_field_info::mode, #name, 0, wrap_function(&CUR_STRUCT::name)
#define FLD_END struct_field_info::END
// Field definitions

@ -36,6 +36,7 @@ distribution.
#include "DataDefs.h"
#include "DataIdentity.h"
#include "LuaWrapper.h"
#include "DataFuncs.h"
#include "MiscUtils.h"
@ -49,6 +50,16 @@ using namespace DFHack::LuaWrapper;
* Identity object read/write methods *
**************************************/
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");
}
void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
{
push_object_internal(state, this, ptr);
@ -264,12 +275,31 @@ static void lookup_field(lua_State *state, int index, const char *mode)
field_error(state, index, "not found", mode);
}
// Resolve the field in the metatable and return
static int get_metafield(lua_State *state)
{
lua_rawget(state, UPVAL_METATABLE);
return 1;
}
static void *find_field(lua_State *state, int index, const char *mode)
{
lookup_field(state, index, mode);
// 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);
void *p = lua_touserdata(state, -1);
lua_pop(state, 1);
// NULL => metafield
if (!p)
get_metafield(state);
return p;
}
@ -295,6 +325,10 @@ static void read_field(lua_State *state, const struct_field_info *field, void *p
return;
}
case struct_field_info::OBJ_METHOD:
case struct_field_info::CLASS_METHOD:
// error
case struct_field_info::PRIMITIVE:
case struct_field_info::SUBSTRUCT:
field->type->lua_read(state, 2, ptr);
@ -339,6 +373,10 @@ static void field_reference(lua_State *state, const struct_field_info *field, vo
push_adhoc_pointer(state, ptr, field->type);
return;
case struct_field_info::OBJ_METHOD:
case struct_field_info::CLASS_METHOD:
// error
case struct_field_info::CONTAINER:
read_field(state, field, ptr);
return;
@ -371,6 +409,10 @@ static void write_field(lua_State *state, const struct_field_info *field, void *
return;
}
case struct_field_info::OBJ_METHOD:
case struct_field_info::CLASS_METHOD:
// error
case struct_field_info::PRIMITIVE:
case struct_field_info::SUBSTRUCT:
case struct_field_info::CONTAINER:
@ -418,13 +460,6 @@ static int meta_ptr_tostring(lua_State *state)
return 1;
}
// Resolve the field in the metatable and return
static int get_metafield(lua_State *state)
{
lua_rawget(state, UPVAL_METATABLE);
return 1;
}
/**
* Metamethod: __index for structures.
*/
@ -433,7 +468,7 @@ 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)
return get_metafield(state);
return 1;
read_field(state, field, ptr + field->offset);
return 1;
}
@ -448,7 +483,7 @@ static int meta_struct_field_reference(lua_State *state)
uint8_t *ptr = get_object_addr(state, 1, 2, "reference");
auto field = (struct_field_info*)find_field(state, 2, "reference");
if (!field)
field_error(state, 2, "builtin property", "reference");
field_error(state, 2, "builtin property or method", "reference");
field_reference(state, field, ptr + field->offset);
return 1;
}
@ -461,7 +496,7 @@ 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)
field_error(state, 2, "builtin property", "write");
field_error(state, 2, "builtin property or method", "write");
write_field(state, field, ptr + field->offset, 3);
return 0;
}
@ -475,7 +510,7 @@ static int meta_primitive_index(lua_State *state)
uint8_t *ptr = get_object_addr(state, 1, 2, "read");
auto type = (type_identity*)find_field(state, 2, "read");
if (!type)
return get_metafield(state);
return 1;
type->lua_read(state, 2, ptr);
return 1;
}
@ -488,7 +523,7 @@ static int meta_primitive_newindex(lua_State *state)
uint8_t *ptr = get_object_addr(state, 1, 2, "write");
auto type = (type_identity*)find_field(state, 2, "write");
if (!type)
field_error(state, 2, "builtin property", "write");
field_error(state, 2, "builtin property or method", "write");
type->lua_write(state, 2, ptr, 3);
return 0;
}
@ -521,7 +556,7 @@ static int lookup_container_field(lua_State *state, int field, const char *mode
if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1))
{
if (mode)
field_error(state, field, "builtin property", mode);
field_error(state, field, "builtin property or method", mode);
lua_pop(state, 1);
get_metafield(state);
@ -727,7 +762,7 @@ static int meta_global_index(lua_State *state)
{
auto field = (struct_field_info*)find_field(state, 2, "read");
if (!field)
return get_metafield(state);
return 1;
void *ptr = *(void**)field->offset;
if (!ptr)
field_error(state, 2, "global address not known", "read");
@ -742,7 +777,7 @@ static int meta_global_newindex(lua_State *state)
{
auto field = (struct_field_info*)find_field(state, 2, "write");
if (!field)
field_error(state, 2, "builtin property", "write");
field_error(state, 2, "builtin property or method", "write");
void *ptr = *(void**)field->offset;
if (!ptr)
field_error(state, 2, "global address not known", "write");
@ -750,27 +785,65 @@ static int meta_global_newindex(lua_State *state)
return 0;
}
/**
* 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);
if (lua_gettop(state) != id->getNumArgs())
field_error(state, UPVAL_METHOD_NAME, "invalid argument count", "invoke");
id->invoke(state, 1);
return 1;
}
/**
* 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)
{
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME);
lua_pushvalue(state, meta_idx);
lua_pushfstring(state, "%s()", name);
lua_pushlightuserdata(state, fun);
lua_pushcclosure(state, meta_call_function, 4);
lua_setfield(state, field_idx, name);
}
/**
* Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack.
*/
static void IndexFields(lua_State *state, struct_identity *pstruct)
{
// stack: fieldtable
// stack: metatable fieldtable
int base = lua_gettop(state);
int base = lua_gettop(state) - 2;
for (struct_identity *p = pstruct; p; p = p->getParent())
{
auto fields = p->getFields();
if (!fields)
continue;
for (; fields; ++fields)
for (int i = 0; fields[i].mode != struct_field_info::END; ++i)
{
switch (fields[i].mode)
{
if (fields->mode == struct_field_info::END)
case struct_field_info::OBJ_METHOD:
AddMethodWrapper(state, base+1, base+2, fields[i].name,
(function_identity_base*)fields[i].type);
break;
case struct_field_info::CLASS_METHOD:
break;
lua_pushstring(state,fields->name);
lua_pushlightuserdata(state,(void*)fields);
lua_rawset(state,base);
default:
lua_pushstring(state,fields[i].name);
lua_pushlightuserdata(state,(void*)&fields[i]);
lua_rawset(state,base+2);
}
}
}
}

@ -49,6 +49,7 @@ namespace DFHack
enum identity_type {
IDTYPE_GLOBAL,
IDTYPE_FUNCTION,
IDTYPE_PRIMITIVE,
IDTYPE_POINTER,
IDTYPE_CONTAINER,
@ -109,12 +110,12 @@ namespace DFHack
constructed_identity(size_t size, TAllocateFn alloc)
: type_identity(size), allocator(alloc) {};
virtual bool isPrimitive() { return false; }
virtual bool isConstructed() { return true; }
virtual bool can_allocate() { return (allocator != NULL); }
virtual void *do_allocate() { return allocator(NULL,NULL); }
virtual void do_copy(void *tgt, const void *src) { allocator(tgt,src); }
public:
virtual bool isPrimitive() { return false; }
virtual bool isConstructed() { return true; }
virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
@ -221,7 +222,9 @@ namespace DFHack
STATIC_ARRAY,
SUBSTRUCT,
CONTAINER,
STL_VECTOR_PTR
STL_VECTOR_PTR,
OBJ_METHOD,
CLASS_METHOD
};
Mode mode;
const char *name;
@ -409,6 +412,14 @@ namespace df
template<class T>
void *allocator_fn(void *out, const void *in) {
if (out) { *(T*)out = *(const T*)in; return out; }
else if (in) { delete (T*)in; return (T*)in; }
else return new T();
}
template<class T>
void *allocator_nodel_fn(void *out, const void *in) {
if (out) { *(T*)out = *(const T*)in; return out; }
else if (in) { return NULL; }
else return new T();
}

@ -0,0 +1,129 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
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.
*/
#pragma once
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include "DataIdentity.h"
#include "LuaWrapper.h"
namespace df {
template<class T> struct function_wrapper {};
/*
* Since templates can't match variable arg count,
* a separate specialization is needed for every
* supported count value...
*
* The FW_TARGS ugliness is needed because of
* commas not wrapped in ()
*/
#define INVOKE_VOID(call) \
call; lua_pushnil(state);
#define INVOKE_RV(call) \
RT rv = call; df::identity_traits<RT>::get()->lua_read(state, UPVAL_METHOD_NAME, &rv);
#define LOAD_CLASS() \
CT *self = (CT*)DFHack::LuaWrapper::get_object_addr(state, base++, UPVAL_METHOD_NAME, "invoke");
#define LOAD_ARG(type) \
type v##type; df::identity_traits<type>::get()->lua_write(state, UPVAL_METHOD_NAME, &v##type, base++);
#define INSTANTIATE_WRAPPERS(Count, FArgs, Args, Loads) \
template<FW_TARGS> struct function_wrapper<void (*) FArgs> { \
static const bool is_method = false; \
static const int num_args = Count; \
static void execute(lua_State *state, int base, void (*cb) FArgs) { Loads; INVOKE_VOID(cb Args); } \
}; \
template<FW_TARGSC class RT> struct function_wrapper<RT (*) FArgs> { \
static const bool is_method = false; \
static const int num_args = Count; \
static void execute(lua_State *state, int base, RT (*cb) FArgs) { Loads; INVOKE_RV(cb Args); } \
}; \
template<FW_TARGSC class CT> struct function_wrapper<void (CT::*) FArgs> { \
static const bool is_method = true; \
static const int num_args = Count+1; \
static void execute(lua_State *state, int base, void (CT::*cb) FArgs) { \
LOAD_CLASS() Loads; INVOKE_VOID((self->*cb) Args); } \
}; \
template<FW_TARGSC class RT, class CT> struct function_wrapper<RT (CT::*) FArgs> { \
static const bool is_method = true; \
static const int num_args = Count+1; \
static void execute(lua_State *state, int base, RT (CT::*cb) FArgs) { \
LOAD_CLASS(); Loads; INVOKE_RV((self->*cb) Args); } \
};
#define FW_TARGSC
#define FW_TARGS
INSTANTIATE_WRAPPERS(0, (), (), ;)
#undef FW_TARGS
#undef FW_TARGSC
#define FW_TARGSC FW_TARGS,
#define FW_TARGS class A1
INSTANTIATE_WRAPPERS(1, (A1), (vA1), LOAD_ARG(A1);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2
INSTANTIATE_WRAPPERS(2, (A1,A2), (vA1,vA2), LOAD_ARG(A1); LOAD_ARG(A2);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3
INSTANTIATE_WRAPPERS(3, (A1,A2,A3), (vA1,vA2,vA3), LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4
INSTANTIATE_WRAPPERS(4, (A1,A2,A3,A4), (vA1,vA2,vA3,vA4),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);)
#undef FW_TARGS
#undef FW_TARGSC
#undef INSTANTIATE_WRAPPERS
#undef INVOKE_VOID
#undef INVOKE_RV
#undef LOAD_CLASS
#undef LOAD_ARG
template<class T>
class function_identity : public function_identity_base {
T ptr;
public:
typedef function_wrapper<T> wrapper;
function_identity(T ptr)
: function_identity_base(wrapper::num_args), ptr(ptr) {};
virtual void invoke(lua_State *state, int base) { wrapper::execute(state, base, ptr); }
};
template<class T>
inline function_identity_base *wrap_function(T ptr) {
static function_identity<T> identity(ptr);
return &identity;
}
}

@ -37,6 +37,23 @@ distribution.
namespace DFHack
{
class DFHACK_EXPORT function_identity_base : public type_identity {
int num_args;
public:
function_identity_base(int num_args) : type_identity(0), num_args(num_args) {};
virtual identity_type type() { return IDTYPE_FUNCTION; }
int getNumArgs() { return num_args; }
std::string getFullName() { return "function"; }
virtual void invoke(lua_State *state, int base) = 0;
virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
};
class DFHACK_EXPORT primitive_identity : public type_identity {
public:
primitive_identity(size_t size) : type_identity(size) {};
@ -145,6 +162,7 @@ namespace DFHack
namespace df
{
using DFHack::function_identity_base;
using DFHack::primitive_identity;
using DFHack::pointer_identity;
using DFHack::container_identity;
@ -451,44 +469,44 @@ namespace df
// Container definitions
template<class Enum, class FT>
primitive_identity *identity_traits<enum_field<Enum,FT> >::get() {
inline primitive_identity *identity_traits<enum_field<Enum,FT> >::get() {
return identity_traits<FT>::get();
}
template<class T>
pointer_identity *identity_traits<T *>::get() {
inline pointer_identity *identity_traits<T *>::get() {
static pointer_identity identity(identity_traits<T>::get());
return &identity;
}
template<class T, int sz>
container_identity *identity_traits<T [sz]>::get() {
inline container_identity *identity_traits<T [sz]>::get() {
static buffer_container_identity identity(sz, identity_traits<T>::get());
return &identity;
}
template<class T>
container_identity *identity_traits<std::vector<T> >::get() {
inline container_identity *identity_traits<std::vector<T> >::get() {
typedef std::vector<T> container;
static stl_container_identity<container> identity("vector", identity_traits<T>::get());
return &identity;
}
template<class T>
stl_ptr_vector_identity *identity_traits<std::vector<T*> >::get() {
inline stl_ptr_vector_identity *identity_traits<std::vector<T*> >::get() {
static stl_ptr_vector_identity identity(identity_traits<T>::get());
return &identity;
}
template<class T>
container_identity *identity_traits<std::deque<T> >::get() {
inline container_identity *identity_traits<std::deque<T> >::get() {
typedef std::deque<T> container;
static stl_container_identity<container> identity("deque", identity_traits<T>::get());
return &identity;
}
template<class T>
bit_container_identity *identity_traits<BitArray<T> >::get() {
inline bit_container_identity *identity_traits<BitArray<T> >::get() {
static type_identity *eid = identity_traits<T>::get();
static enum_identity *reid = eid->type() == DFHack::IDTYPE_ENUM ? (enum_identity*)eid : NULL;
static bit_array_identity identity(reid);
@ -496,7 +514,7 @@ namespace df
}
template<class T>
container_identity *identity_traits<DfArray<T> >::get() {
inline container_identity *identity_traits<DfArray<T> >::get() {
typedef DfArray<T> container;
static stl_container_identity<container> identity("DfArray", identity_traits<T>::get());
return &identity;

@ -1 +1 @@
Subproject commit 55a120d5bc823b783aeb8b6931ccaa31402fd850
Subproject commit a1d71773ef127010391f795337184c56146c8dda