Browse Source

Merge pull request #2059 from TheBrokenRail/safe_to_stacktrace

Add duk_safe_to_stacktrace()
pull/1567/merge
Sami Vaarala 6 years ago
committed by GitHub
parent
commit
edca32cf9c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      AUTHORS.rst
  2. 2
      RELEASES.rst
  3. 1
      doc/release-notes-v2-4.rst
  4. 26
      examples/cmdline/duk_cmdline.c
  5. 21
      examples/eval/eval.c
  6. 42
      src-input/duk_api_stack.c
  7. 2
      src-input/duktape.h.in
  8. 44
      tests/api/test-safe-to-stacktrace.c
  9. 35
      tests/api/test-to-stacktrace.c
  10. 49
      website/api/duk_safe_to_stacktrace.yaml
  11. 28
      website/api/duk_to_stacktrace.yaml

1
AUTHORS.rst

@ -57,6 +57,7 @@ and agreed to irrevocably license their contributions under the Duktape
* Edward Betts (https://github.com/edwardbetts)
* Ozhan Duz (https://github.com/webfolderio)
* Akos Kiss (https://github.com/akosthekiss)
* TheBrokenRail (https://github.com/TheBrokenRail)
Other contributions
===================

2
RELEASES.rst

@ -3430,6 +3430,8 @@ Planned
* Various portability fixes (GH-1931, GH-1976)
* Add duk_to_stacktrace() and duk_safe_to_stacktrace() to make it easier to get stacktraces in C
3.0.0 (XXXX-XX-XX)
------------------

1
doc/release-notes-v2-4.rst

@ -8,6 +8,7 @@ Release overview
TBD.
* Symbol built-in is now enabled by default.
* Add duk_to_stacktrace() and duk_safe_to_stacktrace() to make it easier to get stacktraces in C
Upgrading from Duktape 2.3
==========================

26
examples/cmdline/duk_cmdline.c

@ -180,33 +180,9 @@ static void cmdline_fatal_handler(void *udata, const char *msg) {
abort();
}
static duk_ret_t get_stack_raw(duk_context *ctx, void *udata) {
(void) udata;
if (!duk_is_object(ctx, -1)) {
return 1;
}
if (!duk_has_prop_string(ctx, -1, "stack")) {
return 1;
}
if (!duk_is_error(ctx, -1)) {
/* Not an Error instance, don't read "stack". */
return 1;
}
duk_get_prop_string(ctx, -1, "stack"); /* caller coerces */
duk_remove(ctx, -2);
return 1;
}
/* Print error to stderr and pop error. */
static void print_pop_error(duk_context *ctx, FILE *f) {
/* Print error objects with a stack trace specially.
* Note that getting the stack trace may throw an error
* so this also needs to be safe call wrapped.
*/
(void) duk_safe_call(ctx, get_stack_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
fprintf(f, "%s\n", duk_safe_to_string(ctx, -1));
fprintf(f, "%s\n", duk_safe_to_stacktrace(ctx, -1));
fflush(f);
duk_pop(ctx);
}

21
examples/eval/eval.c

@ -14,18 +14,6 @@ static duk_ret_t native_print(duk_context *ctx) {
return 0;
}
static duk_ret_t eval_raw(duk_context *ctx, void *udata) {
(void) udata;
duk_eval(ctx);
return 1;
}
static duk_ret_t tostring_raw(duk_context *ctx, void *udata) {
(void) udata;
duk_to_string(ctx, -1);
return 1;
}
static void usage_exit(void) {
fprintf(stderr, "Usage: eval <expression> [<expression>] ...\n");
fflush(stderr);
@ -36,6 +24,7 @@ int main(int argc, char *argv[]) {
duk_context *ctx;
int i;
const char *res;
int rc;
if (argc < 2) {
usage_exit();
@ -49,8 +38,12 @@ int main(int argc, char *argv[]) {
for (i = 1; i < argc; i++) {
printf("=== eval: '%s' ===\n", argv[i]);
duk_push_string(ctx, argv[i]);
duk_safe_call(ctx, eval_raw, NULL, 1 /*nargs*/, 1 /*nrets*/);
duk_safe_call(ctx, tostring_raw, NULL, 1 /*nargs*/, 1 /*nrets*/);
rc = duk_peval(ctx);
if (rc != 0) {
duk_safe_to_stacktrace(ctx, -1);
} else {
duk_safe_to_string(ctx, -1);
}
res = duk_get_string(ctx, -1);
printf("%s\n", res ? res : "null");
duk_pop(ctx);

42
src-input/duk_api_stack.c

@ -3067,6 +3067,48 @@ DUK_EXTERNAL const char *duk_safe_to_lstring(duk_hthread *thr, duk_idx_t idx, du
return duk_get_lstring(thr, idx, out_len);
}
DUK_EXTERNAL const char *duk_to_stacktrace(duk_hthread *thr, duk_idx_t idx) {
DUK_ASSERT_API_ENTRY(thr);
idx = duk_require_normalize_index(thr, idx);
if (duk_is_error(thr, idx)) {
duk_get_prop_string(thr, idx, "stack"); /* caller coerces */
if (!duk_is_null_or_undefined(thr, -1)) {
duk_replace(thr, idx);
} else {
duk_pop(thr);
}
return duk_to_string(thr, idx);
} else {
return duk_to_string(thr, idx);
}
}
DUK_LOCAL duk_ret_t duk__safe_to_stacktrace_raw(duk_hthread *thr, void *udata) {
DUK_ASSERT_CTX_VALID(thr);
DUK_UNREF(udata);
duk_to_stacktrace(thr, -1);
return 1;
}
DUK_EXTERNAL const char *duk_safe_to_stacktrace(duk_hthread *thr, duk_idx_t idx) {
int rc;
DUK_ASSERT_API_ENTRY(thr);
idx = duk_require_normalize_index(thr, idx);
duk_dup(thr, idx);
rc = duk_safe_call(thr, duk__safe_to_stacktrace_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
if (rc != 0) {
duk_safe_to_stacktrace(thr, -1);
}
duk_replace(thr, idx);
return duk_get_string(thr, idx);
}
DUK_INTERNAL duk_hstring *duk_to_property_key_hstring(duk_hthread *thr, duk_idx_t idx) {
duk_hstring *h;

2
src-input/duktape.h.in

@ -861,6 +861,8 @@ DUK_EXTERNAL_DECL void duk_to_primitive(duk_context *ctx, duk_idx_t idx, duk_int
/* safe variants of a few coercion operations */
DUK_EXTERNAL_DECL const char *duk_safe_to_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len);
DUK_EXTERNAL_DECL const char *duk_to_stacktrace(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_safe_to_stacktrace(duk_context *ctx, duk_idx_t idx);
#define duk_safe_to_string(ctx,idx) \
duk_safe_to_lstring((ctx), (idx), NULL)

44
tests/api/test-safe-to-stacktrace.c

@ -0,0 +1,44 @@
/*===
*** test_1 (duk_safe_call)
duk_get_top[0] = '0'
duk_safe_to_stacktrace[0] = 'Error
at err (eval:1)
at eval (eval:1) preventsyield'
duk_get_top[1] = '0'
duk_safe_to_stacktrace[1] = '4'
duk_get_top[2] = '0'
duk_safe_to_stacktrace[2] = 'Error
at [anon] (eval:1) preventsyield'
duk_get_top[3] = '0'
==> rc=0, result='undefined'
===*/
static duk_ret_t test_1(duk_context *ctx, void *udata) {
(void) udata;
printf("duk_get_top[0] = '%ld'\n", (long) duk_get_top(ctx));
duk_peval_string(ctx, "function err() { throw new Error(); }; err();");
printf("duk_safe_to_stacktrace[0] = '%s'\n", duk_safe_to_stacktrace(ctx, -1));
duk_pop(ctx);
printf("duk_get_top[1] = '%ld'\n", (long) duk_get_top(ctx));
duk_peval_string(ctx, "2+2");
printf("duk_safe_to_stacktrace[1] = '%s'\n", duk_safe_to_stacktrace(ctx, -1));
duk_pop(ctx);
printf("duk_get_top[2] = '%ld'\n", (long) duk_get_top(ctx));
duk_peval_string(ctx, "var err = new Error(); Object.defineProperty(err, 'stack', {get: function() { throw new Error(); }}); throw err;");
printf("duk_safe_to_stacktrace[2] = '%s'\n", duk_safe_to_stacktrace(ctx, -1));
duk_pop(ctx);
printf("duk_get_top[3] = '%ld'\n", (long) duk_get_top(ctx));
return 0;
}
void test(duk_context *ctx) {
TEST_SAFE_CALL(test_1);
}

35
tests/api/test-to-stacktrace.c

@ -0,0 +1,35 @@
/*===
*** test_1 (duk_safe_call)
duk_get_top[0] = '0'
duk_to_stacktrace[0] = 'Error
at err (eval:1)
at eval (eval:1) preventsyield'
duk_get_top[1] = '0'
duk_to_stacktrace[1] = '4'
duk_get_top[2] = '0'
==> rc=0, result='undefined'
===*/
static duk_ret_t test_1(duk_context *ctx, void *udata) {
(void) udata;
printf("duk_get_top[0] = '%ld'\n", (long) duk_get_top(ctx));
duk_peval_string(ctx, "function err() { throw new Error(); }; err();");
printf("duk_to_stacktrace[0] = '%s'\n", duk_to_stacktrace(ctx, -1));
duk_pop(ctx);
printf("duk_get_top[1] = '%ld'\n", (long) duk_get_top(ctx));
duk_peval_string(ctx, "2+2");
printf("duk_to_stacktrace[1] = '%s'\n", duk_to_stacktrace(ctx, -1));
duk_pop(ctx);
printf("duk_get_top[2] = '%ld'\n", (long) duk_get_top(ctx));
return 0;
}
void test(duk_context *ctx) {
TEST_SAFE_CALL(test_1);
}

49
website/api/duk_safe_to_stacktrace.yaml

@ -0,0 +1,49 @@
name: duk_safe_to_stacktrace
proto: |
const char *duk_safe_to_stacktrace(duk_context *ctx, duk_idx_t idx);
stack: |
[ ... val! ... ] -> [ ... val.stack! ... ]
summary: |
<p>Like <code><a href="#duk_safe_to_string">duk_safe_to_string()</a></code> but
instead of converting a value to a string, it converts an error to a stacktrace.
If the object is not an error, it will fallback to duk_safe_to_string(). If there
is an error when reading .stack, it will return the stacktrace of that error.</p>
example: |
/* Throw Error and print stacktrace */
duk_eval_string(ctx, "function err() { throw new Error(); }; err();");
printf("stacktrace: %s\n", duk_safe_to_stacktrace(ctx, -1)); /* -> "Error at err (eval:1)" */
duk_pop(ctx);
/* Equivalent ECMAScript */
try {
function err() {
throw new Error();
}
err();
} catch (e) {
if (e instanceof Error) {
var stack = e.stack;
if (stack) {
console.log(stack);
} else {
console.log(e.toString());
}
} else {
console.log(e.toString());
}
}
tags:
- stack
- string
- protected
seealso:
- duk_safe_to_string
- duk_to_stacktrace
introduced: 2.4.0

28
website/api/duk_to_stacktrace.yaml

@ -0,0 +1,28 @@
name: duk_to_stacktrace
proto: |
const char *duk_to_stacktrace(duk_context *ctx, duk_idx_t idx);
stack: |
[ ... val! ... ] -> [ ... val.stack! ... ]
summary: |
<p>Like <code><a href="#duk_to_string">duk_to_string()</a></code> but
instead of converting a value to a string, it converts an error to a stacktrace.
If the object is not an error, it will fallback to duk_to_string().</p>
example: |
/* Throw Error and print stacktrace */
duk_eval_string(ctx, "function err() { throw new Error(); }; err();");
printf("stacktrace: %s\n", duk_to_stacktrace(ctx, -1)); /* -> "Error at err (eval:1)" */
duk_pop(ctx);
tags:
- stack
- string
seealso:
- duk_to_string
- duk_safe_to_stacktrace
introduced: 2.4.0
Loading…
Cancel
Save