Skip to content

Commit c85fd87

Browse files
authored
Show JSCFunctionData callbacks in stack traces (#1204)
While here also: - rename some functions for consistency - remove some declarations that duplicate declarations in quickjs.h - add a stack check to js_call_c_function_data Fixes: #1199
1 parent 2d680a9 commit c85fd87

File tree

2 files changed

+85
-22
lines changed

2 files changed

+85
-22
lines changed

api-test.c

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,31 @@ static JSValue eval(JSContext *ctx, const char *code)
1212
return JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_GLOBAL);
1313
}
1414

15+
static JSValue cfunc_callback(JSContext *ctx, JSValueConst this_val,
16+
int argc, JSValueConst *argv)
17+
{
18+
return JS_ThrowTypeError(ctx, "from cfunc");
19+
}
20+
21+
static JSValue cfuncdata_callback(JSContext *ctx, JSValueConst this_val,
22+
int argc, JSValueConst *argv,
23+
int magic, JSValueConst *func_data)
24+
{
25+
return JS_ThrowTypeError(ctx, "from cfuncdata");
26+
}
27+
1528
static void cfunctions(void)
1629
{
1730
uint32_t length;
1831
const char *s;
19-
JSValue ret;
32+
JSValue ret, stack;
2033

2134
JSRuntime *rt = JS_NewRuntime();
2235
JSContext *ctx = JS_NewContext(rt);
23-
JSValue cfunc = JS_NewCFunction(ctx, NULL, "cfunc", 42);
36+
JSValue cfunc = JS_NewCFunction(ctx, cfunc_callback, "cfunc", 42);
2437
JSValue cfuncdata =
25-
JS_NewCFunctionData2(ctx, NULL, "cfuncdata", /*length*/1337, /*magic*/0,
26-
/*data_len*/0, NULL);
38+
JS_NewCFunctionData2(ctx, cfuncdata_callback, "cfuncdata",
39+
/*length*/1337, /*magic*/0, /*data_len*/0, NULL);
2740
JSValue global = JS_GetGlobalObject(ctx);
2841
JS_SetPropertyStr(ctx, global, "cfunc", cfunc);
2942
JS_SetPropertyStr(ctx, global, "cfuncdata", cfuncdata);
@@ -57,6 +70,40 @@ static void cfunctions(void)
5770
assert(0 == JS_ToUint32(ctx, &length, ret));
5871
assert(length == 1337);
5972

73+
ret = eval(ctx, "cfunc()");
74+
assert(JS_IsException(ret));
75+
ret = JS_GetException(ctx);
76+
assert(JS_IsError(ctx, ret));
77+
stack = JS_GetPropertyStr(ctx, ret, "stack");
78+
assert(JS_IsString(stack));
79+
s = JS_ToCString(ctx, stack);
80+
JS_FreeValue(ctx, stack);
81+
assert(s);
82+
assert(!strcmp(s, " at cfunc (native)\n at <eval> (<input>:1:1)\n"));
83+
JS_FreeCString(ctx, s);
84+
s = JS_ToCString(ctx, ret);
85+
JS_FreeValue(ctx, ret);
86+
assert(s);
87+
assert(!strcmp(s, "TypeError: from cfunc"));
88+
JS_FreeCString(ctx, s);
89+
90+
ret = eval(ctx, "cfuncdata()");
91+
assert(JS_IsException(ret));
92+
ret = JS_GetException(ctx);
93+
assert(JS_IsError(ctx, ret));
94+
stack = JS_GetPropertyStr(ctx, ret, "stack");
95+
assert(JS_IsString(stack));
96+
s = JS_ToCString(ctx, stack);
97+
JS_FreeValue(ctx, stack);
98+
assert(s);
99+
assert(!strcmp(s, " at cfuncdata (native)\n at <eval> (<input>:1:1)\n"));
100+
JS_FreeCString(ctx, s);
101+
s = JS_ToCString(ctx, ret);
102+
JS_FreeValue(ctx, ret);
103+
assert(s);
104+
assert(!strcmp(s, "TypeError: from cfuncdata"));
105+
JS_FreeCString(ctx, s);
106+
60107
JS_FreeContext(ctx);
61108
JS_FreeRuntime(rt);
62109
}

