-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Custom output patterns for stack items (fixes #6)
- Loading branch information
1 parent
dc858f0
commit 0fa7fc3
Showing
7 changed files
with
361 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
--macro include('jstack.JStack') | ||
--macro keep('jstack.JStack') | ||
--macro jstack.Tools.addInjectMetaToEntryPoint() | ||
--macro jstack.Tools.initialize() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package haxe; | ||
|
||
enum StackItem { | ||
CFunction; | ||
Module( m : String ); | ||
FilePos( s : Null<StackItem>, file : String, line : Int ); | ||
Method( classname : String, method : String ); | ||
LocalFunction( ?v : Int ); | ||
} | ||
|
||
/** | ||
Get information about the call stack. | ||
**/ | ||
class CallStack { | ||
static var lastException:js.Error; | ||
|
||
static function getStack(e:js.Error):Array<StackItem> { | ||
if (e == null) return []; | ||
// https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi | ||
var oldValue = (untyped Error).prepareStackTrace; | ||
(untyped Error).prepareStackTrace = function (error, callsites :Array<Dynamic>) { | ||
var stack = []; | ||
for (site in callsites) { | ||
if (wrapCallSite != null) site = wrapCallSite(site); | ||
var method = null; | ||
var fullName :String = site.getFunctionName(); | ||
if (fullName != null) { | ||
var idx = fullName.lastIndexOf("."); | ||
if (idx >= 0) { | ||
var className = fullName.substr(0, idx); | ||
var methodName = fullName.substr(idx+1); | ||
method = Method(className, methodName); | ||
} | ||
} | ||
stack.push(FilePos(method, site.getFileName(), site.getLineNumber())); | ||
} | ||
return stack; | ||
} | ||
var a = makeStack(e.stack); | ||
(untyped Error).prepareStackTrace = oldValue; | ||
return a; | ||
} | ||
|
||
// support for source-map-support module | ||
@:noCompletion | ||
public static var wrapCallSite:Dynamic->Dynamic; | ||
|
||
/** | ||
Return the call stack elements, or an empty array if not available. | ||
**/ | ||
public static function callStack() : Array<StackItem> { | ||
try { | ||
throw new js.Error(); | ||
} catch( e : Dynamic ) { | ||
var a = getStack(e); | ||
a.shift(); // remove Stack.callStack() | ||
return a; | ||
} | ||
} | ||
|
||
/** | ||
Return the exception stack : this is the stack elements between | ||
the place the last exception was thrown and the place it was | ||
caught, or an empty array if not available. | ||
**/ | ||
public static function exceptionStack() : Array<StackItem> { | ||
return untyped __define_feature__("haxe.CallStack.exceptionStack", getStack(lastException)); | ||
} | ||
|
||
/** | ||
Returns a representation of the stack as a printable string. | ||
**/ | ||
public static function toString( stack : Array<StackItem> ) { | ||
return jstack.Format.toString(stack); | ||
} | ||
|
||
private static function makeStack(s #if cs : cs.system.diagnostics.StackTrace #elseif hl : hl.NativeArray<hl.Bytes> #end) { | ||
if (s == null) { | ||
return []; | ||
} else if ((untyped __js__("typeof"))(s) == "string") { | ||
// Return the raw lines in browsers that don't support prepareStackTrace | ||
var stack : Array<String> = s.split("\n"); | ||
if( stack[0] == "Error" ) stack.shift(); | ||
var m = []; | ||
var rie10 = ~/^ at ([A-Za-z0-9_. ]+) \(([^)]+):([0-9]+):([0-9]+)\)$/; | ||
for( line in stack ) { | ||
if( rie10.match(line) ) { | ||
var path = rie10.matched(1).split("."); | ||
var meth = path.pop(); | ||
var file = rie10.matched(2); | ||
var line = Std.parseInt(rie10.matched(3)); | ||
m.push(FilePos( meth == "Anonymous function" ? LocalFunction() : meth == "Global code" ? null : Method(path.join("."),meth), file, line )); | ||
} else | ||
m.push(Module(StringTools.trim(line))); // A little weird, but better than nothing | ||
} | ||
return m; | ||
} else { | ||
return cast s; | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package haxe; | ||
|
||
import php.*; | ||
|
||
private typedef NativeTrace = NativeIndexedArray<NativeAssocArray<Dynamic>>; | ||
|
||
/** | ||
Elements return by `CallStack` methods. | ||
**/ | ||
enum StackItem { | ||
CFunction; | ||
Module( m : String ); | ||
FilePos( s : Null<StackItem>, file : String, line : Int ); | ||
Method( classname : String, method : String ); | ||
LocalFunction( ?v : Int ); | ||
} | ||
|
||
class CallStack { | ||
/** | ||
If defined this function will be used to transform call stack entries. | ||
@param String - generated php file name. | ||
@param Int - Line number in generated file. | ||
*/ | ||
static public var mapPosition : String->Int->Null<{?source:String, ?originalLine:Int}>; | ||
|
||
@:ifFeature("haxe.CallStack.exceptionStack") | ||
static var lastExceptionTrace : NativeTrace; | ||
|
||
/** | ||
Return the call stack elements, or an empty array if not available. | ||
**/ | ||
public static function callStack() : Array<StackItem> { | ||
return makeStack(Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS)); | ||
} | ||
|
||
/** | ||
Return the exception stack : this is the stack elements between | ||
the place the last exception was thrown and the place it was | ||
caught, or an empty array if not available. | ||
**/ | ||
public static function exceptionStack() : Array<StackItem> { | ||
return makeStack(lastExceptionTrace == null ? new NativeIndexedArray() : lastExceptionTrace); | ||
} | ||
|
||
/** | ||
Returns a representation of the stack as a printable string. | ||
**/ | ||
public static function toString( stack : Array<StackItem> ) { | ||
return jstack.Format.toString(stack); | ||
} | ||
|
||
@:ifFeature("haxe.CallStack.exceptionStack") | ||
static function saveExceptionTrace( e:Throwable ) : Void { | ||
lastExceptionTrace = e.getTrace(); | ||
|
||
//Reduce exception stack to the place where exception was caught | ||
var currentTrace = Global.debug_backtrace(Const.DEBUG_BACKTRACE_IGNORE_ARGS); | ||
var count = Global.count(currentTrace); | ||
|
||
for (i in -(count - 1)...1) { | ||
var exceptionEntry:NativeAssocArray<Dynamic> = Global.end(lastExceptionTrace); | ||
|
||
if(!Global.isset(exceptionEntry['file']) || !Global.isset(currentTrace[-i]['file'])) { | ||
Global.array_pop(lastExceptionTrace); | ||
} else if (currentTrace[-i]['file'] == exceptionEntry['file'] && currentTrace[-i]['line'] == exceptionEntry['line']) { | ||
Global.array_pop(lastExceptionTrace); | ||
} else { | ||
break; | ||
} | ||
} | ||
|
||
//Remove arguments from trace to avoid blocking some objects from GC | ||
var count = Global.count(lastExceptionTrace); | ||
for (i in 0...count) { | ||
lastExceptionTrace[i]['args'] = new NativeArray(); | ||
} | ||
|
||
var thrownAt = new NativeAssocArray<Dynamic>(); | ||
thrownAt['function'] = ''; | ||
thrownAt['line'] = e.getLine(); | ||
thrownAt['file'] = e.getFile(); | ||
thrownAt['class'] = ''; | ||
thrownAt['args'] = new NativeArray(); | ||
Global.array_unshift(lastExceptionTrace, thrownAt); | ||
} | ||
|
||
static function makeStack (native:NativeTrace) : Array<StackItem> { | ||
var result = []; | ||
var count = Global.count(native); | ||
|
||
for (i in 0...count) { | ||
var entry = native[i]; | ||
var item = null; | ||
|
||
if (i + 1 < count) { | ||
var next = native[i + 1]; | ||
|
||
if(!Global.isset(next['function'])) next['function'] = ''; | ||
if(!Global.isset(next['class'])) next['class'] = ''; | ||
|
||
if ((next['function']:String).indexOf('{closure}') >= 0) { | ||
item = LocalFunction(); | ||
} else if ((next['class']:String).length > 0 && (next['function']:String).length > 0) { | ||
var cls = Boot.getClassName(next['class']); | ||
item = Method(cls, next['function']); | ||
} | ||
} | ||
if (Global.isset(entry['file'])) { | ||
if (mapPosition != null) { | ||
var pos = mapPosition(entry['file'], entry['line']); | ||
if (pos != null && pos.source != null && pos.originalLine != null) { | ||
entry['file'] = pos.source; | ||
entry['line'] = pos.originalLine; | ||
} | ||
} | ||
result.push(FilePos(item, entry['file'], entry['line'])); | ||
} else if (item != null) { | ||
result.push(item); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package jstack; | ||
|
||
import haxe.CallStack.StackItem; | ||
|
||
using StringTools; | ||
|
||
/** | ||
Call stack formatting utils. | ||
**/ | ||
class Format { | ||
public static function toString (stack:Array<StackItem>) : String { | ||
var format = Tools.getFormat(); | ||
var buf = new StringBuf(); | ||
for (item in stack) { | ||
if(format == null) { | ||
buf.add('\nCalled from '); | ||
itemToString(buf, item); | ||
} else { | ||
buf.add('\n'); | ||
buf.add(itemToFormat(format, item)); | ||
} | ||
} | ||
return buf.toString(); | ||
} | ||
|
||
static function itemToString (buf:StringBuf, item:StackItem) { | ||
switch (item) { | ||
case CFunction: | ||
buf.add('a C function'); | ||
case Module(m): | ||
buf.add('module '); | ||
buf.add(m); | ||
case FilePos(item, file, line): | ||
if( item != null ) { | ||
itemToString(buf, item); | ||
buf.add(' ('); | ||
} | ||
buf.add(file); | ||
buf.add(' line '); | ||
buf.add(line); | ||
if (item != null) buf.add(')'); | ||
case Method(cname,meth): | ||
buf.add(cname); | ||
buf.add('.'); | ||
buf.add(meth); | ||
case LocalFunction(n): | ||
buf.add('local function #'); | ||
buf.add(n); | ||
} | ||
} | ||
|
||
static function itemToFormat (format:String, item:StackItem) : String { | ||
switch (item) { | ||
case CFunction: | ||
return 'a C function'; | ||
case Module(m): | ||
return 'module $m'; | ||
case FilePos(s,file,line): | ||
if(file.substr(0, 'file://'.length) == 'file://') { | ||
file = file.substr('file://'.length); | ||
} | ||
var symbol = (s == null ? '' : itemToFormat(format, s)); | ||
return format.replace('%file%', file).replace('%line%', '$line').replace('%symbol%', symbol); | ||
case Method(cname,meth): | ||
return '$cname.$meth'; | ||
case LocalFunction(n): | ||
return 'local function #$n'; | ||
} | ||
} | ||
} |
Oops, something went wrong.