Skip to content

Commit 7ad9807

Browse files
committed
Report async failures via exit code
Fixes: #340
1 parent 5bf3545 commit 7ad9807

File tree

4 files changed

+52
-31
lines changed

4 files changed

+52
-31
lines changed

qjs.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ int main(int argc, char **argv)
321321
{
322322
JSRuntime *rt;
323323
JSContext *ctx;
324+
JSValue ret;
324325
struct trace_malloc_data trace_data = { NULL };
325326
int optind;
326327
char *expr = NULL;
@@ -531,7 +532,11 @@ int main(int argc, char **argv)
531532
if (interactive) {
532533
js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0);
533534
}
534-
js_std_loop(ctx);
535+
ret = js_std_loop(ctx);
536+
if (!JS_IsUndefined(ret)) {
537+
js_std_dump_error1(ctx, ret);
538+
goto fail;
539+
}
535540
}
536541

537542
if (dump_memory) {

quickjs-libc.c

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ typedef struct JSThreadState {
153153
struct list_head port_list; /* list of JSWorkerMessageHandler.link */
154154
int eval_script_recurse; /* only used in the main thread */
155155
int64_t next_timer_id; /* for setTimeout / setInterval */
156+
JSValue exc; /* current exception from one of our handlers */
156157
/* not used in the main thread */
157158
JSWorkerMessagePipe *recv_pipe, *send_pipe;
158159
} JSThreadState;
@@ -2133,51 +2134,61 @@ static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
21332134
return promise;
21342135
}
21352136

2136-
static void call_handler(JSContext *ctx, JSValue func)
2137+
static int call_handler(JSContext *ctx, JSValue func)
21372138
{
2139+
int r;
21382140
JSValue ret, func1;
21392141
/* 'func' might be destroyed when calling itself (if it frees the
21402142
handler), so must take extra care */
21412143
func1 = JS_DupValue(ctx, func);
21422144
ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL);
21432145
JS_FreeValue(ctx, func1);
2144-
if (JS_IsException(ret))
2145-
js_std_dump_error(ctx);
2146+
r = 0;
2147+
if (JS_IsException(ret)) {
2148+
JSRuntime *rt = JS_GetRuntime(ctx);
2149+
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
2150+
ts->exc = JS_GetException(ctx);
2151+
r = -1;
2152+
}
21462153
JS_FreeValue(ctx, ret);
2154+
return r;
21472155
}
21482156

2149-
static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts)
2157+
static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, int *min_delay)
21502158
{
21512159
JSValue func;
21522160
JSOSTimer *th;
2153-
int min_delay;
21542161
int64_t cur_time, delay;
21552162
struct list_head *el;
2163+
int r;
21562164

2157-
if (list_empty(&ts->os_timers))
2158-
return -1;
2165+
if (list_empty(&ts->os_timers)) {
2166+
*min_delay = -1;
2167+
return 0;
2168+
}
21592169

21602170
cur_time = js__hrtime_ms();
2161-
min_delay = 10000;
2171+
*min_delay = INT32_MAX;
21622172

21632173
list_for_each(el, &ts->os_timers) {
21642174
th = list_entry(el, JSOSTimer, link);
21652175
delay = th->timeout - cur_time;
21662176
if (delay > 0) {
2167-
min_delay = min_int(min_delay, delay);
2177+
*min_delay = min_int(*min_delay, delay);
21682178
} else {
2179+
*min_delay = 0;
21692180
func = JS_DupValueRT(rt, th->func);
21702181
if (th->repeats)
21712182
th->timeout = cur_time + th->delay;
21722183
else
21732184
free_timer(rt, th);
2174-
call_handler(ctx, func);
2185+
r = call_handler(ctx, func);
21752186
JS_FreeValueRT(rt, func);
2176-
return 0;
2187+
return r;
21772188
}
21782189
}
21792190

2180-
return min_delay;
2191+
return 0;
21812192
}
21822193

21832194
#if defined(_WIN32)
@@ -2192,7 +2203,8 @@ static int js_os_poll(JSContext *ctx)
21922203

21932204
/* XXX: handle signals if useful */
21942205

2195-
min_delay = js_os_run_timers(rt, ctx, ts);
2206+
if (js_os_run_timers(rt, ctx, ts, &min_delay))
2207+
return -1;
21962208
if (min_delay == 0)
21972209
return 0; // expired timer
21982210
if (min_delay < 0)
@@ -2221,9 +2233,8 @@ static int js_os_poll(JSContext *ctx)
22212233
list_for_each(el, &ts->os_rw_handlers) {
22222234
rh = list_entry(el, JSOSRWHandler, link);
22232235
if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) {
2224-
call_handler(ctx, rh->rw_func[0]);
2236+
return call_handler(ctx, rh->rw_func[0]);
22252237
/* must stop because the list may have been modified */
2226-
break;
22272238
}
22282239
}
22292240
}
@@ -2332,13 +2343,13 @@ static int js_os_poll(JSContext *ctx)
23322343
mask = (uint64_t)1 << sh->sig_num;
23332344
if (os_pending_signals & mask) {
23342345
os_pending_signals &= ~mask;
2335-
call_handler(ctx, sh->func);
2336-
return 0;
2346+
return call_handler(ctx, sh->func);
23372347
}
23382348
}
23392349
}
23402350

