1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        wxLuaDebug.cpp
3 // Purpose:     Debugging I/O functions for wxLua
4 // Author:      J. Winwood, Ray Gilbert, John Labenski
5 // Created:     May 2002
6 // Copyright:   (c) 2012 John Labenski, 2002 Lomtick Software. All rights reserved.
7 // Licence:     wxWidgets licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 #include "wx/wxprec.h"
11 
12 #ifdef __BORLANDC__
13     #pragma hdrstop
14 #endif
15 
16 #ifndef WX_PRECOMP
17     #include "wx/wx.h"
18 #endif
19 
20 #include "wxlua/debug/wxldebug.h"
21 #include "wxlua/wxlcallb.h"
22 
23 wxLuaDebugData wxNullLuaDebugData(false);
24 
25 // ----------------------------------------------------------------------------
26 // wxLuaDebugItem
27 // ----------------------------------------------------------------------------
wxLuaDebugItem(const wxString & itemKey,int itemKeyType,const wxString & itemValue,int itemValueType,const wxString & itemSource,int lua_ref,int idx,int flag)28 wxLuaDebugItem::wxLuaDebugItem(const wxString &itemKey, int itemKeyType,
29                                const wxString &itemValue, int itemValueType,
30                                const wxString &itemSource,
31                                int lua_ref, int idx, int flag)
32                    :m_itemKey(itemKey), m_itemKeyType(itemKeyType),
33                     m_itemValue(itemValue), m_itemValueType(itemValueType),
34                     m_itemSource(itemSource),
35                     m_lua_ref(lua_ref), m_index(idx), m_flag(flag)
36 {
37 }
38 
wxLuaDebugItem(const wxLuaDebugItem & dataItem)39 wxLuaDebugItem::wxLuaDebugItem(const wxLuaDebugItem &dataItem)
40                :m_itemKey(dataItem.m_itemKey), m_itemKeyType(dataItem.m_itemKeyType),
41                 m_itemValue(dataItem.m_itemValue), m_itemValueType(dataItem.m_itemValueType),
42                 m_itemSource(dataItem.m_itemSource),
43                 m_lua_ref(dataItem.m_lua_ref), m_index(dataItem.m_index),
44                 m_flag(dataItem.m_flag)
45 {
46 }
47 
GetRefPtr(wxUIntPtr & ptr) const48 bool wxLuaDebugItem::GetRefPtr(wxUIntPtr& ptr) const
49 {
50     bool key_ref = GetFlagBit(WXLUA_DEBUGITEM_KEY_REF);
51     bool val_ref = GetFlagBit(WXLUA_DEBUGITEM_VALUE_REF);
52 
53     // sanity checks
54     wxCHECK_MSG((key_ref || val_ref), false, wxT("wxLuaDebugItem has neither key or value reference"));
55     wxCHECK_MSG(!(key_ref && val_ref), false, wxT("wxLuaDebugItem has both key and value reference"));
56 
57     return wxString(key_ref ? m_itemKey: m_itemValue).BeforeFirst(wxT(' ')).ToULongLong((wxULongLong_t*)&ptr, 16);
58 }
59 
60 // ----------------------------------------------------------------------------
61 // wxLuaDebugData - Debug Info sent via socket to debugger client
62 // ----------------------------------------------------------------------------
63 
64 class wxLuaDebugDataRefData : public wxObjectRefData
65 {
66 public:
wxLuaDebugDataRefData()67     wxLuaDebugDataRefData() : m_dataArray(wxLuaDebugData::SortFunction) {}
68 
~wxLuaDebugDataRefData()69     virtual ~wxLuaDebugDataRefData()
70     {
71         size_t idx, count = m_dataArray.GetCount();
72         for (idx = 0; idx < count; ++idx)
73         {
74             const wxLuaDebugItem *pData = m_dataArray.Item(idx);
75             delete pData;
76         }
77     }
78 
79     wxLuaDebugItemArray m_dataArray;
80 };
81 
82 #define M_DEBUGREFDATA ((wxLuaDebugDataRefData*)m_refData)
83 
wxLuaDebugData(bool create)84 wxLuaDebugData::wxLuaDebugData(bool create) : wxObject()
85 {
86     if (create)
87         m_refData = new wxLuaDebugDataRefData;
88 }
89 
GetArray()90 wxLuaDebugItemArray* wxLuaDebugData::GetArray()
91 {
92     wxCHECK_MSG(M_DEBUGREFDATA != NULL, NULL, wxT("Invalid ref data"));
93     return &(M_DEBUGREFDATA->m_dataArray);
94 }
GetArray() const95 const wxLuaDebugItemArray* wxLuaDebugData::GetArray() const
96 {
97     wxCHECK_MSG(M_DEBUGREFDATA != NULL, NULL, wxT("Invalid ref data"));
98     return &(M_DEBUGREFDATA->m_dataArray);
99 }
100 
GetCount() const101 size_t wxLuaDebugData::GetCount() const
102 {
103     wxCHECK_MSG(M_DEBUGREFDATA != NULL, 0, wxT("Invalid ref data"));
104     return M_DEBUGREFDATA->m_dataArray.GetCount();
105 }
Item(size_t index) const106 wxLuaDebugItem* wxLuaDebugData::Item(size_t index) const
107 {
108     wxCHECK_MSG(M_DEBUGREFDATA != NULL, NULL, wxT("Invalid ref data"));
109     return M_DEBUGREFDATA->m_dataArray.Item(index);
110 }
Add(wxLuaDebugItem * item)111 void wxLuaDebugData::Add(wxLuaDebugItem* item)
112 {
113     wxCHECK_RET(M_DEBUGREFDATA != NULL, wxT("Invalid ref data"));
114     wxCHECK_RET(item != NULL, wxT("Invalid wxLuaDebugItem"));
115     M_DEBUGREFDATA->m_dataArray.Add(item);
116 }
117 
Copy() const118 wxLuaDebugData wxLuaDebugData::Copy() const
119 {
120     wxCHECK_MSG(M_DEBUGREFDATA != NULL, wxNullLuaDebugData, wxT("Invalid ref data"));
121 
122     wxLuaDebugData copyData(true);
123 
124     size_t idx, count = GetCount();
125     for (idx = 0; idx < count; ++idx)
126     {
127         const wxLuaDebugItem *pOldData = M_DEBUGREFDATA->m_dataArray.Item(idx);
128         if (pOldData != NULL)
129             copyData.Add(new wxLuaDebugItem(*pOldData));
130     }
131 
132     return copyData;
133 }
134 
SortFunction(wxLuaDebugItem * elem1,wxLuaDebugItem * elem2)135 int wxLuaDebugData::SortFunction(wxLuaDebugItem *elem1, wxLuaDebugItem *elem2 )
136 {
137     int ret = 0;
138 
139     long l1 = 0, l2 = 0;
140 
141     // Don't sort numbers by their string representation, but by their value
142     if ((elem1->m_itemKeyType == WXLUA_TNUMBER) &&
143         (elem2->m_itemKeyType == WXLUA_TNUMBER) &&
144         elem1->m_itemKey.BeforeFirst(wxT(' ')).ToLong(&l1) &&
145         elem2->m_itemKey.BeforeFirst(wxT(' ')).ToLong(&l2))
146         ret = l1 - l2;
147     else
148         ret = elem1->m_itemKey.Cmp(elem2->m_itemKey);
149 
150     if (ret == 0) // can be true for unnamed "(*temporary)" vars
151     {
152         ret = elem1->m_itemKeyType - elem2->m_itemKeyType;
153 
154         if (ret == 0)
155         {
156             ret = elem1->m_itemValueType - elem2->m_itemValueType;
157 
158             if (ret == 0)
159             {
160                 ret = elem1->m_itemValue.Cmp(elem2->m_itemValue);
161 
162                 if (ret == 0)
163                     ret = int(elem2->GetFlagBit(WXLUA_DEBUGITEM_KEY_REF)) -
164                           int(elem1->GetFlagBit(WXLUA_DEBUGITEM_KEY_REF));
165             }
166         }
167     }
168 
169     return ret;
170 }
171 
EnumerateStack(lua_State * L)172 int wxLuaDebugData::EnumerateStack(lua_State* L)
173 {
174     wxCHECK_MSG(L, 0, wxT("Invalid lua_State"));
175     wxCHECK_MSG(M_DEBUGREFDATA != NULL, 0, wxT("Invalid ref data"));
176 
177     lua_Debug luaDebug = INIT_LUA_DEBUG;
178     int       stack_frame = 0;
179     int       count = 0;
180 
181     while (lua_getstack(L, stack_frame, &luaDebug) != 0)
182     {
183         if (lua_getinfo(L, "Sln", &luaDebug))
184         {
185             //wxPrintf(wxT("%s\n"), lua_Debug_to_wxString(luaDebug).c_str());
186 
187             // skip stack frames that do not have line number, always add first
188             int  currentLine = luaDebug.currentline;
189             if ((count == 0) || (currentLine != -1))
190             {
191                 wxString name;
192                 wxString source(lua2wx(luaDebug.source));
193 
194                 if (currentLine == -1)
195                     currentLine = 0;
196 
197                 if (luaDebug.name != NULL)
198                     name.Printf(_("function %s line %d"), lua2wx(luaDebug.name).c_str(), currentLine);
199                 else
200                     name.Printf(_("line %d"), currentLine);
201 
202                 Add(new wxLuaDebugItem(name, WXLUA_TNONE, wxEmptyString, WXLUA_TNONE, source, LUA_NOREF, stack_frame, WXLUA_DEBUGITEM_LOCALS));
203                 ++count;
204             }
205         }
206 
207         ++stack_frame;
208     }
209 
210     return count;
211 }
212 
EnumerateStackEntry(lua_State * L,int stack_frame,wxArrayInt & references)213 int wxLuaDebugData::EnumerateStackEntry(lua_State* L, int stack_frame, wxArrayInt& references)
214 {
215     wxCHECK_MSG(L, 0, wxT("Invalid lua_State"));
216     wxCHECK_MSG(M_DEBUGREFDATA != NULL, 0, wxT("Invalid ref data"));
217 
218     lua_Debug luaDebug = INIT_LUA_DEBUG;
219     int count = 0;
220 
221     if (lua_getstack(L, stack_frame, &luaDebug) != 0)
222     {
223         int stack_idx  = 1;
224         wxString name(lua2wx(lua_getlocal(L, &luaDebug, stack_idx)));
225 
226         while (!name.IsEmpty())
227         {
228             //wxPrintf(wxT("%s lua_getlocal :%s\n"), lua_Debug_to_wxString(luaDebug).c_str(), name.c_str());
229 
230             int wxl_valuetype = WXLUA_TNONE;
231             wxString value;
232             wxString source(lua2wx(luaDebug.source));
233 
234             int lua_value_type = GetTypeValue(L, -1, &wxl_valuetype, value);
235 
236             int val_flag_type = 0;
237             int val_ref = LUA_NOREF;
238 
239             if (lua_value_type == LUA_TTABLE)
240             {
241                 val_ref = RefTable(L, -1, &val_flag_type, WXLUA_DEBUGITEM_VALUE_REF, references);
242             }
243             else if (lua_value_type == LUA_TUSERDATA)
244             {
245                 if (lua_getmetatable(L, -1)) // doesn't push anything if nil
246                 {
247                     val_ref = RefTable(L, -1, &val_flag_type, WXLUA_DEBUGITEM_VALUE_REF, references);
248                     lua_pop(L, 1);
249                 }
250             }
251 
252             Add(new wxLuaDebugItem(name, WXLUA_TNONE, value, wxl_valuetype, source, val_ref, 0, val_flag_type));
253             ++count;
254 
255             lua_pop(L, 1); // remove variable value
256 
257             name = lua2wx(lua_getlocal(L, &luaDebug, ++stack_idx));
258         }
259     }
260 
261     return count;
262 }
263 
wxLuaBindClassString(wxLuaBindClass * wxlClass)264 wxString wxLuaBindClassString(wxLuaBindClass* wxlClass)
265 {
266     wxCHECK_MSG(wxlClass, wxEmptyString, wxT("Invalid wxLuaBindClass"));
267     wxString baseClasses;
268     if (wxlClass->baseclassNames)
269     {
270         for (size_t i = 0; wxlClass->baseclassNames[i]; ++i)
271             baseClasses += lua2wx(wxlClass->baseclassNames[i]) + wxT(",");
272     }
273 
274     return wxString::Format(wxT(" (%s, wxluatype=%d, classinfo=%s, baseclass=%s, methods=%d, enums=%d)"),
275                             lua2wx(wxlClass->name).c_str(), *wxlClass->wxluatype,
276                             wxString(wxlClass->classInfo ? wxlClass->classInfo->GetClassName() : wxEmptyString).c_str(),
277                             baseClasses.c_str(),
278                             wxlClass->wxluamethods_n, wxlClass->enums_n);
279 }
280 
EnumerateTable(lua_State * L,int tableRef,int nIndex,wxArrayInt & references)281 int wxLuaDebugData::EnumerateTable(lua_State* L, int tableRef, int nIndex, wxArrayInt& references)
282 {
283     wxCHECK_MSG(L, 0, wxT("Invalid lua_State"));
284     wxCHECK_MSG(M_DEBUGREFDATA != NULL, 0, wxT("Invalid ref data"));
285 
286     int count = 0;
287 
288     int wxl_keytype   = WXLUA_TNONE;
289     int wxl_valuetype = WXLUA_TNONE;
290     wxString value;
291     wxString name;
292 
293     if (tableRef == LUA_GLOBALSINDEX)
294     {
295         lua_pushglobaltable(L);
296         GetTypeValue(L, -1, &wxl_valuetype, value);
297 
298         int flag_type = 0;
299         int val_ref = RefTable(L, -1, &flag_type, WXLUA_DEBUGITEM_VALUE_REF, references);
300         lua_pop(L, 1); // pop globals table
301 
302         Add(new wxLuaDebugItem(wxT("Globals"), WXLUA_TNONE, value, WXLUA_TTABLE, wxEmptyString, val_ref, 0, flag_type));
303     }
304 #if LUA_VERSION_NUM < 502
305     // LUA_ENVIRONINDEX is no longer in 5.2
306     else if (tableRef == LUA_ENVIRONINDEX)
307     {
308         lua_pushvalue(L, LUA_ENVIRONINDEX);
309         GetTypeValue(L, -1, &wxl_valuetype, value);
310 
311         int flag_type = 0;
312         int val_ref = RefTable(L, -1, &flag_type, WXLUA_DEBUGITEM_VALUE_REF, references);
313         lua_pop(L, 1); // pop environment table
314 
315         Add(new wxLuaDebugItem(wxT("Environment"), WXLUA_TNONE, value, WXLUA_TTABLE, wxEmptyString, val_ref, 0, flag_type));
316     }
317 #endif // LUA_VERSION_NUM < 502
318     else if (tableRef == LUA_REGISTRYINDEX)
319     {
320         lua_pushvalue(L, LUA_REGISTRYINDEX);
321         GetTypeValue(L, -1, &wxl_valuetype, value);
322 
323         int flag_type = 0;
324         int val_ref = RefTable(L, -1, &flag_type, WXLUA_DEBUGITEM_VALUE_REF, references);
325         lua_pop(L, 1); // pop registry table
326 
327         Add(new wxLuaDebugItem(wxT("Registry"), WXLUA_TNONE, value, WXLUA_TTABLE, wxEmptyString, val_ref, 0, flag_type));
328     }
329     else
330     {
331         // push the table onto the stack to iterate through
332         if (wxluaR_getref(L, tableRef, &wxlua_lreg_debug_refs_key))
333         {
334             if (lua_isnil(L, -1))
335             {
336                 // assert so we don't crash mysteriously inside Lua on nil
337                 lua_pop(L, 1); // pop nil
338                 wxFAIL_MSG(wxT("Invalid wxLua debug reference"));
339                 return count;
340             }
341 
342             // Check to see if this is a wxLua LUA_REGISTRYINDEX table
343             void *lightuserdata_reg_key = NULL;
344             lua_pushlightuserdata(L, &wxlua_lreg_regtable_key); // push key
345             lua_rawget(L, LUA_REGISTRYINDEX);
346             lua_pushvalue(L, -2); // push value (table we're iterating)
347             lua_rawget(L, -2);
348             lightuserdata_reg_key = lua_touserdata(L, -1); // returns NULL for nil
349             lua_pop(L, 2); // pop wxlua_lreg_regtable_key table and (nil or lightuserdata)
350 
351             // Check if this table/userdata has a metatable
352             if (lua_getmetatable(L, -1)) // if no metatable then nothing is pushed
353             {
354                 // get the type and value
355                 GetTypeValue(L, -1, &wxl_valuetype, value);
356 
357                 int flag_type = 0;
358                 int val_ref = RefTable(L, -1, &flag_type, WXLUA_DEBUGITEM_VALUE_REF, references);
359 
360                 // leading space so it's first when sorted
361                 Add(new wxLuaDebugItem(wxT(" __metatable"), WXLUA_TTABLE, value, wxl_valuetype, wxEmptyString, val_ref, nIndex, flag_type));
362                 ++count;
363 
364                 lua_pop(L, 1); // pop metatable
365             }
366 
367             // start iterating
368             if (lua_istable(L, -1))
369             {
370             lua_pushnil(L);
371             while (lua_next(L, -2) != 0)
372             {
373                 // value at -1, key at -2, table at -3
374 
375                 // get the key type and value
376                 int lua_key_type = GetTypeValue(L, -2, &wxl_keytype, name);
377                 // get the value type and value
378                 int lua_value_type = GetTypeValue(L, -1, &wxl_valuetype, value);
379 
380                 // Handle items within the wxLua LUA_REGISTRYINDEX tables to give more information
381                 if (lightuserdata_reg_key != NULL)
382                 {
383                     if (lightuserdata_reg_key == &wxlua_lreg_types_key)
384                     {
385                         value += wxString::Format(wxT(" (%s)"), wxluaT_typename(L, (int)lua_tonumber(L, -2)).c_str());
386                     }
387                     else if (lightuserdata_reg_key == &wxlua_lreg_classes_key)
388                     {
389                         wxLuaBindClass* wxlClass = (wxLuaBindClass*)lua_touserdata(L, -1);
390                         value += wxLuaBindClassString(wxlClass);
391                     }
392                     else if (lightuserdata_reg_key == &wxlua_lreg_wxluabindings_key)
393                     {
394                         wxLuaBinding* binding = (wxLuaBinding*)lua_touserdata(L, -2);
395                         name = wxString::Format(wxT("wxLuaBinding(%s) -> %s"), name.c_str(), binding->GetBindingName().c_str());
396                         value += wxT(" = ") + binding->GetLuaNamespace();
397                     }
398                     else if (lightuserdata_reg_key == &wxlua_lreg_evtcallbacks_key)
399                     {
400                         wxLuaEventCallback* wxlCallback = (wxLuaEventCallback*)lua_touserdata(L, -2);
401                         wxCHECK_MSG(wxlCallback, count, wxT("Invalid wxLuaEventCallback"));
402 
403                         wxString s(wxlCallback->GetInfo());
404                         name  = s.BeforeFirst(wxT('|'));
405                         value = s.AfterFirst(wxT('|'));
406                     }
407                     else if (lightuserdata_reg_key == &wxlua_lreg_windestroycallbacks_key)
408                     {
409                         // only handle t[wxWindow*] = wxLuaWinDestroyCallback*
410                         wxLuaWinDestroyCallback* wxlDestroyCallBack = (wxLuaWinDestroyCallback*)lua_touserdata(L, -1);
411                         wxCHECK_MSG(wxlDestroyCallBack, count, wxT("Invalid wxLuaWinDestroyCallback"));
412 
413                         wxString s(wxlDestroyCallBack->GetInfo());
414                         name  = s.BeforeFirst(wxT('|'));
415                         value = s.AfterFirst(wxT('|'));
416                     }
417                     else if (lightuserdata_reg_key == &wxlua_lreg_topwindows_key)
418                     {
419                         wxWindow* win = (wxWindow*)lua_touserdata(L, -2);
420                         name += wxT(" ") + wxString(win->GetClassInfo()->GetClassName());
421                     }
422                     else if (lightuserdata_reg_key == &wxlua_lreg_gcobjects_key)
423                     {
424                         int wxl_type_ = (int)lua_tonumber(L, -1);
425                         name = wxString::Format(wxT("%s(%s)"), wxluaT_typename(L, wxl_type_).c_str(), name.c_str());
426                     }
427                     else if (lightuserdata_reg_key == &wxlua_lreg_weakobjects_key)
428                     {
429                         wxString names_weak;
430 
431                         // iterate the table of userdata
432                         lua_pushnil(L);
433                         while (lua_next(L, -2) != 0)
434                         {
435                             // value = -1, key = -2, table = -3
436                             int wxl_type_weak = (int)lua_tonumber(L, -2);
437                             if (!names_weak.IsEmpty()) names_weak += wxT(", ");
438                             names_weak += wxString::Format(wxT("%s(%d)"), wxluaT_typename(L, wxl_type_weak).c_str(), wxl_type_weak);
439                             lua_pop(L, 1); // pop value, lua_next will pop key at end
440                         }
441 
442                         name = wxString::Format(wxT("%s (%s)"), names_weak.c_str(), name.c_str());
443                     }
444                 }
445 
446                 // For these keys we know what is in the value to give more information
447                 if (lua_key_type == LUA_TLIGHTUSERDATA)
448                 {
449                     void* key = lua_touserdata(L, -2);
450 
451                     if (key == &wxlua_lreg_wxeventtype_key)
452                     {
453                         wxEventType eventType = (wxEventType)lua_tonumber(L, -1);
454                         const wxLuaBindEvent* wxlEvent = wxLuaBinding::FindBindEvent(eventType);
455 
456                         if (wxlEvent != NULL)
457                         {
458                             value = wxString::Format(wxT("%d = %s : %s"), eventType, lua2wx(wxlEvent->name).c_str(), wxluaT_typename(L, *wxlEvent->wxluatype).c_str());
459                         }
460                     }
461                     else if (key == &wxlua_metatable_type_key)
462                     {
463                         value += wxString::Format(wxT(" (%s)"), wxluaT_typename(L, (int)lua_tonumber(L, -1)).c_str());
464                     }
465                     else if (key == &wxlua_metatable_wxluabindclass_key)
466                     {
467                         wxLuaBindClass* wxlClass = (wxLuaBindClass*)lua_touserdata(L, -1);
468                         value += wxLuaBindClassString(wxlClass);
469                     }
470                     else if (key == &wxlua_lreg_debug_refs_key)
471                     {
472                         value += wxT(" Note: You cannot traverse refed tables");
473                     }
474                 }
475 
476                 // ----------------------------------------------------------
477                 // Handle the key
478 
479                 int key_flag_type = 0;
480                 int key_ref = LUA_NOREF;
481 
482                 // don't ref anything in this table since it's already refed
483                 if ((lua_key_type == LUA_TTABLE) && (lightuserdata_reg_key != &wxlua_lreg_debug_refs_key))
484                 {
485                     key_ref = RefTable(L, -2, &key_flag_type, WXLUA_DEBUGITEM_KEY_REF, references);
486                 }
487                 else if (lua_key_type == LUA_TUSERDATA)
488                 {
489                     if (lua_getmetatable(L, -2)) // doesn't push anything if nil
490                     {
491                         key_ref = RefTable(L, -2, &key_flag_type, WXLUA_DEBUGITEM_KEY_REF, references);
492                         lua_pop(L, 1);
493                     }
494                 }
495 
496                 // only add the key if we refed it so it can be viewed in the stack dialog
497                 if (key_flag_type != 0)
498                 {
499                     Add(new wxLuaDebugItem(name, wxl_keytype, value, wxl_valuetype, wxEmptyString, key_ref, nIndex, key_flag_type));
500                     ++count;
501                 }
502 
503                 // ----------------------------------------------------------
504                 // Handle the value
505 
506                 int val_flag_type = 0;
507                 int val_ref = LUA_NOREF;
508 
509                 // don't ref anything in this table since it's already refed
510                 if ((lua_value_type == LUA_TTABLE) && (lightuserdata_reg_key != &wxlua_lreg_debug_refs_key))
511                 {
512                     val_ref = RefTable(L, -1, &val_flag_type, WXLUA_DEBUGITEM_VALUE_REF, references);
513                 }
514                 else if (lua_value_type == LUA_TUSERDATA)
515                 {
516                     if (lua_getmetatable(L, -1)) // doesn't push anything if nil
517                     {
518                         val_ref = RefTable(L, -1, &val_flag_type, WXLUA_DEBUGITEM_VALUE_REF, references);
519                         lua_pop(L, 1);
520                     }
521                 }
522 
523                 // Add the value, but not if the value doesn't expand and the key was already added
524                 if ((key_flag_type == 0) || ((key_flag_type != 0) && (val_flag_type != 0)))
525                 {
526                     Add(new wxLuaDebugItem(name, wxl_keytype, value, wxl_valuetype, wxEmptyString, val_ref, nIndex, val_flag_type));
527                     ++count;
528                 }
529 
530                 lua_pop(L, 1); // pop value, leave key
531             }
532             }
533 
534             lua_pop(L, 1); // remove reference
535         }
536     }
537 
538     return count;
539 }
540 
RefTable(lua_State * L,int stack_idx,int * flag_type,int extra_flag,wxArrayInt & references)541 int wxLuaDebugData::RefTable(lua_State* L, int stack_idx, int* flag_type, int extra_flag, wxArrayInt& references)
542 {
543     wxCHECK_MSG(L, LUA_NOREF, wxT("Invalid lua_State"));
544 
545     int lua_ref = LUA_NOREF;
546 
547     if (lua_istable(L, stack_idx))
548     {
549         if (flag_type) *flag_type |= (WXLUA_DEBUGITEM_IS_REFED | extra_flag);
550 
551         lua_ref = wxluaR_isrefed(L, stack_idx, &wxlua_lreg_debug_refs_key); // don't duplicate refs
552 
553         if (lua_ref == LUA_NOREF)
554         {
555             lua_ref = wxluaR_ref(L, stack_idx, &wxlua_lreg_debug_refs_key);
556             references.Add(lua_ref);
557         }
558     }
559 
560     return lua_ref;
561 }
562 
GetTypeValue(lua_State * L,int stack_idx,int * wxl_type_,wxString & value)563 int wxLuaDebugData::GetTypeValue(lua_State *L, int stack_idx, int* wxl_type_, wxString& value)
564 {
565     wxCHECK_MSG(L, 0, wxT("Invalid lua_State"));
566 
567     int l_type    = lua_type(L, stack_idx);
568     int wxl_type  = wxlua_luatowxluatype(l_type);
569 
570     switch (l_type)
571     {
572         case LUA_TNONE:
573         {
574             value = wxEmptyString;
575             break;
576         }
577         case LUA_TNIL:
578         {
579             value = wxT("nil");
580             break;
581         }
582         case LUA_TBOOLEAN:
583         {
584             value = (lua_toboolean(L, stack_idx) != 0) ? wxT("true") : wxT("false");
585             break;
586         }
587         case LUA_TLIGHTUSERDATA:
588         {
589             value = GetUserDataInfo(L, stack_idx, false);
590             break;
591         }
592         case LUA_TNUMBER:
593         {
594             double num = lua_tonumber(L, stack_idx);
595 
596             if ((long)num == num)
597                 value.Printf(wxT("%ld (0x%lx)"), (long)num, (unsigned long)num);
598             else
599                 value.Printf(wxT("%g"), num);
600 
601             break;
602         }
603         case LUA_TSTRING:
604         {
605             value = lua2wx(lua_tostring(L, stack_idx));
606             break;
607         }
608         case LUA_TTABLE:
609         {
610             value = GetTableInfo(L, stack_idx);
611             break;
612         }
613         case LUA_TFUNCTION:
614         {
615             value.Printf(wxT("%p"), lua_topointer(L, stack_idx));
616 
617             if (lua_iscfunction(L, stack_idx))
618                 wxl_type = WXLUA_TCFUNCTION;
619 
620             break;
621         }
622         case LUA_TUSERDATA:
623         {
624             value = GetUserDataInfo(L, stack_idx, true);
625             break;
626         }
627         case LUA_TTHREAD:
628         {
629             value.Printf(wxT("%p"), lua_topointer(L, stack_idx));
630             break;
631         }
632         default :
633         {
634             value = wxEmptyString;
635             break;
636         }
637     }
638 
639     if (wxl_type_) *wxl_type_ = wxl_type;
640 
641     return l_type;
642 }
643 
GetTableInfo(lua_State * L,int stack_idx)644 wxString wxLuaDebugData::GetTableInfo(lua_State *L, int stack_idx)
645 {
646     wxCHECK_MSG(L, wxEmptyString, wxT("Invalid lua_State"));
647 
648     int         nItems   = luaL_getn(L, stack_idx);
649     const void *pItem    = lua_topointer(L, stack_idx);
650 
651     if (nItems > 0)
652         return wxString::Format(wxT("%p (%d array items)"), pItem, nItems);
653 
654     return wxString::Format(wxT("%p"), pItem);
655 }
656 
GetUserDataInfo(lua_State * L,int stack_idx,bool full_userdata)657 wxString wxLuaDebugData::GetUserDataInfo(lua_State *L, int stack_idx, bool full_userdata)
658 {
659     wxCHECK_MSG(L, wxEmptyString, wxT("Invalid lua_State"));
660 
661     void* udata = lua_touserdata(L, stack_idx);
662 
663     wxString s(wxString::Format(wxT("%p"), udata));
664 
665     if (!full_userdata)
666     {
667         // Convert our known keys to something more readable
668         if ((udata == &wxlua_lreg_types_key) ||
669             (udata == &wxlua_lreg_refs_key) ||
670             (udata == &wxlua_lreg_debug_refs_key) ||
671             (udata == &wxlua_lreg_classes_key) ||
672             (udata == &wxlua_lreg_derivedmethods_key) ||
673             (udata == &wxlua_lreg_wxluastate_key) ||
674             (udata == &wxlua_lreg_wxluabindings_key) ||
675             (udata == &wxlua_lreg_weakobjects_key) ||
676             (udata == &wxlua_lreg_gcobjects_key) ||
677             (udata == &wxlua_lreg_evtcallbacks_key) ||
678             (udata == &wxlua_lreg_windestroycallbacks_key) ||
679             (udata == &wxlua_lreg_callbaseclassfunc_key) ||
680             (udata == &wxlua_lreg_wxeventtype_key) ||
681             (udata == &wxlua_lreg_wxluastatedata_key) ||
682             (udata == &wxlua_lreg_regtable_key) ||
683 
684             (udata == &wxlua_metatable_type_key) ||
685             (udata == &wxlua_lreg_topwindows_key) ||
686             (udata == &wxlua_metatable_wxluabindclass_key))
687         {
688             const char* ss = *(const char**)udata;
689             s += wxString::Format(wxT(" (%s)"), lua2wx(ss).c_str());
690         }
691     }
692     else // is full userdata
693     {
694         int wxl_type = wxluaT_type(L, stack_idx);
695 
696         if (wxlua_iswxuserdatatype(wxl_type))
697         {
698             s += wxString::Format(wxT(" (wxltype %d)"), wxl_type);
699 
700             wxString wxltypeName(wxluaT_typename(L, wxl_type));
701             if (!wxltypeName.IsEmpty())
702                 s += wxString::Format(wxT(" '%s'"), wxltypeName.c_str());
703         }
704     }
705 
706     return s;
707 }
708 
709 // ----------------------------------------------------------------------------
710 // wxLuaCheckStack - dumps the contents of the lua_State
711 // ----------------------------------------------------------------------------
712 
wxLuaCheckStack(lua_State * L,const wxString & msg,bool print_to_console)713 wxLuaCheckStack::wxLuaCheckStack(lua_State *L, const wxString &msg, bool print_to_console)
714 {
715     m_luaState = L;
716     m_msg      = msg;
717     m_top      = lua_gettop(m_luaState);
718     m_print_to_console = print_to_console;
719 }
720 
~wxLuaCheckStack()721 wxLuaCheckStack::~wxLuaCheckStack()
722 {
723     if (m_print_to_console)
724         TestStack(wxT("~wxLuaCheckStack"));
725 }
726 
TestStack(const wxString & msg)727 wxString wxLuaCheckStack::TestStack(const wxString &msg)
728 {
729     wxString s;
730     s.Printf(wxT("wxLuaCheckStack::TestStack(L=%p) '%s':'%s': starting top %d ending top %d\n"),
731                     m_luaState, m_msg.c_str(), msg.c_str(), m_top, lua_gettop(m_luaState));
732 
733     if (m_top != lua_gettop(m_luaState)) s += wxT(" **********"); // easy to find
734 
735     OutputMsg(s);
736 
737     return s;
738 }
739 
DumpStack(const wxString & msg)740 wxString wxLuaCheckStack::DumpStack(const wxString& msg)
741 {
742     wxCHECK_MSG(m_luaState, wxEmptyString, wxT("Invalid lua_State"));
743 
744     lua_State* L = m_luaState;
745     int i, count = lua_gettop(L);
746     wxString str;
747     wxString retStr;
748 
749     str.Printf(wxT("wxLuaCheckStack::DumpStack(L=%p), '%s':'%s', items %d, starting top %d\n"), L, m_msg.c_str(), msg.c_str(), count, m_top);
750     retStr += str;
751     OutputMsg(str);
752 
753     wxLuaState wxlState(L);
754 
755     for (i = 1; i <= count; i++)
756     {
757         int wxl_type = 0;
758         wxString value;
759         int l_type = wxLuaDebugData::GetTypeValue(L, i, &wxl_type, value);
760 
761         str.Printf(wxT("  idx %d: l_type = %d, wxl_type = %d : '%s'='%s'\n"),
762                 i, l_type, wxl_type, wxluaT_typename(L, wxl_type).c_str(), value.c_str());
763         retStr += str;
764         OutputMsg(str);
765     }
766 
767     return retStr;
768 }
769 
DumpGlobals(const wxString & msg)770 wxString wxLuaCheckStack::DumpGlobals(const wxString& msg)
771 {
772     wxCHECK_MSG(m_luaState, wxEmptyString, wxT("Invalid lua_State"));
773 
774     wxSortedArrayString tableArray;
775 
776     return DumpTable(LUA_GLOBALSINDEX, wxT("Globals"), msg, tableArray, 0);
777 }
778 
DumpTable(const wxString & tablename,const wxString & msg)779 wxString wxLuaCheckStack::DumpTable(const wxString &tablename, const wxString& msg)
780 {
781     wxCHECK_MSG(m_luaState, wxEmptyString, wxT("Invalid lua_State"));
782 
783     lua_State* L = m_luaState;
784     wxSortedArrayString tableArray;
785     wxString s;
786 
787     // Allow iteration through table1.table2.table3...
788     wxString tname(tablename);
789     lua_pushglobaltable(L);
790 
791     do {
792         lua_pushstring(L, wx2lua(tname.BeforeFirst(wxT('.'))));
793         lua_rawget(L, -2);
794 
795         if (lua_isnil(L, -1) || !lua_istable(L, -1))
796         {
797             lua_pop(L, 2);  // remove table and value
798 
799             s.Printf(wxT("wxLuaCheckStack::DumpTable(L=%p) Table: '%s' cannot be found!\n"), L, tablename.c_str());
800             OutputMsg(s);
801             return s;
802         }
803 
804         lua_remove(L, -2);  // remove previous table
805         tname = tname.AfterFirst(wxT('.'));
806     } while (tname.Len() > 0);
807 
808     s = DumpTable(lua_gettop(L), tablename, msg, tableArray, 0);
809     lua_pop(L, 1);
810 
811     return s;
812 }
813 
DumpTable(int stack_idx,const wxString & msg)814 wxString wxLuaCheckStack::DumpTable(int stack_idx, const wxString& msg)
815 {
816     wxCHECK_MSG(m_luaState, wxEmptyString, wxT("Invalid lua_State"));
817 
818     wxSortedArrayString tableArray;
819 
820     return DumpTable(stack_idx, wxString::Format(wxT("StackIdx=%d"), stack_idx), msg, tableArray, 0);
821 }
822 
DumpTable(int stack_idx,const wxString & tablename,const wxString & msg,wxSortedArrayString & tableArray,int indent)823 wxString wxLuaCheckStack::DumpTable(int stack_idx, const wxString& tablename, const wxString& msg, wxSortedArrayString& tableArray, int indent)
824 {
825     wxCHECK_MSG(m_luaState, wxEmptyString, wxT("Invalid lua_State"));
826 
827     lua_State* L = m_luaState;
828     wxLuaState wxlState(L);
829     wxString indentStr;
830     wxString s;
831 
832     // We only do tables, return error message
833     if (!lua_istable(L, stack_idx))
834     {
835         s.Printf(wxT("wxLuaCheckStack::DumpTable(L=%p) stack idx %d is not a table.\n"), L, stack_idx);
836         OutputMsg(s);
837         return s;
838     }
839 
840     if (indent == 0)
841     {
842         // First time through print header
843         s.Printf(wxT("wxLuaCheckStack::DumpTable(L=%p) Table: '%s'\n"), L, tablename.c_str());
844         OutputMsg(s);
845     }
846     else if (indent > 10)
847     {
848         // Don't let things get out of hand...
849         s.Printf(wxT("wxLuaCheckStack::DumpTable(L=%p) Table depth > 10! Truncating: '%s'\n"), L, tablename.c_str());
850         OutputMsg(s);
851         return s;
852     }
853     else
854     {
855         indentStr = wxString(wxT(' '), indent*2) + wxT(">");
856     }
857 
858     wxString title = wxString::Format(wxT("%sTable Level %d : name '%s'\n"), indentStr.c_str(), indent, tablename.c_str());
859     s += title;
860     OutputMsg(title);
861 
862     lua_pushvalue(L, stack_idx); // push the table to read the top of the stack
863 
864     lua_pushnil(L);
865     while (lua_next(L, -2) != 0)
866     {
867         int keyType = 0, valueType = 0;
868         wxString key, value;
869 
870         wxLuaDebugData::GetTypeValue(L, -2, &keyType,   key);
871         wxLuaDebugData::GetTypeValue(L, -1, &valueType, value);
872 
873         wxString info = wxString::Format(wxT("%s%-32s\t%-16s\t%-20s\t%-16s\n"),
874                 indentStr.c_str(), key.c_str(), wxluaT_typename(L, keyType).c_str(), value.c_str(), wxluaT_typename(L, valueType).c_str());
875         s += info;
876         OutputMsg(info);
877 
878         if (tableArray.Index(value) == wxNOT_FOUND)
879         {
880             if (valueType == WXLUA_TTABLE)
881             {
882                 tableArray.Add(value);
883                 s += DumpTable(lua_gettop(L), tablename + wxT(".") + key, msg, tableArray, indent+1);
884             }
885             else
886             {
887                 tableArray.Add(value);
888             }
889         }
890 
891         lua_pop(L, 1); // pop value
892     }
893 
894     lua_pop(L, 1); // pop pushed table
895 
896     return s;
897 }
898 
OutputMsg(const wxString & msg) const899 void wxLuaCheckStack::OutputMsg(const wxString& msg) const
900 {
901     if (m_print_to_console)
902     {
903 #if  defined(__WXMSW__)
904     //OutputDebugString(msg.c_str());
905     wxPrintf(wxT("%s"), msg.c_str()); fflush(stdout);
906 #else //if defined(__WXGTK__) || defined(__WXMAC__)
907     wxPrintf(wxT("%s"), msg.c_str());
908 #endif
909     }
910 }
911