DebugBase.hx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. package cpp.vm;
  2. import haxe.Stack;
  3. enum DebugToken
  4. {
  5. IDENT(name:String);
  6. CONST(value:Dynamic);
  7. DOT;
  8. LPAREN;
  9. RPAREN;
  10. COMMA;
  11. EQUALS;
  12. LARRAY;
  13. RARRAY;
  14. }
  15. enum DebugExpr
  16. {
  17. EXPR_VALUE(value:Dynamic);
  18. EXPR_FIELD_REF(obj:Dynamic,member:String);
  19. EXPR_ARRAY_REF(obj:Dynamic,index:Int);
  20. EXPR_STACK_REF(name:String);
  21. }
  22. class DebugBase
  23. {
  24. var threadStopped:Bool;
  25. var stillDebugging:Bool;
  26. var inputThread:Thread;
  27. var debugQueue:Deque<Dynamic>;
  28. var files:Array<String>;
  29. var frame:Int;
  30. var stack:Array<StackItem>;
  31. var vars:Array<String>;
  32. public function new()
  33. {
  34. frame = -1;
  35. files = Debugger.getFiles();
  36. stillDebugging = true;
  37. threadStopped = false;
  38. Debugger.setThread();
  39. Debugger.setHandler(onDebug);
  40. debugQueue= new Deque<Dynamic>();
  41. inputThread = Thread.create(inputLoop);
  42. }
  43. function onDebug()
  44. {
  45. threadStopped = true;
  46. onStopped();
  47. while(threadStopped && stillDebugging)
  48. {
  49. var job = debugQueue.pop(true);
  50. job();
  51. }
  52. }
  53. function onHelp() { }
  54. function waitDebugger(inSendResult:Bool=true)
  55. {
  56. debugQueue.add( function() inputThread.sendMessage("ok") );
  57. var result = Thread.readMessage(true);
  58. if (inSendResult)
  59. {
  60. if (result!="ok")
  61. onResult("Debugger out of sync");
  62. else
  63. onResult("ok");
  64. }
  65. }
  66. function onStopped() { }
  67. function onRunning() { }
  68. function showWhere() { }
  69. function showFiles() { }
  70. function showBreakpoints() { }
  71. function onPrint(result:Dynamic) { }
  72. function getNextCommand() : String { return "bye"; }
  73. function onResult(inResult:String) { }
  74. function addBreakpoint(inFile:String, inLine:String)
  75. {
  76. var id = Std.parseInt(inFile);
  77. if (id==null)
  78. {
  79. for(idx in 0...files.length)
  80. if (files[idx]==inFile)
  81. {
  82. id = idx;
  83. break;
  84. }
  85. }
  86. if (id==null)
  87. onResult("Could not find file for " + inFile );
  88. else
  89. {
  90. Debugger.addBreakpoint(id,Std.parseInt(inLine));
  91. onResult("ok");
  92. }
  93. }
  94. static var dot:Int = ".".charCodeAt(0);
  95. static var quote:Int = "\"".charCodeAt(0);
  96. static var comma:Int = ",".charCodeAt(0);
  97. static var equals:Int = "=".charCodeAt(0);
  98. static var minus:Int = "-".charCodeAt(0);
  99. static var a_code:Int = "a".charCodeAt(0);
  100. static var z_code:Int = "z".charCodeAt(0);
  101. static var A_code:Int = "A".charCodeAt(0);
  102. static var Z_code:Int = "Z".charCodeAt(0);
  103. static var __code:Int = "_".charCodeAt(0);
  104. static var num0_code:Int = "0".charCodeAt(0);
  105. static var num9_code:Int = "9".charCodeAt(0);
  106. static var space_code:Int = " ".charCodeAt(0);
  107. static var lparent:Int = "(".charCodeAt(0);
  108. static var rparent:Int = ")".charCodeAt(0);
  109. static var larray:Int = "[".charCodeAt(0);
  110. static var rarray:Int = "]".charCodeAt(0);
  111. function tokenize(inString:String) : Array<DebugToken>
  112. {
  113. var len = inString.length;
  114. var result = new Array<DebugToken>();
  115. var idx = 0;
  116. while(idx<len)
  117. {
  118. var code = inString.charCodeAt(idx);
  119. // Identifier ...
  120. if ( (code>=a_code && code<=z_code) || (code>=A_code && code<=Z_code) || code==__code )
  121. {
  122. var start = idx++;
  123. while(idx<len)
  124. {
  125. code = inString.charCodeAt(idx);
  126. if ( (code>=a_code && code<=z_code) || (code>=A_code && code<=Z_code) || code==__code ||
  127. (code>=num0_code && code<num9_code) )
  128. idx++;
  129. else
  130. break;
  131. }
  132. result.push( IDENT( inString.substr(start, idx-start) ) );
  133. }
  134. else if (code==minus || (code>=num0_code && code<=num9_code) )
  135. {
  136. var start = idx++;
  137. while(idx<len)
  138. {
  139. code = inString.charCodeAt(idx);
  140. if (code==dot || (code>=num0_code && code<=num9_code) )
  141. idx++;
  142. else
  143. break;
  144. }
  145. var val = inString.substr(start, idx-start);
  146. var num = Std.parseFloat(val);
  147. if (!Math.isFinite(num))
  148. throw ("Bad constant '" + val + "'");
  149. result.push( CONST(num) );
  150. }
  151. else if (code==quote)
  152. {
  153. var start = ++idx;
  154. while(idx<len)
  155. {
  156. code = inString.charCodeAt(idx);
  157. if (code==quote)
  158. break;
  159. idx++;
  160. }
  161. var val = inString.substr(start, idx-start);
  162. result.push( CONST(val) );
  163. idx++;
  164. }
  165. else
  166. {
  167. switch(code)
  168. {
  169. case space_code : // do nothing
  170. case lparent : result.push( LPAREN );
  171. case rparent : result.push( RPAREN );
  172. case larray : result.push( LARRAY );
  173. case rarray : result.push( RARRAY );
  174. case dot : result.push( DOT );
  175. case comma : result.push( COMMA );
  176. case equals : result.push( EQUALS );
  177. }
  178. idx++;
  179. }
  180. }
  181. return result;
  182. }
  183. function resolve(inName:String) : DebugExpr
  184. {
  185. if (vars!=null)
  186. {
  187. for(v in vars)
  188. if (v==inName)
  189. return EXPR_STACK_REF(inName);
  190. }
  191. var cls = Type.resolveClass(inName);
  192. if (cls!=null)
  193. return EXPR_VALUE(cls);
  194. return null;
  195. }
  196. function getExpression(inTokens:Array<DebugToken>) : DebugExpr
  197. {
  198. var classPath = "";
  199. var expr:Dynamic = null;
  200. var tok = 0;
  201. var len = inTokens.length;
  202. while(tok < len)
  203. {
  204. switch(inTokens[tok])
  205. {
  206. case IDENT(name):
  207. if (expr!=null)
  208. throw "Misplaced '" + name + "'";
  209. expr = resolve(name);
  210. if (expr==null)
  211. classPath = name;
  212. tok++;
  213. case CONST(value):
  214. if (expr!=null || classPath!="")
  215. throw "Misplaced '" + value + "'";
  216. expr = EXPR_VALUE(value);
  217. tok++;
  218. case DOT:
  219. if (expr==null && classPath=="")
  220. throw "Bad '.' after null value";
  221. tok++;
  222. switch(inTokens[tok])
  223. {
  224. case IDENT(name):
  225. if (expr!=null)
  226. expr = EXPR_FIELD_REF(exprToDynamic(expr),name);
  227. else
  228. {
  229. var qname = classPath + "." + name;
  230. expr = resolve(qname);
  231. classPath = (expr==null) ? qname : "";
  232. }
  233. tok++;
  234. default: throw "Expected field after '.'";
  235. }
  236. case LPAREN:
  237. var args = new Array<Dynamic>();
  238. var lastComma = tok;
  239. var start = ++tok;
  240. var parenOpen = 1;
  241. var arrayOpen = 0;
  242. while(tok<len && (parenOpen!=0 || arrayOpen!=0) )
  243. {
  244. switch(inTokens[tok])
  245. {
  246. case LPAREN: parenOpen++;
  247. case RPAREN: parenOpen--;
  248. case LARRAY: arrayOpen++;
  249. case RARRAY: arrayOpen--;
  250. case COMMA:
  251. if (arrayOpen==0 && parenOpen==1 && expr!=null)
  252. {
  253. args.push( getValue( inTokens.slice(lastComma+1,tok) ) );
  254. lastComma = tok;
  255. }
  256. default:
  257. }
  258. tok++;
  259. }
  260. if (parenOpen!=0 || arrayOpen!=0)
  261. throw "Mismatched '(' "+parenOpen+"/"+arrayOpen;
  262. // Not function call...
  263. if (classPath!="")
  264. throw "Unresolved " + classPath;
  265. if (expr==null)
  266. {
  267. expr = EXPR_VALUE(getValue( inTokens.slice(start,tok-1) ));
  268. }
  269. else
  270. {
  271. if (lastComma+1 < tok-1)
  272. args.push( getValue( inTokens.slice(lastComma+1,tok-1) ) );
  273. expr = EXPR_VALUE( untyped expr.__Run( args ) );
  274. }
  275. case LARRAY:
  276. var start = ++tok;
  277. var parenOpen = 0;
  278. var arrayOpen = 1;
  279. while(tok<len && (parenOpen!=0 || arrayOpen!=0) )
  280. {
  281. switch(inTokens[tok])
  282. {
  283. case LPAREN: parenOpen++;
  284. case RPAREN: parenOpen--;
  285. case LARRAY: arrayOpen++;
  286. case RARRAY: arrayOpen--;
  287. default:
  288. }
  289. tok++;
  290. }
  291. if (parenOpen!=0 || arrayOpen!=0)
  292. throw "Mismatched '['";
  293. if (classPath!=null)
  294. throw "Unresolved " + classPath;
  295. if (expr==null)
  296. throw "Error taking index of null object";
  297. var val:Dynamic = getValue( inTokens.slice(start,tok) );
  298. if ( !Std.is(val,Int) )
  299. throw "Bad array index: " + val;
  300. expr = EXPR_ARRAY_REF(exprToDynamic(expr), Std.int(val));
  301. case RPAREN: throw "Misplaced ')'";
  302. case COMMA: throw "Misplaced ','";
  303. case EQUALS: throw("Misplaced '='");
  304. case RARRAY: throw "Misplaced ']'";
  305. }
  306. }
  307. if (classPath!="")
  308. throw "Unresolved " + classPath;
  309. return expr==null ? EXPR_VALUE(null) : expr;
  310. }
  311. function exprToDynamic(inExpr:DebugExpr)
  312. {
  313. switch(inExpr)
  314. {
  315. case EXPR_VALUE(value): return value;
  316. case EXPR_FIELD_REF(obj,member): return Reflect.getProperty(obj,member);
  317. case EXPR_ARRAY_REF(obj,index): return obj[index];
  318. case EXPR_STACK_REF(name): return Debugger.getStackVar(frame,name);
  319. }
  320. }
  321. function getValue(inTokens:Array<DebugToken>) : Dynamic
  322. {
  323. return exprToDynamic(getExpression(inTokens));
  324. }
  325. function print(inString:String)
  326. {
  327. var tokens:Array<DebugToken> = null;
  328. try
  329. {
  330. tokens = tokenize(inString);
  331. var result = getValue(tokens);
  332. onPrint(result);
  333. onResult("ok");
  334. }
  335. catch (e:Dynamic)
  336. {
  337. onResult("Error while printing : " + e);//+ ( tokens==null ? "" : " : " + tokens) );
  338. }
  339. }
  340. function set(inString:String)
  341. {
  342. var tokens:Array<DebugToken> = null;
  343. try
  344. {
  345. tokens = tokenize(inString);
  346. var equals_pos = -1;
  347. for(i in 0...tokens.length)
  348. {
  349. if (tokens[i]==EQUALS)
  350. {
  351. if (equals_pos>=0)
  352. throw "more than one '='";
  353. equals_pos = i;
  354. }
  355. }
  356. if (equals_pos<0)
  357. throw "use a = b syntax";
  358. if (equals_pos==0 || equals_pos==tokens.length-1)
  359. throw "Misplaced '='";
  360. var lhs = getExpression( tokens.slice(0,equals_pos) );
  361. var rhs = getValue( tokens.slice(equals_pos+1, tokens.length) );
  362. switch(lhs)
  363. {
  364. case EXPR_VALUE(value): throw "left hand side can't be set";
  365. case EXPR_FIELD_REF(obj,member): Reflect.setProperty(obj,member,rhs);
  366. case EXPR_ARRAY_REF(obj,index): obj[index] = rhs;
  367. case EXPR_STACK_REF(name): Debugger.setStackVar(frame,name,rhs);
  368. }
  369. }
  370. catch (e:Dynamic)
  371. {
  372. onResult("Error while setting : " + e);//+ ( tokens==null ? "" : " : " + tokens) );
  373. return;
  374. }
  375. onResult("ok");
  376. }
  377. function setFrame(inFrame:Int)
  378. {
  379. if (stack!=null && inFrame>0 && inFrame <= stack.length )
  380. {
  381. frame = inFrame;
  382. vars = Debugger.getStackVars(frame);
  383. }
  384. }
  385. function getStack()
  386. {
  387. stack = haxe.Stack.callStack();
  388. setFrame(1);
  389. }
  390. function checkStack()
  391. {
  392. if (threadStopped && stack==null)
  393. {
  394. debugQueue.add( getStack );
  395. waitDebugger(false);
  396. }
  397. }
  398. function run()
  399. {
  400. stack = null;
  401. vars = null;
  402. debugQueue.add( function() { threadStopped = false; inputThread.sendMessage("running"); } );
  403. var result = Thread.readMessage(true);
  404. onRunning();
  405. onResult("ok");
  406. }
  407. function inputLoop()
  408. {
  409. while(stillDebugging)
  410. {
  411. checkStack();
  412. var command = getNextCommand();
  413. var words = command.split(" ");
  414. switch(words[0])
  415. {
  416. case "":
  417. onResult("");
  418. // Do nothing
  419. case "bye":
  420. stillDebugging = false;
  421. debugQueue.add( function() { trace("bye"); } );
  422. onResult("ok");
  423. case "exit","quit":
  424. onResult("ok");
  425. Debugger.exit();
  426. case "break","b":
  427. if (words.length==1)
  428. {
  429. if (threadStopped)
  430. onResult("already stopped.");
  431. else
  432. {
  433. Debugger.setBreak(Debugger.BRK_ASAP);
  434. waitDebugger();
  435. }
  436. }
  437. else if (words.length==3)
  438. {
  439. addBreakpoint(words[1],words[2]);
  440. }
  441. else
  442. onResult("Usage: break [file line] - pause execution of one thread [when at certain point]");
  443. case "cont","c":
  444. if (!threadStopped)
  445. onResult("Already running.");
  446. else
  447. run();
  448. case "vars","v":
  449. if (!threadStopped || vars==null)
  450. onResult("Must break first.");
  451. else
  452. {
  453. onPrint(vars);
  454. onResult("ok");
  455. }
  456. case "frame","f":
  457. if (!threadStopped || stack==null )
  458. onResult("Must break first.");
  459. else
  460. {
  461. var f = Std.parseInt(words[1]);
  462. if (f<1 || f>stack.length )
  463. onResult("Stack out of range.");
  464. else
  465. {
  466. debugQueue.add( function() setFrame(f) );
  467. waitDebugger();
  468. }
  469. }
  470. case "where","w":
  471. if (!threadStopped || stack==null)
  472. onResult("Must break first.");
  473. else
  474. {
  475. showWhere();
  476. onResult("ok");
  477. }
  478. case "print","p":
  479. words.shift();
  480. print(words.join(" "));
  481. case "set","s":
  482. words.shift();
  483. set(words.join(" "));
  484. case "files","fi":
  485. {
  486. showFiles();
  487. onResult("ok");
  488. }
  489. case "breakpoints","bp":
  490. {
  491. showBreakpoints();
  492. onResult("ok");
  493. }
  494. case "delete","d":
  495. if (words[1]==null)
  496. {
  497. onResult("Usage : delete N");
  498. }
  499. else
  500. {
  501. var i = Std.parseInt(words[1]);
  502. Debugger.deleteBreakpoint(i);
  503. onResult("ok");
  504. }
  505. case "help","h","?":
  506. {
  507. onHelp();
  508. onResult("ok");
  509. }
  510. default:
  511. onResult("Unknown command:" + command);
  512. }
  513. }
  514. }
  515. }