Skip to content

Sync from bellard/ #984

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions libregexp.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ typedef enum {

#define CAPTURE_COUNT_MAX 255
#define STACK_SIZE_MAX 255
/* must be large enough to have a negligible runtime cost and small
enough to call the interrupt callback often. */
#define INTERRUPT_COUNTER_INIT 10000

/* unicode code points */
#define CP_LS 0x2028
Expand Down Expand Up @@ -1992,6 +1995,7 @@ typedef struct {
bool multi_line;
bool ignore_case;
bool is_unicode;
int interrupt_counter;
void *opaque; /* used for stack overflow check */

size_t state_size;
Expand Down Expand Up @@ -2038,7 +2042,17 @@ static int push_state(REExecContext *s,
return 0;
}

/* return 1 if match, 0 if not match or -1 if error. */
static int lre_poll_timeout(REExecContext *s)
{
if (unlikely(--s->interrupt_counter <= 0)) {
s->interrupt_counter = INTERRUPT_COUNTER_INIT;
if (lre_check_timeout(s->opaque))
return LRE_RET_TIMEOUT;
}
return 0;
}

/* return 1 if match, 0 if not match or < 0 if error. */
static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
StackInt *stack, int stack_len,
const uint8_t *pc, const uint8_t *cptr,
Expand Down Expand Up @@ -2069,6 +2083,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
ret = 0;
recurse:
for(;;) {
if (lre_poll_timeout(s))
return LRE_RET_TIMEOUT;
if (s->state_stack_len == 0)
return ret;
rs = (REExecState *)(s->state_stack +
Expand Down Expand Up @@ -2162,7 +2178,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
ret = push_state(s, capture, stack, stack_len,
pc1, cptr, RE_EXEC_STATE_SPLIT, 0);
if (ret < 0)
return -1;
return LRE_RET_MEMORY_ERROR;
break;
}
case REOP_lookahead:
Expand All @@ -2174,12 +2190,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead,
0);
if (ret < 0)
return -1;
return LRE_RET_MEMORY_ERROR;
break;

case REOP_goto:
val = get_u32(pc);
pc += 4 + (int)val;
if (lre_poll_timeout(s))
return LRE_RET_TIMEOUT;
break;
case REOP_line_start:
if (cptr == s->cbuf)
Expand Down Expand Up @@ -2244,6 +2262,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
pc += 4;
if (--stack[stack_len - 1] != 0) {
pc += (int)val;
if (lre_poll_timeout(s))
return LRE_RET_TIMEOUT;
}
break;
case REOP_push_char_pos:
Expand Down Expand Up @@ -2418,9 +2438,12 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,

q = 0;
for(;;) {
if (lre_poll_timeout(s))
return LRE_RET_TIMEOUT;
res = lre_exec_backtrack(s, capture, stack, stack_len,
pc1, cptr, true);
if (res == -1)
if (res == LRE_RET_MEMORY_ERROR ||
res == LRE_RET_TIMEOUT)
return res;
if (!res)
break;
Expand All @@ -2438,7 +2461,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
RE_EXEC_STATE_GREEDY_QUANT,
q - quant_min);
if (ret < 0)
return -1;
return LRE_RET_MEMORY_ERROR;
}
}
break;
Expand All @@ -2448,7 +2471,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
}
}

