@@ -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,56 +15,145 @@ import luad.base;
1415import core.memory ;
1516
1617import std.traits ;
17- import std.string : toStringz ;
18+ import std.typetuple ;
1819
19- extern (C ) private int classCleaner(lua_State* L)
20+
21+ void pushGetter (T, string member)(lua_State* L)
2022{
21- GC .removeRoot(lua_touserdata(L, 1 ));
22- return 0 ;
23+ alias RT = typeof (mixin (" T." ~ member));
24+ final class C
25+ {
26+ static if ((! isMemberFunction! (T, member) || returnsRef! (AliasMember! (T, member))) && isUserStruct! RT )
27+ {
28+ ref RT get ()
29+ {
30+ T _this = * cast (T* )&this ;
31+ return mixin (" _this." ~ member);
32+ }
33+ }
34+ else
35+ {
36+ RT get ()
37+ {
38+ T _this = * cast (T* )&this ;
39+ return mixin (" _this." ~ member);
40+ }
41+ }
42+ }
43+
44+ lua_pushlightuserdata(L, (&C.init.get ).funcptr);
45+ lua_pushcclosure(L, &methodWrapper! (typeof (&C.init.get ), T, false ), 1 );
2346}
2447
25- private void pushMeta (T)(lua_State* L, T obj )
48+ private void pushGetters (T)(lua_State* L)
2649{
27- if (luaL_newmetatable(L, T.mangleof.ptr) == 0 )
28- return ;
50+ lua_newtable(L); // -2 is getters
51+ lua_newtable(L); // -1 is methods
2952
30- pushValue(L, T.stringof);
31- lua_setfield(L, - 2 , " __dclass" );
53+ // populate getters
54+ foreach (member; __traits (derivedMembers, T))
55+ {
56+ static if (! skipMember! (T, member) &&
57+ ! isStaticMember! (T, member) &&
58+ member != " Monitor" )
59+ {
60+ static if (isMemberFunction! (T, member) && ! isProperty! (T, member))
61+ {
62+ pushMethod! (T, member)(L);
63+ lua_setfield(L, - 2 , member.ptr);
64+ }
65+ else
66+ {
67+ pushGetter! (T, member)(L);
68+ lua_setfield(L, - 3 , member.ptr);
69+ }
70+ }
71+ }
3272
33- pushValue(L, T.mangleof);
34- lua_setfield(L, - 2 , " __dmangle" );
73+ lua_pushcclosure(L, &index, 2 );
74+ }
75+
76+ void pushSetter (T, string member)(lua_State* L)
77+ {
78+ // TODO: This is broken if setter argument is different from the getter's return type...
79+ alias ArgType = typeof (mixin (" T." ~ member));
80+
81+ final class C
82+ {
83+ static if (isUserStruct! ArgType)
84+ {
85+ final void set (ref ArgType value)
86+ {
87+ T _this = * cast (T* )&this ;
88+ mixin (" _this." ~ member) = value;
89+ }
90+ }
91+ else
92+ {
93+ final void set (ArgType value)
94+ {
95+ T _this = * cast (T* )&this ;
96+ mixin (" _this." ~ member) = value;
97+ }
98+ }
99+ }
100+
101+ lua_pushlightuserdata(L, (&C.init.set).funcptr);
102+ lua_pushcclosure(L, &methodWrapper! (typeof (&C.init.set), T, false ), 1 );
103+ }
35104
36- lua_newtable(L); // __index fallback table
105+ private void pushSetters (T)(lua_State* L)
106+ {
107+ lua_newtable(L);
37108
109+ // populate setters
38110 foreach (member; __traits (derivedMembers, T))
39111 {
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
112+ static if (! skipMember! (T, member) &&
113+ ! isStaticMember! (T, member) &&
114+ canWrite! (T, member) && // TODO: move into the setter for readonly fields
115+ member != " Monitor" )
45116 {
46- static if (__traits(getOverloads, T.init , member).length > 0 && ! __traits(isStaticFunction, mixin ( " T. " ~ member) ))
117+ static if (! isMemberFunction ! (T , member) || isProperty ! (T, member))
47118 {
48- pushMethod ! (T, member)(L);
49- lua_setfield(L, - 2 , toStringz( member) );
119+ pushSetter ! (T, member)(L);
120+ lua_setfield(L, - 2 , member.ptr );
50121 }
51122 }
52123 }
53124
125+ lua_pushcclosure(L, &newIndex, 1 );
126+ }
127+
128+ private void pushMeta (T)(lua_State* L)
129+ {
130+ if (luaL_newmetatable(L, T.mangleof.ptr) == 0 )
131+ return ;
132+
133+ pushValue(L, T.stringof);
134+ lua_setfield(L, - 2 , " __dtype" );
135+
136+ // TODO: mangled names can get REALLY long in D, it might be nicer to store a hash instead?
137+ pushValue(L, T.mangleof);
138+ lua_setfield(L, - 2 , " __dmangle" );
139+
140+ lua_pushcfunction(L, &userdataCleaner);
141+ lua_setfield(L, - 2 , " __gc" );
142+
143+ pushGetters! T(L);
54144 lua_setfield(L, - 2 , " __index" );
145+ pushSetters! T(L);
146+ lua_setfield(L, - 2 , " __newindex" );
55147
56148 pushMethod! (T, " toString" )(L);
57149 lua_setfield(L, - 2 , " __tostring" );
58150
59151 pushMethod! (T, " opEquals" )(L);
60152 lua_setfield(L, - 2 , " __eq" );
61153
62- // TODO: handle opCmp here
63-
154+ // TODO: handle opCmp here
64155
65- lua_pushcfunction(L, &classCleaner);
66- lua_setfield(L, - 2 , " __gc" );
156+ // TODO: operators, etc...
67157
68158 lua_pushvalue(L, - 1 );
69159 lua_setfield(L, - 2 , " __metatable" );
@@ -74,34 +164,16 @@ void pushClassInstance(T)(lua_State* L, T obj) if (is(T == class))
74164 T* ud = cast (T* )lua_newuserdata(L, obj.sizeof);
75165 * ud = obj;
76166
77- pushMeta(L, obj);
78- lua_setmetatable(L, - 2 );
79-
80167 GC .addRoot(ud);
168+
169+ pushMeta! T(L);
170+ lua_setmetatable(L, - 2 );
81171}
82172
83- // TODO: handle foreign userdata properly (i.e. raise errors)
84173T getClassInstance (T)(lua_State* L, int idx) if (is (T == class ))
85174{
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
175+ // TODO: handle foreign userdata properly (i.e. raise errors)
176+ verifyType! T(L, idx);
105177
106178 Object obj = * cast (Object * )lua_touserdata(L, idx);
107179 return cast (T)obj;
@@ -112,36 +184,42 @@ template hasCtor(T)
112184 enum hasCtor = __traits(compiles, __traits(getOverloads, T.init, " __ctor" ));
113185}
114186
115- // TODO: exclude private members (I smell DMD bugs...)
116- template isStaticMember (T, string member )
187+ // For use as __call
188+ void pushCallMetaConstructor (T)(lua_State * L )
117189{
118- static if (__traits(compiles, mixin ( " &T. " ~ member)) )
190+ static if (! hasCtor ! T )
119191 {
120- static if (is (typeof (mixin (" &T.init." ~ member)) == delegate ))
121- enum isStaticMember = __traits(isStaticFunction, mixin (" T." ~ member));
122- else
123- enum isStaticMember = true ;
192+ static T ctor (LuaObject self)
193+ {
194+ static if (is (T == class ))
195+ return new T;
196+ else
197+ return T.init;
198+ }
124199 }
125200 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)
135201 {
136- return new T(args);
202+ // TODO: handle each constructor overload in a loop.
203+ // TODO: handle each combination of default args
204+ alias Ctors = typeof (__traits(getOverloads, T.init, " __ctor" ));
205+ alias Args = ParameterTypeTuple! (Ctors[0 ]);
206+
207+ static T ctor (LuaObject self, Args args)
208+ {
209+ static if (is (T == class ))
210+ return new T(args);
211+ else
212+ return T (args);
213+ }
137214 }
138215
139216 pushFunction(L, &ctor);
217+ lua_setfield(L, - 2 , " __call" );
140218}
141219
142220// TODO: Private static fields are mysteriously pushed without error...
143221// TODO: __index should be a function querying the static fields directly
144- void pushStaticTypeInterface (T)(lua_State* L)
222+ void pushStaticTypeInterface (T)(lua_State* L) if ( is (T == class ) || is (T == struct ))
145223{
146224 lua_newtable(L);
147225
@@ -152,11 +230,7 @@ void pushStaticTypeInterface(T)(lua_State* L)
152230 return ;
153231 }
154232
155- static if (hasCtor! T)
156- {
157- pushCallMetaConstructor! T(L);
158- lua_setfield(L, - 2 , " __call" );
159- }
233+ pushCallMetaConstructor! T(L);
160234
161235 lua_newtable(L);
162236
@@ -165,7 +239,12 @@ void pushStaticTypeInterface(T)(lua_State* L)
165239 static if (isStaticMember! (T, member))
166240 {
167241 enum isFunction = is (typeof (mixin (" T." ~ member)) == function );
242+ static if (isFunction)
243+ enum isProperty = (functionAttributes! (mixin (" T." ~ member)) & FunctionAttribute.property);
244+ else
245+ enum isProperty = false ;
168246
247+ // TODO: support static properties
169248 static if (isFunction)
170249 pushValue(L, mixin (" &T." ~ member));
171250 else
0 commit comments