This project has moved and is read-only. For the latest updates, please go here.

JavaScript Error stack trace

Apr 6, 2012 at 10:45 PM
Edited Apr 7, 2012 at 12:32 AM

Hello,
Is it possible to get somehow clean JavaScript stack trace where the error has happened?
I mean execution chain of the JavaScript functions with their names and line numbers?

Right now when an error happens (Jurassic.JavaScriptException is thrown), the exception contains only standard C# stack trace and does not have any field/property that contains JavaScript stack trace.


Greetings,
Alex

PS: I found that JavaScriptException has ErrorObject property which is ErrorInstance, but ErrorInstance also contains only C# stack trace.

PPS: Interesting thing I just found that it seems that in JavaScript the caught error does not contain stack either.

function inner(){ throw Error('failed!'); } 

function outer(){ inner(); }

function getErrorName() { try { outer(); } catch ( e ) { return '' + e.name; } }

getErrorName(); // => Error

function getErrorMessage() { try { outer(); } catch ( e ) { return '' + e.message; } }

getErrorMessage(); // => failed!

function getErrorStack() { try { outer(); } catch ( e ) { return '' + e.stack; } }

getErrorStack(); // => undefined. Here stack trace is expected

 

Apr 7, 2012 at 7:54 AM

Javascript stack traces are unfortunately not implemented.  As you've discovered, there is a stack property, but only in full privilege mode (i.e. not silverlight) and it contains the .NET stack trace, not the javascript stack trace.  This is a long-standing issue, mainly because it is really tough to generate a javascript stack trace without slowing down all function calls.

Apr 9, 2012 at 6:16 AM

For us, the absence of user level stack trace is a show-stopper and I will have to implement it myself somehow. Do you have any thoughts/suggestions on how it can be implemented?

 

BTW, does this project have a donation page (like for example firebug, http://getfirebug.com/contribute)?

Apr 10, 2012 at 5:12 AM

There's an easy way and a hard way.  The hard way is to distill the .NET stack trace into a javascript stack trace.  This is probably doable for function names, but getting line numbers is quite likely impossible.  The easy way (in comparison!) is to maintain a per-thread stack and update it on entry/exit from every function (this will mean wrapping a try-finally around the method body in order to handle exceptions).

There is no donation page - if you want to donate, I'd rather you donate to someone who actually needs the money :-)

Aug 31, 2012 at 7:05 AM

We've been looking around for a JS engine for .NET, and it stood between Jurassic and Jint. At first we choose Jint simply because you got the offending line number in the error message, and because how simple it is to expose .NET objects to JS. But after finding too many bugs in Jint that would be quite hard to fix with Jints current architecture, we've turned to Jurassic instead.

Exposing .NET objects turned out to be pretty easy, and I'd be happy to share the code which takes any CLR object and transforms it into an ObjectInstance.

But the lack of a line number in a run time error is a bit of a show stopper like AC2 said. I understand that you have a lot of other things to do Paul, but could you give some pointers on how you would get started? Getting the stack as well would be a bonus, but it's not nearly as important as the line number, imho.

Aug 31, 2012 at 3:33 PM

I would have added stack traces a long time ago, except I couldn't figure out a way to do it without adding lots of runtime overhead.  Your post made me realize this I didn't actually know how big the performance loss would be, so why not try it and find out?

So I did :-)

It turns out that after making this change SunSpider is ~6% slower, but the unit test suite is *much* slower (not sure how much exactly; I got bored and killed it before it finished).  That makes me hesitant to merge the code into default (hopefully I'm doing something silly!)

For your purposes, I'm guessing it will work fine however.  Grab the source code from the add-error-stack branch.

Aug 31, 2012 at 5:19 PM

That's awesome, thanks! I'm pretty sure that the loss in performance won't be a problem for us. 

Sep 1, 2012 at 10:59 AM

Seems to be working nicely, thanks! Although I don't seem to get the line number in all errors. The following, for example, gives 0 as the line number:

var obj;
obj.method(); // Gives TypeError: undefined cannot be converted to an object, and line number: 0

The line number is 0 even if `obj` is an object but is missing a "method" property.

And another thing, do you have any plans to make it possible to listen to an event when the ScriptEngine throws an exception? Our script-environment is set up so that one script can include another script and call functions in it. Two (or more) ScriptEngines are created, and it would be handy to be able to hook into when an error occured in them to set the script file that the error comes from.

Thanks again!

Sep 3, 2012 at 12:25 AM

You're right - there are a few edge cases I missed.

Regarding figuring out which script file the error occurred in, there is a mechanism for handling this already: the ScriptSource class, which allows you to tell the compiler the path of the script file when you execute it.  This should then be propagated through to the stack trace (though to be honest I haven't tested this).  Give it a try, let me know if it works.

Sep 3, 2012 at 7:30 AM
Edited Sep 3, 2012 at 8:52 AM

No problem, I'll try to find those and fix it myself, you have been more than helpful so far!
I'm struggling with one of them right now; TypeConverter.ToObject() throws a JavaScriptException in some cases, and I don't really know where to find the SourceSpan in that case. The callstack says that it's coming from EvalMethodGenerator.Execute(), then [External Code], and then TypeConverter.ToObject(). I can't seem to find any place where a JavaScriptException is thrown (without using EmitHelpers) where a line number is passed along except in the Lexer or Parser. Could you give me some pointers?

UPDATE: Seems like i found it. EmitConversion.ToObject() adds generated code that calls ReflectionHelpers.TypeConverter_ToObject and TypeConverter.ToObject() throws an exception in two cases. So I added a a static method to TypeConverter that does what ToObject but it takes path, function and line as well and it seems to do the trick.

About the Path property on ScriptSource, seems to be working correctly! As long as it's not an exception which doesn't have a line number, then the path is null as well.

Sep 3, 2012 at 1:29 PM

I was just about to point the figure at EmitConversion.ToObject myself, but you beat me to it :-)

Feb 25, 2013 at 10:37 PM
Hi Paul,
I've created a patch incorporating the change described by andersekdahl plus a few other places where JavaScriptExceptions were being thrown without source info. Would it make sense to import it to the add-error-stack branch? If so, let me know how I can get it to you (I don't see an attachment button here).