Skip to content

Commit

Permalink
Massive refactor to support ref structs.
Browse files Browse the repository at this point in the history
This was required because Lua semantics are always by reference.
With the original struct-by-value implementation, it was impossible to do something like "myStruct.subStruct.i = 10", because, subStruct would be copy.
  • Loading branch information
TurkeyMan committed Aug 20, 2014
1 parent 94e1a42 commit 8b01fc6
Show file tree
Hide file tree
Showing 5 changed files with 400 additions and 269 deletions.
135 changes: 65 additions & 70 deletions luad/conversions/classes.d
Original file line number Diff line number Diff line change
Expand Up @@ -17,50 +17,32 @@ import core.memory;
import std.traits;
import std.typetuple;

extern(C) private int classCleaner(lua_State* L)
{
GC.removeRoot(lua_touserdata(L, 1));
return 0;
}

private extern(C) int indexClass(lua_State* L)
{
auto field = lua_tostring(L, 2);

// check the getter table
lua_getfield(L, lua_upvalueindex(1), field);
if(!lua_isnil(L, -1))
{
lua_pushvalue(L, 1);
lua_call(L, 1, LUA_MULTRET);
return lua_gettop(L) - 2;
}
else
lua_pop(L, 1);

// return method
lua_getfield(L, lua_upvalueindex(2), field);
return 1;
}

private extern(C) int newIndexClass(lua_State* L)
void pushGetter(T, string member)(lua_State* L)
{
auto field = lua_tostring(L, 2);

// call setter
lua_getfield(L, lua_upvalueindex(1), field);
if(!lua_isnil(L, -1))
alias RT = typeof(mixin("T."~member));
final class C
{
lua_pushvalue(L, 1);
lua_pushvalue(L, 3);
lua_call(L, 2, LUA_MULTRET);
}
else
{
// TODO: error?
static if((!isMemberFunction!(T, member) || returnsRef!(AliasMember!(T, member))) && isUserStruct!RT)
{
ref RT get()
{
T _this = *cast(T*)&this;
return mixin("_this."~member);
}
}
else
{
RT get()
{
T _this = *cast(T*)&this;
return mixin("_this."~member);
}
}
}

return 0;
lua_pushlightuserdata(L, (&C.init.get).funcptr);
lua_pushcclosure(L, &methodWrapper!(typeof(&C.init.get), T, false), 1);
}

private void pushGetters(T)(lua_State* L)
Expand All @@ -71,9 +53,9 @@ private void pushGetters(T)(lua_State* L)
// populate getters
foreach(member; __traits(derivedMembers, T))
{
static if(!skipField!(T, member) &&
!isStaticMember!(T, member) &&
member != "Monitor")
static if(!skipMember!(T, member) &&
!isStaticMember!(T, member) &&
member != "Monitor")
{
static if(isMemberFunction!(T, member) && !isProperty!(T, member))
{
Expand All @@ -88,7 +70,36 @@ private void pushGetters(T)(lua_State* L)
}
}

lua_pushcclosure(L, &indexClass, 2);
lua_pushcclosure(L, &index, 2);
}

void pushSetter(T, string member)(lua_State* L)
{
// TODO: This is broken if setter argument is different from the getter's return type...
alias ArgType = typeof(mixin("T."~member));

final class C
{
static if(isUserStruct!ArgType)
{
final void set(ref ArgType value)
{
T _this = *cast(T*)&this;
mixin("_this."~member) = value;
}
}
else
{
final void set(ArgType value)
{
T _this = *cast(T*)&this;
mixin("_this."~member) = value;
}
}
}

lua_pushlightuserdata(L, (&C.init.set).funcptr);
lua_pushcclosure(L, &methodWrapper!(typeof(&C.init.set), T, false), 1);
}

private void pushSetters(T)(lua_State* L)
Expand All @@ -98,10 +109,10 @@ private void pushSetters(T)(lua_State* L)
// populate setters
foreach(member; __traits(derivedMembers, T))
{
static if(!skipField!(T, member) &&
!isStaticMember!(T, member) &&
canWrite!(T, member) &&
member != "Monitor")
static if(!skipMember!(T, member) &&
!isStaticMember!(T, member) &&
canWrite!(T, member) && // TODO: move into the setter for readonly fields
member != "Monitor")
{
static if(!isMemberFunction!(T, member) || isProperty!(T, member))
{
Expand All @@ -111,7 +122,7 @@ private void pushSetters(T)(lua_State* L)
}
}

lua_pushcclosure(L, &newIndexClass, 1);
lua_pushcclosure(L, &newIndex, 1);
}

private void pushMeta(T)(lua_State* L)
Expand All @@ -120,11 +131,13 @@ private void pushMeta(T)(lua_State* L)
return;

pushValue(L, T.stringof);
lua_setfield(L, -2, "__dclass");
lua_setfield(L, -2, "__dtype");

// TODO: mangled names can get REALLY long in D, it might be nicer to store a hash instead?
pushValue(L, T.mangleof);
lua_setfield(L, -2, "__dmangle");

lua_pushcfunction(L, &classCleaner);
lua_pushcfunction(L, &userdataCleaner);
lua_setfield(L, -2, "__gc");

pushGetters!T(L);
Expand Down Expand Up @@ -157,28 +170,10 @@ void pushClassInstance(T)(lua_State* L, T obj) if (is(T == class))
lua_setmetatable(L, -2);
}

//TODO: handle foreign userdata properly (i.e. raise errors)
T getClassInstance(T)(lua_State* L, int idx) if (is(T == class))
{
if(lua_getmetatable(L, idx) == 0)
{
luaL_error(L, "attempt to get 'userdata: %p' as a D object", lua_topointer(L, idx));
}

lua_getfield(L, -1, "__dmangle"); //must be a D object

static if(!is(T == Object)) //must be the right object
{
size_t manglelen;
auto cmangle = lua_tolstring(L, -1, &manglelen);
if(cmangle[0 .. manglelen] != T.mangleof)
{
lua_getfield(L, -2, "__dclass");
auto cname = lua_tostring(L, -1);
luaL_error(L, `attempt to get instance %s as type "%s"`, cname, toStringz(T.stringof));
}
}
lua_pop(L, 2); //metatable and metatable.__dmangle
//TODO: handle foreign userdata properly (i.e. raise errors)
verifyType!T(L, idx);

Object obj = *cast(Object*)lua_touserdata(L, idx);
return cast(T)obj;
Expand Down
Loading

0 comments on commit 8b01fc6

Please sign in to comment.