CallStack.hx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. * Copyright (C)2005-2019 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. import php.*;
  24. private typedef NativeTrace = NativeIndexedArray<NativeAssocArray<Dynamic>>;
  25. enum StackItem {
  26. CFunction;
  27. Module(m:String);
  28. FilePos(s:Null<StackItem>, file:String, line:Int, ?column:Null<Int>);
  29. Method(classname:Null<String>, method:String);
  30. LocalFunction(?v:Int);
  31. }
  32. class CallStack {
  33. /**
  34. If defined this function will be used to transform call stack entries.
  35. @param String - generated php file name.
  36. @param Int - Line number in generated file.
  37. **/
  38. static public var mapPosition:String->Int->Null<{?source:String, ?originalLine:Int}>;
  39. @:ifFeature("haxe.CallStack.exceptionStack")
  40. static var lastExceptionTrace:NativeTrace;
  41. public static function callStack():Array<StackItem> {
  42. return makeStack(Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS));
  43. }
  44. public static function exceptionStack():Array<StackItem> {
  45. return makeStack(lastExceptionTrace == null ? new NativeIndexedArray() : lastExceptionTrace);
  46. }
  47. public static function toString(stack:Array<StackItem>) {
  48. var b = new StringBuf();
  49. for (s in stack) {
  50. b.add("\nCalled from ");
  51. itemToString(b, s);
  52. }
  53. return b.toString();
  54. }
  55. static function itemToString(b:StringBuf, s) {
  56. switch (s) {
  57. case CFunction:
  58. b.add("a C function");
  59. case Module(m):
  60. b.add("module ");
  61. b.add(m);
  62. case FilePos(s, file, line, _):
  63. if (s != null) {
  64. itemToString(b, s);
  65. b.add(" (");
  66. }
  67. b.add(file);
  68. b.add(" line ");
  69. b.add(line);
  70. if (s != null)
  71. b.add(")");
  72. case Method(cname, meth):
  73. b.add(cname == null ? "<unknown>" : cname);
  74. b.add(".");
  75. b.add(meth);
  76. case LocalFunction(n):
  77. b.add("local function");
  78. }
  79. }
  80. @:ifFeature("haxe.CallStack.exceptionStack")
  81. static function saveExceptionTrace(e:Throwable):Void {
  82. lastExceptionTrace = e.getTrace();
  83. // Reduce exception stack to the place where exception was caught
  84. var currentTrace = Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS);
  85. var count = Global.count(currentTrace);
  86. for (i in -(count - 1)...1) {
  87. var exceptionEntry:NativeAssocArray<Dynamic> = Global.end(lastExceptionTrace);
  88. if (!Global.isset(exceptionEntry['file']) || !Global.isset(currentTrace[-i]['file'])) {
  89. Global.array_pop(lastExceptionTrace);
  90. } else if (currentTrace[-i]['file'] == exceptionEntry['file'] && currentTrace[-i]['line'] == exceptionEntry['line']) {
  91. Global.array_pop(lastExceptionTrace);
  92. } else {
  93. break;
  94. }
  95. }
  96. // Remove arguments from trace to avoid blocking some objects from GC
  97. var count = Global.count(lastExceptionTrace);
  98. for (i in 0...count) {
  99. lastExceptionTrace[i]['args'] = new NativeArray();
  100. }
  101. var thrownAt = new NativeAssocArray<Dynamic>();
  102. thrownAt['function'] = '';
  103. thrownAt['line'] = e.getLine();
  104. thrownAt['file'] = e.getFile();
  105. thrownAt['class'] = '';
  106. thrownAt['args'] = new NativeArray();
  107. Global.array_unshift(lastExceptionTrace, thrownAt);
  108. }
  109. static function makeStack(native:NativeTrace):Array<StackItem> {
  110. var result = [];
  111. var count = Global.count(native);
  112. for (i in 0...count) {
  113. var entry = native[i];
  114. var item = null;
  115. if (i + 1 < count) {
  116. var next = native[i + 1];
  117. if (!Global.isset(next['function']))
  118. next['function'] = '';
  119. if (!Global.isset(next['class']))
  120. next['class'] = '';
  121. if ((next['function'] : String).indexOf('{closure}') >= 0) {
  122. item = LocalFunction();
  123. } else if (Global.strlen(next['class']) > 0 && Global.strlen(next['function']) > 0) {
  124. var cls = Boot.getClassName(next['class']);
  125. item = Method(cls, next['function']);
  126. }
  127. }
  128. if (Global.isset(entry['file'])) {
  129. if (mapPosition != null) {
  130. var pos = mapPosition(entry['file'], entry['line']);
  131. if (pos != null && pos.source != null && pos.originalLine != null) {
  132. entry['file'] = pos.source;
  133. entry['line'] = pos.originalLine;
  134. }
  135. }
  136. result.push(FilePos(item, entry['file'], entry['line']));
  137. } else if (item != null) {
  138. result.push(item);
  139. }
  140. }
  141. return result;
  142. }
  143. }