Merged changes from branches/supertux-milestone2-grumbel/ to trunk/supertux/
[supertux.git] / external / squirrel / sqdbg / sqdbgserver.cpp
1 #include <squirrel.h>
2 #include <assert.h>
3 #include <sqstdblob.h>
4 #include "sqrdbg.h"
5 #include "sqdbgserver.h"
6
7
8 #ifndef _UNICODE
9 #define scstrcpy strcpy
10 #else
11 #define scstrcpy wcscpy
12 #endif
13 struct XMLEscape{
14         const SQChar c;
15         const SQChar *esc;
16 };
17
18 #define SQDBG_DEBUG_HOOK _SC("_sqdbg_debug_hook_")
19 #define SQDBG_ERROR_HANDLER _SC("_sqdbg_error_handler_")
20
21 XMLEscape g_escapes[]={
22         {_SC('<'),_SC("&lt;")},{'>',_SC("&gt;")},{_SC('&'),_SC("&amp;")},{_SC('\''),_SC("&apos;")},{_SC('\"'),_SC("&quot;")},{_SC('\n'),_SC("&quot;n")},{_SC('\r'),_SC("&quot;r")},{0, NULL}
23 };
24
25 const SQChar *IntToString(int n)
26 {
27         static SQChar temp[256];
28         scsprintf(temp,_SC("%d"),n);
29         return temp;
30 }
31
32 SQInteger debug_hook(HSQUIRRELVM v);
33 SQInteger error_handler(HSQUIRRELVM v);
34
35 SQInteger beginelement(HSQUIRRELVM v)
36 {
37         SQUserPointer up;
38         const SQChar *name;
39         sq_getuserpointer(v,-1,&up);
40         SQDbgServer *self = (SQDbgServer*)up;
41         sq_getuserpointer(v,-1,&up);
42         sq_getstring(v,2,&name);
43         self->BeginElement(name);
44         return 0;
45 }
46
47 SQInteger endelement(HSQUIRRELVM v)
48 {
49         SQUserPointer up;
50         const SQChar *name;
51         sq_getuserpointer(v,-1,&up);
52         SQDbgServer *self = (SQDbgServer*)up;
53         sq_getuserpointer(v,-1,&up);
54         sq_getstring(v,2,&name);
55         self->EndElement(name);
56         return 0;
57 }
58
59 SQInteger attribute(HSQUIRRELVM v)
60 {
61         SQUserPointer up;
62         const SQChar *name,*value;
63         sq_getuserpointer(v,-1,&up);
64         SQDbgServer *self = (SQDbgServer*)up;
65         sq_getuserpointer(v,-1,&up);
66         sq_getstring(v,2,&name);
67         sq_getstring(v,3,&value);
68         self->Attribute(name,value);
69         return 0;
70 }
71
72 const SQChar *EscapeXMLString(HSQUIRRELVM v,const SQChar *s)
73 {
74
75         SQChar *temp=sq_getscratchpad(v,((int)scstrlen(s)*6) + sizeof(SQChar));
76         SQChar *dest=temp;
77         while(*s!=_SC('\0')){
78                 int i=0;
79                 bool escaped=false;
80                 while(g_escapes[i].esc!=NULL){
81                         if(*s==g_escapes[i].c){
82                                 scstrcpy(dest,g_escapes[i].esc);
83                                 dest+=scstrlen(g_escapes[i].esc);
84                                 escaped=true;
85                                 break;
86                         }
87                         i++;
88                 }
89                 if(!escaped){*dest=*s;*dest++;}
90                 *s++;
91         }
92         *dest=_SC('\0');
93         return temp;
94 }
95
96 SQDbgServer::SQDbgServer(HSQUIRRELVM v)
97 {
98         _ready = false;
99         _nestedcalls = 0;
100         _autoupdate = false;
101         _v = v;
102         _state = eDBG_Running;
103         _accept = INVALID_SOCKET;
104         _endpoint = INVALID_SOCKET;
105         _maxrecursion = 10;
106         sq_resetobject(&_debugroot);
107 }
108
109 SQDbgServer::~SQDbgServer()
110 {
111         sq_release(_v,&_debugroot);
112         if(_accept != INVALID_SOCKET)
113                 sqdbg_closesocket(_accept);
114         if(_endpoint != INVALID_SOCKET)
115                 sqdbg_closesocket(_endpoint);
116 }
117
118 bool SQDbgServer::Init()
119 {
120         //creates  an environment table for the debugger
121
122         sq_newtable(_v);
123         sq_getstackobj(_v,-1,&_debugroot);
124         sq_addref(_v,&_debugroot);
125
126         //creates a emptyslot to store the watches
127         sq_pushstring(_v,_SC("watches"),-1);
128         sq_pushnull(_v);
129         sq_createslot(_v,-3);
130
131         sq_pushstring(_v,_SC("beginelement"),-1);
132         sq_pushuserpointer(_v,this);
133         sq_newclosure(_v,beginelement,1);
134         sq_setparamscheck(_v,2,_SC(".s"));
135         sq_createslot(_v,-3);
136
137         sq_pushstring(_v,_SC("endelement"),-1);
138         sq_pushuserpointer(_v,this);
139         sq_newclosure(_v,endelement,1);
140         sq_setparamscheck(_v,2,_SC(".s"));
141         sq_createslot(_v,-3);
142
143         sq_pushstring(_v,_SC("attribute"),-1);
144         sq_pushuserpointer(_v,this);
145         sq_newclosure(_v,attribute,1);
146         sq_setparamscheck(_v,3,_SC(".ss"));
147         sq_createslot(_v,-3);
148
149         sq_pop(_v,1);
150
151         //stores debug hook and error handler in the registry
152         sq_pushregistrytable(_v);
153
154         sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
155         sq_pushuserpointer(_v,this);
156         sq_newclosure(_v,debug_hook,1);
157         sq_createslot(_v,-3);
158
159         sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
160         sq_pushuserpointer(_v,this);
161         sq_newclosure(_v,error_handler,1);
162         sq_createslot(_v,-3);
163
164
165         sq_pop(_v,1);
166
167         //sets the error handlers
168         SetErrorHandlers();
169         return true;
170 }
171
172 bool SQDbgServer::ReadMsg()
173 {
174         return false;
175 }
176
177 void SQDbgServer::BusyWait()
178 {
179         while( !ReadMsg() )
180                 sleep(0);
181 }
182
183 void SQDbgServer::SendChunk(const SQChar *chunk)
184 {
185         char *buf=NULL;
186         int buf_len=0;
187 #ifdef _UNICODE
188         buf_len=(int)scstrlen(chunk)+1;
189         buf=(char *)sq_getscratchpad(_v,(buf_len)*3);
190         wcstombs((char *)buf,chunk,buf_len);
191 #else
192         buf_len=(int)scstrlen(chunk);
193         buf=(char *)chunk;
194 #endif
195         send(_endpoint,(const char*)buf,(int)strlen((const char *)buf),0);
196 }
197
198
199 void SQDbgServer::Terminated()
200 {
201         BeginElement(_SC("terminated"));
202         EndElement(_SC("terminated"));
203         ::usleep(200);
204 }
205
206 void SQDbgServer::Hook(int type,int line,const SQChar *src,const SQChar *func)
207 {
208         switch(_state){
209         case eDBG_Running:
210                 if(type==_SC('l') && _breakpoints.size()) {
211                         BreakPointSetItor itr = _breakpoints.find(BreakPoint(line,src));
212                         if(itr != _breakpoints.end()) {
213                                 Break(line,src,_SC("breakpoint"));
214                                 BreakExecution();
215                         }
216                 }
217                 break;
218         case eDBG_Suspended:
219                 _nestedcalls=0;
220         case eDBG_StepOver:
221                 switch(type){
222                 case _SC('l'):
223                         if(_nestedcalls==0) {
224                                 Break(line,src,_SC("step"));
225                                 BreakExecution();
226                         }
227                         break;
228                 case _SC('c'):
229                         _nestedcalls++;
230                         break;
231                 case _SC('r'):
232                         if(_nestedcalls==0){
233                                 _nestedcalls=0;
234
235                         }else{
236                                 _nestedcalls--;
237                         }
238                         break;
239                 }
240                 break;
241         case eDBG_StepInto:
242                 switch(type){
243                 case _SC('l'):
244                         _nestedcalls=0;
245                         Break(line,src,_SC("step"));
246                         BreakExecution();
247                         break;
248
249                 }
250                 break;
251         case eDBG_StepReturn:
252                 switch(type){
253                 case _SC('l'):
254                         break;
255                 case _SC('c'):
256                         _nestedcalls++;
257                         break;
258                 case _SC('r'):
259                         if(_nestedcalls==0){
260                                 _nestedcalls=0;
261                                 _state=eDBG_StepOver;
262                         }else{
263                                 _nestedcalls--;
264                         }
265
266                         break;
267                 }
268                 break;
269         case eDBG_Disabled:
270                 break;
271         }
272 }
273
274
275 #define MSG_ID(x,y) ((y<<8)|x)
276 //ab Add Breakpoint
277 //rb Remove Breakpoint
278 //sp Suspend
279 void SQDbgServer::ParseMsg(const char *msg)
280 {
281
282         switch(*((unsigned short *)msg)){
283                 case MSG_ID('a','b'): {
284                         BreakPoint bp;
285                         if(ParseBreakpoint(msg+3,bp)){
286                                 AddBreakpoint(bp);
287                                 scprintf(_SC("added bp %d %s\n"),bp._line,bp._src.c_str());
288                         }
289                         else
290                                 scprintf(_SC("error parsing add breakpoint"));
291                                                          }
292                         break;
293                 case MSG_ID('r','b'): {
294                         BreakPoint bp;
295                         if(ParseBreakpoint(msg+3,bp)){
296                                 RemoveBreakpoint(bp);
297                                 scprintf(_SC("removed bp %d %s\n"),bp._line,bp._src.c_str());
298                         }else
299                                 scprintf(_SC("error parsing remove breakpoint"));
300                                                         }
301                         break;
302                 case MSG_ID('g','o'):
303                         if(_state!=eDBG_Running){
304                                 _state=eDBG_Running;
305                                 BeginDocument();
306                                         BeginElement(_SC("resumed"));
307                                         EndElement(_SC("resumed"));
308                                 EndDocument();
309 //                              Send(_SC("<resumed/>\r\n"));
310                                 scprintf(_SC("go (execution resumed)\n"));
311                         }
312                         break;
313                 case MSG_ID('s','p'):
314                         if(_state!=eDBG_Suspended){
315                                 _state=eDBG_Suspended;
316                                 scprintf(_SC("suspend\n"));
317                         }
318                         break;
319                 case MSG_ID('s','o'):
320                         if(_state==eDBG_Suspended){
321                                 _state=eDBG_StepOver;
322                         }
323                         break;
324                 case MSG_ID('s','i'):
325                         if(_state==eDBG_Suspended){
326                                 _state=eDBG_StepInto;
327                                 scprintf(_SC("step into\n"));
328                         }
329                         break;
330                 case MSG_ID('s','r'):
331                         if(_state==eDBG_Suspended){
332                                 _state=eDBG_StepReturn;
333                                 scprintf(_SC("step return\n"));
334                         }
335                         break;
336                 case MSG_ID('d','i'):
337                         if(_state!=eDBG_Disabled){
338                                 _state=eDBG_Disabled;
339                                 scprintf(_SC("disabled\n"));
340                         }
341                         break;
342                 case MSG_ID('a','w'): {
343                         Watch w;
344                         if(ParseWatch(msg+3,w))
345                         {
346                                 AddWatch(w);
347                                 scprintf(_SC("added watch %d %s\n"),w._id,w._exp.c_str());
348                         }
349                         else
350                                 scprintf(_SC("error parsing add watch"));
351                                                                 }
352                         break;
353                 case MSG_ID('r','w'): {
354                         int id;
355                         if(ParseRemoveWatch(msg+3,id))
356                         {
357                                 RemoveWatch(id);
358                                 scprintf(_SC("added watch %d\n"),id);
359                         }
360                         else
361                                 scprintf(_SC("error parsing remove watch"));
362                                                                 }
363                         break;
364                 case MSG_ID('t','r'):
365                         scprintf(_SC("terminate from user\n"));
366                         break;
367                 case MSG_ID('r','d'):
368                         scprintf(_SC("ready\n"));
369                         _ready=true;
370                         break;
371                 default:
372                         scprintf(_SC("unknown packet"));
373
374         }
375 }
376
377 /*
378         see copyright notice in sqrdbg.h
379 */
380 bool SQDbgServer::ParseBreakpoint(const char *msg,BreakPoint &out)
381 {
382         static char stemp[MAX_BP_PATH];
383         char *ep=NULL;
384         out._line=strtoul(msg,&ep,16);
385         if(ep==msg || (*ep)!=':')return false;
386
387         char *dest=stemp;
388         ep++;
389         while((*ep)!='\n' && (*ep)!='\0')
390         {
391                 *dest=*ep;
392                 *dest++;*ep++;
393         }
394         *dest='\0';
395         *dest++;
396         *dest='\0';
397 #ifdef _UNICODE
398         int len=(int)strlen(stemp);
399         SQChar *p=sq_getscratchpad(_v,(SQInteger)(mbstowcs(NULL,stemp,len)+2)*sizeof(SQChar));
400         size_t destlen=mbstowcs(p,stemp,len);
401         p[destlen]=_SC('\0');
402         out._src=p;
403 #else
404         out._src=stemp;
405 #endif
406         return true;
407 }
408
409 bool SQDbgServer::ParseWatch(const char *msg,Watch &out)
410 {
411         char *ep=NULL;
412         out._id=strtoul(msg,&ep,16);
413         if(ep==msg || (*ep)!=':')return false;
414
415         //char *dest=out._src;
416         ep++;
417         while((*ep)!='\n' && (*ep)!='\0')
418         {
419                 out._exp.append(1,*ep);
420                 *ep++;
421         }
422         return true;
423 }
424
425 bool SQDbgServer::ParseRemoveWatch(const char *msg,int &id)
426 {
427         char *ep=NULL;
428         id=strtoul(msg,&ep,16);
429         if(ep==msg)return false;
430         return true;
431 }
432
433
434 void SQDbgServer::BreakExecution()
435 {
436         _state=eDBG_Suspended;
437         while(_state==eDBG_Suspended){
438                 if(SQ_FAILED(sq_rdbg_update(this)))
439                         exit(0);
440                 usleep(10);
441         }
442 }
443
444 //COMMANDS
445 void SQDbgServer::AddBreakpoint(BreakPoint &bp)
446 {
447         _breakpoints.insert(bp);
448         BeginDocument();
449                 BeginElement(_SC("addbreakpoint"));
450                         Attribute(_SC("line"),IntToString(bp._line));
451                         Attribute(_SC("src"),bp._src.c_str());
452                 EndElement(_SC("addbreakpoint"));
453         EndDocument();
454 }
455
456 void SQDbgServer::AddWatch(Watch &w)
457 {
458         _watches.insert(w);
459 }
460
461 void SQDbgServer::RemoveWatch(int id)
462 {
463         WatchSetItor itor=_watches.find(Watch(id,_SC("")));
464         if(itor==_watches.end()){
465                 BeginDocument();
466                 BeginElement(_SC("error"));
467                         Attribute(_SC("desc"),_SC("the watch does not exists"));
468                 EndElement(_SC("error"));
469         EndDocument();
470         }
471         else{
472                 _watches.erase(itor);
473                 scprintf(_SC("removed watch %d\n"),id);
474         }
475 }
476
477 void SQDbgServer::RemoveBreakpoint(BreakPoint &bp)
478 {
479         BreakPointSetItor itor=_breakpoints.find(bp);
480         if(itor==_breakpoints.end()){
481                 BeginDocument();
482                         BeginElement(_SC("break"));
483                                 Attribute(_SC("desc"),_SC("the breakpoint doesn't exists"));
484                         EndElement(_SC("break"));
485                 EndDocument();
486         }
487         else{
488                 BeginDocument();
489                         BeginElement(_SC("removebreakpoint"));
490                                 Attribute(_SC("line"),IntToString(bp._line));
491                                 Attribute(_SC("src"),bp._src.c_str());
492                         EndElement(_SC("removebreakpoint"));
493                 EndDocument();
494                 _breakpoints.erase(itor);
495         }
496 }
497
498 void SQDbgServer::Break(int line,const SQChar *src,const SQChar *type,const SQChar *error)
499 {
500         if(!error){
501                 BeginDocument();
502                         BeginElement(_SC("break"));
503                                 Attribute(_SC("line"),IntToString(line));
504                                 Attribute(_SC("src"),src);
505                                 Attribute(_SC("type"),type);
506                                 SerializeState();
507                         EndElement(_SC("break"));
508                 EndDocument();
509         }else{
510                 BeginDocument();
511                         BeginElement(_SC("break"));
512                                 Attribute(_SC("line"),IntToString(line));
513                                 Attribute(_SC("src"),src);
514                                 Attribute(_SC("type"),type);
515                                 Attribute(_SC("error"),error);
516                                 SerializeState();
517                         EndElement(_SC("break"));
518                 EndDocument();
519         }
520 }
521
522 void SQDbgServer::SerializeState()
523 {
524         sq_pushnull(_v);
525         sq_setdebughook(_v);
526         sq_pushnull(_v);
527         sq_seterrorhandler(_v);
528         const SQChar *sz;
529         sq_pushobject(_v,_serializefunc);
530         sq_pushobject(_v,_debugroot);
531         sq_pushstring(_v,_SC("watches"),-1);
532         sq_newtable(_v);
533         for(WatchSetItor i=_watches.begin(); i!=_watches.end(); ++i)
534         {
535                 sq_pushinteger(_v,i->_id);
536                 sq_pushstring(_v,i->_exp.c_str(),(int)i->_exp.length());
537                 sq_createslot(_v,-3);
538         }
539         sq_rawset(_v,-3);
540         if(SQ_SUCCEEDED(sq_call(_v,1,SQTrue,SQTrue))){
541                 if(SQ_SUCCEEDED(sqstd_getblob(_v,-1,(SQUserPointer*)&sz)))
542                         SendChunk(sz);
543         }
544         sq_pop(_v,2);
545
546         SetErrorHandlers();
547 }
548
549
550 void SQDbgServer::SetErrorHandlers()
551 {
552         sq_pushregistrytable(_v);
553         sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
554         sq_rawget(_v,-2);
555         sq_setdebughook(_v);
556         sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
557         sq_rawget(_v,-2);
558         sq_seterrorhandler(_v);
559         sq_pop(_v,1);
560 }
561
562 void SQDbgServer::BeginElement(const SQChar *name)
563 {
564         _xmlcurrentement++;
565         XMLElementState *self = &xmlstate[_xmlcurrentement];
566         scstrcpy(self->name,name);
567         self->haschildren = false;
568         if(_xmlcurrentement > 0) {
569                 XMLElementState *parent = &xmlstate[_xmlcurrentement-1];
570                 if(!parent->haschildren) {
571                         SendChunk(_SC(">")); // closes the parent tag
572                         parent->haschildren = true;
573                 }
574         }
575         _scratchstring.resize(2+scstrlen(name));
576         scsprintf(&_scratchstring[0],_SC("<%s"),name);
577         SendChunk(&_scratchstring[0]);
578 }
579
580 void SQDbgServer::Attribute(const SQChar *name,const SQChar *value)
581 {
582         XMLElementState *self = &xmlstate[_xmlcurrentement];
583         assert(!self->haschildren); //cannot have attributes if already has children
584         const SQChar *escval = escape_xml(value);
585         _scratchstring.resize(5+scstrlen(name)+scstrlen(escval));
586         scsprintf(&_scratchstring[0],_SC(" %s=\"%s\""),name,escval);
587         SendChunk(&_scratchstring[0]);
588 }
589
590 void SQDbgServer::EndElement(const SQChar *name)
591 {
592         XMLElementState *self = &xmlstate[_xmlcurrentement];
593         assert(scstrcmp(self->name,name) == 0);
594         if(self->haschildren) {
595                 _scratchstring.resize(4+scstrlen(name));
596                 scsprintf(&_scratchstring[0],_SC("</%s>"),name);
597                 SendChunk(&_scratchstring[0]);
598
599         }
600         else {
601                 SendChunk(_SC("/>"));
602         }
603         _xmlcurrentement--;
604 }
605
606 void SQDbgServer::EndDocument()
607 {
608         SendChunk(_SC("\r\n"));
609 }
610
611 //this can be done much better/faster(do we need that?)
612 const SQChar *SQDbgServer::escape_xml(const SQChar *s)
613 {
614         SQChar *temp=sq_getscratchpad(_v,((int)scstrlen(s)*6) + sizeof(SQChar));
615         SQChar *dest=temp;
616         while(*s!=_SC('\0')){
617                 int i=0;
618                 bool escaped=false;
619                 while(g_escapes[i].esc!=NULL){
620                         if(*s==g_escapes[i].c){
621                                 scstrcpy(dest,g_escapes[i].esc);
622                                 dest+=scstrlen(g_escapes[i].esc);
623                                 escaped=true;
624                                 break;
625                         }
626                         i++;
627                 }
628                 if(!escaped){*dest=*s;*dest++;}
629                 *s++;
630         }
631         *dest=_SC('\0');
632         return temp;
633
634 }