Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Getting the stack trace of an error

Explorer ,
May 08, 2013 May 08, 2013

When I catch an error, I would like to be able to log its stack trace. So far I’ve come up with this little trick:

Error.prototype.toString = function() {

    if (typeof this.stack === "undefined" || this.stack === null) {

        this.stack = "placeholder";

        // The previous line is needed because the next line may indirectly call this method.

        this.stack = $.stack;

    }

    return "Error";

}

This attaches the stack to any manually-created error. For example, I can run:

try {

    throw new Error("I'm an error.");

}

catch (e) {

    $.writeln("Stack: " + e.stack);

}

And this will print the correct stack for the error. However, it only works for errors that I create. Is there any way to get the stack trace of any error?

Full disclosure: I’ve asked this on Stack Overflow without much success. See here for more info: http://stackoverflow.com/questions/16201574/getting-the-stack-trace-of-an-error-in-extendscript

TOPICS
Scripting
5.2K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advisor ,
May 09, 2013 May 09, 2013

hei!

In extendscript there are some problems when extending some objects. Not sure it Error is one of them, but your problem sure looks like that.

Here is a great source of info:

http://forums.adobe.com/message/4133138#4133138

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
May 12, 2013 May 12, 2013

Hi dln385,

I have no answer for your question but what your code reveals is very educative: the throw statement always triggers the toString() method of the throwed object. I never realized that.

// Throwing obj causes obj.toString() invokation

//--------------------------------------

var obj = {

    toString: function(){ alert("Hello guys!"); }

    };

try{ throw obj; } // => Hello guys!

catch(_){}

Another fact I noticed is that throwing anything binds the caught reference to anything, whatever it refers to:

// What you throw is what you catch!

//--------------------------------------

var anything = {}; // works with literals too, e.g. "foobar", or even 5!

try{ throw anything; }

catch(e){ alert( e===anything ); }  // true!

For these reasons the actual Error(...) constructor—which basically behaves as a simple {message, name, etc.} object factory—doesn't seem essential to the try/throw/catch trio. Therefore, as long as you need to handle custom exceptions, you could as well entirely bypass the native Error object:

// No need to use native Error class

//--------------------------------------

function MyException(msg,s)

{

    this.message = msg || '';

    this.name = s || 'Error';

    this.stack = null;

};

MyException.prototype.toString = function()

{

    // Hook $.stack *on first invokation*

    // ---

    this.stack || (this.stack = $.stack.

                replace(/[\r\n]toString\(\)[\r\n]$/,'').

                split(/[\r\n]/)

                );

    // Make toString() ECMA-262 compliant

    // ---

    return this.name + ': ' + this.message;

};

// =================

// Example code

// =================

function inner()

{

    throw new MyException("Something's gone wrong!");

}

function outer()

{

    inner();

}

function main()

{

    try{ outer(); }

    catch(e)

        {

        alert( e );        // => Error: Something's gone wrong!

        alert( e.stack );  // => [myScript.jsx],main(),outer(),inner()

        }

}

main();

Now, your original question still remains, about built-in Error types (Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError). The fact is that ExtendScript does not invoke Error.prototype.toString() when a "native error" occurs. The opposite would be somewhat embarrassing!

// Pure errors (not explicitly thrown) do not involve toString

//--------------------------------------

// Disclaimer: overriding Error.prototype

// is not recommended at all!

Error.prototype.toString = function(){ alert( "Hello guys!" ); };

// Let's cause a RangeError...

try {

    new Array(-1); // silent!

    }

catch(e)

    {

    alert( e.name ); // => RangeError

    }

// By contrast:

try {

    throw new Error("My error"); // => Hello guys!

    }

catch(e)

    {

    alert( e.name ); // => Error

    }

Hope that may help a bit.

@+

Marc

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Nov 11, 2015 Nov 11, 2015

Hi Marc Autret,
I know this is quite an old thread, but hopefully you will get notified anyways about new answers.
I am curious about the custom MyException error class you demonstrated above.

Is there any way to also include the line and the script information in this class.

When I simply put

this.line = $.line           (how the heck do I put proper code blocks here?)

in the function body, then it picks up the line where this.line is defined in the function, not the line where I throw the exception. Is there any way to work around this?
(The only way I could see is making the line property one of the arguments, but then I would need to type $.line every time I throw my custom error. So, is there a more sophisticated way?

Or is it in this case preferrable to augment the native Error class? (I seem to remember that in your blog you did *not* recommend that.)

Thanks a lot!
T R

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guru ,
Nov 11, 2015 Nov 11, 2015

Hi

Try changing the code like this?

function MyException(msg,s, line) 

    this.message = msg || ''; 

    this.name = s || 'Error'; 

    this.stack = null; 

    this.line = line;

}; 

// ================= 

// Example code 

// ================= 

 

function inner() 

    throw new MyException("Something's gone wrong!", $.line);

}

Trevor

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Nov 12, 2015 Nov 12, 2015

Hi Trevor,
thanks for your answer. However, this is what I actually meant by making the line property one of the arguments. I would prefer to avoid that, so that I don't have to type $.line every time I am calling the function. It feel, if I always have to call the exact same argument, there should be a more sophisticated way, as this feels kind of redundant.!

Does anybody know?

Thanks!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Nov 21, 2015 Nov 21, 2015
LATEST

Hi MasterDomino,

I get your point but I can't see any way to avoid the use of $.line in the calling function since $.line acts like an ExtendScript directive that explicitly returns the current line number. The returned value is then rigidly attached to the line from which you call the command, so if you don't call it at the relevant location for your purpose you will necessarily get an irrelevant value.

And even if there were tricks—maybe there are—to somehow freeze $.line, or watch/backup changes in that scope, you still have to explicitly call some command or function to grab the desired line number at the desired location. Only the $.stack property allows you to remotely parse the hierarchy of function calls, because $.stack can be converted into an array and then you can 'pop' items which have been added due to the delegation mechanism. But I can't find out a similar approach for $.line.

@+

Marc

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines