@@ -5,6 +5,7 @@ See the source code for details.
55*/
66module luad.conversions.classes ;
77
8+ import luad.conversions.helpers;
89import luad.conversions.functions;
910
1011import luad.c.all;
@@ -14,94 +15,119 @@ import luad.base;
1415import core.memory ;
1516
1617import std.traits ;
17- import std.string : toStringz;
18+ import std.typetuple ;
19+ import std.typecons ;
1820
19- extern (C ) private int classCleaner(lua_State* L)
20- {
21- GC .removeRoot(lua_touserdata(L, 1 ));
22- return 0 ;
23- }
2421
25- private void pushMeta (T)(lua_State* L, T obj )
22+ private void pushGetters (T)(lua_State* L)
2623{
27- if (luaL_newmetatable(L, T.mangleof.ptr) == 0 )
28- return ;
24+ lua_newtable(L); // -2 is getters
25+ lua_newtable(L); // -1 is methods
2926
30- pushValue(L, T.stringof);
31- lua_setfield(L, - 2 , " __dclass" );
27+ // populate getters
28+ foreach (member; __traits (derivedMembers, T))
29+ {
30+ static if (! skipMember! (T, member) &&
31+ ! isStaticMember! (T, member) &&
32+ member != " Monitor" )
33+ {
34+ static if (isMemberFunction! (T, member) && ! isProperty! (T, member))
35+ {
36+ static if (canCall! (T, member))
37+ {
38+ pushMethod! (T, member)(L);
39+ lua_setfield(L, - 2 , member.ptr);
40+ }
41+ }
42+ else static if (canRead! (T, member)) // TODO: move into the getter for inaccessable fields (...and throw a useful error messasge)
43+ {
44+ pushGetter! (T, member)(L);
45+ lua_setfield(L, - 3 , member.ptr);
46+ }
47+ }
48+ }
3249
33- pushValue (L, T.mangleof );
34- lua_setfield(L, - 2 , " __dmangle " );
50+ lua_pushcclosure (L, &index, 2 );
51+ }
3552
36- lua_newtable(L); // __index fallback table
53+ private void pushSetters (T)(lua_State* L)
54+ {
55+ lua_newtable(L);
3756
57+ // populate setters
3858 foreach (member; __traits (derivedMembers, T))
3959 {
40- static if (__traits(getProtection, __traits(getMember, T, member)) == " public" && // ignore non-public fields
41- member != " this" && member != " __ctor" && // do not handle
42- member != " Monitor" && member != " toHash" && // do not handle
43- member != " toString" && member != " opEquals" && // handle below
44- member != " opCmp" ) // handle below
60+ static if (! skipMember! (T, member) &&
61+ ! isStaticMember! (T, member) &&
62+ canWrite! (T, member) && // TODO: move into the setter for readonly fields (...and throw a useful error messasge)
63+ member != " Monitor" )
4564 {
46- static if (__traits(getOverloads, T.init , member).length > 0 && ! __traits(isStaticFunction, mixin ( " T. " ~ member) ))
65+ static if (! isMemberFunction ! (T , member) || isProperty ! (T, member))
4766 {
48- pushMethod ! (T, member)(L);
49- lua_setfield(L, - 2 , toStringz( member) );
67+ pushSetter ! (T, member)(L);
68+ lua_setfield(L, - 2 , member.ptr );
5069 }
5170 }
5271 }
5372
54- lua_setfield(L, - 2 , " __index" );
55-
56- pushMethod! (T, " toString" )(L);
57- lua_setfield(L, - 2 , " __tostring" );
73+ lua_pushcclosure(L, &newIndex, 1 );
74+ }
5875
59- pushMethod! (T, " opEquals" )(L);
60- lua_setfield(L, - 2 , " __eq" );
76+ private void pushMeta (T)(lua_State* L)
77+ {
78+ if (luaL_newmetatable(L, T.mangleof.ptr) == 0 )
79+ return ;
6180
62- // TODO: handle opCmp here
81+ pushValue(L, T.stringof);
82+ lua_setfield(L, - 2 , " __dtype" );
6383
84+ // TODO: mangled names can get REALLY long in D, it might be nicer to store a hash instead?
85+ pushValue(L, T.mangleof);
86+ lua_setfield(L, - 2 , " __dmangle" );
6487
65- lua_pushcfunction(L, &classCleaner );
88+ lua_pushcfunction(L, &userdataCleaner );
6689 lua_setfield(L, - 2 , " __gc" );
6790
91+ pushGetters! T(L);
92+ lua_setfield(L, - 2 , " __index" );
93+ pushSetters! T(L);
94+ lua_setfield(L, - 2 , " __newindex" );
95+
96+ // TODO: look into why we can't call these on const objects... that's insane, right?
97+ static if (canCall! (T, " toString" ))
98+ {
99+ pushMethod! (T, " toString" )(L);
100+ lua_setfield(L, - 2 , " __tostring" );
101+ }
102+ static if (canCall! (T, " opEquals" ))
103+ {
104+ pushMethod! (T, " opEquals" )(L);
105+ lua_setfield(L, - 2 , " __eq" );
106+ }
107+
108+ // TODO: handle opCmp here
109+
110+ // TODO: operators, etc...
111+
68112 lua_pushvalue(L, - 1 );
69113 lua_setfield(L, - 2 , " __metatable" );
70114}
71115
72116void pushClassInstance (T)(lua_State* L, T obj) if (is (T == class ))
73117{
74- T* ud = cast (T* )lua_newuserdata(L, obj.sizeof);
118+ Rebindable ! T* ud = cast (Rebindable ! T* )lua_newuserdata(L, obj.sizeof);
75119 * ud = obj;
76120
77- pushMeta(L, obj);
78- lua_setmetatable(L, - 2 );
79-
80121 GC .addRoot(ud);
122+
123+ pushMeta! T(L);
124+ lua_setmetatable(L, - 2 );
81125}
82126
83- // TODO: handle foreign userdata properly (i.e. raise errors)
84127T getClassInstance (T)(lua_State* L, int idx) if (is (T == class ))
85128{
86- if (lua_getmetatable(L, idx) == 0 )
87- {
88- luaL_error(L, " attempt to get 'userdata: %p' as a D object" , lua_topointer(L, idx));
89- }
90-
91- lua_getfield(L, - 1 , " __dmangle" ); // must be a D object
92-
93- static if (! is (T == Object )) // must be the right object
94- {
95- size_t manglelen;
96- auto cmangle = lua_tolstring(L, - 1 , &manglelen);
97- if (cmangle[0 .. manglelen] != T.mangleof)
98- {
99- lua_getfield(L, - 2 , " __dclass" );
100- auto cname = lua_tostring(L, - 1 );
101- luaL_error(L, ` attempt to get instance %s as type "%s"` , cname, toStringz(T.stringof));
102- }
103- }
104- lua_pop(L, 2 ); // metatable and metatable.__dmangle
129+ // TODO: handle foreign userdata properly (i.e. raise errors)
130+ verifyType! T(L, idx);
105131
106132 Object obj = * cast (Object * )lua_touserdata(L, idx);
107133 return cast (T)obj;
@@ -112,36 +138,42 @@ template hasCtor(T)
112138 enum hasCtor = __traits(compiles, __traits(getOverloads, T.init, " __ctor" ));
113139}
114140
115- // TODO: exclude private members (I smell DMD bugs...)
116- template isStaticMember (T, string member )
141+ // For use as __call
142+ void pushCallMetaConstructor (T)(lua_State * L )
117143{
118- static if (__traits(compiles, mixin ( " &T. " ~ member)) )
144+ static if (! hasCtor ! T )
119145 {
120- static if (is (typeof (mixin (" &T.init." ~ member)) == delegate ))
121- enum isStaticMember = __traits(isStaticFunction, mixin (" T." ~ member));
122- else
123- enum isStaticMember = true ;
146+ static T ctor (LuaObject self)
147+ {
148+ static if (is (T == class ))
149+ return new T;
150+ else
151+ return T.init;
152+ }
124153 }
125154 else
126- enum isStaticMember = false ;
127- }
128-
129- // For use as __call
130- void pushCallMetaConstructor (T)(lua_State* L)
131- {
132- alias typeof (__traits(getOverloads, T.init, " __ctor" )) Ctor;
133-
134- static T ctor (LuaObject self, ParameterTypeTuple! Ctor args)
135155 {
136- return new T(args);
156+ // TODO: handle each constructor overload in a loop.
157+ // TODO: handle each combination of default args
158+ alias Ctors = typeof (__traits(getOverloads, T.init, " __ctor" ));
159+ alias Args = ParameterTypeTuple! (Ctors[0 ]);
160+
161+ static T ctor (LuaObject self, Args args)
162+ {
163+ static if (is (T == class ))
164+ return new T(args);
165+ else
166+ return T (args);
167+ }
137168 }
138169
139170 pushFunction(L, &ctor);
171+ lua_setfield(L, - 2 , " __call" );
140172}
141173
142174// TODO: Private static fields are mysteriously pushed without error...
143175// TODO: __index should be a function querying the static fields directly
144- void pushStaticTypeInterface (T)(lua_State* L)
176+ void pushStaticTypeInterface (T)(lua_State* L) if ( is (T == class ) || is (T == struct ))
145177{
146178 lua_newtable(L);
147179
@@ -152,11 +184,7 @@ void pushStaticTypeInterface(T)(lua_State* L)
152184 return ;
153185 }
154186
155- static if (hasCtor! T)
156- {
157- pushCallMetaConstructor! T(L);
158- lua_setfield(L, - 2 , " __call" );
159- }
187+ pushCallMetaConstructor! T(L);
160188
161189 lua_newtable(L);
162190
@@ -165,7 +193,12 @@ void pushStaticTypeInterface(T)(lua_State* L)
165193 static if (isStaticMember! (T, member))
166194 {
167195 enum isFunction = is (typeof (mixin (" T." ~ member)) == function );
196+ static if (isFunction)
197+ enum isProperty = (functionAttributes! (mixin (" T." ~ member)) & FunctionAttribute.property);
198+ else
199+ enum isProperty = false ;
168200
201+ // TODO: support static properties
169202 static if (isFunction)
170203 pushValue(L, mixin (" &T." ~ member));
171204 else
0 commit comments