svaarala / duktape

Duktape - embeddable Javascript engine with a focus on portability and compact footprint
MIT License
5.95k stars 515 forks source link

How to get current JavaScript execution stack using duktape #1843

Open rajubngr opened 6 years ago

rajubngr commented 6 years ago

I have a requirement to get the current JavaScript execution stack from duktape c file similar to V8 function listed below . http://bespin.cz/~ondras/html/classv8_1_1StackTrace.html#a030e8de1b13d720bb2bfac5cb8bc914b Is there a way to achieve same in Duktape ? I have created error object from duktape environment but that is giving stack of duktape c file but not JS stack . ex: duktape code duk_idx_t err_idx = duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "invalid argument value: %d", 605); duk_get_prop_string(ctx,-1,"stack"); const char* stack = duk_safe_to_string(ctx,-1); printf("error object stack = %s \n",stack);

output :############## error object stack = TypeError: invalid argument value: 605 at [anon] (examples/kony/Main.c:218) internal at [anon] () native strict preventsyield at eval (eval:22) preventsyield

svaarala commented 6 years ago

You could use http://duktape.org/api.html#duk_inspect_callstack_entry to get the programmatic stack trace.

Error objects have a .stack property which provide a human-readable version. It is backed by a hidden symbol (an internal property) listing the stack trace data in a non-version-compatible internal format.

svaarala commented 6 years ago

The error .stack includes both JS and native call stack entries. In your case there's an initial eval call, which has called an anonymous function, then some function in Main.c, which ultimately has created the stack trace.

If your JS code included other JS calls, they would be visible in .stack too. For example

duk> function foo() { throw new Error("aiee"); }
= undefined
duk> function bar() { foo(); }
= undefined
duk> function quux() { bar(); }
= undefined
duk> quux();
Error: aiee
    at foo (input:1)
    at bar (input:1)
    at quux (input:1)
    at global (input:1) preventsyield
rajubngr commented 6 years ago

Is there a way to get only Javascript stack trace avoiding native stack ?

fatcerberus commented 6 years ago

To clarify a bit, "native" in this case only means Duktape/C functions called from JavaScript code. Your actual native stack (native-to-native calls) isn't included.

rajubngr commented 6 years ago

I mean that avoiding/supressing Duktape/C functions stack .

svaarala commented 6 years ago

@rajubngr But note that all built-in functions, like Array .forEach(), are Duktape/C functions. Do you really want to hide them from the stack? For example:

((o) Duktape [linenoise] 2.2.99 (v2.2.0-135-g8d5fbe7f)
duk> function bar() { decodeURIComponent('%fg'); }
= undefined
duk> function foo(arr) { arr.forEach(bar); }
= undefined
duk> foo([ 1, 2, 3 ])
URIError: invalid input
    at [anon] (duk_bi_global.c:341) internal
    at decodeURIComponent () native strict preventsyield
    at bar (input:1) preventsyield
    at forEach () native strict preventsyield
    at foo (input:1)
    at global (input:1) preventsyield

Here the call sequence is:

If Duktape/C functions, and the final "synthetic" internal call site were removed, the stack trace would look like:

URIError: invalid input
    at bar (input:1) preventsyield
    at foo (input:1)
    at global (input:1) preventsyield

Is this really what you want, i.e. for the .forEach() and decodeURIComponent() to be hidden?

If you just want to hide the synthetic initial line (duktape.c + line number), but keep functions like .forEach(), you could install a Duktape.errCreate(e) function that would read and replace the .stack property after pattern matching and removing the line. That would look something like (formatting added):

duk> Duktape.errCreate = function (e) {
          var stack = e.stack.replace(/^\s*.*internal\n/m, '');
          e.stack = stack;
          return e; }
= {_func:true}
duk> foo([ 1, 2, 3 ])
URIError: invalid input
    at decodeURIComponent () native strict preventsyield
    at bar (input:1) preventsyield
    at forEach () native strict preventsyield
    at foo (input:1)
    at global (input:1) preventsyield
rajubngr commented 6 years ago

Thanks Svaarala I was looking for the same , I want to hide the synthetic initial line (duktape.c + line number).

rajubngr commented 6 years ago

My requirement is to get only Javascript information for error object like current line number of JS file , filename of JavaScript, stack of only Javascript functions and files . If I create error object from Duktape c function , error object should give me filename and lineNumber...etc only of javascript ,like below V8 API . static Local<StackTrace> v8::StackTrace::CurrentStackTrace() http://bespin.cz/~ondras/html/classv8_1_1StackTrace.html#a030e8de1b13d720bb2bfac5cb8bc914b. Is there a way to achieve the same in Duktape ?

svaarala commented 6 years ago

Whether error .fileName and lineNumber should be for a JS call site or the C call site depends on the use case, see discussion in https://github.com/svaarala/duktape/blob/master/doc/error-objects.rst#choosing-filename-and-linenumber-to-blame-for-an-error. The current rules for choosing the file/line are also described there, with notes for future work to allow e.g. functions to be tagged "infrastructure" so they wouldn't be blamed (Duktape does this for its internal call sites magically).

As things stand now, you can use errCreate() to override the default .fileName and .lineNumber with whatever logic you want. You can either use .stack as the input and pattern match that, or use Duktape.act() or its C equivalent duk_inspect_callstack_entry() to walk the callstack.

rajubngr commented 6 years ago

when I create error object from duktape c function using below api duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "invalid argument value: %d", 605);

   Duktape.errCreate = function(err){... return err}; is not getting called
svaarala commented 6 years ago

Can you paste some code? This test case covers using errCreate from C code: https://github.com/svaarala/duktape/blob/master/tests/api/test-errhandler.c.

svaarala commented 6 years ago

@rajubngr Any news on this?

rajubngr commented 6 years ago

@svaarala duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "invalid argument value: %d", 605); Using the above statement , I'm able to see that below function is getting called

Duktape.errCreate = function(err){... return err};

But my actual requirement is when I push error object from Duktape c function , I need to get the last executed javascript statement code details like lineNumber of the javascript code, fileName of the javascript file and stack of javaScript ..etc in Duktape c function .

svaarala commented 6 years ago

Just to be sure, how are you seeing that errCreate is not getting called? Note that if the errCreate() handler throws an error, it will be silently eaten. I'm asking this because the test case linked above does duk_push_error_object() also and covers the errCreate call (specifically in test_4).

svaarala commented 6 years ago

But my actual requirement is when I push error object from Duktape c function , I need to get the last executed javascript statement code details like lineNumber of the javascript code, fileName of the javascript file and stack of javaScript ..etc in Duktape c function .

These are available via http://duktape.org/api.html#duk_inspect_callstack_entry only at this point.

fatcerberus commented 6 years ago

Regarding duk_inspect_callstack_entry() keep in mind that the callstack will include Duktape/C calls so you will need to drill through the entries until you find the first JavaScript one.

rajubngr commented 6 years ago

_Just to be sure, how are you seeing that errCreate is not getting called? Note that if the errCreate() handler throws an error, it will be silently eaten. I'm asking this because the test case linked above does duk_push_error_object() also and covers the errCreate call (specifically in test4).

Sorry there was mistake due to copy paste that resulted in to mentioning errCreate is not getting called but actually errCreate function is getting called when I push error object . As I said my requirement is to get the JavaScript info .... I will try suggested solution of using duk_inspect_callstack_entry()