Skip to content

Commit 81806be

Browse files
Fabrice Bellardsaghul
Fabrice Bellard
authored andcommitted
allow regexp interruption (e.g. with Ctrl-C in the REPL)
1 parent 69332fb commit 81806be

File tree

3 files changed

+60
-11
lines changed

3 files changed

+60
-11
lines changed

libregexp.c

+30-6
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ typedef enum {
5353

5454
#define CAPTURE_COUNT_MAX 255
5555
#define STACK_SIZE_MAX 255
56+
/* must be large enough to have a negligible runtime cost and small
57+
enough to call the interrupt callback often. */
58+
#define INTERRUPT_COUNTER_INIT 10000
5659

5760
/* unicode code points */
5861
#define CP_LS 0x2028
@@ -1992,6 +1995,7 @@ typedef struct {
19921995
bool multi_line;
19931996
bool ignore_case;
19941997
bool is_unicode;
1998+
int interrupt_counter;
19951999
void *opaque; /* used for stack overflow check */
19962000

19972001
size_t state_size;
@@ -2038,7 +2042,17 @@ static int push_state(REExecContext *s,
20382042
return 0;
20392043
}
20402044

2041-
/* return 1 if match, 0 if not match or -1 if error. */
2045+
static int lre_poll_timeout(REExecContext *s)
2046+
{
2047+
if (unlikely(--s->interrupt_counter <= 0)) {
2048+
s->interrupt_counter = INTERRUPT_COUNTER_INIT;
2049+
if (lre_check_timeout(s->opaque))
2050+
return LRE_RET_TIMEOUT;
2051+
}
2052+
return 0;
2053+
}
2054+
2055+
/* return 1 if match, 0 if not match or < 0 if error. */
20422056
static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
20432057
StackInt *stack, int stack_len,
20442058
const uint8_t *pc, const uint8_t *cptr,
@@ -2069,6 +2083,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
20692083
ret = 0;
20702084
recurse:
20712085
for(;;) {
2086+
if (lre_poll_timeout(s))
2087+
return LRE_RET_TIMEOUT;
20722088
if (s->state_stack_len == 0)
20732089
return ret;
20742090
rs = (REExecState *)(s->state_stack +
@@ -2162,7 +2178,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
21622178
ret = push_state(s, capture, stack, stack_len,
21632179
pc1, cptr, RE_EXEC_STATE_SPLIT, 0);
21642180
if (ret < 0)
2165-
return -1;
2181+
return LRE_RET_MEMORY_ERROR;
21662182
break;
21672183
}
21682184
case REOP_lookahead:
@@ -2174,12 +2190,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
21742190
RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead,
21752191
0);
21762192
if (ret < 0)
2177-
return -1;
2193+
return LRE_RET_MEMORY_ERROR;
21782194
break;
21792195

21802196
case REOP_goto:
21812197
val = get_u32(pc);
21822198
pc += 4 + (int)val;
2199+
if (lre_poll_timeout(s))
2200+
return LRE_RET_TIMEOUT;
21832201
break;
21842202
case REOP_line_start:
21852203
if (cptr == s->cbuf)
@@ -2244,6 +2262,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
22442262
pc += 4;
22452263
if (--stack[stack_len - 1] != 0) {
22462264
pc += (int)val;
2265+
if (lre_poll_timeout(s))
2266+
return LRE_RET_TIMEOUT;
22472267
}
22482268
break;
22492269
case REOP_push_char_pos:
@@ -2418,9 +2438,12 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
24182438

24192439
q = 0;
24202440
for(;;) {
2441+
if (lre_poll_timeout(s))
2442+
return LRE_RET_TIMEOUT;
24212443
res = lre_exec_backtrack(s, capture, stack, stack_len,
24222444
pc1, cptr, true);
2423-
if (res == -1)
2445+
if (res == LRE_RET_MEMORY_ERROR ||
2446+
res == LRE_RET_TIMEOUT)
24242447
return res;
24252448
if (!res)
24262449
break;
@@ -2438,7 +2461,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
24382461
RE_EXEC_STATE_GREEDY_QUANT,
24392462
q - quant_min);
24402463
if (ret < 0)
2441-
return -1;
2464+
return LRE_RET_MEMORY_ERROR;
24422465
}
24432466
}
24442467
break;
@@ -2448,7 +2471,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
24482471
}
24492472
}
24502473