/* Return 1 if match, 0 if not match or -1 if error. cindex is the
/* Return 1 if match, 0 if not match or < 0 if error (see LRE_RET_x). cindex is the
starting position of the match and must be such as 0 <= cindex <=
clen. */
int lre_exec(uint8_t **capture,
Expand All @@ -2470,6 +2493,7 @@ int lre_exec(uint8_t **capture,
s->cbuf_type = cbuf_type;
if (s->cbuf_type == 1 && s->is_unicode)
s->cbuf_type = 2;
s->interrupt_counter = INTERRUPT_COUNTER_INIT;
s->opaque = opaque;

s->state_size = sizeof(REExecState) +
Expand Down
5 changes: 5 additions & 0 deletions libregexp.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ extern "C" {
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
#define LRE_FLAG_UNICODE_SETS (1 << 8)

#define LRE_RET_MEMORY_ERROR (-1)
#define LRE_RET_TIMEOUT (-2)

uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
const char *buf, size_t buf_len, int re_flags,
void *opaque);
Expand All @@ -60,6 +63,8 @@ void lre_byte_swap(uint8_t *buf, size_t len, bool is_byte_swapped);

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

/* JS identifier test */
Expand Down
75 changes: 46 additions & 29 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,15 +364,7 @@ typedef struct JSVarRef {
struct {
int __gc_ref_count; /* corresponds to header.ref_count */
uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */

/* 0 : the JSVarRef is on the stack. header.link is an element
of JSStackFrame.var_ref_list.
1 : the JSVarRef is detached. header.link has the normal meanning
*/
uint8_t is_detached : 1;
uint8_t is_arg : 1;
uint16_t var_idx; /* index of the corresponding function variable on
the stack */
bool is_detached;
};
};
JSValue *pvalue; /* pointer to the value, either on the stack or
Expand Down Expand Up @@ -7119,15 +7111,19 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
}

static void JS_ThrowInterrupted(JSContext *ctx)
{
JS_ThrowInternalError(ctx, "interrupted");
JS_SetUncatchableError(ctx, ctx->rt->current_exception);
}

static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
{
JSRuntime *rt = ctx->rt;
ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
if (rt->interrupt_handler) {
if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
/* XXX: should set a specific flag to avoid catching */
JS_ThrowInternalError(ctx, "interrupted");
js_set_uncatchable_error(ctx, ctx->rt->current_exception, true);
JS_ThrowInterrupted(ctx);
return -1;
}
}
Expand Down Expand Up @@ -8930,6 +8926,8 @@ static int JS_SetPropertyInternal2(JSContext *ctx, JSValueConst obj, JSAtom prop
goto retry2;
} else if (!(prs->flags & JS_PROP_WRITABLE)) {
goto read_only_prop;
} else {
break;
}
}
}
Expand Down Expand Up @@ -14438,10 +14436,16 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
{
JSVarRef *var_ref;
struct list_head *el;
JSValue *pvalue;

if (is_arg)
pvalue = &sf->arg_buf[var_idx];
else
pvalue = &sf->var_buf[var_idx];

list_for_each(el, &sf->var_ref_list) {
var_ref = list_entry(el, JSVarRef, header.link);
if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {
if (var_ref->pvalue == pvalue) {
var_ref->header.ref_count++;
return var_ref;
}
Expand All @@ -14452,13 +14456,8 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
return NULL;
var_ref->header.ref_count = 1;
var_ref->is_detached = false;
var_ref->is_arg = is_arg;
var_ref->var_idx = var_idx;
list_add_tail(&var_ref->header.link, &sf->var_ref_list);
if (is_arg)
var_ref->pvalue = &sf->arg_buf[var_idx];
else
var_ref->pvalue = &sf->var_buf[var_idx];
var_ref->pvalue = pvalue;
var_ref->value = JS_UNDEFINED;
return var_ref;
}
Expand Down Expand Up @@ -14687,15 +14686,10 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
{
struct list_head *el, *el1;
JSVarRef *var_ref;
int var_idx;

list_for_each_safe(el, el1, &sf->var_ref_list) {
var_ref = list_entry(el, JSVarRef, header.link);
var_idx = var_ref->var_idx;
if (var_ref->is_arg)
var_ref->value = js_dup(sf->arg_buf[var_idx]);
else
var_ref->value = js_dup(sf->var_buf[var_idx]);
var_ref->value = js_dup(*var_ref->pvalue);
var_ref->pvalue = &var_ref->value;
/* the reference is no longer to a local variable */
var_ref->is_detached = true;
Expand All @@ -14705,13 +14699,15 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)

static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx)
{
JSValue *pvalue;
struct list_head *el, *el1;
JSVarRef *var_ref;

pvalue = &sf->var_buf[var_idx];
list_for_each_safe(el, el1, &sf->var_ref_list) {
var_ref = list_entry(el, JSVarRef, header.link);
if (var_idx == var_ref->var_idx && !var_ref->is_arg) {
var_ref->value = js_dup(sf->var_buf[var_idx]);
if (var_ref->pvalue == pvalue) {
var_ref->value = js_dup(*var_ref->pvalue);
var_ref->pvalue = &var_ref->value;
list_del(&var_ref->header.link);
/* the reference is no longer to a local variable */
Expand Down Expand Up @@ -43886,6 +43882,14 @@ bool lre_check_stack_overflow(void *opaque, size_t alloca_size)
return js_check_stack_overflow(ctx->rt, alloca_size);
}

int lre_check_timeout(void *opaque)
{
JSContext *ctx = opaque;
JSRuntime *rt = ctx->rt;
return (rt->interrupt_handler &&
rt->interrupt_handler(rt, rt->interrupt_opaque));
}

void *lre_realloc(void *opaque, void *ptr, size_t size)
{
JSContext *ctx = opaque;
Expand Down Expand Up @@ -44000,7 +44004,11 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
goto fail;
}
} else {
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
if (rc == LRE_RET_TIMEOUT) {
JS_ThrowInterrupted(ctx);
} else {
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
}
goto fail;
}
} else {
Expand Down Expand Up @@ -44195,7 +44203,11 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValue ar
goto fail;
}
} else {
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
if (ret == LRE_RET_TIMEOUT) {
JS_ThrowInterrupted(ctx);
} else {
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
}
goto fail;
}
break;
Expand Down Expand Up @@ -45371,6 +45383,11 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
tab = JS_UNDEFINED;
prop = JS_UNDEFINED;

if (js_check_stack_overflow(ctx->rt, 0)) {
JS_ThrowStackOverflow(ctx);
goto exception;
}

if (JS_IsObject(val)) {
p = JS_VALUE_GET_OBJ(val);
cl = p->class_id;
Expand Down