Skip to content

Commit 8fa8982

Browse files
author
nitrocaster
committed
Implement script bingings dumper.
1 parent 77ed999 commit 8fa8982

File tree

6 files changed

+511
-36
lines changed

6 files changed

+511
-36
lines changed
Lines changed: 397 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,397 @@
1+
#include "pch.hpp"
2+
#include "BindingsDumper.hpp"
3+
#include <regex>
4+
5+
int BindingsDumper::GetIdentSize() const
6+
{ return options.ShiftWidth*shiftLevel; }
7+
8+
void BindingsDumper::Print(const char *s)
9+
{ writer->w(s, xr_strlen(s)); }
10+
11+
void BindingsDumper::Print(const char *s, int len)
12+
{ writer->w(s, len); }
13+
14+
void BindingsDumper::Printf(const char *format, ...)
15+
{
16+
va_list args;
17+
va_start(args, format);
18+
writer->VPrintf(format, args);
19+
va_end(args);
20+
}
21+
22+
void BindingsDumper::PrintIndented(const char *s)
23+
{ writer->w_printf("%*s" "%s", GetIdentSize(), "", s); }
24+
25+
void BindingsDumper::PrintfIndented(const char *format, ...)
26+
{
27+
writer->w_printf("%*s", GetIdentSize(), "");
28+
va_list args;
29+
va_start(args, format);
30+
writer->VPrintf(format, args);
31+
va_end(args);
32+
}
33+
34+
namespace
35+
{
36+
luabind::detail::function_object* get_upvalue_function(lua_State *ls, int n)
37+
{
38+
using namespace luabind::detail;
39+
function_object *f = nullptr;
40+
if (lua_getupvalue(ls, -1, n))
41+
{
42+
int ltype = lua_type(ls, -1);
43+
if (ltype==LUA_TFUNCTION)
44+
{
45+
if (lua_getupvalue(ls, -1, 1))
46+
{
47+
if (lua_type(ls, -1)==LUA_TUSERDATA)
48+
f = *static_cast<function_object**>(lua_touserdata(ls, -1));
49+
lua_pop(ls, 1); // upvalue
50+
}
51+
}
52+
lua_pop(ls, 1); // upvalue
53+
}
54+
return f;
55+
}
56+
57+
void ReplaceArg(lua_State *ls, int argIndex, const char *subst)
58+
{
59+
lua_pushstring(ls, subst);
60+
lua_replace(ls, argIndex-1);
61+
}
62+
63+
int StripArg(lua_State *ls, int argIndex, bool commaOnly = true)
64+
{
65+
int removed = 1;
66+
lua_remove(ls, argIndex);
67+
const char *comma = lua_tostring(ls, argIndex+1);
68+
if (!commaOnly || !xr_strcmp(comma, ","))
69+
{
70+
lua_remove(ls, argIndex+1);
71+
removed++;
72+
}
73+
return removed;
74+
}
75+
}
76+
77+
void BindingsDumper::PrintFunction(SignatureFormatter formatter, const void *fcontext /*= nullptr*/)
78+
{
79+
using namespace luabind::detail;
80+
bool done = false;
81+
int cfunc = lua_iscfunction(ls, -1);
82+
int luabindFunc = is_luabind_function(ls, -1);
83+
bool hasupvalue = lua_getupvalue(ls, -1, 1)!=nullptr;
84+
if (hasupvalue)
85+
{
86+
if (luabindFunc)
87+
{
88+
if (lua_type(ls, -1)==LUA_TUSERDATA)
89+
{
90+
auto fobj = *static_cast<function_object**>(lua_touserdata(ls, -1));
91+
if (formatter)
92+
{
93+
SignatureFormatterParams params;
94+
params.Function = fobj;
95+
params.Context = fcontext;
96+
(this->*formatter)(params);
97+
}
98+
else
99+
{
100+
int signatureLen = fobj->format_signature(ls, fobj->name.c_str());
101+
auto signature = lua_tostring(ls, -1);
102+
PrintfIndented("%s;\n", signature);
103+
lua_pop(ls, signatureLen);
104+
}
105+
done = true;
106+
}
107+
}
108+
lua_pop(ls, 1); // pop upvalue
109+
}
110+
if (cfunc && !done && hasupvalue) // property
111+
{
112+
const char *propName = lua_tostring(ls, -2);
113+
function_object *getter = get_upvalue_function(ls, 1);
114+
function_object *setter = get_upvalue_function(ls, 2);
115+
R_ASSERT(getter);
116+
int signatureLen = getter->format_signature(ls, "#");
117+
const char *signature = lua_tostring(ls, -1);
118+
int typeLen = std::strchr(signature, '#')-signature-1;
119+
PrintIndented("");
120+
Print(signature, typeLen);
121+
Printf(" %s { get;", propName);
122+
if (setter)
123+
Print(" set;");
124+
Print(" }\n");
125+
lua_pop(ls, signatureLen);
126+
}
127+
}
128+
129+
void BindingsDumper::FormatStaticFunction(const SignatureFormatterParams &params)
130+
{
131+
using namespace luabind::detail;
132+
function_object *fobj = params.Function;
133+
int signatureLen = fobj->format_signature(ls, fobj->name.c_str());
134+
const char *signature = lua_tostring(ls, -1);
135+
PrintfIndented("static %s;\n", signature);
136+
lua_pop(ls, signatureLen);
137+
};
138+
139+
void BindingsDumper::PrintStaticFunction()
140+
{ PrintFunction(&BindingsDumper::FormatStaticFunction); }
141+
142+
void BindingsDumper::FormatMemberFunction(const SignatureFormatterParams &params)
143+
{
144+
auto refClassName = static_cast<const char *>(params.Context);
145+
bool stripReturnValue = false; // for constructors and operators
146+
xr_string funcName;
147+
auto refFuncName = params.Function->name;
148+
if (refFuncName=="__init")
149+
{
150+
funcName = refClassName;
151+
stripReturnValue = true;
152+
}
153+
else // check operators
154+
{
155+
auto it = operatorSubst.find(refFuncName);
156+
if (it!=operatorSubst.end())
157+
{
158+
funcName = it->second;
159+
stripReturnValue = true;
160+
}
161+
else
162+
funcName = refFuncName.c_str();
163+
}
164+
bool concat = !(options.IgnoreDerived || options.StripThis || stripReturnValue);
165+
int signLen = params.Function->format_signature(ls, funcName.c_str(), concat);
166+
if (!concat)
167+
{
168+
int argCount = (signLen-4)/2;
169+
R_ASSERT(argCount>0);
170+
// -n+0 -n+1 -n+2 -n+3 -n+4 -1
171+
// [return_type][ ][func_name][(][arg1][,][arg2]...[)]
172+
int offset = 0;
173+
if (stripReturnValue)
174+
{
175+
offset = StripArg(ls, -signLen, false);
176+
signLen -= offset;
177+
}
178+
int argIndex = -signLen+4-offset;
179+
xr_string arg = lua_tostring(ls, argIndex);
180+
xr_string className = refClassName;
181+
// check if arg matches 'className[ const]{*|&}'
182+
std::regex matcher(className+"( const)?(\\*|&)$");
183+
if (std::regex_match(arg, matcher)) // non-derived member function
184+
{
185+
if (options.StripThis)
186+
signLen -= StripArg(ls, argIndex);
187+
}
188+
else
189+
{
190+
// check special cases: opertators and constructors
191+
// void __tostring(lua_State*, ClassName&); // operator
192+
// void __init(luabind::argument const&, int); // constructor
193+
if (arg=="lua_State*" && argCount>1) // operator?
194+
{
195+
// 1] check next argument:
196+
int nextArgIndex = argIndex+2;
197+
const char *nextArg = lua_tostring(ls, nextArgIndex);
198+
if (className.append("&")!=nextArg) // derived?
199+
{
200+
// if next!=className && ignoreDerived => ignore
201+
if (options.IgnoreDerived)
202+
{
203+
lua_pop(ls, signLen);
204+
return;
205+
}
206+
}
207+
// 2] remove arg+comma
208+
signLen -= StripArg(ls, argIndex);
209+
// 3] if stripThis => remove next
210+
argIndex = nextArgIndex;
211+
}
212+
else if (arg=="luabind::argument const&") // constructor?
213+
{
214+
if (!options.StripThis)
215+
ReplaceArg(ls, argIndex, className.append("&").c_str());
216+
}
217+
else if (options.IgnoreDerived) // some derived function, ignore?
218+
{
219+
lua_pop(ls, signLen);
220+
return;
221+
}
222+
if (options.StripThis)
223+
signLen -= StripArg(ls, argIndex);
224+
}
225+
lua_concat(ls, signLen);
226+
}
227+
const char *signature = lua_tostring(ls, -1);
228+
PrintfIndented("%s;\n", signature);
229+
lua_pop(ls, 1); // pop concatenated signature
230+
};
231+
232+
void BindingsDumper::PrintMemberFunction(const char *className)
233+
{ PrintFunction(&BindingsDumper::FormatMemberFunction, className); }
234+
235+
void BindingsDumper::PrintFunction()
236+
{ PrintFunction(nullptr); }
237+
238+
void BindingsDumper::PrintIntConstant(const char *name, int value)
239+
{ PrintfIndented("const int %s = %d;\n", name, value); }
240+
241+
void BindingsDumper::PrintClass()
242+
{
243+
using namespace luabind;
244+
using namespace luabind::detail;
245+
auto crep = static_cast<class_rep*>(lua_touserdata(ls, -1));
246+
bool cppClass = crep->get_class_type()==class_rep::cpp_class;
247+
PrintIndented(cppClass ? "[cpp]\n" : "[lua]\n");
248+
PrintfIndented("class %s", crep->name());
249+
const auto &bases = crep->bases();
250+
size_t baseCount = bases.size();
251+
if (baseCount)
252+
Print(" : ");
253+
for (u32 i = 0; i<baseCount; i++)
254+
{
255+
if (i)
256+
Print(", ");
257+
const char *baseName = bases[i].base->name();
258+
if (!*baseName)
259+
baseName = "<unknown>";
260+
Print(baseName);
261+
}
262+
Print("\n");
263+
PrintIndented("{\n");
264+
shiftLevel++;
265+
// print static members (static functions + nested classes)
266+
crep->get_default_table(ls);
267+
object staticMembers(from_stack(ls, -1));
268+
for (luabind::iterator it(staticMembers), end; it!=end; it++)
269+
{
270+
auto proxy = *it;
271+
int prev = lua_gettop(ls);
272+
proxy.push(ls);
273+
if (is_class_rep(ls, -1))
274+
PrintClass();
275+
else if (is_luabind_function(ls, -1, false))
276+
PrintStaticFunction();
277+
lua_pop(ls, 1);
278+
R_ASSERT(lua_gettop(ls)==prev);
279+
}
280+
lua_pop(ls, 1); // pop default table
281+
// print constants
282+
auto &constants = crep->static_constants();
283+
for (auto &c : constants)
284+
PrintIntConstant(c.first, c.second);
285+
// print functions and properties
286+
crep->get_table(ls);
287+
object members(from_stack(ls, -1));
288+
for (luabind::iterator it(members), end; it!=end; it++)
289+
{
290+
auto proxy = *it;
291+
int prev = lua_gettop(ls);
292+
proxy.push(ls);
293+
int ltype = luabind::type(proxy);
294+
if (ltype==LUA_TFUNCTION) // XXX: print class members in reverse order
295+
PrintMemberFunction(crep->name());
296+
lua_pop(ls, 1);
297+
R_ASSERT(lua_gettop(ls)==prev);
298+
}
299+
lua_pop(ls, 1); // pop table
300+
shiftLevel--;
301+
PrintIndented("}\n");
302+
}
303+
304+
void BindingsDumper::PrintNamespace(luabind::object &namesp)
305+
{
306+
using namespace luabind;
307+
using namespace luabind::detail;
308+
int scopeFunctions = 0, scopeClasses = 0, scopeNamespaces = 0;
309+
for (luabind::iterator it(namesp), end; it!=end; it++)
310+
{
311+
auto proxy = *it;
312+
int ltype = luabind::type(proxy);
313+
switch (ltype)
314+
{
315+
case LUA_TFUNCTION: // free function
316+
scopeFunctions++;
317+
functions.push(it);
318+
break;
319+
case LUA_TUSERDATA: // class
320+
scopeClasses++;
321+
classes.push(it);
322+
break;
323+
case LUA_TTABLE: // namespace
324+
scopeNamespaces++;
325+
namespaces.push(it);
326+
break;
327+
default:
328+
PrintfIndented("[?] ltype = %s\n", lua_typename(ls, ltype));
329+
break;
330+
}
331+
}
332+
for (int i = 0; i<scopeFunctions; i++)
333+
{
334+
auto proxy = *functions.top();
335+
functions.pop();
336+
proxy.push(ls);
337+
PrintFunction();
338+
lua_pop(ls, 1);
339+
}
340+
for (int i = 0; i<scopeClasses; i++)
341+
{
342+
auto proxy = *classes.top();
343+
classes.pop();
344+
proxy.push(ls);
345+
if (is_class_rep(ls, -1))
346+
PrintClass();
347+
lua_pop(ls, 1);
348+
}
349+
for (int i = 0; i<scopeNamespaces; i++)
350+
{
351+
auto proxy = *namespaces.top();
352+
namespaces.pop();
353+
proxy.push(ls);
354+
object innerNamesp(from_stack(ls, -1));
355+
auto namespaceName = lua_tostring(ls, -2);
356+
PrintfIndented("namespace %s\n", namespaceName);
357+
PrintIndented("{\n");
358+
shiftLevel++;
359+
PrintNamespace(innerNamesp);
360+
shiftLevel--;
361+
PrintIndented("}\n");
362+
lua_pop(ls, 1);
363+
}
364+
}
365+
366+
BindingsDumper::BindingsDumper()
367+
{
368+
std::pair<const char *, const char *> subst[] =
369+
{
370+
{"__add", "operator+"},
371+
{"__sub", "operator-"},
372+
{"__mul", "operator*"},
373+
{"__div", "operator/"},
374+
{"__pow", "operator^"},
375+
{"__lt", "operator<"},
376+
{"__le", "operator<="},
377+
{"__gt", "operator>"},
378+
{"__ge", "operator>="},
379+
{"__eq", "operator=="},
380+
{"__tostring", "operator string"}
381+
};
382+
const u32 substCount = sizeof(subst)/sizeof(*subst);
383+
for (u32 i = 0; i<substCount; i++)
384+
operatorSubst.insert(subst[i]);
385+
}
386+
387+
void BindingsDumper::Dump(lua_State *luaState, IWriter *outStream, const Options &opt)
388+
{
389+
ls = luaState;
390+
options = opt;
391+
shiftLevel = 0;
392+
writer = outStream;
393+
luabind::set_custom_type_marking(false);
394+
luabind::object globals = luabind::globals(ls);
395+
PrintNamespace(globals);
396+
luabind::set_custom_type_marking(true);
397+
}

0 commit comments

Comments
 (0)