diff --git a/haxe/CallStack.hx b/haxe/CallStack.hx new file mode 100644 index 000000000..92c5a1909 --- /dev/null +++ b/haxe/CallStack.hx @@ -0,0 +1,331 @@ +/* + * Copyright (C)2005-2012 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package haxe; + +/** + Elements return by `CallStack` methods. +**/ +enum StackItem { + CFunction; + Module( m : String ); + FilePos( s : Null, file : String, line : Int ); + Method( classname : String, method : String ); + LocalFunction( v : Int ); +} + +/** + Get informations about the call stack. +**/ +class CallStack { + + /** + Return the call stack elements, or an empty array if not available. + **/ + public static function callStack() : Array { + #if neko + var a = makeStack(untyped __dollar__callstack()); + a.shift(); // remove Stack.callStack() + return a; + #elseif flash9 + var a = makeStack( new flash.errors.Error().getStackTrace() ); + a.shift(); // remove Stack.callStack() + return a; + #elseif flash + return makeStack("$s"); + #elseif php + return makeStack("%s"); + #elseif cpp + var s:Array = untyped __global__.__hxcpp_get_call_stack(true); + return makeStack(s); + #elseif js + // https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi + var oldValue = (untyped Error).prepareStackTrace; + (untyped Error).prepareStackTrace = function (error, callsites :Array) { + var stack = []; + for (site in callsites) { + var method = null; + var fullName :String = site.getFunctionName(); + if (fullName != null) { + var idx = fullName.lastIndexOf("."); + if (idx >= 0) { + var className = fullName.substr(0, idx); + var methodName = fullName.substr(idx+1); + method = Method(className, methodName); + } + } + stack.push(FilePos(method, site.getFileName(), site.getLineNumber())); + } + return stack; + } + var a = makeStack(untyped __new__("Error").stack); + a.shift(); // remove Stack.callStack() + (untyped Error).prepareStackTrace = oldValue; + return a; + #elseif java + var stack = []; + for ( el in java.lang.Thread.currentThread().getStackTrace() ) { + var className = el.getClassName(); + var methodName = el.getMethodName(); + var fileName = el.getFileName(); + var lineNumber = el.getLineNumber(); + var method = Method( className, methodName ); + if ( fileName != null || lineNumber >= 0 ) { + stack.push( FilePos( method, fileName, lineNumber ) ); + } + else { + stack.push( method ); + } + } + stack.shift(); + stack.shift(); + stack.pop(); + return stack; + #elseif cs + return makeStack(new cs.system.diagnostics.StackTrace(1, true)); + #elseif python + var stack = []; + var infos = python.lib.Traceback.extract_stack(); + infos.pop(); + infos.reverse(); + for (elem in infos) + stack.push(FilePos(null, elem._1, elem._2)); + return stack; + #else + return []; // Unsupported + #end + } + + /** + Return the exception stack : this is the stack elements between + the place the last exception was thrown and the place it was + caught, or an empty array if not available. + **/ + #if cpp @:noStack #end /* Do not mess up the exception stack */ + public static function exceptionStack() : Array { + #if neko + return makeStack(untyped __dollar__excstack()); + #elseif as3 + return new Array(); + #elseif flash9 + var err : flash.errors.Error = untyped flash.Boot.lastError; + if( err == null ) return new Array(); + var a = makeStack( err.getStackTrace() ); + var c = callStack(); + var i = c.length - 1; + while( i > 0 ) { + if( Std.string(a[a.length-1]) == Std.string(c[i]) ) + a.pop(); + else + break; + i--; + } + return a; + #elseif flash + return makeStack("$e"); + #elseif php + return makeStack("%e"); + #elseif cpp + var s:Array = untyped __global__.__hxcpp_get_exception_stack(); + return makeStack(s); + #elseif java + var stack = []; + for ( el in java.internal.Exceptions.currentException().getStackTrace() ) { + var className = el.getClassName(); + var methodName = el.getMethodName(); + var fileName = el.getFileName(); + var lineNumber = el.getLineNumber(); + var method = Method( className, methodName ); + if ( fileName != null || lineNumber >= 0 ) { + stack.push( FilePos( method, fileName, lineNumber ) ); + } + else { + stack.push( method ); + } + } + // stack.shift(); + stack.shift(); + stack.pop(); + return stack; + #elseif cs + return makeStack(new cs.system.diagnostics.StackTrace(cs.internal.Exceptions.exception, true)); + #elseif python + var stack = []; + var exc = python.lib.Sys.exc_info(); + if (exc._3 != null) + { + var infos = python.lib.Traceback.extract_tb(exc._3); + infos.reverse(); + for (elem in infos) + stack.push(FilePos(null, elem._1, elem._2)); + } + return stack; + #else + return []; // Unsupported + #end + } + + /** + Returns a representation of the stack as a printable string. + **/ + public static function toString( stack : Array ) { + var b = new StringBuf(); + #if cpp + stack = stack.copy (); + stack.reverse(); + #end + for( s in stack ) { + b.add("\nCalled from "); + itemToString(b,s); + } + return b.toString(); + } + + private static function itemToString( b : StringBuf, s ) { + switch( s ) { + case CFunction: + b.add("a C function"); + case Module(m): + b.add("module "); + b.add(m); + case FilePos(s,file,line): + if( s != null ) { + itemToString(b,s); + b.add(" ("); + } + b.add(file); + b.add(" line "); + b.add(line); + if( s != null ) b.add(")"); + case Method(cname,meth): + b.add(cname); + b.add("."); + b.add(meth); + case LocalFunction(n): + b.add("local function #"); + b.add(n); + } + } + + #if cpp @:noStack #end /* Do not mess up the exception stack */ + private static function makeStack(s #if cs : cs.system.diagnostics.StackTrace #end) { + #if neko + var a = new Array(); + var l = untyped __dollar__asize(s); + var i = 0; + while( i < l ) { + var x = s[i++]; + if( x == null ) + a.unshift(CFunction); + else if( untyped __dollar__typeof(x) == __dollar__tstring ) + a.unshift(Module(new String(x))); + else + a.unshift(FilePos(null,new String(untyped x[0]),untyped x[1])); + } + return a; + #elseif flash9 + var a = new Array(); + var r = ~/at ([^\/]+?)\$?(\/[^\(]+)?\(\)(\[(.*?):([0-9]+)\])?/; + var rlambda = ~/^MethodInfo-([0-9]+)$/g; + while( r.match(s) ) { + var cl = r.matched(1).split("::").join("."); + var meth = r.matched(2); + var item; + if( meth == null ) { + if( rlambda.match(cl) ) + item = LocalFunction(Std.parseInt(rlambda.matched(1))); + else + item = Method(cl,"new"); + } else + item = Method(cl,meth.substr(1)); + if( r.matched(3) != null ) + item = FilePos( item, r.matched(4), Std.parseInt(r.matched(5)) ); + a.push(item); + s = r.matchedRight(); + } + return a; + #elseif flash + var a : Array = untyped __eval__(s); + var m = new Array(); + for( i in 0...a.length - if(s == "$s") 2 else 0 ) { + var d = a[i].split("::"); + m.unshift(Method(d[0],d[1])); + } + return m; + #elseif php + if (!untyped __call__("isset", __var__("GLOBALS", s))) + return []; + var a : Array = untyped __var__("GLOBALS", s); + var m = []; + for( i in 0...a.length - ((s == "%s") ? 2 : 0)) { + var d = a[i].split("::"); + m.unshift(Method(d[0],d[1])); + } + return m; + #elseif cpp + var stack : Array = s; + var m = new Array(); + for(func in stack) { + var words = func.split("::"); + if (words.length==0) + m.unshift(CFunction) + else if (words.length==2) + m.unshift(Method(words[0],words[1])); + else if (words.length==4) + m.unshift(FilePos( Method(words[0],words[1]),words[2],Std.parseInt(words[3]))); + } + return m; + #elseif js + if ((untyped __js__("typeof"))(s) == "string") { + // Return the raw lines in browsers that don't support prepareStackTrace + var stack : Array = s.split("\n"); + var m = []; + for( line in stack ) { + m.push(Module(line)); // A little weird, but better than nothing + } + return m; + } else { + return cast s; + } + #elseif cs + var stack = []; + for (i in 0...s.FrameCount) + { + var frame = s.GetFrame(i); + var m = frame.GetMethod(); + + var method = StackItem.Method(m.ReflectedType.ToString(), m.Name); + + var fileName = frame.GetFileName(); + var lineNumber = frame.GetFileLineNumber(); + + if (fileName != null || lineNumber >= 0) + stack.push(FilePos(method, fileName, lineNumber)); + else + stack.push(method); + } + return stack; + #else + return null; + #end + } + +}