mirror of
https://github.com/DoneJS-Runtime/quickjs-done-nextgen.git
synced 2025-01-09 17:43:15 +00:00
Implement Error.captureStackTrace
This commit is contained in:
parent
0b0b794605
commit
c351133dcc
3 changed files with 70 additions and 9 deletions
|
@ -62,4 +62,7 @@ of ES features present in NG:
|
||||||
|
|
||||||
Some non-standard but widely used APIs have also been added:
|
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
quickjs.c
41
quickjs.c
|
@ -6612,14 +6612,15 @@ static const char *get_func_name(JSContext *ctx, JSValue func)
|
||||||
#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
|
#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
|
||||||
/* only taken into account if filename is provided */
|
/* only taken into account if filename is provided */
|
||||||
#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1)
|
#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
|
/* if filename != NULL, an additional level is added with the filename
|
||||||
and line number information (used for parse error). */
|
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,
|
const char *filename, int line_num, int col_num,
|
||||||
int backtrace_flags)
|
int backtrace_flags)
|
||||||
{
|
{
|
||||||
JSStackFrame *sf;
|
JSStackFrame *sf, *sf_start;
|
||||||
JSValue stack, prepare, saved_exception;
|
JSValue stack, prepare, saved_exception;
|
||||||
DynBuf dbuf;
|
DynBuf dbuf;
|
||||||
const char *func_name_str;
|
const char *func_name_str;
|
||||||
|
@ -6668,7 +6669,20 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj,
|
||||||
if (filename && (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL))
|
if (filename && (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL))
|
||||||
goto done;
|
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) {
|
if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
|
||||||
backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
|
backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
|
||||||
continue;
|
continue;
|
||||||
|
@ -6811,7 +6825,7 @@ static JSValue JS_MakeError(JSContext *ctx, JSErrorEnum error_num,
|
||||||
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
||||||
}
|
}
|
||||||
if (add_backtrace)
|
if (add_backtrace)
|
||||||
build_backtrace(ctx, obj, NULL, 0, 0, 0);
|
build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0, 0);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17380,7 +17394,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
|
||||||
before if the exception happens in a bytecode
|
before if the exception happens in a bytecode
|
||||||
operation */
|
operation */
|
||||||
sf->cur_pc = pc;
|
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)) {
|
if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
|
||||||
while (sp > stack_buf) {
|
while (sp > stack_buf) {
|
||||||
|
@ -18968,7 +18982,7 @@ int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const
|
||||||
backtrace_flags = 0;
|
backtrace_flags = 0;
|
||||||
if (s->cur_func && s->cur_func->backtrace_barrier)
|
if (s->cur_func && s->cur_func->backtrace_barrier)
|
||||||
backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
|
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);
|
s->line_num, s->col_num, backtrace_flags);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -23470,7 +23484,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
|
||||||
backtrace_flags = 0;
|
backtrace_flags = 0;
|
||||||
if (s->cur_func && s->cur_func->backtrace_barrier)
|
if (s->cur_func && s->cur_func->backtrace_barrier)
|
||||||
backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
|
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->filename,
|
||||||
s->token.line_num,
|
s->token.line_num,
|
||||||
s->token.col_num,
|
s->token.col_num,
|
||||||
|
@ -37909,7 +37923,7 @@ static JSValue js_error_constructor(JSContext *ctx, JSValue new_target,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* skip the Error() function in the backtrace */
|
/* 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;
|
return obj;
|
||||||
exception:
|
exception:
|
||||||
JS_FreeValue(ctx, obj);
|
JS_FreeValue(ctx, obj);
|
||||||
|
@ -37999,8 +38013,19 @@ static JSValue js_error_set_prepareStackTrace(JSContext *ctx, JSValue this_val,
|
||||||
return JS_UNDEFINED;
|
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[] = {
|
static const JSCFunctionListEntry js_error_funcs[] = {
|
||||||
JS_CFUNC_DEF("isError", 1, js_error_isError ),
|
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("stackTraceLimit", js_error_get_stackTraceLimit, js_error_set_stackTraceLimit ),
|
||||||
JS_CGETSET_DEF("prepareStackTrace", js_error_get_prepareStackTrace, js_error_set_prepareStackTrace ),
|
JS_CGETSET_DEF("prepareStackTrace", js_error_get_prepareStackTrace, js_error_set_prepareStackTrace ),
|
||||||
};
|
};
|
||||||
|
|
|
@ -81,6 +81,37 @@ function test_exception_stack_size_limit()
|
||||||
assert(!f.isNative());
|
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)
|
function my_func(a, b)
|
||||||
{
|
{
|
||||||
return a + b;
|
return a + b;
|
||||||
|
@ -1051,4 +1082,6 @@ test_exception_source_pos();
|
||||||
test_function_source_pos();
|
test_function_source_pos();
|
||||||
test_exception_prepare_stack();
|
test_exception_prepare_stack();
|
||||||
test_exception_stack_size_limit();
|
test_exception_stack_size_limit();
|
||||||
|
test_exception_capture_stack_trace();
|
||||||
|
test_exception_capture_stack_trace_filter();
|
||||||
test_cur_pc();
|
test_cur_pc();
|
||||||
|
|
Loading…
Reference in a new issue