CallStack.hx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /*
  2. * Copyright (C)2005-2016 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package haxe;
  23. /**
  24. Elements return by `CallStack` methods.
  25. **/
  26. enum StackItem {
  27. CFunction;
  28. Module( m : String );
  29. FilePos( s : Null<StackItem>, file : String, line : Int );
  30. Method( classname : String, method : String );
  31. LocalFunction( ?v : Int );
  32. }
  33. /**
  34. Get informations about the call stack.
  35. **/
  36. class CallStack {
  37. #if js
  38. static var lastException:js.Error;
  39. static function getStack(e:js.Error):Array<StackItem> {
  40. if (e == null) return [];
  41. // https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
  42. var oldValue = (untyped Error).prepareStackTrace;
  43. (untyped Error).prepareStackTrace = function (error, callsites :Array<Dynamic>) {
  44. var stack = [];
  45. for (site in callsites) {
  46. if (wrapCallSite != null) site = wrapCallSite(site);
  47. var method = null;
  48. var fullName :String = site.getFunctionName();
  49. if (fullName != null) {
  50. var idx = fullName.lastIndexOf(".");
  51. if (idx >= 0) {
  52. var className = fullName.substr(0, idx);
  53. var methodName = fullName.substr(idx+1);
  54. method = Method(className, methodName);
  55. }
  56. }
  57. stack.push(FilePos(method, site.getFileName(), site.getLineNumber()));
  58. }
  59. return stack;
  60. }
  61. var a = makeStack(e.stack);
  62. (untyped Error).prepareStackTrace = oldValue;
  63. return a;
  64. }
  65. // support for source-map-support module
  66. @:noCompletion
  67. public static var wrapCallSite:Dynamic->Dynamic;
  68. #end
  69. /**
  70. Return the call stack elements, or an empty array if not available.
  71. **/
  72. public static function callStack() : Array<StackItem> {
  73. #if neko
  74. var a = makeStack(untyped __dollar__callstack());
  75. a.shift(); // remove Stack.callStack()
  76. return a;
  77. #elseif flash
  78. var a = makeStack( new flash.errors.Error().getStackTrace() );
  79. a.shift(); // remove Stack.callStack()
  80. return a;
  81. #elseif php
  82. return makeStack("%s");
  83. #elseif cpp
  84. var s:Array<String> = untyped __global__.__hxcpp_get_call_stack(true);
  85. return makeStack(s);
  86. #elseif js
  87. try {
  88. throw new js.Error();
  89. } catch( e : Dynamic ) {
  90. var a = getStack(e);
  91. a.shift(); // remove Stack.callStack()
  92. return a;
  93. }
  94. #elseif java
  95. var stack = [];
  96. for ( el in java.lang.Thread.currentThread().getStackTrace() ) {
  97. var className = el.getClassName();
  98. var methodName = el.getMethodName();
  99. var fileName = el.getFileName();
  100. var lineNumber = el.getLineNumber();
  101. var method = Method( className, methodName );
  102. if ( fileName != null || lineNumber >= 0 ) {
  103. stack.push( FilePos( method, fileName, lineNumber ) );
  104. }
  105. else {
  106. stack.push( method );
  107. }
  108. }
  109. stack.shift();
  110. stack.shift();
  111. stack.pop();
  112. return stack;
  113. #elseif cs
  114. return makeStack(new cs.system.diagnostics.StackTrace(1, true));
  115. #elseif python
  116. var stack = [];
  117. var infos = python.lib.Traceback.extract_stack();
  118. infos.pop();
  119. infos.reverse();
  120. for (elem in infos)
  121. stack.push(FilePos(null, elem._1, elem._2));
  122. return stack;
  123. #elseif lua
  124. var stack = [];
  125. var infos = lua.Debug.traceback();
  126. var luastack = infos.split("\n").slice(2,-1);
  127. for (s in luastack){
  128. var parts = s.split(":");
  129. var file = parts[0];
  130. var line = parts[1];
  131. // TODO: Give more information for FilePos
  132. stack.push(FilePos(null, file, Std.parseInt(line)));
  133. }
  134. return stack;
  135. #else
  136. return []; // Unsupported
  137. #end
  138. }
  139. #if hl
  140. @:hlNative("std", "exception_stack") static function _getExceptionStack() : hl.types.NativeArray<hl.types.Bytes> { return null; }
  141. #end
  142. /**
  143. Return the exception stack : this is the stack elements between
  144. the place the last exception was thrown and the place it was
  145. caught, or an empty array if not available.
  146. **/
  147. #if cpp @:noStack #end /* Do not mess up the exception stack */
  148. public static function exceptionStack() : Array<StackItem> {
  149. #if neko
  150. return makeStack(untyped __dollar__excstack());
  151. #elseif as3
  152. return new Array();
  153. #elseif hl
  154. return makeStack(_getExceptionStack());
  155. #elseif flash
  156. var err : flash.errors.Error = untyped flash.Boot.lastError;
  157. if( err == null ) return new Array();
  158. var a = makeStack( err.getStackTrace() );
  159. var c = callStack();
  160. var i = c.length - 1;
  161. while( i > 0 ) {
  162. if( Std.string(a[a.length-1]) == Std.string(c[i]) )
  163. a.pop();
  164. else
  165. break;
  166. i--;
  167. }
  168. return a;
  169. #elseif php
  170. return makeStack("%e");
  171. #elseif cpp
  172. var s:Array<String> = untyped __global__.__hxcpp_get_exception_stack();
  173. return makeStack(s);
  174. #elseif java
  175. var stack = [];
  176. for ( el in java.internal.Exceptions.currentException().getStackTrace() ) {
  177. var className = el.getClassName();
  178. var methodName = el.getMethodName();
  179. var fileName = el.getFileName();
  180. var lineNumber = el.getLineNumber();
  181. var method = Method( className, methodName );
  182. if ( fileName != null || lineNumber >= 0 ) {
  183. stack.push( FilePos( method, fileName, lineNumber ) );
  184. }
  185. else {
  186. stack.push( method );
  187. }
  188. }
  189. // stack.shift();
  190. stack.shift();
  191. stack.pop();
  192. return stack;
  193. #elseif cs
  194. return makeStack(new cs.system.diagnostics.StackTrace(cs.internal.Exceptions.exception, true));
  195. #elseif python
  196. var stack = [];
  197. var exc = python.lib.Sys.exc_info();
  198. if (exc._3 != null)
  199. {
  200. var infos = python.lib.Traceback.extract_tb(exc._3);
  201. infos.reverse();
  202. for (elem in infos)
  203. stack.push(FilePos(null, elem._1, elem._2));
  204. }
  205. return stack;
  206. #elseif js
  207. return untyped __define_feature__("haxe.CallStack.exceptionStack", getStack(lastException));
  208. #else
  209. return []; // Unsupported
  210. #end
  211. }
  212. /**
  213. Returns a representation of the stack as a printable string.
  214. **/
  215. public static function toString( stack : Array<StackItem> ) {
  216. var b = new StringBuf();
  217. for( s in stack ) {
  218. b.add("\nCalled from ");
  219. itemToString(b,s);
  220. }
  221. return b.toString();
  222. }
  223. private static function itemToString( b : StringBuf, s ) {
  224. switch( s ) {
  225. case CFunction:
  226. b.add("a C function");
  227. case Module(m):
  228. b.add("module ");
  229. b.add(m);
  230. case FilePos(s,file,line):
  231. if( s != null ) {
  232. itemToString(b,s);
  233. b.add(" (");
  234. }
  235. b.add(file);
  236. b.add(" line ");
  237. b.add(line);
  238. if( s != null ) b.add(")");
  239. case Method(cname,meth):
  240. b.add(cname);
  241. b.add(".");
  242. b.add(meth);
  243. case LocalFunction(n):
  244. b.add("local function #");
  245. b.add(n);
  246. }
  247. }
  248. #if cpp @:noStack #end /* Do not mess up the exception stack */
  249. private static function makeStack(s #if cs : cs.system.diagnostics.StackTrace #elseif hl : hl.types.NativeArray<hl.types.Bytes> #end) {
  250. #if neko
  251. var a = new Array();
  252. var l = untyped __dollar__asize(s);
  253. var i = 0;
  254. while( i < l ) {
  255. var x = s[i++];
  256. if( x == null )
  257. a.unshift(CFunction);
  258. else if( untyped __dollar__typeof(x) == __dollar__tstring )
  259. a.unshift(Module(new String(x)));
  260. else
  261. a.unshift(FilePos(null,new String(untyped x[0]),untyped x[1]));
  262. }
  263. return a;
  264. #elseif flash
  265. var a = new Array();
  266. var r = ~/at ([^\/]+?)\$?(\/[^\(]+)?\(\)(\[(.*?):([0-9]+)\])?/;
  267. var rlambda = ~/^MethodInfo-([0-9]+)$/g;
  268. while( r.match(s) ) {
  269. var cl = r.matched(1).split("::").join(".");
  270. var meth = r.matched(2);
  271. var item;
  272. if( meth == null ) {
  273. if( rlambda.match(cl) )
  274. item = LocalFunction(Std.parseInt(rlambda.matched(1)));
  275. else
  276. item = Method(cl,"new");
  277. } else
  278. item = Method(cl,meth.substr(1));
  279. if( r.matched(3) != null )
  280. item = FilePos( item, r.matched(4), Std.parseInt(r.matched(5)) );
  281. a.push(item);
  282. s = r.matchedRight();
  283. }
  284. return a;
  285. #elseif php
  286. if (!untyped __call__("isset", __var__("GLOBALS", s)))
  287. return [];
  288. var a : Array<String> = untyped __var__("GLOBALS", s);
  289. var m = [];
  290. for( i in 0...a.length - ((s == "%s") ? 2 : 0)) {
  291. var d = a[i].split("::");
  292. m.unshift(Method(d[0],d[1]));
  293. }
  294. return m;
  295. #elseif cpp
  296. var stack : Array<String> = s;
  297. var m = new Array<StackItem>();
  298. for(func in stack) {
  299. var words = func.split("::");
  300. if (words.length==0)
  301. m.push(CFunction)
  302. else if (words.length==2)
  303. m.push(Method(words[0],words[1]));
  304. else if (words.length==4)
  305. m.push(FilePos( Method(words[0],words[1]),words[2],Std.parseInt(words[3])));
  306. }
  307. return m;
  308. #elseif js
  309. if (s == null) {
  310. return [];
  311. } else if ((untyped __js__("typeof"))(s) == "string") {
  312. // Return the raw lines in browsers that don't support prepareStackTrace
  313. var stack : Array<String> = s.split("\n");
  314. if( stack[0] == "Error" ) stack.shift();
  315. var m = [];
  316. var rie10 = ~/^ at ([A-Za-z0-9_. ]+) \(([^)]+):([0-9]+):([0-9]+)\)$/;
  317. for( line in stack ) {
  318. if( rie10.match(line) ) {
  319. var path = rie10.matched(1).split(".");
  320. var meth = path.pop();
  321. var file = rie10.matched(2);
  322. var line = Std.parseInt(rie10.matched(3));
  323. m.push(FilePos( meth == "Anonymous function" ? LocalFunction() : meth == "Global code" ? null : Method(path.join("."),meth), file, line ));
  324. } else
  325. m.push(Module(StringTools.trim(line))); // A little weird, but better than nothing
  326. }
  327. return m;
  328. } else {
  329. return cast s;
  330. }
  331. #elseif cs
  332. var stack = [];
  333. for (i in 0...s.FrameCount)
  334. {
  335. var frame = s.GetFrame(i);
  336. var m = frame.GetMethod();
  337. if (m == null) {
  338. continue;
  339. }
  340. var method = StackItem.Method(m.ReflectedType.ToString(), m.Name);
  341. var fileName = frame.GetFileName();
  342. var lineNumber = frame.GetFileLineNumber();
  343. if (fileName != null || lineNumber >= 0)
  344. stack.push(FilePos(method, fileName, lineNumber));
  345. else
  346. stack.push(method);
  347. }
  348. return stack;
  349. #elseif hl
  350. var stack = [];
  351. for( i in 0...s.length-1 ) {
  352. var str = @:privateAccess String.fromUCS2(s[i]);
  353. stack.push(Module(str));
  354. }
  355. return stack;
  356. #else
  357. return null;
  358. #end
  359. }
  360. }