CallStack.hx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. /**
  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, ?column:Int);
  30. Method(classname:Null<String>, method:String);
  31. LocalFunction(?v:Int);
  32. }
  33. /**
  34. Get information about the call stack.
  35. **/
  36. @:allow(haxe.Exception)
  37. @:using(haxe.CallStack)
  38. abstract CallStack(Array<StackItem>) from Array<StackItem> {
  39. /**
  40. The length of this stack.
  41. **/
  42. public var length(get,never):Int;
  43. inline function get_length():Int return this.length;
  44. /**
  45. Return the call stack elements, or an empty array if not available.
  46. **/
  47. public static function callStack():Array<StackItem> {
  48. return NativeStackTrace.toHaxe(NativeStackTrace.callStack());
  49. }
  50. /**
  51. Return the exception stack : this is the stack elements between
  52. the place the last exception was thrown and the place it was
  53. caught, or an empty array if not available.
  54. Set `fullStack` parameter to true in order to return the full exception stack.
  55. May not work if catch type was a derivative from `haxe.Exception`.
  56. **/
  57. public static function exceptionStack( fullStack = false ):Array<StackItem> {
  58. var eStack:CallStack = NativeStackTrace.toHaxe(NativeStackTrace.exceptionStack());
  59. return (fullStack ? eStack : eStack.subtract(callStack())).asArray();
  60. }
  61. /**
  62. Returns a representation of the stack as a printable string.
  63. **/
  64. static public function toString(stack:CallStack):String {
  65. var b = new StringBuf();
  66. for (s in stack.asArray()) {
  67. b.add('\nCalled from ');
  68. itemToString(b, s);
  69. }
  70. return b.toString();
  71. }
  72. /**
  73. Returns a range of entries of current stack from the beginning to the the
  74. common part of this and `stack`.
  75. **/
  76. public function subtract(stack:CallStack):CallStack {
  77. var startIndex = -1;
  78. var i = -1;
  79. while(++i < this.length) {
  80. for(j in 0...stack.length) {
  81. if(equalItems(this[i], stack[j])) {
  82. if(startIndex < 0) {
  83. startIndex = i;
  84. }
  85. ++i;
  86. if(i >= this.length) break;
  87. } else {
  88. startIndex = -1;
  89. }
  90. }
  91. if(startIndex >= 0) break;
  92. }
  93. return startIndex >= 0 ? this.slice(0, startIndex) : this;
  94. }
  95. /**
  96. Make a copy of the stack.
  97. **/
  98. public inline function copy():CallStack {
  99. return this.copy();
  100. }
  101. @:arrayAccess public inline function get(index:Int):StackItem {
  102. return this[index];
  103. }
  104. inline function asArray():Array<StackItem> {
  105. return this;
  106. }
  107. static function equalItems(item1:Null<StackItem>, item2:Null<StackItem>):Bool {
  108. return switch([item1, item2]) {
  109. case [null, null]: true;
  110. case [CFunction, CFunction]: true;
  111. case [Module(m1), Module(m2)]:
  112. m1 == m2;
  113. case [FilePos(item1, file1, line1, col1), FilePos(item2, file2, line2, col2)]:
  114. file1 == file2 && line1 == line2 && col1 == col2 && equalItems(item1, item2);
  115. case [Method(class1, method1), Method(class2, method2)]:
  116. class1 == class2 && method1 == method2;
  117. case [LocalFunction(v1), LocalFunction(v2)]:
  118. v1 == v2;
  119. case _: false;
  120. }
  121. }
  122. static function exceptionToString(e:Exception):String {
  123. if(e.previous == null) {
  124. return 'Exception: ${e.toString()}${e.stack}';
  125. }
  126. var result = '';
  127. var e:Null<Exception> = e;
  128. var prev:Null<Exception> = null;
  129. while(e != null) {
  130. if(prev == null) {
  131. result = 'Exception: ${e.message}${e.stack}' + result;
  132. } else {
  133. var prevStack = @:privateAccess e.stack.subtract(prev.stack);
  134. result = 'Exception: ${e.message}${prevStack}\n\nNext ' + result;
  135. }
  136. prev = e;
  137. e = e.previous;
  138. }
  139. return result;
  140. }
  141. static function itemToString(b:StringBuf, s) {
  142. switch (s) {
  143. case CFunction:
  144. b.add("a C function");
  145. case Module(m):
  146. b.add("module ");
  147. b.add(m);
  148. case FilePos(s, file, line, col):
  149. if (s != null) {
  150. itemToString(b, s);
  151. b.add(" (");
  152. }
  153. b.add(file);
  154. b.add(" line ");
  155. b.add(line);
  156. if (col != null) {
  157. b.add(" column ");
  158. b.add(col);
  159. }
  160. if (s != null)
  161. b.add(")");
  162. case Method(cname, meth):
  163. b.add(cname == null ? "<unknown>" : cname);
  164. b.add(".");
  165. b.add(meth);
  166. case LocalFunction(n):
  167. b.add("local function #");
  168. b.add(n);
  169. }
  170. }
  171. }