diff --git a/src/mujs/VERSION b/src/mujs/VERSION new file mode 100644 index 0000000..1f54a82 --- /dev/null +++ b/src/mujs/VERSION @@ -0,0 +1 @@ +commit 32064e10caa45e68fc87331ca53d042049cc9396 diff --git a/src/mujs/jsarray.c b/src/mujs/jsarray.c index 962d59f..ca30cba 100644 --- a/src/mujs/jsarray.c +++ b/src/mujs/jsarray.c @@ -240,7 +240,7 @@ static void Ap_slice(js_State *J) static int Ap_sort_cmp(js_State *J, int idx_a, int idx_b) { js_Object *obj = js_tovalue(J, 0)->u.object; - if (obj->u.a.simple) { + if (obj->u.a.simple && idx_b < obj->u.a.flat_length) { js_Value *val_a = &obj->u.a.array[idx_a]; js_Value *val_b = &obj->u.a.array[idx_b]; int und_a = val_a->t.type == JS_TUNDEFINED; @@ -326,7 +326,7 @@ static int Ap_sort_cmp(js_State *J, int idx_a, int idx_b) static void Ap_sort_swap(js_State *J, int idx_a, int idx_b) { js_Object *obj = js_tovalue(J, 0)->u.object; - if (obj->u.a.simple) { + if (obj->u.a.simple && idx_b < obj->u.a.flat_length) { js_Value tmp = obj->u.a.array[idx_a]; obj->u.a.array[idx_a] = obj->u.a.array[idx_b]; obj->u.a.array[idx_b] = tmp; @@ -354,7 +354,7 @@ static int Ap_sort_leaf(js_State *J, int i, int end) int lc = (j << 1) + 1; /* left child */ int rc = (j << 1) + 2; /* right child */ while (rc < end) { - if (Ap_sort_cmp(J, rc, lc) > 0) + if (Ap_sort_cmp(J, lc, rc) <= 0) j = rc; else j = lc; @@ -369,8 +369,9 @@ static int Ap_sort_leaf(js_State *J, int i, int end) static void Ap_sort_sift(js_State *J, int i, int end) { int j = Ap_sort_leaf(J, i, end); - while (Ap_sort_cmp(J, i, j) > 0) + while (j > i && Ap_sort_cmp(J, i, j) > 0) { j = (j - 1) >> 1; /* parent */ + } while (j > i) { Ap_sort_swap(J, i, j); j = (j - 1) >> 1; /* parent */ diff --git a/src/mujs/jserror.c b/src/mujs/jserror.c index 6dd3bb1..3841954 100644 --- a/src/mujs/jserror.c +++ b/src/mujs/jserror.c @@ -53,6 +53,13 @@ static void Ep_toString(js_State *J) } } +static void Ep_get_stack(js_State *J) +{ + Ep_toString(J); + js_getproperty(J, 0, "stackTrace"); + js_concat(J); +} + static int jsB_ErrorX(js_State *J, js_Object *prototype) { js_pushobject(J, jsV_newobject(J, JS_CERROR, prototype)); @@ -61,7 +68,7 @@ static int jsB_ErrorX(js_State *J, js_Object *prototype) js_defproperty(J, -2, "message", JS_DONTENUM); } if (jsB_stacktrace(J, 1)) - js_defproperty(J, -2, "stack", JS_DONTENUM); + js_defproperty(J, -2, "stackTrace", JS_DONTENUM); return 1; } @@ -71,7 +78,7 @@ static void js_newerrorx(js_State *J, const char *message, js_Object *prototype) js_pushstring(J, message); js_setproperty(J, -2, "message"); if (jsB_stacktrace(J, 0)) - js_setproperty(J, -2, "stack"); + js_setproperty(J, -2, "stackTrace"); } #define DERROR(name, Name) \ @@ -105,8 +112,13 @@ void jsB_initerror(js_State *J) { js_pushobject(J, J->Error_prototype); { - jsB_props(J, "name", "Error"); - jsB_propf(J, "Error.prototype.toString", Ep_toString, 0); + jsB_props(J, "name", "Error"); + jsB_propf(J, "Error.prototype.toString", Ep_toString, 0); + jsB_props(J, "message", ""); + + js_newcfunction(J, Ep_get_stack, "stack", 0); + js_pushnull(J); + js_defaccessor(J, -3, "stack", JS_READONLY | JS_DONTENUM | JS_DONTCONF); } js_newcconstructor(J, jsB_Error, jsB_Error, "Error", 1); js_defglobal(J, "Error", JS_DONTENUM); diff --git a/src/mujs/jsi.h b/src/mujs/jsi.h index 6956d97..c3bb06e 100644 --- a/src/mujs/jsi.h +++ b/src/mujs/jsi.h @@ -270,6 +270,9 @@ struct js_State js_Object *gcroot; /* gc scan list */ + int runlimit; + int memlimit; + /* environments on the call stack but currently not in scope */ int envtop; js_Environment *envstack[JS_ENVLIMIT]; diff --git a/src/mujs/jslex.c b/src/mujs/jslex.c index 8fd9ab6..6dee5f2 100644 --- a/src/mujs/jslex.c +++ b/src/mujs/jslex.c @@ -102,7 +102,7 @@ static int jsY_findkeyword(js_State *J, const char *s) J->text = keywords[i]; return TK_BREAK + i; /* first keyword + i */ } - J->text = js_intern(J, s); + J->text = s; return TK_IDENTIFIER; } @@ -204,14 +204,15 @@ static void textinit(js_State *J) static void textpush(js_State *J, Rune c) { - int n; + int n, newcap; if (c == EOF) n = 1; else n = runelen(c); if (J->lexbuf.len + n > J->lexbuf.cap) { - J->lexbuf.cap = J->lexbuf.cap * 2; + newcap = J->lexbuf.cap * 2; J->lexbuf.text = js_realloc(J, J->lexbuf.text, J->lexbuf.cap); + J->lexbuf.cap = newcap; } if (c == EOF) J->lexbuf.text[J->lexbuf.len++] = 0; @@ -449,7 +450,7 @@ static int lexstring(js_State *J) s = textend(J); - J->text = js_intern(J, s); + J->text = s; return TK_STRING; } @@ -523,7 +524,7 @@ static int lexregexp(js_State *J) if (g > 1 || i > 1 || m > 1) jsY_error(J, "duplicated flag in regular expression"); - J->text = js_intern(J, s); + J->text = s; J->number = 0; if (g) J->number += JS_REGEXP_G; if (i) J->number += JS_REGEXP_I; @@ -828,7 +829,7 @@ static int lexjsonstring(js_State *J) s = textend(J); - J->text = js_intern(J, s); + J->text = s; return TK_STRING; } diff --git a/src/mujs/json.c b/src/mujs/json.c index 6dd8f09..5aacbc0 100644 --- a/src/mujs/json.c +++ b/src/mujs/json.c @@ -45,7 +45,6 @@ static void jsonexpect(js_State *J, int t) static void jsonvalue(js_State *J) { int i; - const char *name; switch (J->lookahead) { case TK_STRING: @@ -66,11 +65,12 @@ static void jsonvalue(js_State *J) do { if (J->lookahead != TK_STRING) js_syntaxerror(J, "JSON: unexpected token: %s (expected string)", jsY_tokenstring(J->lookahead)); - name = J->text; + js_pushstring(J, J->text); jsonnext(J); jsonexpect(J, ':'); jsonvalue(J); - js_setproperty(J, -2, name); + js_setproperty(J, -3, js_tostring(J, -2)); + js_pop(J, 1); } while (jsonaccept(J, ',')); jsonexpect(J, '}'); break; diff --git a/src/mujs/jsparse.c b/src/mujs/jsparse.c index 95ebfc1..694842e 100644 --- a/src/mujs/jsparse.c +++ b/src/mujs/jsparse.c @@ -99,7 +99,7 @@ static js_Ast *jsP_list(js_Ast *head) static js_Ast *jsP_newstrnode(js_State *J, enum js_AstType type, const char *s) { js_Ast *node = jsP_newnode(J, type, J->lexline, 0, 0, 0, 0); - node->string = s; + node->string = js_intern(J, s); return node; } diff --git a/src/mujs/jsrun.c b/src/mujs/jsrun.c index e06b212..4c37d3e 100644 --- a/src/mujs/jsrun.c +++ b/src/mujs/jsrun.c @@ -35,9 +35,29 @@ static void js_outofmemory(js_State *J) js_throw(J); } +static void js_runlimit(js_State *J) +{ + STACK[TOP].t.type = JS_TLITSTR; + STACK[TOP].u.litstr = "script ran too long"; + ++TOP; + js_throw(J); +} + +void js_setlimit(js_State *J, int runlimit, int memlimit) +{ + J->runlimit = runlimit; + J->memlimit = memlimit; +} + void *js_malloc(js_State *J, int size) { - void *ptr = J->alloc(J->actx, NULL, size); + void *ptr; + if (J->memlimit > 0) { + if (size >= J->memlimit) + js_outofmemory(J); + J->memlimit -= size; + } + ptr = J->alloc(J->actx, NULL, size); if (!ptr) js_outofmemory(J); return ptr; @@ -45,6 +65,12 @@ void *js_malloc(js_State *J, int size) void *js_realloc(js_State *J, void *ptr, int size) { + if (J->memlimit > 0) { + // TODO: track released memory + if (size >= J->memlimit) + js_outofmemory(J); + J->memlimit -= size; + } ptr = J->alloc(J->actx, ptr, size); if (!ptr) js_outofmemory(J); @@ -61,6 +87,7 @@ char *js_strdup(js_State *J, const char *s) void js_free(js_State *J, void *ptr) { + // TODO: track released memory (J->memlimit) J->alloc(J->actx, ptr, 0); } @@ -1547,7 +1574,7 @@ static int jsR_isindex(js_State *J, int idx, int *k) static void jsR_run(js_State *J, js_Function *F) { js_Function **FT = F->funtab; - const char **VT = F->vartab-1; + const char **VT = F->vartab ? F->vartab - 1 : NULL; int lightweight = F->lightweight; js_Instruction *pcstart = F->code; js_Instruction *pc = F->code; @@ -1571,6 +1598,12 @@ static void jsR_run(js_State *J, js_Function *F) pc += sizeof(str) / sizeof(*pc) while (1) { + if (J->runlimit > 0) { + if (J->runlimit == 1) + js_runlimit(J); + --J->runlimit; + } + if (J->gccounter > J->gcthresh) js_gc(J, 0); diff --git a/src/mujs/jsstring.c b/src/mujs/jsstring.c index da12431..30464c8 100644 --- a/src/mujs/jsstring.c +++ b/src/mujs/jsstring.c @@ -547,6 +547,11 @@ static void Sp_replace_regexp(js_State *J) re->last = 0; + if (js_try(J)) { + js_free(J, sb); + js_throw(J); + } + loop: s = m.sub[0].sp; n = m.sub[0].ep - m.sub[0].sp; @@ -622,10 +627,6 @@ end: js_puts(J, &sb, s + n); js_putc(J, &sb, 0); - if (js_try(J)) { - js_free(J, sb); - js_throw(J); - } js_pushstring(J, sb ? sb->s : ""); js_endtry(J); js_free(J, sb); @@ -647,6 +648,11 @@ static void Sp_replace_string(js_State *J) } n = strlen(needle); + if (js_try(J)) { + js_free(J, sb); + js_throw(J); + } + if (js_iscallable(J, 2)) { js_copy(J, 2); js_pushundefined(J); @@ -683,10 +689,6 @@ static void Sp_replace_string(js_State *J) js_putc(J, &sb, 0); } - if (js_try(J)) { - js_free(J, sb); - js_throw(J); - } js_pushstring(J, sb ? sb->s : ""); js_endtry(J); js_free(J, sb); diff --git a/src/mujs/jsvalue.c b/src/mujs/jsvalue.c index 4cbdd49..f309d0f 100644 --- a/src/mujs/jsvalue.c +++ b/src/mujs/jsvalue.c @@ -170,7 +170,7 @@ const char *js_itoa(char *out, int v) unsigned int a; int i = 0; if (v < 0) { - a = -v; + a = -(unsigned)v; /* cast to avoid -INT_MIN signed overflow UB */ *s++ = '-'; } else { a = v; diff --git a/src/mujs/mujs.h b/src/mujs/mujs.h index 8107d9f..70f587c 100644 --- a/src/mujs/mujs.h +++ b/src/mujs/mujs.h @@ -9,7 +9,7 @@ extern "C" { #define JS_VERSION_MAJOR 1 #define JS_VERSION_MINOR 3 -#define JS_VERSION_PATCH 5 +#define JS_VERSION_PATCH 8 #define JS_VERSION (JS_VERSION_MAJOR * 10000 + JS_VERSION_MINOR * 100 + JS_VERSION_PATCH) #define JS_CHECKVERSION(x,y,z) (JS_VERSION >= ((x) * 10000 + (y) * 100 + (z))) @@ -56,6 +56,7 @@ void js_setreport(js_State *J, js_Report report); js_Panic js_atpanic(js_State *J, js_Panic panic); void js_freestate(js_State *J); void js_gc(js_State *J, int report); +void js_setlimit(js_State *J, int runlimit, int memlimit); int js_dostring(js_State *J, const char *source); int js_dofile(js_State *J, const char *filename);