quickjs.c

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,8 +1120,6 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
11201120
JSValue val, bool is_array_ctor);
11211121
static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
11221122
JSValueConst val, int flags, int scope_idx);
1123-
JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...);
1124-
11251123
static __maybe_unused void JS_DumpString(JSRuntime *rt, JSString *p);
11261124
static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
11271125
static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
@@ -1234,7 +1232,7 @@ static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
12341232
static JSProperty *add_property(JSContext *ctx,
12351233
JSObject *p, JSAtom prop, int prop_flags);
12361234
static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
1237-
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
1235+
static JSValue JS_ThrowStackOverflow(JSContext *ctx);
12381236
static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
12391237
static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
12401238
static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
@@ -1276,7 +1274,7 @@ static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
12761274
static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx);
12771275
static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
12781276
bool is_arg);
1279-
static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
1277+
static JSValue js_call_generator_function(JSContext *ctx, JSValueConst func_obj,
12801278
JSValueConst this_obj,
12811279
int argc, JSValueConst *argv,
12821280
int flags);
@@ -1342,7 +1340,7 @@ static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
13421340
static void js_c_function_data_finalizer(JSRuntime *rt, JSValueConst val);
13431341
static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
13441342
JS_MarkFunc *mark_func);
1345-
static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
1343+
static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj,
13461344
JSValueConst this_val,
13471345
int argc, JSValueConst *argv, int flags);
13481346
static JSAtom js_symbol_to_atom(JSContext *ctx, JSValueConst val);
@@ -1871,9 +1869,9 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
18711869
rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;
18721870

18731871
rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
1874-
rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
1872+
rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_call_c_function_data;
18751873
rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
1876-
rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
1874+
rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_call_generator_function;
18771875
if (init_shape_hash(rt))
18781876
goto fail;
18791877

@@ -5433,26 +5431,44 @@ static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
54335431
}
54345432
}
54355433

5436-
static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
5434+
static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj,
54375435
JSValueConst this_val,
54385436
int argc, JSValueConst *argv, int flags)
54395437
{
5440-
JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);
5438+
JSRuntime *rt = ctx->rt;
5439+
JSStackFrame sf_s, *sf = &sf_s, *prev_sf;
5440+
JSCFunctionDataRecord *s;
54415441
JSValueConst *arg_buf;
5442+
JSValue ret;
5443+
size_t stack_size;
5444+
int arg_count;
54425445
int i;
54435446

5444-
/* XXX: could add the function on the stack for debug */
5445-
if (unlikely(argc < s->length)) {
5446-
arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
5447+
s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);
5448+
if (!s)
5449+
return JS_EXCEPTION; // can't really happen
5450+
arg_buf = argv;
5451+
arg_count = s->length;
5452+
if (unlikely(argc < arg_count)) {
5453+
stack_size = arg_count * sizeof(arg_buf[0]);
5454+
if (js_check_stack_overflow(rt, stack_size))
5455+
return JS_ThrowStackOverflow(ctx);
5456+
arg_buf = alloca(stack_size);
54475457
for(i = 0; i < argc; i++)
54485458
arg_buf[i] = argv[i];
5449-
for(i = argc; i < s->length; i++)
5459+
for(i = argc; i < arg_count; i++)
54505460
arg_buf[i] = JS_UNDEFINED;
5451-
} else {
5452-
arg_buf = argv;
54535461
}
5454-
5455-
return s->func(ctx, this_val, argc, arg_buf, s->magic, vc(s->data));
5462+
prev_sf = rt->current_stack_frame;
5463+
sf->prev_frame = prev_sf;
5464+
rt->current_stack_frame = sf;
5465+
// TODO(bnoordhuis) switch realms like js_call_c_function does
5466+
sf->is_strict_mode = false;
5467+
sf->cur_func = unsafe_unconst(func_obj);
5468+
sf->arg_count = argc;
5469+
ret = s->func(ctx, this_val, argc, arg_buf, s->magic, vc(s->data));
5470+
rt->current_stack_frame = sf->prev_frame;
5471+
return ret;
54565472
}
54575473

54585474
JSValue JS_NewCFunctionData2(JSContext *ctx, JSCFunctionData *func,
@@ -19107,7 +19123,7 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
1910719123
return ret;
1910819124
}
1910919125

19110-
static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
19126+
static JSValue js_call_generator_function(JSContext *ctx, JSValueConst func_obj,
1911119127
JSValueConst this_obj,
1911219128
int argc, JSValueConst *argv,
1911319129
int flags)

0 commit comments

Comments
 (0)