NativeStackTrace.hx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package haxe;
  2. import php.*;
  3. import haxe.CallStack.StackItem;
  4. private typedef NativeTrace = NativeIndexedArray<NativeAssocArray<Dynamic>>;
  5. /**
  6. Do not use manually.
  7. **/
  8. @:dox(hide)
  9. @:noCompletion
  10. @:allow(haxe.Exception)
  11. class NativeStackTrace {
  12. /**
  13. If defined this function will be used to transform call stack entries.
  14. @param String - generated php file name.
  15. @param Int - Line number in generated file.
  16. **/
  17. static public var mapPosition:String->Int->Null<{?source:String, ?originalLine:Int}>;
  18. static var lastExceptionTrace:Null<NativeTrace>;
  19. @:ifFeature('haxe.NativeStackTrace.exceptionStack')
  20. static public function saveStack(e:Throwable) {
  21. var nativeTrace = e.getTrace();
  22. // Reduce exception stack to the place where exception was caught
  23. var currentTrace = Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS);
  24. var count = Global.count(currentTrace);
  25. for (i in -(count - 1)...1) {
  26. var exceptionEntry:NativeAssocArray<Dynamic> = Global.end(nativeTrace);
  27. if (!Global.isset(exceptionEntry['file']) || !Global.isset(currentTrace[-i]['file'])) {
  28. Global.array_pop(nativeTrace);
  29. } else if (currentTrace[-i]['file'] == exceptionEntry['file'] && currentTrace[-i]['line'] == exceptionEntry['line']) {
  30. Global.array_pop(nativeTrace);
  31. } else {
  32. break;
  33. }
  34. }
  35. // Remove arguments from trace to avoid blocking some objects from GC
  36. var count = Global.count(nativeTrace);
  37. for (i in 0...count) {
  38. nativeTrace[i]['args'] = new NativeArray();
  39. }
  40. lastExceptionTrace = complementTrace(nativeTrace, e);
  41. }
  42. static public inline function callStack():NativeTrace {
  43. return Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS);
  44. }
  45. static public function exceptionStack():NativeTrace {
  46. return lastExceptionTrace == null ? new NativeIndexedArray() : lastExceptionTrace;
  47. }
  48. static public function toHaxe(native:NativeTrace, skip:Int = 0):Array<StackItem> {
  49. var result = [];
  50. var count = Global.count(native);
  51. for (i in 0...count) {
  52. if(skip > i) {
  53. continue;
  54. }
  55. var entry = native[i];
  56. var item = null;
  57. if (i + 1 < count) {
  58. var next = native[i + 1];
  59. if (!Global.isset(next['function']))
  60. next['function'] = '';
  61. if (!Global.isset(next['class']))
  62. next['class'] = '';
  63. if ((next['function'] : String).indexOf('{closure}') >= 0) {
  64. item = LocalFunction();
  65. } else if (Global.strlen(next['class']) > 0 && Global.strlen(next['function']) > 0) {
  66. var cls = Boot.getClassName(next['class']);
  67. item = Method(cls, next['function']);
  68. }
  69. }
  70. if (Global.isset(entry['file'])) {
  71. if (mapPosition != null) {
  72. var pos = mapPosition(entry['file'], entry['line']);
  73. if (pos != null && pos.source != null && pos.originalLine != null) {
  74. entry['file'] = pos.source;
  75. entry['line'] = pos.originalLine;
  76. }
  77. }
  78. result.push(FilePos(item, entry['file'], entry['line']));
  79. } else if (item != null) {
  80. result.push(item);
  81. }
  82. }
  83. return result;
  84. }
  85. static function complementTrace(nativeTrace:NativeTrace, e:Throwable):NativeTrace {
  86. var thrownAt = new NativeAssocArray<Dynamic>();
  87. thrownAt['function'] = '';
  88. thrownAt['line'] = e.getLine();
  89. thrownAt['file'] = e.getFile();
  90. thrownAt['class'] = '';
  91. thrownAt['args'] = new NativeArray();
  92. Global.array_unshift(nativeTrace, thrownAt);
  93. return nativeTrace;
  94. }
  95. }