2451-
/* Return 1 if match, 0 if not match or -1 if error. cindex is the
2474+
/* Return 1 if match, 0 if not match or < 0 if error (see LRE_RET_x). cindex is the
24522475
starting position of the match and must be such as 0 <= cindex <=
24532476
clen. */
24542477
int lre_exec(uint8_t **capture,
@@ -2470,6 +2493,7 @@ int lre_exec(uint8_t **capture,
24702493
s->cbuf_type = cbuf_type;
24712494
if (s->cbuf_type == 1 && s->is_unicode)
24722495
s->cbuf_type = 2;
2496+
s->interrupt_counter = INTERRUPT_COUNTER_INIT;
24732497
s->opaque = opaque;
24742498

24752499
s->state_size = sizeof(REExecState) +

libregexp.h

+5
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ extern "C" {
4343
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
4444
#define LRE_FLAG_UNICODE_SETS (1 << 8)
4545

46+
#define LRE_RET_MEMORY_ERROR (-1)
47+
#define LRE_RET_TIMEOUT (-2)
48+
4649
uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
4750
const char *buf, size_t buf_len, int re_flags,
4851
void *opaque);
@@ -60,6 +63,8 @@ void lre_byte_swap(uint8_t *buf, size_t len, bool is_byte_swapped);
6063

6164
/* must be provided by the user */
6265
bool lre_check_stack_overflow(void *opaque, size_t alloca_size);
66+
/* must be provided by the user, return non zero if time out */
67+
int lre_check_timeout(void *opaque);
6368
void *lre_realloc(void *opaque, void *ptr, size_t size);
6469

6570
/* JS identifier test */

quickjs.c

+25-5
Original file line numberDiff line numberDiff line change
@@ -7111,15 +7111,19 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
71117111
return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
71127112
}
71137113

7114+
static void JS_ThrowInterrupted(JSContext *ctx)
7115+
{
7116+
JS_ThrowInternalError(ctx, "interrupted");
7117+
JS_SetUncatchableError(ctx, ctx->rt->current_exception);
7118+
}
7119+
71147120
static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
71157121
{
71167122
JSRuntime *rt = ctx->rt;
71177123
ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
71187124
if (rt->interrupt_handler) {
71197125
if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
7120-
/* XXX: should set a specific flag to avoid catching */
7121-
JS_ThrowInternalError(ctx, "interrupted");
7122-
js_set_uncatchable_error(ctx, ctx->rt->current_exception, true);
7126+
JS_ThrowInterrupted(ctx);
71237127
return -1;
71247128
}
71257129
}
@@ -43878,6 +43882,14 @@ bool lre_check_stack_overflow(void *opaque, size_t alloca_size)
4387843882
return js_check_stack_overflow(ctx->rt, alloca_size);
4387943883
}
4388043884

43885+
int lre_check_timeout(void *opaque)
43886+
{
43887+
JSContext *ctx = opaque;
43888+
JSRuntime *rt = ctx->rt;
43889+
return (rt->interrupt_handler &&
43890+
rt->interrupt_handler(rt, rt->interrupt_opaque));
43891+
}
43892+
4388143893
void *lre_realloc(void *opaque, void *ptr, size_t size)
4388243894
{
4388343895
JSContext *ctx = opaque;
@@ -43992,7 +44004,11 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
4399244004
goto fail;
4399344005
}
4399444006
} else {
43995-
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
44007+
if (rc == LRE_RET_TIMEOUT) {
44008+
JS_ThrowInterrupted(ctx);
44009+
} else {
44010+
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
44011+
}
4399644012
goto fail;
4399744013
}
4399844014
} else {
@@ -44187,7 +44203,11 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValue ar
4418744203
goto fail;
4418844204
}
4418944205
} else {
44190-
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
44206+
if (ret == LRE_RET_TIMEOUT) {
44207+
JS_ThrowInterrupted(ctx);
44208+
} else {
44209+
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
44210+
}
4419144211
goto fail;
4419244212
}
4419344213
break;

0 commit comments

Comments
 (0)