Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/mujs/jsrun.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/mujs/jsrun.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,2063 @@ +#include "jsi.h" +#include "utf.h" + +#include <assert.h> + +static void jsR_run(js_State *J, js_Function *F); + +/* Push values on stack */ + +#define STACK (J->stack) +#define TOP (J->top) +#define BOT (J->bot) + +static void js_trystackoverflow(js_State *J) +{ + STACK[TOP].t.type = JS_TLITSTR; + STACK[TOP].u.litstr = "exception stack overflow"; + ++TOP; + js_throw(J); +} + +static void js_stackoverflow(js_State *J) +{ + STACK[TOP].t.type = JS_TLITSTR; + STACK[TOP].u.litstr = "stack overflow"; + ++TOP; + js_throw(J); +} + +static void js_outofmemory(js_State *J) +{ + STACK[TOP].t.type = JS_TLITSTR; + STACK[TOP].u.litstr = "out of memory"; + ++TOP; + js_throw(J); +} + +void *js_malloc(js_State *J, int size) +{ + void *ptr = J->alloc(J->actx, NULL, size); + if (!ptr) + js_outofmemory(J); + return ptr; +} + +void *js_realloc(js_State *J, void *ptr, int size) +{ + ptr = J->alloc(J->actx, ptr, size); + if (!ptr) + js_outofmemory(J); + return ptr; +} + +char *js_strdup(js_State *J, const char *s) +{ + int n = strlen(s) + 1; + char *p = js_malloc(J, n); + memcpy(p, s, n); + return p; +} + +void js_free(js_State *J, void *ptr) +{ + J->alloc(J->actx, ptr, 0); +} + +js_String *jsV_newmemstring(js_State *J, const char *s, int n) +{ + js_String *v = js_malloc(J, soffsetof(js_String, p) + n + 1); + memcpy(v->p, s, n); + v->p[n] = 0; + v->gcmark = 0; + v->gcnext = J->gcstr; + J->gcstr = v; + ++J->gccounter; + return v; +} + +#define CHECKSTACK(n) if (TOP + n >= JS_STACKSIZE) js_stackoverflow(J) + +void js_pushvalue(js_State *J, js_Value v) +{ + CHECKSTACK(1); + STACK[TOP] = v; + ++TOP; +} + +void js_pushundefined(js_State *J) +{ + CHECKSTACK(1); + STACK[TOP].t.type = JS_TUNDEFINED; + ++TOP; +} + +void js_pushnull(js_State *J) +{ + CHECKSTACK(1); + STACK[TOP].t.type = JS_TNULL; + ++TOP; +} + +void js_pushboolean(js_State *J, int v) +{ + CHECKSTACK(1); + STACK[TOP].t.type = JS_TBOOLEAN; + STACK[TOP].u.boolean = !!v; + ++TOP; +} + +void js_pushnumber(js_State *J, double v) +{ + CHECKSTACK(1); + STACK[TOP].t.type = JS_TNUMBER; + STACK[TOP].u.number = v; + ++TOP; +} + +void js_pushstring(js_State *J, const char *v) +{ + size_t n = strlen(v); + if (n > JS_STRLIMIT) + js_rangeerror(J, "invalid string length"); + CHECKSTACK(1); + if (n <= soffsetof(js_Value, t.type)) { + char *s = STACK[TOP].u.shrstr; + while (n--) *s++ = *v++; + *s = 0; + STACK[TOP].t.type = JS_TSHRSTR; + } else { + STACK[TOP].t.type = JS_TMEMSTR; + STACK[TOP].u.memstr = jsV_newmemstring(J, v, n); + } + ++TOP; +} + +void js_pushlstring(js_State *J, const char *v, int n) +{ + if (n > JS_STRLIMIT) + js_rangeerror(J, "invalid string length"); + CHECKSTACK(1); + if (n <= soffsetof(js_Value, t.type)) { + char *s = STACK[TOP].u.shrstr; + while (n--) *s++ = *v++; + *s = 0; + STACK[TOP].t.type = JS_TSHRSTR; + } else { + STACK[TOP].t.type = JS_TMEMSTR; + STACK[TOP].u.memstr = jsV_newmemstring(J, v, n); + } + ++TOP; +} + +void js_pushliteral(js_State *J, const char *v) +{ + CHECKSTACK(1); + STACK[TOP].t.type = JS_TLITSTR; + STACK[TOP].u.litstr = v; + ++TOP; +} + +void js_pushobject(js_State *J, js_Object *v) +{ + CHECKSTACK(1); + STACK[TOP].t.type = JS_TOBJECT; + STACK[TOP].u.object = v; + ++TOP; +} + +void js_pushglobal(js_State *J) +{ + js_pushobject(J, J->G); +} + +void js_currentfunction(js_State *J) +{ + CHECKSTACK(1); + if (BOT > 0) + STACK[TOP] = STACK[BOT-1]; + else + STACK[TOP].t.type = JS_TUNDEFINED; + ++TOP; +} + +void *js_currentfunctiondata(js_State *J) +{ + if (BOT > 0) + return STACK[BOT-1].u.object->u.c.data; + return NULL; +} + +/* Read values from stack */ + +static js_Value *stackidx(js_State *J, int idx) +{ + static js_Value undefined = { { {0}, JS_TUNDEFINED } }; + idx = idx < 0 ? TOP + idx : BOT + idx; + if (idx < 0 || idx >= TOP) + return &undefined; + return STACK + idx; +} + +js_Value *js_tovalue(js_State *J, int idx) +{ + return stackidx(J, idx); +} + +int js_isdefined(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TUNDEFINED; } +int js_isundefined(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TUNDEFINED; } +int js_isnull(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNULL; } +int js_isboolean(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TBOOLEAN; } +int js_isnumber(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNUMBER; } +int js_isstring(js_State *J, int idx) { enum js_Type t = stackidx(J, idx)->t.type; return t == JS_TSHRSTR || t == JS_TLITSTR || t == JS_TMEMSTR; } +int js_isprimitive(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TOBJECT; } +int js_isobject(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TOBJECT; } +int js_iscoercible(js_State *J, int idx) { js_Value *v = stackidx(J, idx); return v->t.type != JS_TUNDEFINED && v->t.type != JS_TNULL; } + +int js_iscallable(js_State *J, int idx) +{ + js_Value *v = stackidx(J, idx); + if (v->t.type == JS_TOBJECT) + return v->u.object->type == JS_CFUNCTION || + v->u.object->type == JS_CSCRIPT || + v->u.object->type == JS_CCFUNCTION; + return 0; +} + +int js_isarray(js_State *J, int idx) +{ + js_Value *v = stackidx(J, idx); + return v->t.type == JS_TOBJECT && v->u.object->type == JS_CARRAY; +} + +int js_isregexp(js_State *J, int idx) +{ + js_Value *v = stackidx(J, idx); + return v->t.type == JS_TOBJECT && v->u.object->type == JS_CREGEXP; +} + +int js_isuserdata(js_State *J, int idx, const char *tag) +{ + js_Value *v = stackidx(J, idx); + if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA) + return !strcmp(tag, v->u.object->u.user.tag); + return 0; +} + +int js_iserror(js_State *J, int idx) +{ + js_Value *v = stackidx(J, idx); + return v->t.type == JS_TOBJECT && v->u.object->type == JS_CERROR; +} + +const char *js_typeof(js_State *J, int idx) +{ + js_Value *v = stackidx(J, idx); + switch (v->t.type) { + default: + case JS_TSHRSTR: return "string"; + case JS_TUNDEFINED: return "undefined"; + case JS_TNULL: return "object"; + case JS_TBOOLEAN: return "boolean"; + case JS_TNUMBER: return "number"; + case JS_TLITSTR: return "string"; + case JS_TMEMSTR: return "string"; + case JS_TOBJECT: + if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION) + return "function"; + return "object"; + } +} + +int js_type(js_State *J, int idx) +{ + js_Value *v = stackidx(J, idx); + switch (v->t.type) { + default: + case JS_TSHRSTR: return JS_ISSTRING; + case JS_TUNDEFINED: return JS_ISUNDEFINED; + case JS_TNULL: return JS_ISNULL; + case JS_TBOOLEAN: return JS_ISBOOLEAN; + case JS_TNUMBER: return JS_ISNUMBER; + case JS_TLITSTR: return JS_ISSTRING; + case JS_TMEMSTR: return JS_ISSTRING; + case JS_TOBJECT: + if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION) + return JS_ISFUNCTION; + return JS_ISOBJECT; + } +} + +int js_toboolean(js_State *J, int idx) +{ + return jsV_toboolean(J, stackidx(J, idx)); +} + +double js_tonumber(js_State *J, int idx) +{ + return jsV_tonumber(J, stackidx(J, idx)); +} + +int js_tointeger(js_State *J, int idx) +{ + return jsV_numbertointeger(jsV_tonumber(J, stackidx(J, idx))); +} + +int js_toint32(js_State *J, int idx) +{ + return jsV_numbertoint32(jsV_tonumber(J, stackidx(J, idx))); +} + +unsigned int js_touint32(js_State *J, int idx) +{ + return jsV_numbertouint32(jsV_tonumber(J, stackidx(J, idx))); +} + +short js_toint16(js_State *J, int idx) +{ + return jsV_numbertoint16(jsV_tonumber(J, stackidx(J, idx))); +} + +unsigned short js_touint16(js_State *J, int idx) +{ + return jsV_numbertouint16(jsV_tonumber(J, stackidx(J, idx))); +} + +const char *js_tostring(js_State *J, int idx) +{ + return jsV_tostring(J, stackidx(J, idx)); +} + +js_Object *js_toobject(js_State *J, int idx) +{ + return jsV_toobject(J, stackidx(J, idx)); +} + +void js_toprimitive(js_State *J, int idx, int hint) +{ + jsV_toprimitive(J, stackidx(J, idx), hint); +} + +js_Regexp *js_toregexp(js_State *J, int idx) +{ + js_Value *v = stackidx(J, idx); + if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CREGEXP) + return &v->u.object->u.r; + js_typeerror(J, "not a regexp"); +} + +void *js_touserdata(js_State *J, int idx, const char *tag) +{ + js_Value *v = stackidx(J, idx); + if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA) + if (!strcmp(tag, v->u.object->u.user.tag)) + return v->u.object->u.user.data; + js_typeerror(J, "not a %s", tag); +} + +static js_Object *jsR_tofunction(js_State *J, int idx) +{ + js_Value *v = stackidx(J, idx); + if (v->t.type == JS_TUNDEFINED || v->t.type == JS_TNULL) + return NULL; + if (v->t.type == JS_TOBJECT) + if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION) + return v->u.object; + js_typeerror(J, "not a function"); +} + +/* Stack manipulation */ + +int js_gettop(js_State *J) +{ + return TOP - BOT; +} + +void js_pop(js_State *J, int n) +{ + TOP -= n; + if (TOP < BOT) { + TOP = BOT; + js_error(J, "stack underflow!"); + } +} + +void js_remove(js_State *J, int idx) +{ + idx = idx < 0 ? TOP + idx : BOT + idx; + if (idx < BOT || idx >= TOP) + js_error(J, "stack error!"); + for (;idx < TOP - 1; ++idx) + STACK[idx] = STACK[idx+1]; + --TOP; +} + +void js_insert(js_State *J, int idx) +{ + js_error(J, "not implemented yet"); +} + +void js_replace(js_State* J, int idx) +{ + idx = idx < 0 ? TOP + idx : BOT + idx; + if (idx < BOT || idx >= TOP) + js_error(J, "stack error!"); + STACK[idx] = STACK[--TOP]; +} + +void js_copy(js_State *J, int idx) +{ + CHECKSTACK(1); + STACK[TOP] = *stackidx(J, idx); + ++TOP; +} + +void js_dup(js_State *J) +{ + CHECKSTACK(1); + STACK[TOP] = STACK[TOP-1]; + ++TOP; +} + +void js_dup2(js_State *J) +{ + CHECKSTACK(2); + STACK[TOP] = STACK[TOP-2]; + STACK[TOP+1] = STACK[TOP-1]; + TOP += 2; +} + +void js_rot2(js_State *J) +{ + /* A B -> B A */ + js_Value tmp = STACK[TOP-1]; /* A B (B) */ + STACK[TOP-1] = STACK[TOP-2]; /* A A */ + STACK[TOP-2] = tmp; /* B A */ +} + +void js_rot3(js_State *J) +{ + /* A B C -> C A B */ + js_Value tmp = STACK[TOP-1]; /* A B C (C) */ + STACK[TOP-1] = STACK[TOP-2]; /* A B B */ + STACK[TOP-2] = STACK[TOP-3]; /* A A B */ + STACK[TOP-3] = tmp; /* C A B */ +} + +void js_rot4(js_State *J) +{ + /* A B C D -> D A B C */ + js_Value tmp = STACK[TOP-1]; /* A B C D (D) */ + STACK[TOP-1] = STACK[TOP-2]; /* A B C C */ + STACK[TOP-2] = STACK[TOP-3]; /* A B B C */ + STACK[TOP-3] = STACK[TOP-4]; /* A A B C */ + STACK[TOP-4] = tmp; /* D A B C */ +} + +void js_rot2pop1(js_State *J) +{ + /* A B -> B */ + STACK[TOP-2] = STACK[TOP-1]; + --TOP; +} + +void js_rot3pop2(js_State *J) +{ + /* A B C -> C */ + STACK[TOP-3] = STACK[TOP-1]; + TOP -= 2; +} + +void js_rot(js_State *J, int n) +{ + int i; + js_Value tmp = STACK[TOP-1]; + for (i = 1; i < n; ++i) + STACK[TOP-i] = STACK[TOP-i-1]; + STACK[TOP-i] = tmp; +} + +/* Property access that takes care of attributes and getters/setters */ + +int js_isarrayindex(js_State *J, const char *p, int *idx) +{ + int n = 0; + + /* check for empty string */ + if (p[0] == 0) + return 0; + + /* check for '0' and integers with leading zero */ + if (p[0] == '0') + return (p[1] == 0) ? *idx = 0, 1 : 0; + + while (*p) { + int c = *p++; + if (c >= '0' && c <= '9') { + if (n >= INT_MAX / 10) + return 0; + n = n * 10 + (c - '0'); + } else { + return 0; + } + } + return *idx = n, 1; +} + +static void js_pushrune(js_State *J, Rune rune) +{ + char buf[UTFmax + 1]; + if (rune >= 0) { + buf[runetochar(buf, &rune)] = 0; + js_pushstring(J, buf); + } else { + js_pushundefined(J); + } +} + +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.flat_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.flat_length = 0; + obj->u.a.flat_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; + int k; + + if (obj->type == JS_CARRAY) { + if (!strcmp(name, "length")) { + 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.flat_length) { + js_pushvalue(J, obj->u.a.array[k]); + return 1; + } + return 0; + } + } + } + + else if (obj->type == JS_CSTRING) { + if (!strcmp(name, "length")) { + js_pushnumber(J, obj->u.s.length); + return 1; + } + if (js_isarrayindex(J, name, &k)) { + if (k >= 0 && k < obj->u.s.length) { + js_pushrune(J, js_runeat(J, obj->u.s.string, k)); + return 1; + } + } + } + + else if (obj->type == JS_CREGEXP) { + if (!strcmp(name, "source")) { + js_pushstring(J, obj->u.r.source); + return 1; + } + if (!strcmp(name, "global")) { + js_pushboolean(J, obj->u.r.flags & JS_REGEXP_G); + return 1; + } + if (!strcmp(name, "ignoreCase")) { + js_pushboolean(J, obj->u.r.flags & JS_REGEXP_I); + return 1; + } + if (!strcmp(name, "multiline")) { + js_pushboolean(J, obj->u.r.flags & JS_REGEXP_M); + return 1; + } + if (!strcmp(name, "lastIndex")) { + js_pushnumber(J, obj->u.r.last); + return 1; + } + } + + else if (obj->type == JS_CUSERDATA) { + if (obj->u.user.has && obj->u.user.has(J, obj->u.user.data, name)) + return 1; + } + + ref = jsV_getproperty(J, obj, name); + if (ref) { + if (ref->getter) { + js_pushobject(J, ref->getter); + js_pushobject(J, obj); + js_call(J, 0); + } else { + js_pushvalue(J, ref->value); + } + return 1; + } + + return 0; +} + +static void jsR_getproperty(js_State *J, js_Object *obj, const char *name) +{ + if (!jsR_hasproperty(J, obj, name)) + 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) { + if (k >= 0 && k < obj->u.a.flat_length) { + js_pushvalue(J, obj->u.a.array[k]); + return 1; + } + return 0; + } + 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.flat_length) { + assert(newlen == obj->u.a.flat_length + 1); + if (newlen > obj->u.a.flat_capacity) { + int newcap = obj->u.a.flat_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.flat_capacity = newcap; + } + obj->u.a.flat_length = newlen; + } + if (newlen > obj->u.a.length) + 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); + js_Property *ref; + int k; + int own; + + if (obj->type == JS_CARRAY) { + if (!strcmp(name, "length")) { + double rawlen = jsV_tonumber(J, value); + 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) { + obj->u.a.length = newlen; + if (newlen <= obj->u.a.flat_length) + obj->u.a.flat_length = newlen; + } else { + jsV_resizearray(J, obj, newlen); + } + return; + } + + if (js_isarrayindex(J, name, &k)) { + if (obj->u.a.simple) { + if (k >= 0 && k <= obj->u.a.flat_length) { + jsR_setarrayindex(J, obj, k, value); + } else { + jsR_unflattenarray(J, obj); + if (obj->u.a.length < k + 1) + obj->u.a.length = k + 1; + } + } else { + if (obj->u.a.length < k + 1) + obj->u.a.length = k + 1; + } + } + } + + else if (obj->type == JS_CSTRING) { + if (!strcmp(name, "length")) + goto readonly; + if (js_isarrayindex(J, name, &k)) + if (k >= 0 && k < obj->u.s.length) + goto readonly; + } + + else if (obj->type == JS_CREGEXP) { + if (!strcmp(name, "source")) goto readonly; + if (!strcmp(name, "global")) goto readonly; + if (!strcmp(name, "ignoreCase")) goto readonly; + if (!strcmp(name, "multiline")) goto readonly; + if (!strcmp(name, "lastIndex")) { + obj->u.r.last = jsV_tointeger(J, value); + return; + } + } + + else if (obj->type == JS_CUSERDATA) { + if (obj->u.user.put && obj->u.user.put(J, obj->u.user.data, name)) + return; + } + + /* First try to find a setter in prototype chain */ + ref = jsV_getpropertyx(J, obj, name, &own); + if (ref) { + if (ref->setter) { + js_pushobject(J, ref->setter); + js_pushobject(J, obj); + js_pushvalue(J, *value); + js_call(J, 1); + js_pop(J, 1); + return; + } else { + if (J->strict) + if (ref->getter) + js_typeerror(J, "setting property '%s' that only has a getter", name); + if (ref->atts & JS_READONLY) + goto readonly; + } + } + + /* Property not found on this object, so create one */ + if (!ref || !own) { + if (transient) { + if (J->strict) + js_typeerror(J, "cannot create property '%s' on transient object", name); + return; + } + ref = jsV_setproperty(J, obj, name); + } + + if (ref) { + if (!(ref->atts & JS_READONLY)) + ref->value = *value; + else + goto readonly; + } + + return; + +readonly: + if (J->strict) + 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.flat_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) +{ + js_Property *ref; + int k; + + 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) { + if (!strcmp(name, "length")) + goto readonly; + if (js_isarrayindex(J, name, &k)) + if (k >= 0 && k < obj->u.s.length) + goto readonly; + } + + else if (obj->type == JS_CREGEXP) { + if (!strcmp(name, "source")) goto readonly; + if (!strcmp(name, "global")) goto readonly; + if (!strcmp(name, "ignoreCase")) goto readonly; + if (!strcmp(name, "multiline")) goto readonly; + if (!strcmp(name, "lastIndex")) goto readonly; + } + + else if (obj->type == JS_CUSERDATA) { + if (obj->u.user.put && obj->u.user.put(J, obj->u.user.data, name)) + return; + } + + ref = jsV_setproperty(J, obj, name); + if (ref) { + if (value) { + if (!(ref->atts & JS_READONLY)) + ref->value = *value; + else if (J->strict) + js_typeerror(J, "'%s' is read-only", name); + } + if (getter) { + if (!(ref->atts & JS_DONTCONF)) + ref->getter = getter; + else if (J->strict) + js_typeerror(J, "'%s' is non-configurable", name); + } + if (setter) { + if (!(ref->atts & JS_DONTCONF)) + ref->setter = setter; + else if (J->strict) + js_typeerror(J, "'%s' is non-configurable", name); + } + ref->atts |= atts; + } + + return; + +readonly: + if (J->strict || throw) + js_typeerror(J, "'%s' is read-only or non-configurable", name); +} + +static int jsR_delproperty(js_State *J, js_Object *obj, const char *name) +{ + js_Property *ref; + int k; + + 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) { + if (!strcmp(name, "length")) + goto dontconf; + if (js_isarrayindex(J, name, &k)) + if (k >= 0 && k < obj->u.s.length) + goto dontconf; + } + + else if (obj->type == JS_CREGEXP) { + if (!strcmp(name, "source")) goto dontconf; + if (!strcmp(name, "global")) goto dontconf; + if (!strcmp(name, "ignoreCase")) goto dontconf; + if (!strcmp(name, "multiline")) goto dontconf; + if (!strcmp(name, "lastIndex")) goto dontconf; + } + + else if (obj->type == JS_CUSERDATA) { + if (obj->u.user.delete && obj->u.user.delete(J, obj->u.user.data, name)) + return 1; + } + + ref = jsV_getownproperty(J, obj, name); + if (ref) { + if (ref->atts & JS_DONTCONF) + goto dontconf; + jsV_delproperty(J, obj, name); + } + return 1; + +dontconf: + if (J->strict) + js_typeerror(J, "'%s' is non-configurable", name); + return 0; +} + +static void jsR_delindex(js_State *J, js_Object *obj, int k) +{ + char buf[32]; + /* Allow deleting last element of a simple array without unflattening */ + if (obj->type == JS_CARRAY && obj->u.a.simple && k == obj->u.a.flat_length - 1) + obj->u.a.flat_length = k; + else + jsR_delproperty(J, obj, js_itoa(buf, k)); +} + +/* Registry, global and object property accessors */ + +const char *js_ref(js_State *J) +{ + js_Value *v = stackidx(J, -1); + const char *s; + char buf[32]; + switch (v->t.type) { + case JS_TUNDEFINED: s = "_Undefined"; break; + case JS_TNULL: s = "_Null"; break; + case JS_TBOOLEAN: + s = v->u.boolean ? "_True" : "_False"; + break; + case JS_TOBJECT: + sprintf(buf, "%p", (void*)v->u.object); + s = js_intern(J, buf); + break; + default: + sprintf(buf, "%d", J->nextref++); + s = js_intern(J, buf); + break; + } + js_setregistry(J, s); + return s; +} + +void js_unref(js_State *J, const char *ref) +{ + js_delregistry(J, ref); +} + +void js_getregistry(js_State *J, const char *name) +{ + jsR_getproperty(J, J->R, name); +} + +void js_setregistry(js_State *J, const char *name) +{ + jsR_setproperty(J, J->R, name, 0); + js_pop(J, 1); +} + +void js_delregistry(js_State *J, const char *name) +{ + jsR_delproperty(J, J->R, name); +} + +void js_getglobal(js_State *J, const char *name) +{ + jsR_getproperty(J, J->G, name); +} + +void js_setglobal(js_State *J, const char *name) +{ + jsR_setproperty(J, J->G, name, 0); + js_pop(J, 1); +} + +void js_defglobal(js_State *J, const char *name, int atts) +{ + jsR_defproperty(J, J->G, name, atts, stackidx(J, -1), NULL, NULL, 0); + js_pop(J, 1); +} + +void js_delglobal(js_State *J, const char *name) +{ + jsR_delproperty(J, J->G, name); +} + +void js_getproperty(js_State *J, int idx, const char *name) +{ + jsR_getproperty(J, js_toobject(J, idx), name); +} + +void js_setproperty(js_State *J, int idx, const char *name) +{ + jsR_setproperty(J, js_toobject(J, idx), name, !js_isobject(J, idx)); + js_pop(J, 1); +} + +void js_defproperty(js_State *J, int idx, const char *name, int atts) +{ + jsR_defproperty(J, js_toobject(J, idx), name, atts, stackidx(J, -1), NULL, NULL, 1); + js_pop(J, 1); +} + +void js_delproperty(js_State *J, int idx, const char *name) +{ + jsR_delproperty(J, js_toobject(J, idx), name); +} + +void js_defaccessor(js_State *J, int idx, const char *name, int atts) +{ + jsR_defproperty(J, js_toobject(J, idx), name, atts, NULL, jsR_tofunction(J, -2), jsR_tofunction(J, -1), 1); + js_pop(J, 2); +} + +int js_hasproperty(js_State *J, int idx, const char *name) +{ + 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) +{ + jsR_delindex(J, js_toobject(J, idx), i); +} + +/* Iterator */ + +void js_pushiterator(js_State *J, int idx, int own) +{ + js_pushobject(J, jsV_newiterator(J, js_toobject(J, idx), own)); +} + +const char *js_nextiterator(js_State *J, int idx) +{ + return jsV_nextiterator(J, js_toobject(J, idx)); +} + +/* Environment records */ + +js_Environment *jsR_newenvironment(js_State *J, js_Object *vars, js_Environment *outer) +{ + js_Environment *E = js_malloc(J, sizeof *E); + E->gcmark = 0; + E->gcnext = J->gcenv; + J->gcenv = E; + ++J->gccounter; + + E->outer = outer; + E->variables = vars; + return E; +} + +static void js_initvar(js_State *J, const char *name, int idx) +{ + jsR_defproperty(J, J->E->variables, name, JS_DONTENUM | JS_DONTCONF, stackidx(J, idx), NULL, NULL, 0); +} + +static int js_hasvar(js_State *J, const char *name) +{ + js_Environment *E = J->E; + do { + js_Property *ref = jsV_getproperty(J, E->variables, name); + if (ref) { + if (ref->getter) { + js_pushobject(J, ref->getter); + js_pushobject(J, E->variables); + js_call(J, 0); + } else { + js_pushvalue(J, ref->value); + } + return 1; + } + E = E->outer; + } while (E); + return 0; +} + +static void js_setvar(js_State *J, const char *name) +{ + js_Environment *E = J->E; + do { + js_Property *ref = jsV_getproperty(J, E->variables, name); + if (ref) { + if (ref->setter) { + js_pushobject(J, ref->setter); + js_pushobject(J, E->variables); + js_copy(J, -3); + js_call(J, 1); + js_pop(J, 1); + return; + } + if (!(ref->atts & JS_READONLY)) + ref->value = *stackidx(J, -1); + else if (J->strict) + js_typeerror(J, "'%s' is read-only", name); + return; + } + E = E->outer; + } while (E); + if (J->strict) + js_referenceerror(J, "assignment to undeclared variable '%s'", name); + jsR_setproperty(J, J->G, name, 0); +} + +static int js_delvar(js_State *J, const char *name) +{ + js_Environment *E = J->E; + do { + js_Property *ref = jsV_getownproperty(J, E->variables, name); + if (ref) { + if (ref->atts & JS_DONTCONF) { + if (J->strict) + js_typeerror(J, "'%s' is non-configurable", name); + return 0; + } + jsV_delproperty(J, E->variables, name); + return 1; + } + E = E->outer; + } while (E); + return jsR_delproperty(J, J->G, name); +} + +/* Function calls */ + +static void jsR_savescope(js_State *J, js_Environment *newE) +{ + if (J->envtop + 1 >= JS_ENVLIMIT) + js_stackoverflow(J); + J->envstack[J->envtop++] = J->E; + J->E = newE; +} + +static void jsR_restorescope(js_State *J) +{ + J->E = J->envstack[--J->envtop]; +} + +static void jsR_calllwfunction(js_State *J, int n, js_Function *F, js_Environment *scope) +{ + js_Value v; + int i; + + jsR_savescope(J, scope); + + if (n > F->numparams) { + js_pop(J, n - F->numparams); + n = F->numparams; + } + + for (i = n; i < F->varlen; ++i) + js_pushundefined(J); + + jsR_run(J, F); + v = *stackidx(J, -1); + TOP = --BOT; /* clear stack */ + js_pushvalue(J, v); + + jsR_restorescope(J); +} + +static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment *scope) +{ + js_Value v; + int i; + + scope = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope); + + jsR_savescope(J, scope); + + if (F->arguments) { + js_newarguments(J); + if (!J->strict) { + js_currentfunction(J); + js_defproperty(J, -2, "callee", JS_DONTENUM); + } + js_pushnumber(J, n); + js_defproperty(J, -2, "length", JS_DONTENUM); + for (i = 0; i < n; ++i) { + js_copy(J, i + 1); + js_setindex(J, -2, i); + } + js_initvar(J, "arguments", -1); + js_pop(J, 1); + } + + for (i = 0; i < n && i < F->numparams; ++i) + js_initvar(J, F->vartab[i], i + 1); + js_pop(J, n); + + for (; i < F->varlen; ++i) { + js_pushundefined(J); + js_initvar(J, F->vartab[i], -1); + js_pop(J, 1); + } + + jsR_run(J, F); + v = *stackidx(J, -1); + TOP = --BOT; /* clear stack */ + js_pushvalue(J, v); + + jsR_restorescope(J); +} + +static void jsR_callscript(js_State *J, int n, js_Function *F, js_Environment *scope) +{ + js_Value v; + int i; + + if (scope) + jsR_savescope(J, scope); + + /* scripts take no arguments */ + js_pop(J, n); + + for (i = 0; i < F->varlen; ++i) { + /* Bug 701886: don't redefine existing vars in eval/scripts */ + if (!js_hasvar(J, F->vartab[i])) { + js_pushundefined(J); + js_initvar(J, F->vartab[i], -1); + js_pop(J, 1); + } + } + + jsR_run(J, F); + v = *stackidx(J, -1); + TOP = --BOT; /* clear stack */ + js_pushvalue(J, v); + + if (scope) + jsR_restorescope(J); +} + +static void jsR_callcfunction(js_State *J, int n, int min, js_CFunction F) +{ + int save_top; + int i; + js_Value v; + + for (i = n; i < min; ++i) + js_pushundefined(J); + + save_top = TOP; + F(J); + if (TOP > save_top) { + v = *stackidx(J, -1); + TOP = --BOT; /* clear stack */ + js_pushvalue(J, v); + } else { + TOP = --BOT; /* clear stack */ + js_pushundefined(J); + } +} + +static void jsR_pushtrace(js_State *J, const char *name, const char *file, int line) +{ + if (J->tracetop + 1 == JS_ENVLIMIT) + js_error(J, "call stack overflow"); + ++J->tracetop; + J->trace[J->tracetop].name = name; + J->trace[J->tracetop].file = file; + J->trace[J->tracetop].line = line; +} + +void js_call(js_State *J, int n) +{ + js_Object *obj; + int savebot; + + if (n < 0) + js_rangeerror(J, "number of arguments cannot be negative"); + + if (!js_iscallable(J, -n-2)) + js_typeerror(J, "%s is not callable", js_typeof(J, -n-2)); + + obj = js_toobject(J, -n-2); + + savebot = BOT; + BOT = TOP - n - 1; + + if (obj->type == JS_CFUNCTION) { + jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line); + if (obj->u.f.function->lightweight) + jsR_calllwfunction(J, n, obj->u.f.function, obj->u.f.scope); + else + jsR_callfunction(J, n, obj->u.f.function, obj->u.f.scope); + --J->tracetop; + } else if (obj->type == JS_CSCRIPT) { + jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line); + jsR_callscript(J, n, obj->u.f.function, obj->u.f.scope); + --J->tracetop; + } else if (obj->type == JS_CCFUNCTION) { + jsR_pushtrace(J, obj->u.c.name, "native", 0); + jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.function); + --J->tracetop; + } + + BOT = savebot; +} + +void js_construct(js_State *J, int n) +{ + js_Object *obj; + js_Object *prototype; + js_Object *newobj; + + if (!js_iscallable(J, -n-1)) + js_typeerror(J, "%s is not callable", js_typeof(J, -n-1)); + + obj = js_toobject(J, -n-1); + + /* built-in constructors create their own objects, give them a 'null' this */ + if (obj->type == JS_CCFUNCTION && obj->u.c.constructor) { + int savebot = BOT; + js_pushnull(J); + if (n > 0) + js_rot(J, n + 1); + BOT = TOP - n - 1; + + jsR_pushtrace(J, obj->u.c.name, "native", 0); + jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.constructor); + --J->tracetop; + + BOT = savebot; + return; + } + + /* extract the function object's prototype property */ + js_getproperty(J, -n - 1, "prototype"); + if (js_isobject(J, -1)) + prototype = js_toobject(J, -1); + else + prototype = J->Object_prototype; + js_pop(J, 1); + + /* create a new object with above prototype, and shift it into the 'this' slot */ + newobj = jsV_newobject(J, JS_COBJECT, prototype); + js_pushobject(J, newobj); + if (n > 0) + js_rot(J, n + 1); + + /* and save a copy to return */ + js_pushobject(J, newobj); + js_rot(J, n + 3); + + /* call the function */ + js_call(J, n); + + /* if result is not an object, return the original object we created */ + if (!js_isobject(J, -1)) { + js_pop(J, 1); + } else { + js_rot2pop1(J); + } +} + +void js_eval(js_State *J) +{ + if (!js_isstring(J, -1)) + return; + js_loadeval(J, "(eval)", js_tostring(J, -1)); + js_rot2pop1(J); + js_copy(J, 0); /* copy 'this' */ + js_call(J, 0); +} + +int js_pconstruct(js_State *J, int n) +{ + int savetop = TOP - n - 2; + if (js_try(J)) { + /* clean up the stack to only hold the error object */ + STACK[savetop] = STACK[TOP-1]; + TOP = savetop + 1; + return 1; + } + js_construct(J, n); + js_endtry(J); + return 0; +} + +int js_pcall(js_State *J, int n) +{ + int savetop = TOP - n - 2; + if (js_try(J)) { + /* clean up the stack to only hold the error object */ + STACK[savetop] = STACK[TOP-1]; + TOP = savetop + 1; + return 1; + } + js_call(J, n); + js_endtry(J); + return 0; +} + +/* Exceptions */ + +void *js_savetrypc(js_State *J, js_Instruction *pc) +{ + if (J->trytop == JS_TRYLIMIT) + js_trystackoverflow(J); + J->trybuf[J->trytop].E = J->E; + J->trybuf[J->trytop].envtop = J->envtop; + J->trybuf[J->trytop].tracetop = J->tracetop; + J->trybuf[J->trytop].top = J->top; + J->trybuf[J->trytop].bot = J->bot; + J->trybuf[J->trytop].strict = J->strict; + J->trybuf[J->trytop].pc = pc; + return J->trybuf[J->trytop++].buf; +} + +void *js_savetry(js_State *J) +{ + if (J->trytop == JS_TRYLIMIT) + js_trystackoverflow(J); + J->trybuf[J->trytop].E = J->E; + J->trybuf[J->trytop].envtop = J->envtop; + J->trybuf[J->trytop].tracetop = J->tracetop; + J->trybuf[J->trytop].top = J->top; + J->trybuf[J->trytop].bot = J->bot; + J->trybuf[J->trytop].strict = J->strict; + J->trybuf[J->trytop].pc = NULL; + return J->trybuf[J->trytop++].buf; +} + +void js_endtry(js_State *J) +{ + if (J->trytop == 0) + js_error(J, "endtry: exception stack underflow"); + --J->trytop; +} + +void js_throw(js_State *J) +{ + if (J->trytop > 0) { + js_Value v = *stackidx(J, -1); + --J->trytop; + J->E = J->trybuf[J->trytop].E; + J->envtop = J->trybuf[J->trytop].envtop; + J->tracetop = J->trybuf[J->trytop].tracetop; + J->top = J->trybuf[J->trytop].top; + J->bot = J->trybuf[J->trytop].bot; + J->strict = J->trybuf[J->trytop].strict; + js_pushvalue(J, v); + longjmp(J->trybuf[J->trytop].buf, 1); + } + if (J->panic) + J->panic(J); + abort(); +} + +/* Main interpreter loop */ + +static void js_dumpvalue(js_State *J, js_Value v) +{ + switch (v.t.type) { + case JS_TUNDEFINED: printf("undefined"); break; + case JS_TNULL: printf("null"); break; + case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break; + case JS_TNUMBER: printf("%.9g", v.u.number); break; + case JS_TSHRSTR: printf("'%s'", v.u.shrstr); break; + case JS_TLITSTR: printf("'%s'", v.u.litstr); break; + case JS_TMEMSTR: printf("'%s'", v.u.memstr->p); break; + case JS_TOBJECT: + if (v.u.object == J->G) { + printf("[Global]"); + break; + } + switch (v.u.object->type) { + case JS_COBJECT: printf("[Object %p]", (void*)v.u.object); break; + case JS_CARRAY: printf("[Array %p]", (void*)v.u.object); break; + case JS_CFUNCTION: + printf("[Function %p, %s, %s:%d]", + (void*)v.u.object, + v.u.object->u.f.function->name, + v.u.object->u.f.function->filename, + v.u.object->u.f.function->line); + break; + case JS_CSCRIPT: printf("[Script %s]", v.u.object->u.f.function->filename); break; + case JS_CCFUNCTION: printf("[CFunction %s]", v.u.object->u.c.name); break; + case JS_CBOOLEAN: printf("[Boolean %d]", v.u.object->u.boolean); break; + case JS_CNUMBER: printf("[Number %g]", v.u.object->u.number); break; + case JS_CSTRING: printf("[String'%s']", v.u.object->u.s.string); break; + case JS_CERROR: printf("[Error]"); break; + case JS_CARGUMENTS: printf("[Arguments %p]", (void*)v.u.object); break; + case JS_CITERATOR: printf("[Iterator %p]", (void*)v.u.object); break; + case JS_CUSERDATA: + printf("[Userdata %s %p]", v.u.object->u.user.tag, v.u.object->u.user.data); + break; + default: printf("[Object %p]", (void*)v.u.object); break; + } + break; + } +} + +static void js_stacktrace(js_State *J) +{ + int n; + printf("stack trace:\n"); + for (n = J->tracetop; n >= 0; --n) { + const char *name = J->trace[n].name; + const char *file = J->trace[n].file; + int line = J->trace[n].line; + if (line > 0) { + if (name[0]) + printf("\tat %s (%s:%d)\n", name, file, line); + else + printf("\tat %s:%d\n", file, line); + } else + printf("\tat %s (%s)\n", name, file); + } +} + +static void js_dumpstack(js_State *J) +{ + int i; + printf("stack {\n"); + for (i = 0; i < TOP; ++i) { + putchar(i == BOT ? '>' : ' '); + printf("%4d: ", i); + js_dumpvalue(J, STACK[i]); + putchar('\n'); + } + printf("}\n"); +} + +void js_trap(js_State *J, int pc) +{ + js_dumpstack(J); + js_stacktrace(J); +} + +static int jsR_isindex(js_State *J, int idx, int *k) +{ + js_Value *v = stackidx(J, idx); + if (v->t.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; + const char **VT = F->vartab-1; + int lightweight = F->lightweight; + js_Instruction *pcstart = F->code; + js_Instruction *pc = F->code; + enum js_OpCode opcode; + int offset; + int savestrict; + + const char *str; + js_Object *obj; + double x, y; + unsigned int ux, uy; + int ix, iy, okay; + int b; + int transient; + + savestrict = J->strict; + J->strict = F->strict; + +#define READSTRING() \ + memcpy(&str, pc, sizeof(str)); \ + pc += sizeof(str) / sizeof(*pc) + + while (1) { + if (J->gccounter > J->gcthresh) + js_gc(J, 0); + + J->trace[J->tracetop].line = *pc++; + + opcode = *pc++; + + switch (opcode) { + case OP_POP: js_pop(J, 1); break; + case OP_DUP: js_dup(J); break; + case OP_DUP2: js_dup2(J); break; + case OP_ROT2: js_rot2(J); break; + case OP_ROT3: js_rot3(J); break; + case OP_ROT4: js_rot4(J); break; + + case OP_INTEGER: + js_pushnumber(J, *pc++ - 32768); + break; + + case OP_NUMBER: + memcpy(&x, pc, sizeof(x)); + pc += sizeof(x) / sizeof(*pc); + js_pushnumber(J, x); + break; + + case OP_STRING: + READSTRING(); + js_pushliteral(J, str); + break; + + case OP_CLOSURE: js_newfunction(J, FT[*pc++], J->E); break; + case OP_NEWOBJECT: js_newobject(J); break; + case OP_NEWARRAY: js_newarray(J); break; + case OP_NEWREGEXP: + READSTRING(); + js_newregexp(J, str, *pc++); + break; + + case OP_UNDEF: js_pushundefined(J); break; + case OP_NULL: js_pushnull(J); break; + case OP_TRUE: js_pushboolean(J, 1); break; + case OP_FALSE: js_pushboolean(J, 0); break; + + case OP_THIS: + if (J->strict) { + js_copy(J, 0); + } else { + if (js_iscoercible(J, 0)) + js_copy(J, 0); + else + js_pushglobal(J); + } + break; + + case OP_CURRENT: + js_currentfunction(J); + break; + + case OP_GETLOCAL: + if (lightweight) { + CHECKSTACK(1); + STACK[TOP++] = STACK[BOT + *pc++]; + } else { + str = VT[*pc++]; + if (!js_hasvar(J, str)) + js_referenceerror(J, "'%s' is not defined", str); + } + break; + + case OP_SETLOCAL: + if (lightweight) { + STACK[BOT + *pc++] = STACK[TOP-1]; + } else { + js_setvar(J, VT[*pc++]); + } + break; + + case OP_DELLOCAL: + if (lightweight) { + ++pc; + js_pushboolean(J, 0); + } else { + b = js_delvar(J, VT[*pc++]); + js_pushboolean(J, b); + } + break; + + case OP_GETVAR: + READSTRING(); + if (!js_hasvar(J, str)) + js_referenceerror(J, "'%s' is not defined", str); + break; + + case OP_HASVAR: + READSTRING(); + if (!js_hasvar(J, str)) + js_pushundefined(J); + break; + + case OP_SETVAR: + READSTRING(); + js_setvar(J, str); + break; + + case OP_DELVAR: + READSTRING(); + b = js_delvar(J, str); + js_pushboolean(J, b); + break; + + case OP_IN: + str = js_tostring(J, -2); + if (!js_isobject(J, -1)) + js_typeerror(J, "operand to 'in' is not an object"); + b = js_hasproperty(J, -1, str); + js_pop(J, 2 + b); + js_pushboolean(J, b); + break; + + case OP_SKIPARRAY: + js_setlength(J, -1, js_getlength(J, -1) + 1); + break; + case OP_INITARRAY: + js_setindex(J, -2, js_getlength(J, -2)); + break; + + case OP_INITPROP: + obj = js_toobject(J, -3); + str = js_tostring(J, -2); + jsR_setproperty(J, obj, str, 0); + js_pop(J, 2); + break; + + case OP_INITGETTER: + obj = js_toobject(J, -3); + str = js_tostring(J, -2); + jsR_defproperty(J, obj, str, 0, NULL, jsR_tofunction(J, -1), NULL, 0); + js_pop(J, 2); + break; + + case OP_INITSETTER: + obj = js_toobject(J, -3); + str = js_tostring(J, -2); + jsR_defproperty(J, obj, str, 0, NULL, NULL, jsR_tofunction(J, -1), 0); + js_pop(J, 2); + break; + + case OP_GETPROP: + 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; + + case OP_GETPROP_S: + READSTRING(); + obj = js_toobject(J, -1); + jsR_getproperty(J, obj, str); + js_rot2pop1(J); + break; + + case OP_SETPROP: + 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; + + case OP_SETPROP_S: + READSTRING(); + obj = js_toobject(J, -2); + transient = !js_isobject(J, -2); + jsR_setproperty(J, obj, str, transient); + js_rot2pop1(J); + break; + + case OP_DELPROP: + str = js_tostring(J, -1); + obj = js_toobject(J, -2); + b = jsR_delproperty(J, obj, str); + js_pop(J, 2); + js_pushboolean(J, b); + break; + + case OP_DELPROP_S: + READSTRING(); + obj = js_toobject(J, -1); + b = jsR_delproperty(J, obj, str); + js_pop(J, 1); + js_pushboolean(J, b); + break; + + case OP_ITERATOR: + if (js_iscoercible(J, -1)) { + obj = jsV_newiterator(J, js_toobject(J, -1), 0); + js_pop(J, 1); + js_pushobject(J, obj); + } + break; + + case OP_NEXTITER: + if (js_isobject(J, -1)) { + obj = js_toobject(J, -1); + str = jsV_nextiterator(J, obj); + if (str) { + js_pushstring(J, str); + js_pushboolean(J, 1); + } else { + js_pop(J, 1); + js_pushboolean(J, 0); + } + } else { + js_pop(J, 1); + js_pushboolean(J, 0); + } + break; + + /* Function calls */ + + case OP_EVAL: + js_eval(J); + break; + + case OP_CALL: + js_call(J, *pc++); + break; + + case OP_NEW: + js_construct(J, *pc++); + break; + + /* Unary operators */ + + case OP_TYPEOF: + str = js_typeof(J, -1); + js_pop(J, 1); + js_pushliteral(J, str); + break; + + case OP_POS: + x = js_tonumber(J, -1); + js_pop(J, 1); + js_pushnumber(J, x); + break; + + case OP_NEG: + x = js_tonumber(J, -1); + js_pop(J, 1); + js_pushnumber(J, -x); + break; + + case OP_BITNOT: + ix = js_toint32(J, -1); + js_pop(J, 1); + js_pushnumber(J, ~ix); + break; + + case OP_LOGNOT: + b = js_toboolean(J, -1); + js_pop(J, 1); + js_pushboolean(J, !b); + break; + + case OP_INC: + x = js_tonumber(J, -1); + js_pop(J, 1); + js_pushnumber(J, x + 1); + break; + + case OP_DEC: + x = js_tonumber(J, -1); + js_pop(J, 1); + js_pushnumber(J, x - 1); + break; + + case OP_POSTINC: + x = js_tonumber(J, -1); + js_pop(J, 1); + js_pushnumber(J, x + 1); + js_pushnumber(J, x); + break; + + case OP_POSTDEC: + x = js_tonumber(J, -1); + js_pop(J, 1); + js_pushnumber(J, x - 1); + js_pushnumber(J, x); + break; + + /* Multiplicative operators */ + + case OP_MUL: + x = js_tonumber(J, -2); + y = js_tonumber(J, -1); + js_pop(J, 2); + js_pushnumber(J, x * y); + break; + + case OP_DIV: + x = js_tonumber(J, -2); + y = js_tonumber(J, -1); + js_pop(J, 2); + js_pushnumber(J, x / y); + break; + + case OP_MOD: + x = js_tonumber(J, -2); + y = js_tonumber(J, -1); + js_pop(J, 2); + js_pushnumber(J, fmod(x, y)); + break; + + /* Additive operators */ + + case OP_ADD: + js_concat(J); + break; + + case OP_SUB: + x = js_tonumber(J, -2); + y = js_tonumber(J, -1); + js_pop(J, 2); + js_pushnumber(J, x - y); + break; + + /* Shift operators */ + + case OP_SHL: + ix = js_toint32(J, -2); + uy = js_touint32(J, -1); + js_pop(J, 2); + js_pushnumber(J, ix << (uy & 0x1F)); + break; + + case OP_SHR: + ix = js_toint32(J, -2); + uy = js_touint32(J, -1); + js_pop(J, 2); + js_pushnumber(J, ix >> (uy & 0x1F)); + break; + + case OP_USHR: + ux = js_touint32(J, -2); + uy = js_touint32(J, -1); + js_pop(J, 2); + js_pushnumber(J, ux >> (uy & 0x1F)); + break; + + /* Relational operators */ + + case OP_LT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b < 0); break; + case OP_GT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b > 0); break; + case OP_LE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b <= 0); break; + case OP_GE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b >= 0); break; + + case OP_INSTANCEOF: + b = js_instanceof(J); + js_pop(J, 2); + js_pushboolean(J, b); + break; + + /* Equality */ + + case OP_EQ: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, b); break; + case OP_NE: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, !b); break; + case OP_STRICTEQ: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, b); break; + case OP_STRICTNE: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, !b); break; + + case OP_JCASE: + offset = *pc++; + b = js_strictequal(J); + if (b) { + js_pop(J, 2); + pc = pcstart + offset; + } else { + js_pop(J, 1); + } + break; + + /* Binary bitwise operators */ + + case OP_BITAND: + ix = js_toint32(J, -2); + iy = js_toint32(J, -1); + js_pop(J, 2); + js_pushnumber(J, ix & iy); + break; + + case OP_BITXOR: + ix = js_toint32(J, -2); + iy = js_toint32(J, -1); + js_pop(J, 2); + js_pushnumber(J, ix ^ iy); + break; + + case OP_BITOR: + ix = js_toint32(J, -2); + iy = js_toint32(J, -1); + js_pop(J, 2); + js_pushnumber(J, ix | iy); + break; + + /* Try and Catch */ + + case OP_THROW: + js_throw(J); + + case OP_TRY: + offset = *pc++; + if (js_trypc(J, pc)) { + pc = J->trybuf[J->trytop].pc; + } else { + pc = pcstart + offset; + } + break; + + case OP_ENDTRY: + js_endtry(J); + break; + + case OP_CATCH: + READSTRING(); + obj = jsV_newobject(J, JS_COBJECT, NULL); + js_pushobject(J, obj); + js_rot2(J); + js_setproperty(J, -2, str); + J->E = jsR_newenvironment(J, obj, J->E); + js_pop(J, 1); + break; + + case OP_ENDCATCH: + J->E = J->E->outer; + break; + + /* With */ + + case OP_WITH: + obj = js_toobject(J, -1); + J->E = jsR_newenvironment(J, obj, J->E); + js_pop(J, 1); + break; + + case OP_ENDWITH: + J->E = J->E->outer; + break; + + /* Branching */ + + case OP_DEBUGGER: + js_trap(J, (int)(pc - pcstart) - 1); + break; + + case OP_JUMP: + pc = pcstart + *pc; + break; + + case OP_JTRUE: + offset = *pc++; + b = js_toboolean(J, -1); + js_pop(J, 1); + if (b) + pc = pcstart + offset; + break; + + case OP_JFALSE: + offset = *pc++; + b = js_toboolean(J, -1); + js_pop(J, 1); + if (!b) + pc = pcstart + offset; + break; + + case OP_RETURN: + J->strict = savestrict; + return; + } + } +}
