123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- package haxe;
- import js.Syntax;
- import js.lib.Error;
- import haxe.CallStack.StackItem;
- // https://v8.dev/docs/stack-trace-api
- @:native("Error")
- private extern class V8Error {
- static var prepareStackTrace:(error:Error, structuredStackTrace:Array<V8CallSite>)->Any;
- }
- typedef V8CallSite = {
- function getFunctionName():String;
- function getFileName():String;
- function getLineNumber():Int;
- function getColumnNumber():Int;
- }
- /**
- Do not use manually.
- **/
- @:dox(hide)
- @:noCompletion
- @:allow(haxe.Exception)
- class NativeStackTrace {
- static var lastError:Error;
- // support for source-map-support module
- @:noCompletion
- public static var wrapCallSite:V8CallSite->V8CallSite;
- @:ifFeature('haxe.NativeStackTrace.exceptionStack')
- static public inline function saveStack(e:Error):Void {
- lastError = e;
- }
- static public function callStack():Any {
- var e:Null<Error> = new Error('');
- var stack = tryHaxeStack(e);
- //Internet Explorer provides call stack only if error was thrown
- if(Syntax.typeof(stack) == "undefined") {
- try throw e catch(e:Exception) {}
- stack = e.stack;
- }
- return normalize(stack, 2);
- }
- static public function exceptionStack():Any {
- return normalize(tryHaxeStack(lastError));
- }
- static public function toHaxe(s:Null<Any>, skip:Int = 0):Array<StackItem> {
- if (s == null) {
- return [];
- } else if (Syntax.typeof(s) == "string") {
- // Return the raw lines in browsers that don't support prepareStackTrace
- var stack:Array<String> = (s:String).split("\n");
- if (stack[0] == "Error")
- stack.shift();
- var m = [];
- for (i in 0...stack.length) {
- if(skip > i) continue;
- var line = stack[i];
- var matched:Null<Array<String>> = Syntax.code('{0}.match(/^ at ([A-Za-z0-9_. ]+) \\(([^)]+):([0-9]+):([0-9]+)\\)$/)', line);
- if (matched != null) {
- var path = matched[1].split(".");
- if(path[0] == "$hxClasses") {
- path.shift();
- }
- var meth = path.pop();
- var file = matched[2];
- var line = Std.parseInt(matched[3]);
- var column = Std.parseInt(matched[4]);
- m.push(FilePos(meth == "Anonymous function" ? LocalFunction() : meth == "Global code" ? null : Method(path.join("."), meth), file, line,
- column));
- } else {
- m.push(Module(StringTools.trim(line))); // A little weird, but better than nothing
- }
- }
- return m;
- } else if(skip > 0 && Syntax.code('Array.isArray({0})', s)) {
- return (s:Array<StackItem>).slice(skip);
- } else {
- return cast s;
- }
- }
- static function tryHaxeStack(e:Null<Error>):Any {
- if (e == null) {
- return [];
- }
- // https://v8.dev/docs/stack-trace-api
- var oldValue = V8Error.prepareStackTrace;
- V8Error.prepareStackTrace = prepareHxStackTrace;
- var stack = e.stack;
- V8Error.prepareStackTrace = oldValue;
- return stack;
- }
- static function prepareHxStackTrace(e:Error, callsites:Array<V8CallSite>):Any {
- var stack = [];
- for (site in callsites) {
- if (wrapCallSite != null)
- site = wrapCallSite(site);
- var method = null;
- var fullName = site.getFunctionName();
- if (fullName != null) {
- var idx = fullName.lastIndexOf(".");
- if (idx >= 0) {
- var className = fullName.substring(0, idx);
- var methodName = fullName.substring(idx + 1);
- method = Method(className, methodName);
- } else {
- method = Method(null, fullName);
- }
- }
- var fileName = site.getFileName();
- var fileAddr = fileName == null ? -1 : fileName.indexOf("file:");
- if (wrapCallSite != null && fileAddr > 0)
- fileName = fileName.substring(fileAddr + 6);
- stack.push(FilePos(method, fileName, site.getLineNumber(), site.getColumnNumber()));
- }
- return stack;
- }
- static function normalize(stack:Any, skipItems:Int = 0):Any {
- if(Syntax.code('Array.isArray({0})', stack) && skipItems > 0) {
- return (stack:Array<StackItem>).slice(skipItems);
- } else if(Syntax.typeof(stack) == "string") {
- switch (stack:String).substring(0, 6) {
- case 'Error:' | 'Error\n': skipItems += 1;
- case _:
- }
- return skipLines(stack, skipItems);
- } else {
- //nothing we can do
- return stack;
- }
- }
- static function skipLines(stack:String, skip:Int, pos:Int = 0):String {
- return if(skip > 0) {
- pos = stack.indexOf('\n', pos);
- return pos < 0 ? '' : skipLines(stack, --skip, pos + 1);
- } else {
- return stack.substring(pos);
- }
- }
- }
|