Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/docs/diff.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,7 @@ of ES features present in NG:

Some non-standard but widely used APIs have also been added:

- V8's `Error.prepareStackTrace` and `Error.stackTraceLimit`
- V8's [stack trace API](https://v8.dev/docs/stack-trace-api)
- `Error.captureStackTrace`
- `Error.prepareStackTrace`
- `Error.stackTraceLimit`
41 changes: 33 additions & 8 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -6612,14 +6612,15 @@ static const char *get_func_name(JSContext *ctx, JSValue func)
#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
/* only taken into account if filename is provided */
#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1)
#define JS_BACKTRACE_FLAG_FILTER_FUNC (1 << 2)

/* if filename != NULL, an additional level is added with the filename
and line number information (used for parse error). */
static void build_backtrace(JSContext *ctx, JSValue error_obj,
static void build_backtrace(JSContext *ctx, JSValue error_obj, JSValue filter_func,
const char *filename, int line_num, int col_num,
int backtrace_flags)
{
JSStackFrame *sf;
JSStackFrame *sf, *sf_start;
JSValue stack, prepare, saved_exception;
DynBuf dbuf;
const char *func_name_str;
Expand Down Expand Up @@ -6668,7 +6669,20 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj,
if (filename && (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL))
goto done;

for (sf = rt->current_stack_frame; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) {
sf_start = rt->current_stack_frame;

/* Find the frame we want to start from. Note that when a filter is used the filter
function will be the first, but we also specify we want to skip the first one. */
if (backtrace_flags & JS_BACKTRACE_FLAG_FILTER_FUNC) {
for (sf = sf_start; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) {
if (js_same_value(ctx, sf->cur_func, filter_func)) {
sf_start = sf;
break;
}
}
}

for (sf = sf_start; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) {
if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
continue;
Expand Down Expand Up @@ -6811,7 +6825,7 @@ static JSValue JS_MakeError(JSContext *ctx, JSErrorEnum error_num,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
}
if (add_backtrace)
build_backtrace(ctx, obj, NULL, 0, 0, 0);
build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0, 0);
return obj;
}

Expand Down Expand Up @@ -17380,7 +17394,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
before if the exception happens in a bytecode
operation */
sf->cur_pc = pc;
build_backtrace(ctx, rt->current_exception, NULL, 0, 0, 0);
build_backtrace(ctx, rt->current_exception, JS_UNDEFINED, NULL, 0, 0, 0);
}
if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
while (sp > stack_buf) {
Expand Down Expand Up @@ -18968,7 +18982,7 @@ int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const
backtrace_flags = 0;
if (s->cur_func && s->cur_func->backtrace_barrier)
backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
build_backtrace(ctx, ctx->rt->current_exception, s->filename,
build_backtrace(ctx, ctx->rt->current_exception, JS_UNDEFINED, s->filename,
s->line_num, s->col_num, backtrace_flags);
return -1;
}
Expand Down Expand Up @@ -23470,7 +23484,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
backtrace_flags = 0;
if (s->cur_func && s->cur_func->backtrace_barrier)
backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
build_backtrace(s->ctx, s->ctx->rt->current_exception,
build_backtrace(s->ctx, s->ctx->rt->current_exception, JS_UNDEFINED,
s->filename,
s->token.line_num,
s->token.col_num,
Expand Down Expand Up @@ -37909,7 +37923,7 @@ static JSValue js_error_constructor(JSContext *ctx, JSValue new_target,
}

/* skip the Error() function in the backtrace */
build_backtrace(ctx, obj, NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
return obj;
exception:
JS_FreeValue(ctx, obj);
Expand Down Expand Up @@ -37999,8 +38013,19 @@ static JSValue js_error_set_prepareStackTrace(JSContext *ctx, JSValue this_val,
return JS_UNDEFINED;
}

static JSValue js_error_capture_stack_trace(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
JSValue v = argv[0];
if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
return JS_ThrowTypeErrorNotAnObject(ctx);
build_backtrace(ctx, v, argv[1], NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL|JS_BACKTRACE_FLAG_FILTER_FUNC);
return JS_UNDEFINED;
}

static const JSCFunctionListEntry js_error_funcs[] = {
JS_CFUNC_DEF("isError", 1, js_error_isError ),
JS_CFUNC_DEF("captureStackTrace", 2, js_error_capture_stack_trace),
JS_CGETSET_DEF("stackTraceLimit", js_error_get_stackTraceLimit, js_error_set_stackTraceLimit ),
JS_CGETSET_DEF("prepareStackTrace", js_error_get_prepareStackTrace, js_error_set_prepareStackTrace ),
};
Expand Down
33 changes: 33 additions & 0 deletions tests/test_builtin.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,37 @@ function test_exception_stack_size_limit()
assert(!f.isNative());
}

function test_exception_capture_stack_trace()
{
var o = {};

assertThrows(TypeError, (function() {
Error.captureStackTrace();
}));

Error.captureStackTrace(o);

assert(typeof o.stack === 'string');
assert(o.stack.includes('test_exception_capture_stack_trace'));
}

function test_exception_capture_stack_trace_filter()
{
var o = {};
const fun1 = () => { fun2(); };
const fun2 = () => { fun3(); };
const fun3 = () => { log_stack(); };
function log_stack() {
Error.captureStackTrace(o, fun3);
}
fun1();

Error.captureStackTrace(o);

assert(!o.stack.includes('fun3'));
assert(!o.stack.includes('log_stack'));
}

function my_func(a, b)
{
return a + b;
Expand Down Expand Up @@ -1051,4 +1082,6 @@ test_exception_source_pos();
test_function_source_pos();
test_exception_prepare_stack();
test_exception_stack_size_limit();
test_exception_capture_stack_trace();
test_exception_capture_stack_trace_filter();
test_cur_pc();