@@ -394,6 +394,7 @@ struct JSContext {
394
394
JSValue promise_ctor;
395
395
JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
396
396
JSValue error_ctor;
397
+ JSValue error_back_trace;
397
398
JSValue error_prepare_stack;
398
399
int error_stack_trace_limit;
399
400
JSValue iterator_ctor;
@@ -2305,6 +2306,7 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
2305
2306
ctx->regexp_ctor = JS_NULL;
2306
2307
ctx->promise_ctor = JS_NULL;
2307
2308
ctx->error_ctor = JS_NULL;
2309
+ ctx->error_back_trace = JS_UNDEFINED;
2308
2310
ctx->error_prepare_stack = JS_UNDEFINED;
2309
2311
ctx->error_stack_trace_limit = 10;
2310
2312
init_list_head(&ctx->loaded_modules);
@@ -2424,6 +2426,7 @@ static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
2424
2426
JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
2425
2427
}
2426
2428
JS_MarkValue(rt, ctx->error_ctor, mark_func);
2429
+ JS_MarkValue(rt, ctx->error_back_trace, mark_func);
2427
2430
JS_MarkValue(rt, ctx->error_prepare_stack, mark_func);
2428
2431
for(i = 0; i < rt->class_count; i++) {
2429
2432
JS_MarkValue(rt, ctx->class_proto[i], mark_func);
@@ -2491,6 +2494,7 @@ void JS_FreeContext(JSContext *ctx)
2491
2494
JS_FreeValue(ctx, ctx->native_error_proto[i]);
2492
2495
}
2493
2496
JS_FreeValue(ctx, ctx->error_ctor);
2497
+ JS_FreeValue(ctx, ctx->error_back_trace);
2494
2498
JS_FreeValue(ctx, ctx->error_prepare_stack);
2495
2499
for(i = 0; i < rt->class_count; i++) {
2496
2500
JS_FreeValue(ctx, ctx->class_proto[i]);
@@ -6616,14 +6620,28 @@ static const char *get_func_name(JSContext *ctx, JSValue func)
6616
6620
return JS_ToCString(ctx, val);
6617
6621
}
6618
6622
6623
+ /* Note: it is important that no exception is returned by this function */
6624
+ static bool can_add_backtrace(JSValue obj)
6625
+ {
6626
+ JSObject *p;
6627
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
6628
+ return false;
6629
+ p = JS_VALUE_GET_OBJ(obj);
6630
+ if (p->class_id != JS_CLASS_ERROR)
6631
+ return false;
6632
+ if (find_own_property1(p, JS_ATOM_stack))
6633
+ return false;
6634
+ return true;
6635
+ }
6636
+
6619
6637
#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
6620
6638
/* only taken into account if filename is provided */
6621
6639
#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1)
6622
6640
#define JS_BACKTRACE_FLAG_FILTER_FUNC (1 << 2)
6623
6641
6624
6642
/* if filename != NULL, an additional level is added with the filename
6625
6643
and line number information (used for parse error). */
6626
- static void build_backtrace(JSContext *ctx, JSValue error_obj , JSValue filter_func,
6644
+ static void build_backtrace(JSContext *ctx, JSValue error_val , JSValue filter_func,
6627
6645
const char *filename, int line_num, int col_num,
6628
6646
int backtrace_flags)
6629
6647
{
@@ -6634,7 +6652,7 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj, JSValue filter_fu
6634
6652
const char *str1;
6635
6653
JSObject *p;
6636
6654
JSFunctionBytecode *b;
6637
- bool backtrace_barrier, has_prepare;
6655
+ bool backtrace_barrier, has_prepare, has_filter_func ;
6638
6656
JSRuntime *rt;
6639
6657
JSCallSiteData csd[64];
6640
6658
uint32_t i;
@@ -6646,6 +6664,7 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj, JSValue filter_fu
6646
6664
stack_trace_limit = max_int(stack_trace_limit, 0);
6647
6665
rt = ctx->rt;
6648
6666
has_prepare = false;
6667
+ has_filter_func = backtrace_flags & JS_BACKTRACE_FLAG_FILTER_FUNC;
6649
6668
i = 0;
6650
6669
6651
6670
if (!rt->in_prepare_stack_trace && !JS_IsNull(ctx->error_ctor)) {
@@ -6681,7 +6700,7 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj, JSValue filter_fu
6681
6700
6682
6701
/* Find the frame we want to start from. Note that when a filter is used the filter
6683
6702
function will be the first, but we also specify we want to skip the first one. */
6684
- if (backtrace_flags & JS_BACKTRACE_FLAG_FILTER_FUNC ) {
6703
+ if (has_filter_func ) {
6685
6704
for (sf = sf_start; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) {
6686
6705
if (js_same_value(ctx, sf->cur_func, filter_func)) {
6687
6706
sf_start = sf;
@@ -6717,23 +6736,24 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj, JSValue filter_fu
6717
6736
dbuf_printf(&dbuf, " at %s", str1);
6718
6737
JS_FreeCString(ctx, func_name_str);
6719
6738
6720
- if (b) {
6739
+ if (b && sf->cur_pc ) {
6721
6740
const char *atom_str;
6722
6741
int line_num1, col_num1;
6742
+ uint32_t pc;
6723
6743
6724
- /* Bytecode functions must have cur_pc set in the stack frame. */
6725
- if (sf->cur_pc == NULL)
6726
- abort();
6727
-
6728
- line_num1 = find_line_num(ctx, b,
6729
- sf->cur_pc - b->byte_code_buf - 1,
6730
- &col_num1);
6744
+ pc = sf->cur_pc - b->byte_code_buf - 1;
6745
+ line_num1 = find_line_num(ctx, b, pc, &col_num1);
6731
6746
atom_str = b->filename ? JS_AtomToCString(ctx, b->filename) : NULL;
6732
6747
dbuf_printf(&dbuf, " (%s", atom_str ? atom_str : "<null>");
6733
6748
JS_FreeCString(ctx, atom_str);
6734
6749
if (line_num1 != -1)
6735
6750
dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1);
6736
6751
dbuf_putc(&dbuf, ')');
6752
+ } else if (b) {
6753
+ // FIXME(bnoordhuis) Missing `sf->cur_pc = pc` in bytecode
6754
+ // handler in JS_CallInternal. Almost never user observable
6755
+ // except with intercepting JS proxies that throw exceptions.
6756
+ dbuf_printf(&dbuf, " (missing)");
6737
6757
} else {
6738
6758
dbuf_printf(&dbuf, " (native)");
6739
6759
}
@@ -6769,7 +6789,7 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj, JSValue filter_fu
6769
6789
JS_FreeValue(ctx, csd[k].func_name);
6770
6790
}
6771
6791
JSValue args[] = {
6772
- error_obj ,
6792
+ error_val ,
6773
6793
stack,
6774
6794
};
6775
6795
JSValue stack2 = JS_Call(ctx, prepare, ctx->error_ctor, countof(args), args);
@@ -6790,21 +6810,14 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj, JSValue filter_fu
6790
6810
}
6791
6811
6792
6812
rt->in_prepare_stack_trace = false;
6793
- JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, stack, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6794
- }
6795
-
6796
- /* Note: it is important that no exception is returned by this function */
6797
- static bool is_backtrace_needed(JSContext *ctx, JSValue obj)
6798
- {
6799
- JSObject *p;
6800
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
6801
- return false;
6802
- p = JS_VALUE_GET_OBJ(obj);
6803
- if (p->class_id != JS_CLASS_ERROR)
6804
- return false;
6805
- if (find_own_property1(p, JS_ATOM_stack))
6806
- return false;
6807
- return true;
6813
+ if (JS_IsUndefined(ctx->error_back_trace))
6814
+ ctx->error_back_trace = js_dup(stack);
6815
+ if (has_filter_func || can_add_backtrace(error_val)) {
6816
+ JS_DefinePropertyValue(ctx, error_val, JS_ATOM_stack, stack,
6817
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6818
+ } else {
6819
+ JS_FreeValue(ctx, stack);
6820
+ }
6808
6821
}
6809
6822
6810
6823
JSValue JS_NewError(JSContext *ctx)
@@ -17432,13 +17445,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
17432
17445
}
17433
17446
}
17434
17447
exception:
17435
- if (is_backtrace_needed(ctx, rt->current_exception)) {
17436
- /* add the backtrace information now (it is not done
17437
- before if the exception happens in a bytecode
17438
- operation */
17439
- sf->cur_pc = pc;
17440
- build_backtrace(ctx, rt->current_exception, JS_UNDEFINED, NULL, 0, 0, 0);
17441
- }
17448
+ sf->cur_pc = pc;
17449
+ build_backtrace(ctx, rt->current_exception, JS_UNDEFINED, NULL, 0, 0, 0);
17442
17450
if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
17443
17451
while (sp > stack_buf) {
17444
17452
JSValue val = *--sp;
@@ -17453,6 +17461,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
17453
17461
} else {
17454
17462
*sp++ = rt->current_exception;
17455
17463
rt->current_exception = JS_UNINITIALIZED;
17464
+ JS_FreeValueRT(rt, ctx->error_back_trace);
17465
+ ctx->error_back_trace = JS_UNDEFINED;
17456
17466
pc = b->byte_code_buf + pos;
17457
17467
goto restart;
17458
17468
}
@@ -25570,6 +25580,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
25570
25580
js_parse_error(s, "line terminator not allowed after throw");
25571
25581
goto fail;
25572
25582
}
25583
+ emit_source_loc(s);
25573
25584
if (js_parse_expr(s))
25574
25585
goto fail;
25575
25586
emit_op(s, OP_throw);
@@ -56168,7 +56179,9 @@ bool JS_DetectModule(const char *input, size_t input_len)
56168
56179
}
56169
56180
56170
56181
uintptr_t js_std_cmd(int cmd, ...) {
56182
+ JSContext *ctx;
56171
56183
JSRuntime *rt;
56184
+ JSValue *pv;
56172
56185
uintptr_t rv;
56173
56186
va_list ap;
56174
56187
@@ -56183,6 +56196,12 @@ uintptr_t js_std_cmd(int cmd, ...) {
56183
56196
rt = va_arg(ap, JSRuntime *);
56184
56197
rt->libc_opaque = va_arg(ap, void *);
56185
56198
break;
56199
+ case 2: // ErrorBackTrace
56200
+ ctx = va_arg(ap, JSContext *);
56201
+ pv = va_arg(ap, JSValue *);
56202
+ *pv = ctx->error_back_trace;
56203
+ ctx->error_back_trace = JS_UNDEFINED;
56204
+ break;
56186
56205
default:
56187
56206
rv = -1;
56188
56207
}
0 commit comments