2341-
min_delay = js_os_run_timers(rt, ctx, ts);
2351+
if (js_os_run_timers(rt, ctx, ts, &min_delay))
2352+
return -1;
23422353
if (min_delay == 0)
23432354
return 0; // expired timer
23442355
if (min_delay < 0)
@@ -2379,15 +2390,13 @@ static int js_os_poll(JSContext *ctx)
23792390
rh = list_entry(el, JSOSRWHandler, link);
23802391
if (!JS_IsNull(rh->rw_func[0]) &&
23812392
FD_ISSET(rh->fd, &rfds)) {
2382-
call_handler(ctx, rh->rw_func[0]);
2393+
return call_handler(ctx, rh->rw_func[0]);
23832394
/* must stop because the list may have been modified */
2384-
goto done;
23852395
}
23862396
if (!JS_IsNull(rh->rw_func[1]) &&
23872397
FD_ISSET(rh->fd, &wfds)) {
2388-
call_handler(ctx, rh->rw_func[1]);
2398+
return call_handler(ctx, rh->rw_func[1]);
23892399
/* must stop because the list may have been modified */
2390-
goto done;
23912400
}
23922401
}
23932402

@@ -3879,6 +3888,7 @@ void js_std_init_handlers(JSRuntime *rt)
38793888
init_list_head(&ts->port_list);
38803889

38813890
ts->next_timer_id = 1;
3891+
ts->exc = JS_UNDEFINED;
38823892

38833893
JS_SetRuntimeOpaque(rt, ts);
38843894

@@ -3938,7 +3948,7 @@ static void js_dump_obj(JSContext *ctx, FILE *f, JSValue val)
39383948
}
39393949
}
39403950

3941-
static void js_std_dump_error1(JSContext *ctx, JSValue exception_val)
3951+
void js_std_dump_error1(JSContext *ctx, JSValue exception_val)
39423952
{
39433953
JSValue val;
39443954
BOOL is_error;
@@ -3974,8 +3984,10 @@ void js_std_promise_rejection_tracker(JSContext *ctx, JSValue promise,
39743984
}
39753985

39763986
/* main loop which calls the user JS callbacks */
3977-
void js_std_loop(JSContext *ctx)
3987+
JSValue js_std_loop(JSContext *ctx)
39783988
{
3989+
JSRuntime *rt = JS_GetRuntime(ctx);
3990+
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
39793991
JSContext *ctx1;
39803992
int err;
39813993

@@ -3985,7 +3997,8 @@ void js_std_loop(JSContext *ctx)
39853997
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
39863998
if (err <= 0) {
39873999
if (err < 0) {
3988-
js_std_dump_error(ctx1);
4000+
ts->exc = JS_GetException(ctx1);
4001+
goto done;
39894002
}
39904003
break;
39914004
}
@@ -3994,6 +4007,8 @@ void js_std_loop(JSContext *ctx)
39944007
if (!os_poll_func || os_poll_func(ctx))
39954008
break;
39964009
}
4010+
done:
4011+
return ts->exc;
39974012
}
39984013

39994014
/* Wait for a promise and execute pending jobs while waiting for

quickjs-libc.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
3737
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
3838
JSModuleDef *js_init_module_bjson(JSContext *ctx, const char *module_name);
3939
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
40-
void js_std_loop(JSContext *ctx);
40+
JSValue js_std_loop(JSContext *ctx);
4141
JSValue js_std_await(JSContext *ctx, JSValue obj);
4242
void js_std_init_handlers(JSRuntime *rt);
4343
void js_std_free_handlers(JSRuntime *rt);
4444
void js_std_dump_error(JSContext *ctx);
45+
void js_std_dump_error1(JSContext *ctx, JSValue exception_val);
4546
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
4647
int js_module_set_import_meta(JSContext *ctx, JSValue func_val,
4748
JS_BOOL use_realpath, JS_BOOL is_main);

tests/test_std.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,10 @@ function test_timeout_order()
280280
if (globalThis.__running_with_sanitizer__) return;
281281

282282
var s = "";
283-
os.setTimeout(a, 100);
284-
os.setTimeout(b, 200);
285-
os.setTimeout(d, 500);
286-
function a() { s += "a"; os.setTimeout(c, 200); }
283+
os.setTimeout(a, 0);
284+
os.setTimeout(b, 100);
285+
os.setTimeout(d, 700);
286+
function a() { s += "a"; os.setTimeout(c, 300); }
287287
function b() { s += "b"; }
288288
function c() { s += "c"; }
289289
function d() { assert(s === "abc"); } // not "acb"

0 commit comments

Comments
 (0)