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