123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590 |
- package cpp.vm;
- import haxe.Stack;
- enum DebugToken
- {
- IDENT(name:String);
- CONST(value:Dynamic);
- DOT;
- LPAREN;
- RPAREN;
- COMMA;
- EQUALS;
- LARRAY;
- RARRAY;
- }
- enum DebugExpr
- {
- EXPR_VALUE(value:Dynamic);
- EXPR_FIELD_REF(obj:Dynamic,member:String);
- EXPR_ARRAY_REF(obj:Dynamic,index:Int);
- EXPR_STACK_REF(name:String);
- }
- class DebugBase
- {
- var threadStopped:Bool;
- var stillDebugging:Bool;
- var inputThread:Thread;
- var debugQueue:Deque<Dynamic>;
- var files:Array<String>;
- var frame:Int;
- var stack:Array<StackItem>;
- var vars:Array<String>;
-
- public function new()
- {
- frame = -1;
- files = Debugger.getFiles();
- stillDebugging = true;
- threadStopped = false;
- Debugger.setThread();
- Debugger.setHandler(onDebug);
- debugQueue= new Deque<Dynamic>();
- inputThread = Thread.create(inputLoop);
- }
- function onDebug()
- {
- threadStopped = true;
- onStopped();
- while(threadStopped && stillDebugging)
- {
- var job = debugQueue.pop(true);
- job();
- }
- }
- function onHelp() { }
- function waitDebugger(inSendResult:Bool=true)
- {
- debugQueue.add( function() inputThread.sendMessage("ok") );
- var result = Thread.readMessage(true);
- if (inSendResult)
- {
- if (result!="ok")
- onResult("Debugger out of sync");
- else
- onResult("ok");
- }
- }
- function onStopped() { }
- function onRunning() { }
- function showWhere() { }
- function showFiles() { }
- function showBreakpoints() { }
- function onPrint(result:Dynamic) { }
- function getNextCommand() : String { return "bye"; }
- function onResult(inResult:String) { }
- function addBreakpoint(inFile:String, inLine:String)
- {
- var id = Std.parseInt(inFile);
- if (id==null)
- {
- for(idx in 0...files.length)
- if (files[idx]==inFile)
- {
- id = idx;
- break;
- }
- }
- if (id==null)
- onResult("Could not find file for " + inFile );
- else
- {
- Debugger.addBreakpoint(id,Std.parseInt(inLine));
- onResult("ok");
- }
-
- }
- static var dot:Int = ".".charCodeAt(0);
- static var quote:Int = "\"".charCodeAt(0);
- static var comma:Int = ",".charCodeAt(0);
- static var equals:Int = "=".charCodeAt(0);
- static var minus:Int = "-".charCodeAt(0);
- static var a_code:Int = "a".charCodeAt(0);
- static var z_code:Int = "z".charCodeAt(0);
- static var A_code:Int = "A".charCodeAt(0);
- static var Z_code:Int = "Z".charCodeAt(0);
- static var __code:Int = "_".charCodeAt(0);
- static var num0_code:Int = "0".charCodeAt(0);
- static var num9_code:Int = "9".charCodeAt(0);
- static var space_code:Int = " ".charCodeAt(0);
- static var lparent:Int = "(".charCodeAt(0);
- static var rparent:Int = ")".charCodeAt(0);
- static var larray:Int = "[".charCodeAt(0);
- static var rarray:Int = "]".charCodeAt(0);
- function tokenize(inString:String) : Array<DebugToken>
- {
- var len = inString.length;
- var result = new Array<DebugToken>();
- var idx = 0;
- while(idx<len)
- {
- var code = inString.charCodeAt(idx);
- // Identifier ...
- if ( (code>=a_code && code<=z_code) || (code>=A_code && code<=Z_code) || code==__code )
- {
- var start = idx++;
- while(idx<len)
- {
- code = inString.charCodeAt(idx);
- if ( (code>=a_code && code<=z_code) || (code>=A_code && code<=Z_code) || code==__code ||
- (code>=num0_code && code<num9_code) )
- idx++;
- else
- break;
- }
- result.push( IDENT( inString.substr(start, idx-start) ) );
- }
- else if (code==minus || (code>=num0_code && code<=num9_code) )
- {
- var start = idx++;
- while(idx<len)
- {
- code = inString.charCodeAt(idx);
- if (code==dot || (code>=num0_code && code<=num9_code) )
- idx++;
- else
- break;
- }
- var val = inString.substr(start, idx-start);
- var num = Std.parseFloat(val);
- if (!Math.isFinite(num))
- throw ("Bad constant '" + val + "'");
- result.push( CONST(num) );
-
- }
- else if (code==quote)
- {
- var start = ++idx;
- while(idx<len)
- {
- code = inString.charCodeAt(idx);
- if (code==quote)
- break;
- idx++;
- }
- var val = inString.substr(start, idx-start);
- result.push( CONST(val) );
- idx++;
- }
- else
- {
- switch(code)
- {
- case space_code : // do nothing
- case lparent : result.push( LPAREN );
- case rparent : result.push( RPAREN );
- case larray : result.push( LARRAY );
- case rarray : result.push( RARRAY );
- case dot : result.push( DOT );
- case comma : result.push( COMMA );
- case equals : result.push( EQUALS );
- }
- idx++;
- }
- }
- return result;
- }
- function resolve(inName:String) : DebugExpr
- {
- if (vars!=null)
- {
- for(v in vars)
- if (v==inName)
- return EXPR_STACK_REF(inName);
- }
- var cls = Type.resolveClass(inName);
- if (cls!=null)
- return EXPR_VALUE(cls);
- return null;
- }
- function getExpression(inTokens:Array<DebugToken>) : DebugExpr
- {
- var classPath = "";
- var expr:Dynamic = null;
- var tok = 0;
- var len = inTokens.length;
- while(tok < len)
- {
- switch(inTokens[tok])
- {
- case IDENT(name):
- if (expr!=null)
- throw "Misplaced '" + name + "'";
- expr = resolve(name);
- if (expr==null)
- classPath = name;
- tok++;
- case CONST(value):
- if (expr!=null || classPath!="")
- throw "Misplaced '" + value + "'";
- expr = EXPR_VALUE(value);
- tok++;
- case DOT:
- if (expr==null && classPath=="")
- throw "Bad '.' after null value";
- tok++;
- switch(inTokens[tok])
- {
- case IDENT(name):
- if (expr!=null)
- expr = EXPR_FIELD_REF(exprToDynamic(expr),name);
- else
- {
- var qname = classPath + "." + name;
- expr = resolve(qname);
- classPath = (expr==null) ? qname : "";
- }
- tok++;
- default: throw "Expected field after '.'";
- }
- case LPAREN:
- var args = new Array<Dynamic>();
- var lastComma = tok;
- var start = ++tok;
- var parenOpen = 1;
- var arrayOpen = 0;
- while(tok<len && (parenOpen!=0 || arrayOpen!=0) )
- {
- switch(inTokens[tok])
- {
- case LPAREN: parenOpen++;
- case RPAREN: parenOpen--;
- case LARRAY: arrayOpen++;
- case RARRAY: arrayOpen--;
- case COMMA:
- if (arrayOpen==0 && parenOpen==1 && expr!=null)
- {
- args.push( getValue( inTokens.slice(lastComma+1,tok) ) );
- lastComma = tok;
- }
- default:
- }
- tok++;
- }
- if (parenOpen!=0 || arrayOpen!=0)
- throw "Mismatched '(' "+parenOpen+"/"+arrayOpen;
- // Not function call...
- if (classPath!="")
- throw "Unresolved " + classPath;
- if (expr==null)
- {
- expr = EXPR_VALUE(getValue( inTokens.slice(start,tok-1) ));
- }
- else
- {
- if (lastComma+1 < tok-1)
- args.push( getValue( inTokens.slice(lastComma+1,tok-1) ) );
- expr = EXPR_VALUE( untyped expr.__Run( args ) );
- }
- case LARRAY:
- var start = ++tok;
- var parenOpen = 0;
- var arrayOpen = 1;
- while(tok<len && (parenOpen!=0 || arrayOpen!=0) )
- {
- switch(inTokens[tok])
- {
- case LPAREN: parenOpen++;
- case RPAREN: parenOpen--;
- case LARRAY: arrayOpen++;
- case RARRAY: arrayOpen--;
- default:
- }
- tok++;
- }
- if (parenOpen!=0 || arrayOpen!=0)
- throw "Mismatched '['";
- if (classPath!=null)
- throw "Unresolved " + classPath;
- if (expr==null)
- throw "Error taking index of null object";
- var val:Dynamic = getValue( inTokens.slice(start,tok) );
- if ( !Std.is(val,Int) )
- throw "Bad array index: " + val;
- expr = EXPR_ARRAY_REF(exprToDynamic(expr), Std.int(val));
- case RPAREN: throw "Misplaced ')'";
- case COMMA: throw "Misplaced ','";
- case EQUALS: throw("Misplaced '='");
- case RARRAY: throw "Misplaced ']'";
- }
- }
- if (classPath!="")
- throw "Unresolved " + classPath;
- return expr==null ? EXPR_VALUE(null) : expr;
- }
- function exprToDynamic(inExpr:DebugExpr)
- {
- switch(inExpr)
- {
- case EXPR_VALUE(value): return value;
- case EXPR_FIELD_REF(obj,member): return Reflect.getProperty(obj,member);
- case EXPR_ARRAY_REF(obj,index): return obj[index];
- case EXPR_STACK_REF(name): return Debugger.getStackVar(frame,name);
- }
- }
- function getValue(inTokens:Array<DebugToken>) : Dynamic
- {
- return exprToDynamic(getExpression(inTokens));
- }
- function print(inString:String)
- {
- var tokens:Array<DebugToken> = null;
- try
- {
- tokens = tokenize(inString);
- var result = getValue(tokens);
- onPrint(result);
- onResult("ok");
- }
- catch (e:Dynamic)
- {
- onResult("Error while printing : " + e);//+ ( tokens==null ? "" : " : " + tokens) );
- }
- }
- function set(inString:String)
- {
- var tokens:Array<DebugToken> = null;
- try
- {
- tokens = tokenize(inString);
- var equals_pos = -1;
- for(i in 0...tokens.length)
- {
- if (tokens[i]==EQUALS)
- {
- if (equals_pos>=0)
- throw "more than one '='";
- equals_pos = i;
- }
- }
- if (equals_pos<0)
- throw "use a = b syntax";
- if (equals_pos==0 || equals_pos==tokens.length-1)
- throw "Misplaced '='";
- var lhs = getExpression( tokens.slice(0,equals_pos) );
- var rhs = getValue( tokens.slice(equals_pos+1, tokens.length) );
- switch(lhs)
- {
- case EXPR_VALUE(value): throw "left hand side can't be set";
- case EXPR_FIELD_REF(obj,member): Reflect.setProperty(obj,member,rhs);
- case EXPR_ARRAY_REF(obj,index): obj[index] = rhs;
- case EXPR_STACK_REF(name): Debugger.setStackVar(frame,name,rhs);
- }
- }
- catch (e:Dynamic)
- {
- onResult("Error while setting : " + e);//+ ( tokens==null ? "" : " : " + tokens) );
- return;
- }
- onResult("ok");
- }
- function setFrame(inFrame:Int)
- {
- if (stack!=null && inFrame>0 && inFrame <= stack.length )
- {
- frame = inFrame;
- vars = Debugger.getStackVars(frame);
- }
- }
- function getStack()
- {
- stack = haxe.Stack.callStack();
- setFrame(1);
- }
- function checkStack()
- {
- if (threadStopped && stack==null)
- {
- debugQueue.add( getStack );
- waitDebugger(false);
- }
- }
- function run()
- {
- stack = null;
- vars = null;
- debugQueue.add( function() { threadStopped = false; inputThread.sendMessage("running"); } );
- var result = Thread.readMessage(true);
- onRunning();
- onResult("ok");
- }
- function inputLoop()
- {
- while(stillDebugging)
- {
- checkStack();
- var command = getNextCommand();
- var words = command.split(" ");
- switch(words[0])
- {
- case "":
- onResult("");
- // Do nothing
- case "bye":
- stillDebugging = false;
- debugQueue.add( function() { trace("bye"); } );
- onResult("ok");
- case "exit","quit":
- onResult("ok");
- Debugger.exit();
- case "break","b":
- if (words.length==1)
- {
- if (threadStopped)
- onResult("already stopped.");
- else
- {
- Debugger.setBreak(Debugger.BRK_ASAP);
- waitDebugger();
- }
- }
- else if (words.length==3)
- {
- addBreakpoint(words[1],words[2]);
- }
- else
- onResult("Usage: break [file line] - pause execution of one thread [when at certain point]");
- case "cont","c":
- if (!threadStopped)
- onResult("Already running.");
- else
- run();
- case "vars","v":
- if (!threadStopped || vars==null)
- onResult("Must break first.");
- else
- {
- onPrint(vars);
- onResult("ok");
- }
- case "frame","f":
- if (!threadStopped || stack==null )
- onResult("Must break first.");
- else
- {
- var f = Std.parseInt(words[1]);
- if (f<1 || f>stack.length )
- onResult("Stack out of range.");
- else
- {
- debugQueue.add( function() setFrame(f) );
- waitDebugger();
- }
- }
-
- case "where","w":
- if (!threadStopped || stack==null)
- onResult("Must break first.");
- else
- {
- showWhere();
- onResult("ok");
- }
- case "print","p":
- words.shift();
- print(words.join(" "));
- case "set","s":
- words.shift();
- set(words.join(" "));
- case "files","fi":
- {
- showFiles();
- onResult("ok");
- }
- case "breakpoints","bp":
- {
- showBreakpoints();
- onResult("ok");
- }
- case "delete","d":
- if (words[1]==null)
- {
- onResult("Usage : delete N");
- }
- else
- {
- var i = Std.parseInt(words[1]);
- Debugger.deleteBreakpoint(i);
- onResult("ok");
- }
- case "help","h","?":
- {
- onHelp();
- onResult("ok");
- }
-
- default:
- onResult("Unknown command:" + command);
- }
- }
- }
- }
|