Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package mujs for openSUSE:Factory checked in at 2023-01-14 20:32:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mujs (Old) and /work/SRC/openSUSE:Factory/.mujs.new.32243 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "mujs" Sat Jan 14 20:32:33 2023 rev:8 rq:1058365 version:1.3.2 Changes: -------- --- /work/SRC/openSUSE:Factory/mujs/mujs.changes 2022-05-19 22:49:29.962348682 +0200 +++ /work/SRC/openSUSE:Factory/.mujs.new.32243/mujs.changes 2023-01-14 20:32:35.613523214 +0100 @@ -1,0 +2,32 @@ +Sat Jan 14 11:41:55 UTC 2023 - ecsos <ec...@opensuse.org> + +- Update to 1.3.2 + * https://git.ghostscript.com/?p=mujs.git;a=log;h=refs/tags/1.3.2 + * 1.3.2 patch release for UAF bug fix. + * Bug 706057: Fix use-after-free in getOwnPropertyDescriptor. + * Set length of output array Array.prototype.map. +- Update to 1.3.1 + * https://git.ghostscript.com/?p=mujs.git;a=log;h=refs/tags/1.3.1 + * Make a patch release for important iterator bug fix. + * Issue #166: Use special iterator for string and array indices. + * Enable choice of library version for shell + * Use $(@D) instead of $(dir $@) +- Update to 1.3.0 + * https://git.ghostscript.com/?p=mujs.git;a=log;h=refs/tags/1.3.0 + * Avoid freeing buffer twice in case of error. + * Fast path for "simple" arrays. + * Bug 705775: Fix double fclose in pretty-printing tool. + * Makefile: fix parallel builds + * Guard state initialization with try to avoid panic in initialization. + * Add js_isbooleanobject and js_isdateobject functions. + * Issue #161: Cope with empty programs in mujs-pp. + * Issue #161: Don't fclose a FILE that is NULL. + * Issue #162: Check stack overflow during regexp compilation. + * Bug 705052: Don't use private STACK/TOP macros in jsstate.c + * Add "console" object to mujs shell. + * Issue #156: Fix check for duplicate formal parameters when strict. + * Some minor optimizations to Ap_join. + * array join: avoid strcat, speedup from O(N^2) to O(N) +- Drop mujs-1.2.0-stack-exhaustion.patch because now in upstream. + +------------------------------------------------------------------- Old: ---- mujs-1.2.0-stack-exhaustion.patch mujs-1.2.0.tar.xz New: ---- mujs-1.3.2.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ mujs.spec ++++++ --- /var/tmp/diff_new_pack.FvMk0m/_old 2023-01-14 20:32:37.809536183 +0100 +++ /var/tmp/diff_new_pack.FvMk0m/_new 2023-01-14 20:32:37.813536206 +0100 @@ -1,7 +1,7 @@ # # spec file for package mujs # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,14 +17,13 @@ Name: mujs -Version: 1.2.0 +Version: 1.3.2 Release: 0 Summary: An embeddable Javascript interpreter License: AGPL-3.0-or-later Group: Development/Languages/C and C++ URL: https://mujs.com Source0: https://mujs.com/downloads/%{name}-%{version}.tar.xz -Patch0: %{name}-1.2.0-stack-exhaustion.patch BuildRequires: pkgconfig BuildRequires: pkgconfig(readline) ++++++ mujs-1.2.0.tar.xz -> mujs-1.3.2.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/Makefile new/mujs-1.3.2/Makefile --- old/mujs-1.2.0/Makefile 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/Makefile 2022-11-07 16:24:56.000000000 +0100 @@ -12,7 +12,7 @@ ifeq "$(wildcard .git)" ".git" VERSION := $(shell git describe --tags --always) else - VERSION := $(shell basename $$PWD | sed -e s,^mujs-,,) + VERSION := $(patsubst mujs-%,%,$(notdir $(CURDIR))) endif ifeq ($(shell uname),Darwin) @@ -75,31 +75,34 @@ jsdump.c: astnames.h opnames.h $(OUT)/%.o: %.c $(HDRS) - @ mkdir -p $(dir $@) + @ mkdir -p $(@D) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< $(OUT)/libmujs.o: one.c $(HDRS) - @ mkdir -p $(dir $@) + @ mkdir -p $(@D) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< $(OUT)/libmujs.a: $(OUT)/libmujs.o - @ mkdir -p $(dir $@) + @ mkdir -p $(@D) $(AR) cr $@ $^ $(OUT)/libmujs.$(SO_EXT): one.c $(HDRS) - @ mkdir -p $(dir $@) + @ mkdir -p $(@D) $(CC) $(CFLAGS) $(CPPFLAGS) -fPIC -shared $(LDFLAGS) -o $@ $< -lm -$(OUT)/mujs: $(OUT)/libmujs.o $(OUT)/main.o - @ mkdir -p $(dir $@) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBREADLINE) -lm +libmujs ?= libmujs.a -$(OUT)/mujs-pp: $(OUT)/libmujs.o $(OUT)/pp.o - @ mkdir -p $(dir $@) - $(CC) $(LDFLAGS) -o $@ $^ -lm +$(OUT)/mujs: $(OUT)/main.o $(OUT)/$(libmujs) + @ mkdir -p $(@D) + $(CC) $(LDFLAGS) -o $@ $< -L$(OUT) -l:$(libmujs) $(LIBREADLINE) -lm + +$(OUT)/mujs-pp: $(OUT)/pp.o $(OUT)/$(libmujs) + @ mkdir -p $(@D) + $(CC) $(LDFLAGS) -o $@ $< -L$(OUT) -l:$(libmujs) -lm .PHONY: $(OUT)/mujs.pc $(OUT)/mujs.pc: + @ mkdir -p $(dir $@) @ echo Creating $@ @ echo > $@ Name: mujs @ echo >> $@ Description: MuJS embeddable Javascript interpreter diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsarray.c new/mujs-1.3.2/jsarray.c --- old/mujs-1.2.0/jsarray.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsarray.c 2022-11-07 16:24:56.000000000 +0100 @@ -17,30 +17,6 @@ js_setproperty(J, idx < 0 ? idx - 1 : idx, "length"); } -int js_hasindex(js_State *J, int idx, int i) -{ - char buf[32]; - return js_hasproperty(J, idx, js_itoa(buf, i)); -} - -void js_getindex(js_State *J, int idx, int i) -{ - char buf[32]; - js_getproperty(J, idx, js_itoa(buf, i)); -} - -void js_setindex(js_State *J, int idx, int i) -{ - char buf[32]; - js_setproperty(J, idx, js_itoa(buf, i)); -} - -void js_delindex(js_State *J, int idx, int i) -{ - char buf[32]; - js_delproperty(J, idx, js_itoa(buf, i)); -} - static void jsB_new_Array(js_State *J) { int i, top = js_gettop(J); @@ -91,7 +67,7 @@ const char *sep; const char *r; int seplen; - int k, n, len; + int k, n, len, rlen; len = js_getlength(J, 0); @@ -113,33 +89,40 @@ js_throw(J); } - n = 1; + n = 0; for (k = 0; k < len; ++k) { js_getindex(J, 0, k); - if (js_isundefined(J, -1) || js_isnull(J, -1)) - r = ""; - else + if (js_iscoercible(J, -1)) { r = js_tostring(J, -1); - n += strlen(r); + rlen = strlen(r); + } else { + rlen = 0; + } if (k == 0) { - if (n > JS_STRLIMIT) - js_rangeerror(J, "invalid string length"); - out = js_malloc(J, (int)n); - strcpy(out, r); + out = js_malloc(J, rlen + 1); + if (rlen > 0) { + memcpy(out, r, rlen); + n += rlen; + } } else { - n += seplen; - if (n > JS_STRLIMIT) + if (n + seplen + rlen > JS_STRLIMIT) js_rangeerror(J, "invalid string length"); - out = js_realloc(J, out, (int)n); - strcat(out, sep); - strcat(out, r); + out = js_realloc(J, out, n + seplen + rlen + 1); + if (seplen > 0) { + memcpy(out + n, sep, seplen); + n += seplen; + } + if (rlen > 0) { + memcpy(out + n, r, rlen); + n += rlen; + } } js_pop(J, 1); } - js_pushstring(J, out); + js_pushlstring(J, out, n); js_endtry(J); js_free(J, out); } @@ -600,6 +583,7 @@ js_pop(J, 1); } } + js_setlength(J, -1, len); } static void Ap_filter(js_State *J) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jscompile.c new/mujs-1.3.2/jscompile.c --- old/mujs-1.2.0/jscompile.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jscompile.c 2022-11-07 16:24:56.000000000 +0100 @@ -1366,7 +1366,7 @@ emitfunction(J, F, newfun(J, stm->line, stm->a, stm->b, stm->c, 0, F->strict)); emitline(J, F, stm); emit(J, F, OP_SETLOCAL); - emitarg(J, F, addlocal(J, F, stm->a, 0)); + emitarg(J, F, addlocal(J, F, stm->a, 1)); emit(J, F, OP_POP); } list = list->b; @@ -1400,7 +1400,7 @@ if (findlocal(J, F, name->string) < 0) { emit(J, F, OP_CURRENT); emit(J, F, OP_SETLOCAL); - emitarg(J, F, addlocal(J, F, name, 0)); + emitarg(J, F, addlocal(J, F, name, 1)); emit(J, F, OP_POP); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsdump.c new/mujs-1.3.2/jsdump.c --- old/mujs-1.2.0/jsdump.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsdump.c 2022-11-07 16:24:56.000000000 +0100 @@ -682,11 +682,13 @@ void jsP_dumpsyntax(js_State *J, js_Ast *prog, int dominify) { minify = dominify; - if (prog->type == AST_LIST) - pstmlist(-1, prog); - else { - pstm(0, prog); - nl(); + if (prog) { + if (prog->type == AST_LIST) + pstmlist(-1, prog); + else { + pstm(0, prog); + nl(); + } } if (minify > 1) putchar('\n'); @@ -768,11 +770,13 @@ void jsP_dumplist(js_State *J, js_Ast *prog) { minify = 0; - if (prog->type == AST_LIST) - sblock(0, prog); - else - snode(0, prog); - nl(); + if (prog) { + if (prog->type == AST_LIST) + sblock(0, prog); + else + snode(0, prog); + nl(); + } } /* Compiled code */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsgc.c new/mujs-1.3.2/jsgc.c --- old/mujs-1.2.0/jsgc.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsgc.c 2022-11-07 16:24:56.000000000 +0100 @@ -42,6 +42,8 @@ js_free(J, obj->u.r.source); js_regfreex(J->alloc, J->actx, obj->u.r.prog); } + if (obj->type == JS_CARRAY && obj->u.a.simple) + js_free(J, obj->u.a.array); if (obj->type == JS_CITERATOR) jsG_freeiterator(J, obj->u.iter.head); if (obj->type == JS_CUSERDATA && obj->u.user.finalize) @@ -100,6 +102,16 @@ jsG_markproperty(J, mark, obj->properties); if (obj->prototype && obj->prototype->gcmark != mark) jsG_markobject(J, mark, obj->prototype); + if (obj->type == JS_CARRAY && obj->u.a.simple) { + int i; + for (i = 0; i < obj->u.a.length; ++i) { + js_Value *v = &obj->u.a.array[i]; + if (v->type == JS_TMEMSTR && v->u.memstr->gcmark != mark) + v->u.memstr->gcmark = mark; + if (v->type == JS_TOBJECT && v->u.object->gcmark != mark) + jsG_markobject(J, mark, v->u.object); + } + } if (obj->type == JS_CITERATOR && obj->u.iter.target->gcmark != mark) { jsG_markobject(J, mark, obj->u.iter.target); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsi.h new/mujs-1.3.2/jsi.h --- old/mujs-1.2.0/jsi.h 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsi.h 2022-11-07 16:24:56.000000000 +0100 @@ -86,6 +86,9 @@ #ifndef JS_TRYLIMIT #define JS_TRYLIMIT 64 /* exception stack size */ #endif +#ifndef JS_ARRAYLIMIT +#define JS_ARRAYLIMIT (1<<26) /* limit arrays to 64M entries (1G of flat array data) */ +#endif #ifndef JS_GCFACTOR /* * GC will try to trigger when memory usage is this value times the minimum @@ -241,6 +244,8 @@ unsigned int seed; /* Math.random seed */ + char scratch[12]; /* scratch buffer for iterating over array indices */ + int nextref; /* for js_ref use */ js_Object *R; /* registry of hidden values */ js_Object *G; /* the global object */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsobject.c new/mujs-1.3.2/jsobject.c --- old/mujs-1.2.0/jsobject.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsobject.c 2022-11-07 16:24:56.000000000 +0100 @@ -62,7 +62,24 @@ { js_Object *self = js_toobject(J, 0); const char *name = js_tostring(J, 1); - js_Property *ref = jsV_getownproperty(J, self, name); + js_Property *ref; + int k; + + if (self->type == JS_CSTRING) { + if (js_isarrayindex(J, name, &k) && k >= 0 && k < self->u.s.length) { + js_pushboolean(J, 1); + return; + } + } + + if (self->type == JS_CARRAY && self->u.a.simple) { + if (js_isarrayindex(J, name, &k) && k >= 0 && k < self->u.a.length) { + js_pushboolean(J, 1); + return; + } + } + + ref = jsV_getownproperty(J, self, name); js_pushboolean(J, ref != NULL); } @@ -110,31 +127,32 @@ js_typeerror(J, "not an object"); obj = js_toobject(J, 1); ref = jsV_getproperty(J, obj, js_tostring(J, 2)); - if (!ref) + if (!ref) { + // TODO: builtin properties (string and array index and length, regexp flags, etc) js_pushundefined(J); - else { + } else { js_newobject(J); if (!ref->getter && !ref->setter) { js_pushvalue(J, ref->value); - js_setproperty(J, -2, "value"); + js_defproperty(J, -2, "value", 0); js_pushboolean(J, !(ref->atts & JS_READONLY)); - js_setproperty(J, -2, "writable"); + js_defproperty(J, -2, "writable", 0); } else { if (ref->getter) js_pushobject(J, ref->getter); else js_pushundefined(J); - js_setproperty(J, -2, "get"); + js_defproperty(J, -2, "get", 0); if (ref->setter) js_pushobject(J, ref->setter); else js_pushundefined(J); - js_setproperty(J, -2, "set"); + js_defproperty(J, -2, "set", 0); } js_pushboolean(J, !(ref->atts & JS_DONTENUM)); - js_setproperty(J, -2, "enumerable"); + js_defproperty(J, -2, "enumerable", 0); js_pushboolean(J, !(ref->atts & JS_DONTCONF)); - js_setproperty(J, -2, "configurable"); + js_defproperty(J, -2, "configurable", 0); } } @@ -152,6 +170,7 @@ static void O_getOwnPropertyNames(js_State *J) { js_Object *obj; + char name[32]; int k; int i; @@ -169,13 +188,21 @@ if (obj->type == JS_CARRAY) { js_pushliteral(J, "length"); js_setindex(J, -2, i++); + if (obj->u.a.simple) { + for (k = 0; k < obj->u.a.length; ++k) { + js_itoa(name, k); + js_pushstring(J, name); + js_setindex(J, -2, i++); + } + } } if (obj->type == JS_CSTRING) { js_pushliteral(J, "length"); js_setindex(J, -2, i++); for (k = 0; k < obj->u.s.length; ++k) { - js_pushnumber(J, k); + js_itoa(name, k); + js_pushstring(J, name); js_setindex(J, -2, i++); } } @@ -221,7 +248,7 @@ } if (js_hasproperty(J, -1, "value")) { hasvalue = 1; - js_setproperty(J, -3, name); + js_defproperty(J, -3, name, 0); } if (!writable) atts |= JS_READONLY; @@ -359,9 +386,12 @@ static void O_preventExtensions(js_State *J) { + js_Object *obj; if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); - js_toobject(J, 1)->extensible = 0; + obj = js_toobject(J, 1); + jsR_unflattenarray(J, obj); + obj->extensible = 0; js_copy(J, 1); } @@ -389,6 +419,7 @@ js_typeerror(J, "not an object"); obj = js_toobject(J, 1); + jsR_unflattenarray(J, obj); obj->extensible = 0; if (obj->properties->level) @@ -446,6 +477,7 @@ js_typeerror(J, "not an object"); obj = js_toobject(J, 1); + jsR_unflattenarray(J, obj); obj->extensible = 0; if (obj->properties->level) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/json.c new/mujs-1.3.2/json.c --- old/mujs-1.2.0/json.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/json.c 2022-11-07 16:24:56.000000000 +0100 @@ -15,6 +15,16 @@ return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CSTRING; } +int js_isbooleanobject(js_State *J, int idx) +{ + return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CBOOLEAN; +} + +int js_isdateobject(js_State *J, int idx) +{ + return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CDATE; +} + static void jsonnext(js_State *J) { J->lookahead = jsY_lexjson(J); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsproperty.c new/mujs-1.3.2/jsproperty.c --- old/mujs-1.2.0/jsproperty.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsproperty.c 2022-11-07 16:24:56.000000000 +0100 @@ -1,6 +1,8 @@ #include "jsi.h" #include "jsvalue.h" +#include <assert.h> + /* Use an AA-tree to quickly look up properties in objects: @@ -226,16 +228,20 @@ /* Flatten hierarchy of enumerable properties into an iterator object */ +static js_Iterator *itnewnode(js_State *J, const char *name, js_Iterator *next) { + js_Iterator *node = js_malloc(J, sizeof(js_Iterator)); + node->name = name; + node->next = next; + return node; +} + static js_Iterator *itwalk(js_State *J, js_Iterator *iter, js_Property *prop, js_Object *seen) { if (prop->right != &sentinel) iter = itwalk(J, iter, prop->right, seen); if (!(prop->atts & JS_DONTENUM)) { if (!seen || !jsV_getenumproperty(J, seen, prop->name)) { - js_Iterator *head = js_malloc(J, sizeof *head); - head->name = prop->name; - head->next = iter; - iter = head; + iter = itnewnode(J, prop->name, iter); } } if (prop->left != &sentinel) @@ -255,10 +261,10 @@ js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own) { - char buf[32]; - int k; js_Object *io = jsV_newobject(J, JS_CITERATOR, NULL); io->u.iter.target = obj; + io->u.iter.i = 0; + io->u.iter.n = 0; if (own) { io->u.iter.head = NULL; if (obj->properties != &sentinel) @@ -266,26 +272,13 @@ } else { io->u.iter.head = itflatten(J, obj); } - if (obj->type == JS_CSTRING) { - js_Iterator *tail = io->u.iter.head; - if (tail) - while (tail->next) - tail = tail->next; - for (k = 0; k < obj->u.s.length; ++k) { - js_itoa(buf, k); - if (!jsV_getenumproperty(J, obj, buf)) { - js_Iterator *node = js_malloc(J, sizeof *node); - node->name = js_intern(J, js_itoa(buf, k)); - node->next = NULL; - if (!tail) - io->u.iter.head = tail = node; - else { - tail->next = node; - tail = node; - } - } - } - } + + if (obj->type == JS_CSTRING) + io->u.iter.n = obj->u.s.length; + + if (obj->type == JS_CARRAY && obj->u.a.simple) + io->u.iter.n = obj->u.a.length; + return io; } @@ -294,6 +287,11 @@ int k; if (io->type != JS_CITERATOR) js_typeerror(J, "not an iterator"); + if (io->u.iter.i < io->u.iter.n) { + js_itoa(J->scratch, io->u.iter.i); + io->u.iter.i++; + return J->scratch; + } while (io->u.iter.head) { js_Iterator *next = io->u.iter.head->next; const char *name = io->u.iter.head->name; @@ -304,6 +302,9 @@ if (io->u.iter.target->type == JS_CSTRING) if (js_isarrayindex(J, name, &k) && k < io->u.iter.target->u.s.length) return name; + if (io->u.iter.target->type == JS_CARRAY && io->u.iter.target->u.a.simple) + if (js_isarrayindex(J, name, &k) && k < io->u.iter.target->u.a.length) + return name; } return NULL; } @@ -315,6 +316,7 @@ char buf[32]; const char *s; int k; + assert(!obj->u.a.simple); if (newlen < obj->u.a.length) { if (obj->u.a.length > obj->count * 2) { js_Object *it = jsV_newiterator(J, obj, 1); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsrun.c new/mujs-1.3.2/jsrun.c --- old/mujs-1.2.0/jsrun.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsrun.c 2022-11-07 16:24:56.000000000 +0100 @@ -5,6 +5,8 @@ #include "utf.h" +#include <assert.h> + static void jsR_run(js_State *J, js_Function *F); /* Push values on stack */ @@ -517,6 +519,28 @@ } } +void jsR_unflattenarray(js_State *J, js_Object *obj) { + if (obj->type == JS_CARRAY && obj->u.a.simple) { + js_Property *ref; + int i; + char name[32]; + if (js_try(J)) { + obj->properties = NULL; + js_throw(J); + } + for (i = 0; i < obj->u.a.length; ++i) { + js_itoa(name, i); + ref = jsV_setproperty(J, obj, name); + ref->value = obj->u.a.array[i]; + } + js_free(J, obj->u.a.array); + obj->u.a.simple = 0; + obj->u.a.capacity = 0; + obj->u.a.array = NULL; + js_endtry(J); + } +} + static int jsR_hasproperty(js_State *J, js_Object *obj, const char *name) { js_Property *ref; @@ -527,6 +551,14 @@ js_pushnumber(J, obj->u.a.length); return 1; } + if (obj->u.a.simple) { + if (js_isarrayindex(J, name, &k)) { + if (k >= 0 && k < obj->u.a.length) { + js_pushvalue(J, obj->u.a.array[k]); + return 1; + } + } + } } else if (obj->type == JS_CSTRING) { @@ -591,6 +623,45 @@ js_pushundefined(J); } +static int jsR_hasindex(js_State *J, js_Object *obj, int k) +{ + char buf[32]; + if (obj->type == JS_CARRAY && obj->u.a.simple && k >= 0 && k < obj->u.a.length) { + js_pushvalue(J, obj->u.a.array[k]); + return 1; + } + return jsR_hasproperty(J, obj, js_itoa(buf, k)); +} + +static void jsR_getindex(js_State *J, js_Object *obj, int k) +{ + if (!jsR_hasindex(J, obj, k)) + js_pushundefined(J); +} + +static void jsR_setarrayindex(js_State *J, js_Object *obj, int k, js_Value *value) +{ + int newlen = k + 1; + assert(obj->u.a.simple); + assert(k >= 0); + if (newlen > JS_ARRAYLIMIT) + js_rangeerror(J, "array too large"); + if (newlen > obj->u.a.length) { + assert(newlen == obj->u.a.length + 1); + if (newlen > obj->u.a.capacity) { + int newcap = obj->u.a.capacity; + if (newcap == 0) + newcap = 8; + while (newcap < newlen) + newcap <<= 1; + obj->u.a.array = js_realloc(J, obj->u.a.array, newcap * sizeof(js_Value)); + obj->u.a.capacity = newcap; + } + obj->u.a.length = newlen; + } + obj->u.a.array[k] = *value; +} + static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, int transient) { js_Value *value = stackidx(J, -1); @@ -604,12 +675,30 @@ int newlen = jsV_numbertointeger(rawlen); if (newlen != rawlen || newlen < 0) js_rangeerror(J, "invalid array length"); + if (newlen > JS_ARRAYLIMIT) + js_rangeerror(J, "array too large"); + if (obj->u.a.simple) { + if (newlen <= obj->u.a.length) { + obj->u.a.length = newlen; + return; + } + jsR_unflattenarray(J, obj); + } jsV_resizearray(J, obj, newlen); return; } - if (js_isarrayindex(J, name, &k)) - if (k >= obj->u.a.length) + + if (js_isarrayindex(J, name, &k)) { + if (obj->u.a.simple) { + if (k >= 0 && k <= obj->u.a.length) { + jsR_setarrayindex(J, obj, k, value); + return; + } + jsR_unflattenarray(J, obj); + } + if (k + 1 > obj->u.a.length) obj->u.a.length = k + 1; + } } else if (obj->type == JS_CSTRING) { @@ -679,6 +768,16 @@ js_typeerror(J, "'%s' is read-only", name); } +static void jsR_setindex(js_State *J, js_Object *obj, int k, int transient) +{ + char buf[32]; + if (obj->type == JS_CARRAY && obj->u.a.simple && k >= 0 && k <= obj->u.a.length) { + jsR_setarrayindex(J, obj, k, stackidx(J, -1)); + } else { + jsR_setproperty(J, obj, js_itoa(buf, k), transient); + } +} + static void jsR_defproperty(js_State *J, js_Object *obj, const char *name, int atts, js_Value *value, js_Object *getter, js_Object *setter, int throw) @@ -689,6 +788,8 @@ if (obj->type == JS_CARRAY) { if (!strcmp(name, "length")) goto readonly; + if (obj->u.a.simple) + jsR_unflattenarray(J, obj); } else if (obj->type == JS_CSTRING) { @@ -750,6 +851,8 @@ if (obj->type == JS_CARRAY) { if (!strcmp(name, "length")) goto dontconf; + if (obj->u.a.simple) + jsR_unflattenarray(J, obj); } else if (obj->type == JS_CSTRING) { @@ -889,6 +992,28 @@ return jsR_hasproperty(J, js_toobject(J, idx), name); } +void js_getindex(js_State *J, int idx, int i) +{ + jsR_getindex(J, js_toobject(J, idx), i); +} + +int js_hasindex(js_State *J, int idx, int i) +{ + return jsR_hasindex(J, js_toobject(J, idx), i); +} + +void js_setindex(js_State *J, int idx, int i) +{ + jsR_setindex(J, js_toobject(J, idx), i, !js_isobject(J, idx)); + js_pop(J, 1); +} + +void js_delindex(js_State *J, int idx, int i) +{ + char buf[32]; + js_delproperty(J, idx, js_itoa(buf, i)); +} + /* Iterator */ void js_pushiterator(js_State *J, int idx, int own) @@ -1360,6 +1485,16 @@ js_stacktrace(J); } +static int jsR_isindex(js_State *J, int idx, int *k) +{ + js_Value *v = stackidx(J, idx); + if (v->type == JS_TNUMBER) { + *k = v->u.number; + return *k == v->u.number && *k >= 0; + } + return 0; +} + static void jsR_run(js_State *J, js_Function *F) { js_Function **FT = F->funtab; @@ -1532,9 +1667,14 @@ break; case OP_GETPROP: - str = js_tostring(J, -1); - obj = js_toobject(J, -2); - jsR_getproperty(J, obj, str); + if (jsR_isindex(J, -1, &ix)) { + obj = js_toobject(J, -2); + jsR_getindex(J, obj, ix); + } else { + str = js_tostring(J, -1); + obj = js_toobject(J, -2); + jsR_getproperty(J, obj, str); + } js_rot3pop2(J); break; @@ -1546,10 +1686,16 @@ break; case OP_SETPROP: - str = js_tostring(J, -2); - obj = js_toobject(J, -3); - transient = !js_isobject(J, -3); - jsR_setproperty(J, obj, str, transient); + if (jsR_isindex(J, -2, &ix)) { + obj = js_toobject(J, -3); + transient = !js_isobject(J, -3); + jsR_setindex(J, obj, ix, transient); + } else { + str = js_tostring(J, -2); + obj = js_toobject(J, -3); + transient = !js_isobject(J, -3); + jsR_setproperty(J, obj, str, transient); + } js_rot3pop2(J); break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsstate.c new/mujs-1.3.2/jsstate.c --- old/mujs-1.2.0/jsstate.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsstate.c 2022-11-07 16:24:56.000000000 +0100 @@ -10,9 +10,9 @@ static int js_ptry(js_State *J) { if (J->trytop == JS_TRYLIMIT) { - STACK[TOP].type = JS_TLITSTR; - STACK[TOP].u.litstr = "exception stack overflow"; - ++TOP; + J->stack[J->top].type = JS_TLITSTR; + J->stack[J->top].u.litstr = "exception stack overflow"; + ++J->top; return 1; } return 0; @@ -322,6 +322,11 @@ J->nextref = 0; J->gcthresh = 0; /* reaches stability within ~ 2-5 GC cycles */ + if (js_try(J)) { + js_freestate(J); + return NULL; + } + J->R = jsV_newobject(J, JS_COBJECT, NULL); J->G = jsV_newobject(J, JS_COBJECT, NULL); J->E = jsR_newenvironment(J, J->G, NULL); @@ -329,5 +334,6 @@ jsB_init(J); + js_endtry(J); return J; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsvalue.c new/mujs-1.3.2/jsvalue.c --- old/mujs-1.2.0/jsvalue.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsvalue.c 2022-11-07 16:24:56.000000000 +0100 @@ -423,7 +423,9 @@ void js_newarray(js_State *J) { - js_pushobject(J, jsV_newobject(J, JS_CARRAY, J->Array_prototype)); + js_Object *obj = jsV_newobject(J, JS_CARRAY, J->Array_prototype); + obj->u.a.simple = 1; + js_pushobject(J, obj); } void js_newboolean(js_State *J, int v) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/jsvalue.h new/mujs-1.3.2/jsvalue.h --- old/mujs-1.2.0/jsvalue.h 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/jsvalue.h 2022-11-07 16:24:56.000000000 +0100 @@ -93,6 +93,9 @@ } s; struct { int length; + int simple; /* true if array has only non-sparse array properties */ + int capacity; + js_Value *array; } a; struct { js_Function *function; @@ -109,7 +112,8 @@ js_Regexp r; struct { js_Object *target; - js_Iterator *head; + int i, n; /* for array part */ + js_Iterator *head; /* for object part */ } iter; struct { const char *tag; @@ -149,6 +153,7 @@ js_Object *js_toobject(js_State *J, int idx); void js_pushvalue(js_State *J, js_Value v); void js_pushobject(js_State *J, js_Object *v); +void jsR_unflattenarray(js_State *J, js_Object *obj); /* jsvalue.c */ int jsV_toboolean(js_State *J, js_Value *v); @@ -158,7 +163,7 @@ js_Object *jsV_toobject(js_State *J, js_Value *v); void jsV_toprimitive(js_State *J, js_Value *v, int preferred); -const char *js_itoa(char buf[32], int a); +const char *js_itoa(char *buf, int a); double js_stringtofloat(const char *s, char **ep); int jsV_numbertointeger(double n); int jsV_numbertoint32(double n); @@ -182,6 +187,9 @@ void jsV_resizearray(js_State *J, js_Object *obj, int newlen); +void jsV_unflattenarray(js_State *J, js_Object *obj); +void jsV_growarray(js_State *J, js_Object *obj); + /* jsdump.c */ void js_dumpobject(js_State *J, js_Object *obj); void js_dumpvalue(js_State *J, js_Value v); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/main.c new/mujs-1.3.2/main.c --- old/mujs-1.2.0/main.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/main.c 2022-11-07 16:24:56.000000000 +0100 @@ -230,6 +230,10 @@ "};\n" ; +static const char *console_js = + "var console = { log: print, debug: print, warn: print, error: print };" +; + static int eval_print(js_State *J, const char *source) { if (js_ploadstring(J, "[stdin]", source)) { @@ -307,6 +311,10 @@ } J = js_newstate(NULL, NULL, strict ? JS_STRICT : 0); + if (!J) { + fprintf(stderr, "Could not initialize MuJS.\n"); + exit(1); + } js_newcfunction(J, jsB_gc, "gc", 0); js_setglobal(J, "gc"); @@ -337,6 +345,7 @@ js_dostring(J, require_js); js_dostring(J, stacktrace_js); + js_dostring(J, console_js); if (xoptind == argc) { interactive = 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/mujs.h new/mujs-1.3.2/mujs.h --- old/mujs-1.2.0/mujs.h 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/mujs.h 2022-11-07 16:24:56.000000000 +0100 @@ -8,8 +8,8 @@ #endif #define JS_VERSION_MAJOR 1 -#define JS_VERSION_MINOR 2 -#define JS_VERSION_PATCH 0 +#define JS_VERSION_MINOR 3 +#define JS_VERSION_PATCH 2 #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))) @@ -198,6 +198,8 @@ int js_iserror(js_State *J, int idx); int js_isnumberobject(js_State *J, int idx); int js_isstringobject(js_State *J, int idx); +int js_isbooleanobject(js_State *J, int idx); +int js_isdateobject(js_State *J, int idx); int js_toboolean(js_State *J, int idx); double js_tonumber(js_State *J, int idx); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/pp.c new/mujs-1.3.2/pp.c --- old/mujs-1.2.0/pp.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/pp.c 2022-11-07 16:24:56.000000000 +0100 @@ -34,7 +34,7 @@ if (js_try(J)) { js_free(J, s); - fclose(f); + if (f) fclose(f); js_throw(J); } @@ -44,31 +44,25 @@ } if (fseek(f, 0, SEEK_END) < 0) { - fclose(f); js_error(J, "cannot seek in file: '%s'", filename); } n = ftell(f); if (n < 0) { - fclose(f); js_error(J, "cannot tell in file: '%s'", filename); } if (fseek(f, 0, SEEK_SET) < 0) { - fclose(f); js_error(J, "cannot seek in file: '%s'", filename); } s = js_malloc(J, n + 1); /* add space for string terminator */ if (!s) { - fclose(f); js_error(J, "cannot allocate storage for file contents: '%s'", filename); } t = fread(s, 1, (size_t)n, f); if (t != n) { - js_free(J, s); - fclose(f); js_error(J, "cannot read data from file: '%s'", filename); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mujs-1.2.0/regexp.c new/mujs-1.3.2/regexp.c --- old/mujs-1.2.0/regexp.c 2021-12-08 12:56:12.000000000 +0100 +++ new/mujs-1.3.2/regexp.c 2022-11-07 16:24:56.000000000 +0100 @@ -622,25 +622,26 @@ Reinst *y; }; -static int count(struct cstate *g, Renode *node) +static int count(struct cstate *g, Renode *node, int depth) { int min, max, n; if (!node) return 0; + if (++depth > REG_MAXREC) die(g, "stack overflow"); switch (node->type) { default: return 1; - case P_CAT: return count(g, node->x) + count(g, node->y); - case P_ALT: return count(g, node->x) + count(g, node->y) + 2; + case P_CAT: return count(g, node->x, depth) + count(g, node->y, depth); + case P_ALT: return count(g, node->x, depth) + count(g, node->y, depth) + 2; case P_REP: min = node->m; max = node->n; - if (min == max) n = count(g, node->x) * min; - else if (max < REPINF) n = count(g, node->x) * max + (max - min); - else n = count(g, node->x) * (min + 1) + 2; + if (min == max) n = count(g, node->x, depth) * min; + else if (max < REPINF) n = count(g, node->x, depth) * max + (max - min); + else n = count(g, node->x, depth) * (min + 1) + 2; if (n < 0 || n > REG_MAXPROG) die(g, "program too large"); return n; - case P_PAR: return count(g, node->x) + 2; - case P_PLA: return count(g, node->x) + 2; - case P_NLA: return count(g, node->x) + 2; + case P_PAR: return count(g, node->x, depth) + 2; + case P_PLA: return count(g, node->x, depth) + 2; + case P_NLA: return count(g, node->x, depth) + 2; } } @@ -903,7 +904,7 @@ putchar('\n'); #endif - n = 6 + count(&g, node); + n = 6 + count(&g, node, 0); if (n < 0 || n > REG_MAXPROG) die(&g, "program too large");